00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "editorwatcher.h"
00020
00021 #include <config.h>
00022
00023 #include <kdebug.h>
00024 #include <klocale.h>
00025 #include <kmessagebox.h>
00026 #include <kopenwith.h>
00027 #include <kprocess.h>
00028 #include <kuserprofile.h>
00029
00030 #include <qsocketnotifier.h>
00031
00032 #include <cassert>
00033
00034
00035 #ifdef HAVE_INOTIFY
00036 #include <sys/ioctl.h>
00037 #include <unistd.h>
00038 #include <fcntl.h>
00039 #include <sys/syscall.h>
00040 #include <linux/types.h>
00041
00042 #define _S390_BITOPS_H
00043 #include <linux/inotify.h>
00044
00045 static inline int inotify_init (void)
00046 {
00047 return syscall (__NR_inotify_init);
00048 }
00049
00050 static inline int inotify_add_watch (int fd, const char *name, __u32 mask)
00051 {
00052 return syscall (__NR_inotify_add_watch, fd, name, mask);
00053 }
00054
00055 static inline int inotify_rm_watch (int fd, __u32 wd)
00056 {
00057 return syscall (__NR_inotify_rm_watch, fd, wd);
00058 }
00059 #endif
00060
00061 using namespace KMail;
00062
00063 EditorWatcher::EditorWatcher(const KURL & url, const QString &mimeType, bool openWith, QObject * parent) :
00064 QObject( parent ),
00065 mUrl( url ),
00066 mMimeType( mimeType ),
00067 mOpenWith( openWith ),
00068 mEditor( 0 ),
00069 mHaveInotify( false ),
00070 mFileOpen( false ),
00071 mEditorRunning( false ),
00072 mFileModified( true )
00073 {
00074 assert( mUrl.isLocalFile() );
00075 connect( &mTimer, SIGNAL(timeout()), SLOT(checkEditDone()) );
00076 }
00077
00078 bool EditorWatcher::start()
00079 {
00080
00081 KURL::List list;
00082 list.append( mUrl );
00083 KService::Ptr offer = KServiceTypeProfile::preferredService( mMimeType, "Application" );
00084 if ( mOpenWith || !offer ) {
00085 KOpenWithDlg dlg( list, i18n("Edit with:"), QString::null, 0 );
00086 if ( !dlg.exec() )
00087 return false;
00088 offer = dlg.service();
00089 if ( !offer )
00090 return false;
00091 }
00092
00093 #ifdef HAVE_INOTIFY
00094
00095 mInotifyFd = inotify_init();
00096 if ( mInotifyFd > 0 ) {
00097 mInotifyWatch = inotify_add_watch( mInotifyFd, mUrl.path().latin1(), IN_CLOSE | IN_OPEN | IN_MODIFY );
00098 if ( mInotifyWatch >= 0 ) {
00099 QSocketNotifier *sn = new QSocketNotifier( mInotifyFd, QSocketNotifier::Read, this );
00100 connect( sn, SIGNAL(activated(int)), SLOT(inotifyEvent()) );
00101 mHaveInotify = true;
00102 mFileModified = false;
00103 }
00104 } else {
00105 kdWarning(5006) << k_funcinfo << "Failed to activate INOTIFY!" << endl;
00106 }
00107 #endif
00108
00109
00110 QStringList params = KRun::processDesktopExec( *offer, list, false );
00111 mEditor = new KProcess( this );
00112 *mEditor << params;
00113 connect( mEditor, SIGNAL(processExited(KProcess*)), SLOT(editorExited()) );
00114 if ( !mEditor->start() )
00115 return false;
00116 mEditorRunning = true;
00117
00118 mEditTime.start();
00119 return true;
00120 }
00121
00122 void EditorWatcher::inotifyEvent()
00123 {
00124 assert( mHaveInotify );
00125 #ifdef HAVE_INOTIFY
00126 int pending = -1;
00127 char buffer[4096];
00128 ioctl( mInotifyFd, FIONREAD, &pending );
00129 while ( pending > 0 ) {
00130 int size = read( mInotifyFd, buffer, QMIN( pending, (int)sizeof(buffer) ) );
00131 pending -= size;
00132 if ( size < 0 )
00133 break;
00134 int offset = 0;
00135 while ( size > 0 ) {
00136 struct inotify_event *event = (struct inotify_event *) &buffer[offset];
00137 size -= sizeof( struct inotify_event ) + event->len;
00138 offset += sizeof( struct inotify_event ) + event->len;
00139 if ( event->mask & IN_OPEN )
00140 mFileOpen = true;
00141 if ( event->mask & IN_CLOSE )
00142 mFileOpen = false;
00143 if ( event->mask & IN_MODIFY )
00144 mFileModified = true;
00145 }
00146 }
00147 #endif
00148 mTimer.start( 500, true );
00149
00150 }
00151
00152 void EditorWatcher::editorExited()
00153 {
00154 mEditorRunning = false;
00155 mTimer.start( 500, true );
00156 }
00157
00158 void EditorWatcher::checkEditDone()
00159 {
00160 if ( mEditorRunning || (mFileOpen && mHaveInotify) )
00161 return;
00162
00163
00164 if ( mEditTime.elapsed() <= 3000 ) {
00165 KMessageBox::error( 0, i18n("KMail is unable to detect when the choosen editor is closed. "
00166 "To avoid data loss, editing the attachment will be aborted."), i18n("Unable to edit attachment") );
00167 }
00168
00169 emit editDone( this );
00170 deleteLater();
00171 }
00172
00173
00174
00175 #include "editorwatcher.moc"