kmail

editorwatcher.cpp
1 /*
2  Copyright (c) 2007 Volker Krause <vkrause@kde.org>
3 
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.
8 
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.
13 
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.
17 */
18 
19 #include "editorwatcher.h"
20 
21 #include <config.h>
22 
23 #include <kdebug.h>
24 #include <tdelocale.h>
25 #include <tdemessagebox.h>
26 #include <kopenwith.h>
27 #include <tdeprocess.h>
28 #include <kuserprofile.h>
29 
30 #include <tqsocketnotifier.h>
31 
32 #include <cassert>
33 
34 // inotify stuff taken from tdelibs/tdeio/tdeio/kdirwatch.cpp
35 #ifdef HAVE_SYS_INOTIFY
36 #include <sys/ioctl.h>
37 #include <sys/inotify.h>
38 #include <fcntl.h>
39 #elif HAVE_INOTIFY
40 #include <sys/ioctl.h>
41 #include <unistd.h>
42 #include <sys/inotify.h>
43 #include <sys/syscall.h>
44 #include <linux/types.h>
45 // Linux kernel headers are documented to not compile
46 #define _S390_BITOPS_H
47 #endif
48 
49 using namespace KMail;
50 
51 EditorWatcher::EditorWatcher(const KURL & url, const TQString &mimeType, bool openWith,
52  TQObject * parent, TQWidget *parentWidget) :
53  TQObject( parent ),
54  mUrl( url ),
55  mMimeType( mimeType ),
56  mOpenWith( openWith ),
57  mEditor( 0 ),
58  mParentWidget( parentWidget ),
59  mHaveInotify( false ),
60  mFileOpen( false ),
61  mEditorRunning( false ),
62  mFileModified( true ), // assume the worst unless we know better
63  mDone( false )
64 {
65  assert( mUrl.isLocalFile() );
66  connect( &mTimer, TQ_SIGNAL(timeout()), TQ_SLOT(checkEditDone()) );
67 }
68 
69 bool EditorWatcher::start()
70 {
71  // find an editor
72  KURL::List list;
73  list.append( mUrl );
74  KService::Ptr offer = KServiceTypeProfile::preferredService( mMimeType, "Application" );
75  if ( mOpenWith || !offer ) {
76  KOpenWithDlg dlg( list, i18n("Edit with:"), TQString(), 0 );
77  if ( !dlg.exec() )
78  return false;
79  offer = dlg.service();
80  if ( !offer )
81  return false;
82  }
83 
84 #ifdef HAVE_INOTIFY
85  // monitor file
86  mInotifyFd = inotify_init();
87  if ( mInotifyFd > 0 ) {
88  mInotifyWatch = inotify_add_watch( mInotifyFd, mUrl.path().latin1(), IN_CLOSE | IN_OPEN | IN_MODIFY );
89  if ( mInotifyWatch >= 0 ) {
90  TQSocketNotifier *sn = new TQSocketNotifier( mInotifyFd, TQSocketNotifier::Read, this );
91  connect( sn, TQ_SIGNAL(activated(int)), TQ_SLOT(inotifyEvent()) );
92  mHaveInotify = true;
93  mFileModified = false;
94  }
95  } else {
96  kdWarning(5006) << k_funcinfo << "Failed to activate INOTIFY!" << endl;
97  }
98 #endif
99 
100  // start the editor
101  TQStringList params = KRun::processDesktopExec( *offer, list, false );
102  mEditor = new TDEProcess( this );
103  *mEditor << params;
104  connect( mEditor, TQ_SIGNAL(processExited(TDEProcess*)), TQ_SLOT(editorExited()) );
105  if ( !mEditor->start() )
106  return false;
107  mEditorRunning = true;
108 
109  mEditTime.start();
110  return true;
111 }
112 
113 void EditorWatcher::inotifyEvent()
114 {
115  assert( mHaveInotify );
116 #ifdef HAVE_INOTIFY
117  int pending = -1;
118  char buffer[4096];
119  ioctl( mInotifyFd, FIONREAD, &pending );
120  while ( pending > 0 ) {
121  int size = read( mInotifyFd, buffer, TQMIN( 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;
136  }
137  }
138 #endif
139  mTimer.start( 500, true );
140 
141 }
142 
143 void EditorWatcher::editorExited()
144 {
145  mEditorRunning = false;
146  mTimer.start( 500, true );
147 }
148 
149 void EditorWatcher::checkEditDone()
150 {
151  if ( mEditorRunning || (mFileOpen && mHaveInotify) || mDone )
152  return;
153  // protect us against double-deletion by calling this method again while
154  // the subeventloop of the message box is running
155  mDone = true;
156  // nobody can edit that fast, we seem to be unable to detect
157  // when the editor will be closed
158  if ( mEditTime.elapsed() <= 3000 ) {
159  KMessageBox::information(
160  mParentWidget,
161  i18n( "KMail is unable to detect when the chosen editor is closed. "
162  "To avoid data loss, editing the attachment will be aborted." ),
163  i18n( "Unable to edit attachment" ),
164  "UnableToEditAttachment" );
165 
166  }
167 
168  emit editDone( this );
169  deleteLater();
170 }
171 
172 #include "editorwatcher.moc"
EditorWatcher(const KURL &url, const TQString &mimeType, bool openWith, TQObject *parent, TQWidget *parentWidget)
Constructs an EditorWatcher.
folderdiaquotatab.h
Definition: aboutdata.cpp:40