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_SYS_INOTIFY
00036 #include <sys/ioctl.h>
00037 #include <sys/inotify.h>
00038 #include <fcntl.h>
00039 #elif HAVE_INOTIFY
00040 #include <sys/ioctl.h>
00041 #include <unistd.h>
00042 #include <fcntl.h>
00043 #include <sys/syscall.h>
00044 #include <linux/types.h>
00045
00046 #define _S390_BITOPS_H
00047 #include <linux/inotify.h>
00048
00049 static inline int inotify_init (void)
00050 {
00051 return syscall (__NR_inotify_init);
00052 }
00053
00054 static inline int inotify_add_watch (int fd, const char *name, __u32 mask)
00055 {
00056 return syscall (__NR_inotify_add_watch, fd, name, mask);
00057 }
00058
00059 static inline int inotify_rm_watch (int fd, __u32 wd)
00060 {
00061 return syscall (__NR_inotify_rm_watch, fd, wd);
00062 }
00063 #endif
00064
00065 using namespace KMail;
00066
00067 EditorWatcher::EditorWatcher(const KURL & url, const QString &mimeType, bool openWith,
00068 QObject * parent, QWidget *parentWidget) :
00069 QObject( parent ),
00070 mUrl( url ),
00071 mMimeType( mimeType ),
00072 mOpenWith( openWith ),
00073 mEditor( 0 ),
00074 mParentWidget( parentWidget ),
00075 mHaveInotify( false ),
00076 mFileOpen( false ),
00077 mEditorRunning( false ),
00078 mFileModified( true ),
00079 mDone( false )
00080 {
00081 assert( mUrl.isLocalFile() );
00082 connect( &mTimer, SIGNAL(timeout()), SLOT(checkEditDone()) );
00083 }
00084
00085 bool EditorWatcher::start()
00086 {
00087
00088 KURL::List list;
00089 list.append( mUrl );
00090 KService::Ptr offer = KServiceTypeProfile::preferredService( mMimeType, "Application" );
00091 if ( mOpenWith || !offer ) {
00092 KOpenWithDlg dlg( list, i18n("Edit with:"), QString::null, mParentWidget );
00093 if ( !dlg.exec() )
00094 return false;
00095 offer = dlg.service();
00096 if ( !offer )
00097 return false;
00098 }
00099
00100 #ifdef HAVE_INOTIFY
00101
00102 mInotifyFd = inotify_init();
00103 if ( mInotifyFd > 0 ) {
00104 mInotifyWatch = inotify_add_watch( mInotifyFd, mUrl.path().latin1(), IN_CLOSE | IN_OPEN | IN_MODIFY );
00105 if ( mInotifyWatch >= 0 ) {
00106 QSocketNotifier *sn = new QSocketNotifier( mInotifyFd, QSocketNotifier::Read, this );
00107 connect( sn, SIGNAL(activated(int)), SLOT(inotifyEvent()) );
00108 mHaveInotify = true;
00109 mFileModified = false;
00110 }
00111 } else {
00112 kdWarning(5006) << k_funcinfo << "Failed to activate INOTIFY!" << endl;
00113 }
00114 #endif
00115
00116
00117 QStringList params = KRun::processDesktopExec( *offer, list, false );
00118 mEditor = new KProcess( this );
00119 *mEditor << params;
00120 connect( mEditor, SIGNAL(processExited(KProcess*)), SLOT(editorExited()) );
00121 if ( !mEditor->start() )
00122 return false;
00123 mEditorRunning = true;
00124
00125 mEditTime.start();
00126 return true;
00127 }
00128
00129 void EditorWatcher::inotifyEvent()
00130 {
00131 assert( mHaveInotify );
00132 #ifdef HAVE_INOTIFY
00133 int pending = -1;
00134 char buffer[4096];
00135 ioctl( mInotifyFd, FIONREAD, &pending );
00136 while ( pending > 0 ) {
00137 int size = read( mInotifyFd, buffer, QMIN( pending, (int)sizeof(buffer) ) );
00138 pending -= size;
00139 if ( size < 0 )
00140 break;
00141 int offset = 0;
00142 while ( size > 0 ) {
00143 struct inotify_event *event = (struct inotify_event *) &buffer[offset];
00144 size -= sizeof( struct inotify_event ) + event->len;
00145 offset += sizeof( struct inotify_event ) + event->len;
00146 if ( event->mask & IN_OPEN )
00147 mFileOpen = true;
00148 if ( event->mask & IN_CLOSE )
00149 mFileOpen = false;
00150 if ( event->mask & IN_MODIFY )
00151 mFileModified = true;
00152 }
00153 }
00154 #endif
00155 mTimer.start( 500, true );
00156
00157 }
00158
00159 void EditorWatcher::editorExited()
00160 {
00161 mEditorRunning = false;
00162 mTimer.start( 500, true );
00163 }
00164
00165 void EditorWatcher::checkEditDone()
00166 {
00167 if ( mEditorRunning || (mFileOpen && mHaveInotify) || mDone )
00168 return;
00169
00170 static QStringList readOnlyMimeTypes;
00171 if ( readOnlyMimeTypes.isEmpty() ) {
00172 readOnlyMimeTypes << "message/rfc822"
00173 << "application/pdf";
00174 }
00175
00176
00177
00178 mDone = true;
00179
00180
00181 const bool isReadOnlyMimeType = ( readOnlyMimeTypes.contains( mMimeType ) ||
00182 mMimeType.startsWith( "image/" ) );
00183
00184
00185
00186 if ( mEditTime.elapsed() <= 3000 && !isReadOnlyMimeType ) {
00187 KMessageBox::information(
00188 mParentWidget,
00189 i18n( "KMail is unable to detect when the chosen editor is closed. "
00190 "To avoid data loss, editing the attachment will be aborted." ),
00191 i18n( "Unable to edit attachment" ),
00192 "UnableToEditAttachment" );
00193
00194 }
00195
00196 emit editDone( this );
00197 deleteLater();
00198 }
00199
00200 #include "editorwatcher.moc"