Scale identity combobox and send/cancel buttons according to screen DPI.
[kdepim.git] / messageviewer / editorwatcher.cpp
blob8a7956c5cf48f43a23b9923312f4e8845d90dccb
1 /*
2 Copyright (c) 2007 Volker Krause <vkrause@kde.org>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 #include <config-messageviewer.h>
20 #include "editorwatcher.h"
21 #include "autoqpointer.h"
23 #include <kdebug.h>
24 #include <klocale.h>
25 #include <kmessagebox.h>
26 #include <kopenwithdialog.h>
27 #include <kprocess.h>
28 #include <kmimetypetrader.h>
29 #include <krun.h>
31 #include <qsocketnotifier.h>
33 #include <cassert>
35 // inotify stuff taken from kdelibs/kio/kio/kdirwatch.cpp
36 #ifdef HAVE_SYS_INOTIFY_H
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <sys/inotify.h>
40 #include <sys/ioctl.h>
41 #endif
43 namespace MessageViewer {
44 EditorWatcher::EditorWatcher( const KUrl & url, const QString &mimeType, bool openWith,
45 QObject * parent, QWidget *parentWidget ) :
46 QObject( parent ),
47 mUrl( url ),
48 mMimeType( mimeType ),
49 mOpenWith( openWith ),
50 mEditor( 0 ),
51 mParentWidget( parentWidget ),
52 mHaveInotify( false ),
53 mFileOpen( false ),
54 mEditorRunning( false ),
55 mFileModified( true ), // assume the worst unless we know better
56 mDone( false )
58 assert( mUrl.isLocalFile() );
59 mTimer.setSingleShot( true );
60 connect( &mTimer, SIGNAL(timeout()), SLOT(checkEditDone()) );
63 bool EditorWatcher::start()
65 // find an editor
66 KUrl::List list;
67 list.append( mUrl );
68 KService::Ptr offer = KMimeTypeTrader::self()->preferredService( mMimeType, "Application" );
69 if ( mOpenWith || !offer ) {
70 #ifndef Q_OS_WINCE
71 AutoQPointer<KOpenWithDialog> dlg( new KOpenWithDialog( list, i18n("Edit with:"),
72 QString(), mParentWidget ) );
73 int dlgrc = dlg->exec();
74 if ( dlgrc && dlg ) {
75 offer = dlg->service();
77 if ( !dlgrc || !offer )
78 #endif
79 return false;
82 #ifdef HAVE_SYS_INOTIFY_H
83 // monitor file
84 mInotifyFd = inotify_init();
85 if ( mInotifyFd > 0 ) {
86 mInotifyWatch = inotify_add_watch( mInotifyFd, mUrl.path().toLatin1(), IN_CLOSE | IN_OPEN | IN_MODIFY );
87 if ( mInotifyWatch >= 0 ) {
88 QSocketNotifier *sn = new QSocketNotifier( mInotifyFd, QSocketNotifier::Read, this );
89 connect( sn, SIGNAL(activated(int)), SLOT(inotifyEvent()) );
90 mHaveInotify = true;
91 mFileModified = false;
93 } else {
94 kWarning() << "Failed to activate INOTIFY!";
96 #endif
98 // start the editor
99 QStringList params = KRun::processDesktopExec( *offer, list, false );
100 mEditor = new KProcess( this );
101 mEditor->setProgram( params );
102 connect( mEditor, SIGNAL(finished(int,QProcess::ExitStatus)),
103 SLOT(editorExited()) );
104 mEditor->start();
105 if ( !mEditor->waitForStarted() )
106 return false;
107 mEditorRunning = true;
109 mEditTime.start();
110 return true;
113 void EditorWatcher::inotifyEvent()
115 assert( mHaveInotify );
116 #ifdef HAVE_SYS_INOTIFY_H
117 int pending = -1;
118 char buffer[4096];
119 ioctl( mInotifyFd, FIONREAD, &pending );
120 while ( pending > 0 ) {
121 int size = read( mInotifyFd, buffer, qMin( pending, (int)sizeof(buffer) ) );
122 pending -= size;
123 if ( size < 0 )
124 break; // error
125 int offset = 0;
126 while ( size > 0 ) {
127 struct inotify_event *event = (struct inotify_event *) &buffer[offset];
128 size -= sizeof( struct inotify_event ) + event->len;
129 offset += sizeof( struct inotify_event ) + event->len;
130 if ( event->mask & IN_OPEN )
131 mFileOpen = true;
132 if ( event->mask & IN_CLOSE )
133 mFileOpen = false;
134 if ( event->mask & IN_MODIFY )
135 mFileModified = true;
138 #endif
139 mTimer.start( 500 );
143 void EditorWatcher::editorExited()
145 mEditorRunning = false;
146 mTimer.start( 500 );
149 void EditorWatcher::checkEditDone()
151 if ( mEditorRunning || (mFileOpen && mHaveInotify) || mDone )
152 return;
154 static QStringList readOnlyMimeTypes;
155 if ( readOnlyMimeTypes.isEmpty() ) {
156 readOnlyMimeTypes << "message/rfc822"
157 << "application/pdf";
160 // protect us against double-deletion by calling this method again while
161 // the subeventloop of the message box is running
162 mDone = true;
164 // check if it's a mime type that's mostly handled read-only
165 const bool isReadOnlyMimeType = ( readOnlyMimeTypes.contains( mMimeType ) ||
166 mMimeType.startsWith( "image/" ) );
168 // nobody can edit that fast, we seem to be unable to detect
169 // when the editor will be closed
170 if ( mEditTime.elapsed() <= 3000 && !isReadOnlyMimeType ) {
171 KMessageBox::information( mParentWidget,
172 i18n( "KMail is unable to detect when the chosen editor is closed. "
173 "To avoid data loss, editing the attachment will be aborted." ),
174 i18n( "Unable to edit attachment" ),
175 "UnableToEditAttachment" );
178 emit editDone( this );
179 deleteLater();
182 #include "editorwatcher.moc"