editorwatcher.cpp
00001 /* 00002 Copyright (c) 2007 Volker Krause <vkrause@kde.org> 00003 00004 This program is free software; you can redistribute it and/or modify 00005 it under the terms of the GNU General Public License as published by 00006 the Free Software Foundation; either version 2 of the License, or 00007 (at your option) any later version. 00008 00009 This program is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 GNU General Public License for more details. 00013 00014 You should have received a copy of the GNU General Public License 00015 along with this program; if not, write to the Free Software 00016 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 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 <tqsocketnotifier.h> 00031 00032 #include <cassert> 00033 00034 // inotify stuff taken from kdelibs/kio/kio/kdirwatch.cpp 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 <sys/inotify.h> 00043 #include <sys/syscall.h> 00044 #include <linux/types.h> 00045 // Linux kernel headers are documented to not compile 00046 #define _S390_BITOPS_H 00047 #endif 00048 00049 using namespace KMail; 00050 00051 EditorWatcher::EditorWatcher(const KURL & url, const TQString &mimeType, bool openWith, 00052 TQObject * parent, TQWidget *parentWidget) : 00053 TQObject( parent ), 00054 mUrl( url ), 00055 mMimeType( mimeType ), 00056 mOpenWith( openWith ), 00057 mEditor( 0 ), 00058 mParentWidget( parentWidget ), 00059 mHaveInotify( false ), 00060 mFileOpen( false ), 00061 mEditorRunning( false ), 00062 mFileModified( true ), // assume the worst unless we know better 00063 mDone( false ) 00064 { 00065 assert( mUrl.isLocalFile() ); 00066 connect( &mTimer, TQT_SIGNAL(timeout()), TQT_SLOT(checkEditDone()) ); 00067 } 00068 00069 bool EditorWatcher::start() 00070 { 00071 // find an editor 00072 KURL::List list; 00073 list.append( mUrl ); 00074 KService::Ptr offer = KServiceTypeProfile::preferredService( mMimeType, "Application" ); 00075 if ( mOpenWith || !offer ) { 00076 KOpenWithDlg dlg( list, i18n("Edit with:"), TQString(), 0 ); 00077 if ( !dlg.exec() ) 00078 return false; 00079 offer = dlg.service(); 00080 if ( !offer ) 00081 return false; 00082 } 00083 00084 #ifdef HAVE_INOTIFY 00085 // monitor file 00086 mInotifyFd = inotify_init(); 00087 if ( mInotifyFd > 0 ) { 00088 mInotifyWatch = inotify_add_watch( mInotifyFd, mUrl.path().latin1(), IN_CLOSE | IN_OPEN | IN_MODIFY ); 00089 if ( mInotifyWatch >= 0 ) { 00090 TQSocketNotifier *sn = new TQSocketNotifier( mInotifyFd, TQSocketNotifier::Read, this ); 00091 connect( sn, TQT_SIGNAL(activated(int)), TQT_SLOT(inotifyEvent()) ); 00092 mHaveInotify = true; 00093 mFileModified = false; 00094 } 00095 } else { 00096 kdWarning(5006) << k_funcinfo << "Failed to activate INOTIFY!" << endl; 00097 } 00098 #endif 00099 00100 // start the editor 00101 TQStringList params = KRun::processDesktopExec( *offer, list, false ); 00102 mEditor = new KProcess( this ); 00103 *mEditor << params; 00104 connect( mEditor, TQT_SIGNAL(processExited(KProcess*)), TQT_SLOT(editorExited()) ); 00105 if ( !mEditor->start() ) 00106 return false; 00107 mEditorRunning = true; 00108 00109 mEditTime.start(); 00110 return true; 00111 } 00112 00113 void EditorWatcher::inotifyEvent() 00114 { 00115 assert( mHaveInotify ); 00116 #ifdef HAVE_INOTIFY 00117 int pending = -1; 00118 char buffer[4096]; 00119 ioctl( mInotifyFd, FIONREAD, &pending ); 00120 while ( pending > 0 ) { 00121 int size = read( mInotifyFd, buffer, TQMIN( pending, (int)sizeof(buffer) ) ); 00122 pending -= size; 00123 if ( size < 0 ) 00124 break; // error 00125 int offset = 0; 00126 while ( size > 0 ) { 00127 struct inotify_event *event = (struct inotify_event *) &buffer[offset]; 00128 size -= sizeof( struct inotify_event ) + event->len; 00129 offset += sizeof( struct inotify_event ) + event->len; 00130 if ( event->mask & IN_OPEN ) 00131 mFileOpen = true; 00132 if ( event->mask & IN_CLOSE ) 00133 mFileOpen = false; 00134 if ( event->mask & IN_MODIFY ) 00135 mFileModified = true; 00136 } 00137 } 00138 #endif 00139 mTimer.start( 500, true ); 00140 00141 } 00142 00143 void EditorWatcher::editorExited() 00144 { 00145 mEditorRunning = false; 00146 mTimer.start( 500, true ); 00147 } 00148 00149 void EditorWatcher::checkEditDone() 00150 { 00151 if ( mEditorRunning || (mFileOpen && mHaveInotify) || mDone ) 00152 return; 00153 // protect us against double-deletion by calling this method again while 00154 // the subeventloop of the message box is running 00155 mDone = true; 00156 // nobody can edit that fast, we seem to be unable to detect 00157 // when the editor will be closed 00158 if ( mEditTime.elapsed() <= 3000 ) { 00159 KMessageBox::information( 00160 mParentWidget, 00161 i18n( "KMail is unable to detect when the chosen editor is closed. " 00162 "To avoid data loss, editing the attachment will be aborted." ), 00163 i18n( "Unable to edit attachment" ), 00164 "UnableToEditAttachment" ); 00165 00166 } 00167 00168 emit editDone( this ); 00169 deleteLater(); 00170 } 00171 00172 #include "editorwatcher.moc"