diff options
Diffstat (limited to 'kparts/part.cpp')
-rw-r--r-- | kparts/part.cpp | 705 |
1 files changed, 705 insertions, 0 deletions
diff --git a/kparts/part.cpp b/kparts/part.cpp new file mode 100644 index 000000000..35e320ae6 --- /dev/null +++ b/kparts/part.cpp @@ -0,0 +1,705 @@ +/* This file is part of the KDE project + Copyright (C) 1999 Simon Hausmann <hausmann@kde.org> + (C) 1999 David Faure <faure@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include <kparts/part.h> +#include <kparts/event.h> +#include <kparts/plugin.h> +#include <kparts/mainwindow.h> +#include <kparts/partmanager.h> + +#include <qapplication.h> +#include <qfile.h> +#include <qpoint.h> +#include <qpointarray.h> +#include <qpainter.h> +#include <qtextstream.h> +#include <qfileinfo.h> + +#include <kinstance.h> +#include <klocale.h> +#include <ktempfile.h> +#include <kmessagebox.h> +#include <kio/job.h> +#include <kstandarddirs.h> +#include <kfiledialog.h> +#include <kdirnotify_stub.h> + +#include <stdio.h> +#include <unistd.h> +#include <assert.h> +#include <kdebug.h> + +template class QPtrList<KXMLGUIClient>; + +using namespace KParts; + +namespace KParts +{ + +class PartBasePrivate +{ +public: + PartBasePrivate() + { + m_pluginLoadingMode = PartBase::LoadPlugins; + } + ~PartBasePrivate() + { + } + PartBase::PluginLoadingMode m_pluginLoadingMode; +}; + +class PartPrivate +{ +public: + PartPrivate() + { + m_bSelectable = true; + } + ~PartPrivate() + { + } + + bool m_bSelectable; +}; +} + +PartBase::PartBase() +{ + d = new PartBasePrivate; + m_obj = 0L; +} + +PartBase::~PartBase() +{ + delete d; +} + +void PartBase::setPartObject( QObject *obj ) +{ + m_obj = obj; +} + +QObject *PartBase::partObject() const +{ + return m_obj; +} + +void PartBase::setInstance( KInstance *inst ) +{ + setInstance( inst, true ); +} + +void PartBase::setInstance( KInstance *inst, bool bLoadPlugins ) +{ + KXMLGUIClient::setInstance( inst ); + KGlobal::locale()->insertCatalogue( inst->instanceName() ); + // install 'instancename'data resource type + KGlobal::dirs()->addResourceType( inst->instanceName() + "data", + KStandardDirs::kde_default( "data" ) + + QString::fromLatin1( inst->instanceName() ) + '/' ); + if ( bLoadPlugins ) + loadPlugins( m_obj, this, instance() ); +} + +void PartBase::loadPlugins( QObject *parent, KXMLGUIClient *parentGUIClient, KInstance *instance ) +{ + if( d->m_pluginLoadingMode != DoNotLoadPlugins ) + Plugin::loadPlugins( parent, parentGUIClient, instance, d->m_pluginLoadingMode == LoadPlugins ); +} + +void PartBase::setPluginLoadingMode( PluginLoadingMode loadingMode ) +{ + d->m_pluginLoadingMode = loadingMode; +} + +Part::Part( QObject *parent, const char* name ) + : QObject( parent, name ) +{ + d = new PartPrivate; + m_widget = 0L; + m_manager = 0L; + PartBase::setPartObject( this ); +} + +Part::~Part() +{ + kdDebug(1000) << "Part::~Part " << this << endl; + + if ( m_widget ) + { + // We need to disconnect first, to avoid calling it ! + disconnect( m_widget, SIGNAL( destroyed() ), + this, SLOT( slotWidgetDestroyed() ) ); + } + + if ( m_manager ) + m_manager->removePart(this); + + if ( m_widget ) + { + kdDebug(1000) << "deleting widget " << m_widget << " " << m_widget->name() << endl; + delete (QWidget*) m_widget; + } + + delete d; +} + +void Part::embed( QWidget * parentWidget ) +{ + if ( widget() ) + widget()->reparent( parentWidget, 0, QPoint( 0, 0 ), true ); +} + +QWidget *Part::widget() +{ + return m_widget; +} + +void Part::setManager( PartManager *manager ) +{ + m_manager = manager; +} + +PartManager *Part::manager() const +{ + return m_manager; +} + +Part *Part::hitTest( QWidget *widget, const QPoint & ) +{ + if ( (QWidget *)m_widget != widget ) + return 0L; + + return this; +} + +void Part::setWidget( QWidget *widget ) +{ + assert ( !m_widget ); // otherwise we get two connects + m_widget = widget; + connect( m_widget, SIGNAL( destroyed() ), + this, SLOT( slotWidgetDestroyed() ) ); + + // Tell the actionCollection() which widget its + // action shortcuts should be connected to. + actionCollection()->setWidget( widget ); + + // Since KParts objects are XML-based, shortcuts should + // be connected to the widget when the XML settings + // are processed, rather than on KAction construction. + actionCollection()->setAutoConnectShortcuts( false ); +} + +void Part::setSelectable( bool selectable ) +{ + d->m_bSelectable = selectable; +} + +bool Part::isSelectable() const +{ + return d->m_bSelectable; +} + +void Part::customEvent( QCustomEvent *event ) +{ + if ( PartActivateEvent::test( event ) ) + { + partActivateEvent( (PartActivateEvent *)event ); + return; + } + + if ( PartSelectEvent::test( event ) ) + { + partSelectEvent( (PartSelectEvent *)event ); + return; + } + + if ( GUIActivateEvent::test( event ) ) + { + guiActivateEvent( (GUIActivateEvent *)event ); + return; + } + + QObject::customEvent( event ); +} + +void Part::partActivateEvent( PartActivateEvent * ) +{ +} + +void Part::partSelectEvent( PartSelectEvent * ) +{ +} + +void Part::guiActivateEvent( GUIActivateEvent * ) +{ +} + +QWidget *Part::hostContainer( const QString &containerName ) +{ + if ( !factory() ) + return 0L; + + return factory()->container( containerName, this ); +} + +void Part::slotWidgetDestroyed() +{ + kdDebug(1000) << "KPart::slotWidgetDestroyed(), deleting part " << name() << endl; + m_widget = 0; + delete this; +} + +////////////////////////////////////////////////// + +namespace KParts +{ + +class ReadOnlyPartPrivate +{ +public: + ReadOnlyPartPrivate() + { + m_job = 0L; + m_uploadJob = 0L; + m_showProgressInfo = true; + m_saveOk = false; + m_waitForSave = false; + m_duringSaveAs = false; + } + ~ReadOnlyPartPrivate() + { + } + + KIO::FileCopyJob * m_job; + KIO::FileCopyJob * m_uploadJob; + KURL m_originalURL; // for saveAs + QString m_originalFilePath; // for saveAs + bool m_showProgressInfo : 1; + bool m_saveOk : 1; + bool m_waitForSave : 1; + bool m_duringSaveAs : 1; +}; + +} + +ReadOnlyPart::ReadOnlyPart( QObject *parent, const char *name ) + : Part( parent, name ), m_bTemp( false ) +{ + d = new ReadOnlyPartPrivate; +} + +ReadOnlyPart::~ReadOnlyPart() +{ + ReadOnlyPart::closeURL(); + delete d; +} + +void ReadOnlyPart::setProgressInfoEnabled( bool show ) +{ + d->m_showProgressInfo = show; +} + +bool ReadOnlyPart::isProgressInfoEnabled() const +{ + return d->m_showProgressInfo; +} + +#ifndef KDE_NO_COMPAT +void ReadOnlyPart::showProgressInfo( bool show ) +{ + d->m_showProgressInfo = show; +} +#endif + +bool ReadOnlyPart::openURL( const KURL &url ) +{ + if ( !url.isValid() ) + return false; + if ( !closeURL() ) + return false; + m_url = url; + if ( m_url.isLocalFile() ) + { + emit started( 0 ); + m_file = m_url.path(); + bool ret = openFile(); + if (ret) + { + emit completed(); + emit setWindowCaption( m_url.prettyURL() ); + }; + return ret; + } + else + { + m_bTemp = true; + // Use same extension as remote file. This is important for mimetype-determination (e.g. koffice) + QString fileName = url.fileName(); + QFileInfo fileInfo(fileName); + QString ext = fileInfo.extension(); + QString extension; + if ( !ext.isEmpty() && url.query().isNull() ) // not if the URL has a query, e.g. cgi.pl?something + extension = "."+ext; // keep the '.' + KTempFile tempFile( QString::null, extension ); + m_file = tempFile.name(); + + KURL destURL; + destURL.setPath( m_file ); + d->m_job = KIO::file_copy( m_url, destURL, 0600, true, false, d->m_showProgressInfo ); + d->m_job->setWindow( widget() ? widget()->topLevelWidget() : 0 ); + emit started( d->m_job ); + connect( d->m_job, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotJobFinished ( KIO::Job * ) ) ); + return true; + } +} + +void ReadOnlyPart::abortLoad() +{ + if ( d->m_job ) + { + //kdDebug(1000) << "Aborting job " << d->m_job << endl; + d->m_job->kill(); + d->m_job = 0; + } +} + +bool ReadOnlyPart::closeURL() +{ + abortLoad(); //just in case + + if ( m_bTemp ) + { + unlink( QFile::encodeName(m_file) ); + m_bTemp = false; + } + // It always succeeds for a read-only part, + // but the return value exists for reimplementations + // (e.g. pressing cancel for a modified read-write part) + return true; +} + +void ReadOnlyPart::slotJobFinished( KIO::Job * job ) +{ + kdDebug(1000) << "ReadOnlyPart::slotJobFinished" << endl; + assert( job == d->m_job ); + d->m_job = 0; + if (job->error()) + emit canceled( job->errorString() ); + else + { + if ( openFile() ) + emit setWindowCaption( m_url.prettyURL() ); + emit completed(); + } +} + +void ReadOnlyPart::guiActivateEvent( GUIActivateEvent * event ) +{ + if (event->activated()) + { + if (!m_url.isEmpty()) + { + kdDebug(1000) << "ReadOnlyPart::guiActivateEvent -> " << m_url.prettyURL() << endl; + emit setWindowCaption( m_url.prettyURL() ); + } else emit setWindowCaption( "" ); + } +} + +bool ReadOnlyPart::openStream( const QString& mimeType, const KURL& url ) +{ + if ( !closeURL() ) + return false; + m_url = url; + return doOpenStream( mimeType ); +} + +bool ReadOnlyPart::writeStream( const QByteArray& data ) +{ + return doWriteStream( data ); +} + +bool ReadOnlyPart::closeStream() +{ + return doCloseStream(); +} + +////////////////////////////////////////////////// + +ReadWritePart::ReadWritePart( QObject *parent, const char *name ) + : ReadOnlyPart( parent, name ), m_bModified( false ), m_bClosing( false ) +{ + m_bReadWrite = true; +} + +ReadWritePart::~ReadWritePart() +{ + // parent destructor will delete temp file + // we can't call our own closeURL() here, because + // "cancel" wouldn't cancel anything. We have to assume + // the app called closeURL() before destroying us. +} + +void ReadWritePart::setReadWrite( bool readwrite ) +{ + // Perhaps we should check isModified here and issue a warning if true + m_bReadWrite = readwrite; +} + +void ReadWritePart::setModified( bool modified ) +{ + kdDebug(1000) << "ReadWritePart::setModified( " << (modified ? "true" : "false") << ")" << endl; + if ( !m_bReadWrite && modified ) + { + kdError(1000) << "Can't set a read-only document to 'modified' !" << endl; + return; + } + m_bModified = modified; +} + +void ReadWritePart::setModified() +{ + setModified( true ); +} + +bool ReadWritePart::queryClose() +{ + if ( !isReadWrite() || !isModified() ) + return true; + + QString docName = url().fileName(); + if (docName.isEmpty()) docName = i18n( "Untitled" ); + + int res = KMessageBox::warningYesNoCancel( widget(), + i18n( "The document \"%1\" has been modified.\n" + "Do you want to save your changes or discard them?" ).arg( docName ), + i18n( "Close Document" ), KStdGuiItem::save(), KStdGuiItem::discard() ); + + bool abortClose=false; + bool handled=false; + + switch(res) { + case KMessageBox::Yes : + sigQueryClose(&handled,&abortClose); + if (!handled) + { + if (m_url.isEmpty()) + { + KURL url = KFileDialog::getSaveURL(); + if (url.isEmpty()) + return false; + + saveAs( url ); + } + else + { + save(); + } + } else if (abortClose) return false; + return waitSaveComplete(); + case KMessageBox::No : + return true; + default : // case KMessageBox::Cancel : + return false; + } +} + +bool ReadWritePart::closeURL() +{ + abortLoad(); //just in case + if ( isReadWrite() && isModified() ) + { + if (!queryClose()) + return false; + } + // Not modified => ok and delete temp file. + return ReadOnlyPart::closeURL(); +} + +bool ReadWritePart::closeURL( bool promptToSave ) +{ + return promptToSave ? closeURL() : ReadOnlyPart::closeURL(); +} + +bool ReadWritePart::save() +{ + d->m_saveOk = false; + if ( m_file.isEmpty() ) // document was created empty + prepareSaving(); + if( saveFile() ) + return saveToURL(); + else + emit canceled(QString::null); + return false; +} + +bool ReadWritePart::saveAs( const KURL & kurl ) +{ + if (!kurl.isValid()) + { + kdError(1000) << "saveAs: Malformed URL " << kurl.url() << endl; + return false; + } + d->m_duringSaveAs = true; + d->m_originalURL = m_url; + d->m_originalFilePath = m_file; + m_url = kurl; // Store where to upload in saveToURL + prepareSaving(); + bool result = save(); // Save local file and upload local file + if (result) + emit setWindowCaption( m_url.prettyURL() ); + else + { + m_url = d->m_originalURL; + m_file = d->m_originalFilePath; + d->m_duringSaveAs = false; + d->m_originalURL = KURL(); + d->m_originalFilePath = QString::null; + } + + return result; +} + +// Set m_file correctly for m_url +void ReadWritePart::prepareSaving() +{ + // Local file + if ( m_url.isLocalFile() ) + { + if ( m_bTemp ) // get rid of a possible temp file first + { // (happens if previous url was remote) + unlink( QFile::encodeName(m_file) ); + m_bTemp = false; + } + m_file = m_url.path(); + } + else + { // Remote file + // We haven't saved yet, or we did but locally - provide a temp file + if ( m_file.isEmpty() || !m_bTemp ) + { + KTempFile tempFile; + m_file = tempFile.name(); + m_bTemp = true; + } + // otherwise, we already had a temp file + } +} + +bool ReadWritePart::saveToURL() +{ + if ( m_url.isLocalFile() ) + { + setModified( false ); + emit completed(); + // if m_url is a local file there won't be a temp file -> nothing to remove + assert( !m_bTemp ); + d->m_saveOk = true; + d->m_duringSaveAs = false; + d->m_originalURL = KURL(); + d->m_originalFilePath = QString::null; + return true; // Nothing to do + } + else + { + if (d->m_uploadJob) + { + unlink(QFile::encodeName(d->m_uploadJob->srcURL().path())); + d->m_uploadJob->kill(); + d->m_uploadJob = 0; + } + KTempFile tempFile; + QString uploadFile = tempFile.name(); + KURL uploadUrl; + uploadUrl.setPath( uploadFile ); + tempFile.unlink(); + // Create hardlink + if (::link(QFile::encodeName(m_file), QFile::encodeName(uploadFile)) != 0) + { + // Uh oh, some error happened. + return false; + } + d->m_uploadJob = KIO::file_move( uploadUrl, m_url, -1, true /*overwrite*/ ); + d->m_uploadJob->setWindow( widget() ? widget()->topLevelWidget() : 0 ); + connect( d->m_uploadJob, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotUploadFinished (KIO::Job *) ) ); + return true; + } +} + +void ReadWritePart::slotUploadFinished( KIO::Job * ) +{ + if (d->m_uploadJob->error()) + { + unlink(QFile::encodeName(d->m_uploadJob->srcURL().path())); + QString error = d->m_uploadJob->errorString(); + d->m_uploadJob = 0; + if (d->m_duringSaveAs) { + m_url = d->m_originalURL; + m_file = d->m_originalFilePath; + } + emit canceled( error ); + } + else + { + KDirNotify_stub allDirNotify("*", "KDirNotify*"); + KURL dirUrl( m_url ); + dirUrl.setPath( dirUrl.directory() ); + allDirNotify.FilesAdded( dirUrl ); + + d->m_uploadJob = 0; + setModified( false ); + emit completed(); + d->m_saveOk = true; + } + d->m_duringSaveAs = false; + d->m_originalURL = KURL(); + d->m_originalFilePath = QString::null; + if (d->m_waitForSave) + { + qApp->exit_loop(); + } +} + +// Trolls: Nothing to see here, please step away. +void qt_enter_modal( QWidget *widget ); +void qt_leave_modal( QWidget *widget ); + +bool ReadWritePart::waitSaveComplete() +{ + if (!d->m_uploadJob) + return d->m_saveOk; + + d->m_waitForSave = true; + + QWidget dummy(0,0,WType_Dialog | WShowModal); + dummy.setFocusPolicy( QWidget::NoFocus ); + qt_enter_modal(&dummy); + qApp->enter_loop(); + qt_leave_modal(&dummy); + + d->m_waitForSave = false; + + return d->m_saveOk; +} + +#include "part.moc" + +// vim:sw=2:ts=8:et |