|
|
|
/*
|
|
|
|
Copyright (c) 2007 Volker Krause <vkrause@kde.org>
|
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "editorwatcher.h"
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <klocale.h>
|
|
|
|
#include <kmessagebox.h>
|
|
|
|
#include <kopenwith.h>
|
|
|
|
#include <kprocess.h>
|
|
|
|
#include <kuserprofile.h>
|
|
|
|
|
|
|
|
#include <tqsocketnotifier.h>
|
|
|
|
|
|
|
|
#include <cassert>
|
|
|
|
|
|
|
|
// inotify stuff taken from kdelibs/kio/kio/kdirwatch.cpp
|
|
|
|
#ifdef HAVE_SYS_INOTIFY
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <sys/inotify.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#elif HAVE_INOTIFY
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <sys/inotify.h>
|
|
|
|
#include <sys/syscall.h>
|
|
|
|
#include <linux/types.h>
|
|
|
|
// Linux kernel headers are documented to not compile
|
|
|
|
#define _S390_BITOPS_H
|
|
|
|
#endif
|
|
|
|
|
|
|
|
using namespace KMail;
|
|
|
|
|
|
|
|
EditorWatcher::EditorWatcher(const KURL & url, const TQString &mimeType, bool openWith, TQObject * parent) :
|
|
|
|
TQObject( parent ),
|
|
|
|
mUrl( url ),
|
|
|
|
mMimeType( mimeType ),
|
|
|
|
mOpenWith( openWith ),
|
|
|
|
mEditor( 0 ),
|
|
|
|
mHaveInotify( false ),
|
|
|
|
mFileOpen( false ),
|
|
|
|
mEditorRunning( false ),
|
|
|
|
mFileModified( true ), // assume the worst unless we know better
|
|
|
|
mDone( false )
|
|
|
|
{
|
|
|
|
assert( mUrl.isLocalFile() );
|
|
|
|
connect( &mTimer, TQT_SIGNAL(timeout()), TQT_SLOT(checkEditDone()) );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool EditorWatcher::start()
|
|
|
|
{
|
|
|
|
// find an editor
|
|
|
|
KURL::List list;
|
|
|
|
list.append( mUrl );
|
|
|
|
KService::Ptr offer = KServiceTypeProfile::preferredService( mMimeType, "Application" );
|
|
|
|
if ( mOpenWith || !offer ) {
|
|
|
|
KOpenWithDlg dlg( list, i18n("Edit with:"), TQString::null, 0 );
|
|
|
|
if ( !dlg.exec() )
|
|
|
|
return false;
|
|
|
|
offer = dlg.service();
|
|
|
|
if ( !offer )
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef HAVE_INOTIFY
|
|
|
|
// monitor file
|
|
|
|
mInotifyFd = inotify_init();
|
|
|
|
if ( mInotifyFd > 0 ) {
|
|
|
|
mInotifyWatch = inotify_add_watch( mInotifyFd, mUrl.path().latin1(), IN_CLOSE | IN_OPEN | IN_MODIFY );
|
|
|
|
if ( mInotifyWatch >= 0 ) {
|
|
|
|
TQSocketNotifier *sn = new TQSocketNotifier( mInotifyFd, TQSocketNotifier::Read, this );
|
|
|
|
connect( sn, TQT_SIGNAL(activated(int)), TQT_SLOT(inotifyEvent()) );
|
|
|
|
mHaveInotify = true;
|
|
|
|
mFileModified = false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
kdWarning(5006) << k_funcinfo << "Failed to activate INOTIFY!" << endl;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// start the editor
|
|
|
|
TQStringList params = KRun::processDesktopExec( *offer, list, false );
|
|
|
|
mEditor = new KProcess( this );
|
|
|
|
*mEditor << params;
|
|
|
|
connect( mEditor, TQT_SIGNAL(processExited(KProcess*)), TQT_SLOT(editorExited()) );
|
|
|
|
if ( !mEditor->start() )
|
|
|
|
return false;
|
|
|
|
mEditorRunning = true;
|
|
|
|
|
|
|
|
mEditTime.start();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorWatcher::inotifyEvent()
|
|
|
|
{
|
|
|
|
assert( mHaveInotify );
|
|
|
|
#ifdef HAVE_INOTIFY
|
|
|
|
int pending = -1;
|
|
|
|
char buffer[4096];
|
|
|
|
ioctl( mInotifyFd, FIONREAD, &pending );
|
|
|
|
while ( pending > 0 ) {
|
|
|
|
int size = read( mInotifyFd, buffer, QMIN( pending, (int)sizeof(buffer) ) );
|
|
|
|
pending -= size;
|
|
|
|
if ( size < 0 )
|
|
|
|
break; // error
|
|
|
|
int offset = 0;
|
|
|
|
while ( size > 0 ) {
|
|
|
|
struct inotify_event *event = (struct inotify_event *) &buffer[offset];
|
|
|
|
size -= sizeof( struct inotify_event ) + event->len;
|
|
|
|
offset += sizeof( struct inotify_event ) + event->len;
|
|
|
|
if ( event->mask & IN_OPEN )
|
|
|
|
mFileOpen = true;
|
|
|
|
if ( event->mask & IN_CLOSE )
|
|
|
|
mFileOpen = false;
|
|
|
|
if ( event->mask & IN_MODIFY )
|
|
|
|
mFileModified = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
mTimer.start( 500, true );
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorWatcher::editorExited()
|
|
|
|
{
|
|
|
|
mEditorRunning = false;
|
|
|
|
mTimer.start( 500, true );
|
|
|
|
}
|
|
|
|
|
|
|
|
void EditorWatcher::checkEditDone()
|
|
|
|
{
|
|
|
|
if ( mEditorRunning || (mFileOpen && mHaveInotify) || mDone )
|
|
|
|
return;
|
|
|
|
// protect us against double-deletion by calling this method again while
|
|
|
|
// the subeventloop of the message box is running
|
|
|
|
mDone = true;
|
|
|
|
// nobody can edit that fast, we seem to be unable to detect
|
|
|
|
// when the editor will be closed
|
|
|
|
if ( mEditTime.elapsed() <= 3000 ) {
|
|
|
|
KMessageBox::error( 0, i18n("KMail is unable to detect when the choosen editor is closed. "
|
|
|
|
"To avoid data loss, editing the attachment will be aborted."), i18n("Unable to edit attachment") );
|
|
|
|
}
|
|
|
|
|
|
|
|
emit editDone( this );
|
|
|
|
deleteLater();
|
|
|
|
}
|
|
|
|
|
|
|
|
#include "editorwatcher.moc"
|