19 #include <config-messageviewer.h>
25 #include <kmessagebox.h>
26 #include <kopenwithdialog.h>
28 #include <kmimetypetrader.h>
31 #include <qsocketnotifier.h>
36 #ifdef HAVE_SYS_INOTIFY_H
39 #include <sys/inotify.h>
40 #include <sys/ioctl.h>
43 namespace MessageViewer {
48 mMimeType( mimeType ),
50 mParentWidget( parentWidget ),
53 mOpenWithOption( option ),
54 mHaveInotify( false ),
56 mEditorRunning( false ),
57 mFileModified( true ),
60 assert( mUrl.isLocalFile() );
62 connect( &mTimer, SIGNAL(timeout()), SLOT(checkEditDone()) );
67 #ifdef HAVE_SYS_INOTIFY_H
68 ::close( mInotifyFd );
77 KService::Ptr offer = KMimeTypeTrader::self()->preferredService( mMimeType,
QLatin1String(
"Application") );
81 const int dlgrc = dlg->exec();
83 offer = dlg->service();
91 #ifdef HAVE_SYS_INOTIFY_H
93 mInotifyFd = inotify_init();
94 if ( mInotifyFd > 0 ) {
95 (void)fcntl(mInotifyFd, F_SETFD, FD_CLOEXEC);
96 mInotifyWatch = inotify_add_watch( mInotifyFd, mUrl.path().toLatin1(), IN_CLOSE | IN_OPEN | IN_MODIFY | IN_ATTRIB );
97 if ( mInotifyWatch >= 0 ) {
99 connect( sn, SIGNAL(activated(
int)), SLOT(inotifyEvent()) );
101 mFileModified =
false;
104 kWarning() <<
"Failed to activate INOTIFY!";
109 const QStringList params = KRun::processDesktopExec( *offer, list,
false );
110 mEditor =
new KProcess(
this );
111 mEditor->setProgram( params );
112 connect( mEditor, SIGNAL(finished(
int,QProcess::ExitStatus)),
113 SLOT(editorExited()) );
115 if ( !mEditor->waitForStarted() )
117 mEditorRunning =
true;
125 return mFileModified;
133 void EditorWatcher::inotifyEvent()
135 assert( mHaveInotify );
137 #ifdef HAVE_SYS_INOTIFY_H
139 int offsetStartRead = 0;
141 assert( mInotifyFd > -1 );
142 ioctl( mInotifyFd, FIONREAD, &pending );
144 while ( pending > 0 ) {
146 const int bytesToRead = qMin( pending, (
int)
sizeof( buf ) - offsetStartRead );
148 int bytesAvailable = read( mInotifyFd, &buf[offsetStartRead], bytesToRead );
149 pending -= bytesAvailable;
150 bytesAvailable += offsetStartRead;
153 int offsetCurrent = 0;
154 while ( bytesAvailable >= (
int)
sizeof(
struct inotify_event ) ) {
155 const struct inotify_event *
const event = (
struct inotify_event *) &buf[offsetCurrent];
156 const int eventSize =
sizeof(
struct inotify_event ) +
event->len;
157 if ( bytesAvailable < eventSize ) {
161 bytesAvailable -= eventSize;
162 offsetCurrent += eventSize;
163 if (
event->mask & IN_OPEN ) {
166 if (
event->mask & IN_CLOSE ) {
169 if (
event->mask & (IN_MODIFY|IN_ATTRIB) ) {
170 mFileModified =
true;
174 if (bytesAvailable > 0) {
176 memmove(buf, &buf[offsetCurrent], bytesAvailable);
177 offsetStartRead = bytesAvailable;
186 void EditorWatcher::editorExited()
188 mEditorRunning =
false;
192 void EditorWatcher::checkEditDone()
194 if ( mEditorRunning || (mFileOpen && mHaveInotify) || mDone )
198 if ( readOnlyMimeTypes.
isEmpty() ) {
208 const bool isReadOnlyMimeType = ( readOnlyMimeTypes.
contains( mMimeType ) ||
213 if ( mEditTime.
elapsed() <= 3000 && !isReadOnlyMimeType ) {
214 KMessageBox::information( mParentWidget,
215 i18n(
"KMail is unable to detect when the chosen editor is closed. "
216 "To avoid data loss, editing the attachment will be aborted." ),
217 i18n(
"Unable to edit attachment" ),
EditorWatcher(const KUrl &url, const QString &mimeType, OpenWithOption option, QObject *parent, QWidget *parentWidget)
Constructs an EditorWatcher.
bool contains(const QString &str, Qt::CaseSensitivity cs) const
void editDone(MessageViewer::EditorWatcher *watcher)
virtual bool event(QEvent *e)
ErrorEditorWatcher start()
A QPointer which when destructed, deletes the object it points to.
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void setSingleShot(bool singleShot)