kde-playground/kdepim/messageviewer/viewer/editorwatcher.cpp
2015-04-14 21:49:29 +00:00

179 lines
5.8 KiB
C++

/*
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 <config-messageviewer.h>
#include "editorwatcher.h"
#include "utils/autoqpointer.h"
#include <kdebug.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <kopenwithdialog.h>
#include <kprocess.h>
#include <kmimetypetrader.h>
#include <krun.h>
#include <qsocketnotifier.h>
#include <cassert>
// inotify stuff taken from kdelibs/kio/kio/kdirwatch.cpp
#ifdef HAVE_SYS_INOTIFY_H
#include <unistd.h>
#include <fcntl.h>
#include <sys/inotify.h>
#include <sys/ioctl.h>
#endif
namespace MessageViewer {
EditorWatcher::EditorWatcher( const KUrl & url, const QString &mimeType, bool openWith,
QObject * parent, QWidget *parentWidget ) :
QObject( parent ),
mUrl( url ),
mMimeType( mimeType ),
mOpenWith( openWith ),
mEditor( 0 ),
mParentWidget( parentWidget ),
mHaveInotify( false ),
mFileOpen( false ),
mEditorRunning( false ),
mFileModified( true ), // assume the worst unless we know better
mDone( false )
{
assert( mUrl.isLocalFile() );
mTimer.setSingleShot( true );
connect( &mTimer, SIGNAL(timeout()), SLOT(checkEditDone()) );
}
bool EditorWatcher::start()
{
// find an editor
KUrl::List list;
list.append( mUrl );
KService::Ptr offer = KMimeTypeTrader::self()->preferredService( mMimeType, QLatin1String("Application") );
if ( mOpenWith || !offer ) {
AutoQPointer<KOpenWithDialog> dlg( new KOpenWithDialog( list, i18n("Edit with:"),
QString(), mParentWidget ) );
const int dlgrc = dlg->exec();
if ( dlgrc && dlg ) {
offer = dlg->service();
}
if ( !dlgrc || !offer )
return false;
}
#ifdef HAVE_SYS_INOTIFY_H
// monitor file
mInotifyFd = inotify_init();
if ( mInotifyFd > 0 ) {
mInotifyWatch = inotify_add_watch( mInotifyFd, mUrl.path().toLatin1(), IN_CLOSE | IN_OPEN | IN_MODIFY );
if ( mInotifyWatch >= 0 ) {
QSocketNotifier *sn = new QSocketNotifier( mInotifyFd, QSocketNotifier::Read, this );
connect( sn, SIGNAL(activated(int)), SLOT(inotifyEvent()) );
mHaveInotify = true;
mFileModified = false;
}
} else {
kWarning() << "Failed to activate INOTIFY!";
}
#endif
// start the editor
const QStringList params = KRun::processDesktopExec( *offer, list, false );
mEditor = new KProcess( this );
mEditor->setProgram( params );
connect( mEditor, SIGNAL(finished(int,QProcess::ExitStatus)),
SLOT(editorExited()) );
mEditor->start();
if ( !mEditor->waitForStarted() )
return false;
mEditorRunning = true;
mEditTime.start();
return true;
}
void EditorWatcher::inotifyEvent()
{
assert( mHaveInotify );
#ifdef HAVE_SYS_INOTIFY_H
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 );
}
void EditorWatcher::editorExited()
{
mEditorRunning = false;
mTimer.start( 500 );
}
void EditorWatcher::checkEditDone()
{
if ( mEditorRunning || (mFileOpen && mHaveInotify) || mDone )
return;
static QStringList readOnlyMimeTypes;
if ( readOnlyMimeTypes.isEmpty() ) {
readOnlyMimeTypes << QLatin1String("message/rfc822")
<< QLatin1String("application/pdf");
}
// protect us against double-deletion by calling this method again while
// the subeventloop of the message box is running
mDone = true;
// check if it's a mime type that's mostly handled read-only
const bool isReadOnlyMimeType = ( readOnlyMimeTypes.contains( mMimeType ) ||
mMimeType.startsWith( QLatin1String("image/") ) );
// nobody can edit that fast, we seem to be unable to detect
// when the editor will be closed
if ( mEditTime.elapsed() <= 3000 && !isReadOnlyMimeType ) {
KMessageBox::information( mParentWidget,
i18n( "KMail is unable to detect when the chosen editor is closed. "
"To avoid data loss, editing the attachment will be aborted." ),
i18n( "Unable to edit attachment" ),
QLatin1String("UnableToEditAttachment") );
}
emit editDone( this );
deleteLater();
}
}