kparts Library API Documentation

part.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 1999 Simon Hausmann <hausmann@kde.org>
00003              (C) 1999 David Faure <faure@kde.org>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; either
00008    version 2 of the License, or (at your option) any later version.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00018    Boston, MA 02111-1307, USA.
00019 */
00020 
00021 #include <kparts/part.h>
00022 #include <kparts/event.h>
00023 #include <kparts/plugin.h>
00024 #include <kparts/mainwindow.h>
00025 #include <kparts/partmanager.h>
00026 
00027 #include <qapplication.h>
00028 #include <qfile.h>
00029 #include <qpoint.h>
00030 #include <qpointarray.h>
00031 #include <qpainter.h>
00032 #include <qtextstream.h>
00033 #include <qfileinfo.h>
00034 
00035 #include <kinstance.h>
00036 #include <klocale.h>
00037 #include <ktempfile.h>
00038 #include <kmessagebox.h>
00039 #include <kio/job.h>
00040 #include <kstandarddirs.h>
00041 #include <kfiledialog.h>
00042 #include <kdirnotify_stub.h>
00043 
00044 #include <stdio.h>
00045 #include <unistd.h>
00046 #include <assert.h>
00047 #include <kdebug.h>
00048 
00049 template class QPtrList<KXMLGUIClient>;
00050 
00051 using namespace KParts;
00052 
00053 namespace KParts
00054 {
00055 
00056 class PartBasePrivate
00057 {
00058 public:
00059   PartBasePrivate()
00060   {
00061       m_pluginLoadingMode = PartBase::LoadPlugins;
00062   }
00063   ~PartBasePrivate()
00064   {
00065   }
00066   PartBase::PluginLoadingMode m_pluginLoadingMode;
00067 };
00068 
00069 class PartPrivate
00070 {
00071 public:
00072   PartPrivate()
00073   {
00074     m_bSelectable = true;
00075   }
00076   ~PartPrivate()
00077   {
00078   }
00079 
00080   bool m_bSelectable;
00081 };
00082 }
00083 
00084 PartBase::PartBase()
00085 {
00086   d = new PartBasePrivate;
00087   m_obj = 0L;
00088 }
00089 
00090 PartBase::~PartBase()
00091 {
00092   delete d;
00093 }
00094 
00095 void PartBase::setPartObject( QObject *obj )
00096 {
00097   m_obj = obj;
00098 }
00099 
00100 QObject *PartBase::partObject() const
00101 {
00102   return m_obj;
00103 }
00104 
00105 void PartBase::setInstance( KInstance *inst )
00106 {
00107   setInstance( inst, true );
00108 }
00109 
00110 void PartBase::setInstance( KInstance *inst, bool bLoadPlugins )
00111 {
00112   KXMLGUIClient::setInstance( inst );
00113   KGlobal::locale()->insertCatalogue( inst->instanceName() );
00114   // install 'instancename'data resource type
00115   KGlobal::dirs()->addResourceType( inst->instanceName() + "data",
00116                                     KStandardDirs::kde_default( "data" )
00117                                     + QString::fromLatin1( inst->instanceName() ) + '/' );
00118   if ( bLoadPlugins )
00119     loadPlugins( m_obj, this, instance() );
00120 }
00121 
00122 void PartBase::loadPlugins( QObject *parent, KXMLGUIClient *parentGUIClient, KInstance *instance )
00123 {
00124   if( d->m_pluginLoadingMode != DoNotLoadPlugins )
00125     Plugin::loadPlugins( parent, parentGUIClient, instance, d->m_pluginLoadingMode == LoadPlugins );
00126 }
00127 
00128 void PartBase::setPluginLoadingMode( PluginLoadingMode loadingMode )
00129 {
00130     d->m_pluginLoadingMode = loadingMode;
00131 }
00132 
00133 Part::Part( QObject *parent, const char* name )
00134  : QObject( parent, name )
00135 {
00136   d = new PartPrivate;
00137   m_widget = 0L;
00138   m_manager = 0L;
00139   PartBase::setPartObject( this );
00140 }
00141 
00142 Part::~Part()
00143 {
00144   kdDebug(1000) << "Part::~Part " << this << endl;
00145 
00146   if ( m_widget )
00147   {
00148     // We need to disconnect first, to avoid calling it !
00149     disconnect( m_widget, SIGNAL( destroyed() ),
00150                 this, SLOT( slotWidgetDestroyed() ) );
00151   }
00152 
00153   if ( m_manager )
00154     m_manager->removePart(this);
00155 
00156   if ( m_widget )
00157   {
00158     kdDebug(1000) << "deleting widget " << m_widget << " " << m_widget->name() << endl;
00159     delete (QWidget*) m_widget;
00160   }
00161 
00162   delete d;
00163 }
00164 
00165 void Part::embed( QWidget * parentWidget )
00166 {
00167   if ( widget() )
00168     widget()->reparent( parentWidget, 0, QPoint( 0, 0 ), true );
00169 }
00170 
00171 QWidget *Part::widget()
00172 {
00173   return m_widget;
00174 }
00175 
00176 void Part::setManager( PartManager *manager )
00177 {
00178   m_manager = manager;
00179 }
00180 
00181 PartManager *Part::manager() const
00182 {
00183   return m_manager;
00184 }
00185 
00186 Part *Part::hitTest( QWidget *widget, const QPoint & )
00187 {
00188   if ( (QWidget *)m_widget != widget )
00189     return 0L;
00190 
00191   return this;
00192 }
00193 
00194 void Part::setWidget( QWidget *widget )
00195 {
00196   assert ( !m_widget ); // otherwise we get two connects
00197   m_widget = widget;
00198   connect( m_widget, SIGNAL( destroyed() ),
00199            this, SLOT( slotWidgetDestroyed() ) );
00200 
00201   // Tell the actionCollection() which widget its
00202   //  action shortcuts should be connected to.
00203   actionCollection()->setWidget( widget );
00204 
00205   // Since KParts objects are XML-based, shortcuts should
00206   //  be connected to the widget when the XML settings
00207   //  are processed, rather than on KAction construction.
00208   actionCollection()->setAutoConnectShortcuts( false );
00209 }
00210 
00211 void Part::setSelectable( bool selectable )
00212 {
00213   d->m_bSelectable = selectable;
00214 }
00215 
00216 bool Part::isSelectable() const
00217 {
00218   return d->m_bSelectable;
00219 }
00220 
00221 void Part::customEvent( QCustomEvent *event )
00222 {
00223   if ( PartActivateEvent::test( event ) )
00224   {
00225     partActivateEvent( (PartActivateEvent *)event );
00226     return;
00227   }
00228 
00229   if ( PartSelectEvent::test( event ) )
00230   {
00231     partSelectEvent( (PartSelectEvent *)event );
00232     return;
00233   }
00234 
00235   if ( GUIActivateEvent::test( event ) )
00236   {
00237     guiActivateEvent( (GUIActivateEvent *)event );
00238     return;
00239   }
00240 
00241   QObject::customEvent( event );
00242 }
00243 
00244 void Part::partActivateEvent( PartActivateEvent * )
00245 {
00246 }
00247 
00248 void Part::partSelectEvent( PartSelectEvent * )
00249 {
00250 }
00251 
00252 void Part::guiActivateEvent( GUIActivateEvent * )
00253 {
00254 }
00255 
00256 QWidget *Part::hostContainer( const QString &containerName )
00257 {
00258   if ( !factory() )
00259     return 0L;
00260 
00261   return factory()->container( containerName, this );
00262 }
00263 
00264 void Part::slotWidgetDestroyed()
00265 {
00266   kdDebug(1000) << "KPart::slotWidgetDestroyed(), deleting part " << name() << endl;
00267   m_widget = 0;
00268   delete this;
00269 }
00270 
00272 
00273 namespace KParts
00274 {
00275 
00276 class ReadOnlyPartPrivate
00277 {
00278 public:
00279   ReadOnlyPartPrivate()
00280   {
00281     m_job = 0L;
00282     m_uploadJob = 0L;
00283     m_showProgressInfo = true;
00284     m_saveOk = false;
00285     m_waitForSave = false;
00286     m_duringSaveAs = false;
00287   }
00288   ~ReadOnlyPartPrivate()
00289   {
00290   }
00291 
00292   KIO::FileCopyJob * m_job;
00293   KIO::FileCopyJob * m_uploadJob;
00294   KURL m_originalURL;
00295   bool m_showProgressInfo : 1;
00296   bool m_saveOk : 1;
00297   bool m_waitForSave : 1;
00298   bool m_duringSaveAs : 1;
00299 };
00300 
00301 }
00302 
00303 ReadOnlyPart::ReadOnlyPart( QObject *parent, const char *name )
00304  : Part( parent, name ), m_bTemp( false )
00305 {
00306   d = new ReadOnlyPartPrivate;
00307 }
00308 
00309 ReadOnlyPart::~ReadOnlyPart()
00310 {
00311   ReadOnlyPart::closeURL();
00312   delete d;
00313 }
00314 
00315 void ReadOnlyPart::setProgressInfoEnabled( bool show )
00316 {
00317   d->m_showProgressInfo = show;
00318 }
00319 
00320 bool ReadOnlyPart::isProgressInfoEnabled() const
00321 {
00322   return d->m_showProgressInfo;
00323 }
00324 
00325 #ifndef KDE_NO_COMPAT
00326 void ReadOnlyPart::showProgressInfo( bool show )
00327 {
00328   d->m_showProgressInfo = show;
00329 }
00330 #endif
00331 
00332 bool ReadOnlyPart::openURL( const KURL &url )
00333 {
00334   if ( !url.isValid() )
00335     return false;
00336   if ( !closeURL() )
00337     return false;
00338   m_url = url;
00339   if ( m_url.isLocalFile() )
00340   {
00341     emit started( 0 );
00342     m_file = m_url.path();
00343     bool ret = openFile();
00344     if (ret)
00345     {
00346         emit completed();
00347         emit setWindowCaption( m_url.prettyURL() );
00348     };
00349     return ret;
00350   }
00351   else
00352   {
00353     m_bTemp = true;
00354     // Use same extension as remote file. This is important for mimetype-determination (e.g. koffice)
00355     QString fileName = url.fileName();
00356     QFileInfo fileInfo(fileName);
00357     QString ext = fileInfo.extension();
00358     QString extension;
00359     if ( !ext.isEmpty() && url.query().isNull() ) // not if the URL has a query, e.g. cgi.pl?something
00360         extension = "."+ext; // keep the '.'
00361     KTempFile tempFile( QString::null, extension );
00362     m_file = tempFile.name();
00363 
00364     KURL destURL;
00365     destURL.setPath( m_file );
00366     d->m_job = KIO::file_copy( m_url, destURL, 0600, true, false, d->m_showProgressInfo );
00367     d->m_job->setWindow( widget() ? widget()->topLevelWidget() : 0 );
00368     emit started( d->m_job );
00369     connect( d->m_job, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotJobFinished ( KIO::Job * ) ) );
00370     return true;
00371   }
00372 }
00373 
00374 void ReadOnlyPart::abortLoad()
00375 {
00376   if ( d->m_job )
00377   {
00378     //kdDebug(1000) << "Aborting job " << d->m_job << endl;
00379     d->m_job->kill();
00380     d->m_job = 0;
00381   }
00382 }
00383 
00384 bool ReadOnlyPart::closeURL()
00385 {
00386   abortLoad(); //just in case
00387 
00388   if ( m_bTemp )
00389   {
00390     unlink( QFile::encodeName(m_file) );
00391     m_bTemp = false;
00392   }
00393   // It always succeeds for a read-only part,
00394   // but the return value exists for reimplementations
00395   // (e.g. pressing cancel for a modified read-write part)
00396   return true;
00397 }
00398 
00399 void ReadOnlyPart::slotJobFinished( KIO::Job * job )
00400 {
00401   kdDebug(1000) << "ReadOnlyPart::slotJobFinished" << endl;
00402   assert( job == d->m_job );
00403   d->m_job = 0;
00404   if (job->error())
00405     emit canceled( job->errorString() );
00406   else
00407   {
00408     if ( openFile() )
00409       emit setWindowCaption( m_url.prettyURL() );
00410     emit completed();
00411   }
00412 }
00413 
00414 void ReadOnlyPart::guiActivateEvent( GUIActivateEvent * event )
00415 {
00416   if (event->activated())
00417   {
00418     if (!m_url.isEmpty())
00419     {
00420       kdDebug(1000) << "ReadOnlyPart::guiActivateEvent -> " << m_url.prettyURL() << endl;
00421       emit setWindowCaption( m_url.prettyURL() );
00422     } else emit setWindowCaption( "" );
00423   }
00424 }
00425 
00426 bool ReadOnlyPart::openStream( const QString& mimeType, const KURL& url )
00427 {
00428     if ( !closeURL() )
00429         return false;
00430     m_url = url;
00431     return doOpenStream( mimeType );
00432 }
00433 
00434 bool ReadOnlyPart::writeStream( const QByteArray& data )
00435 {
00436     return doWriteStream( data );
00437 }
00438 
00439 bool ReadOnlyPart::closeStream()
00440 {
00441     return doCloseStream();
00442 }
00443 
00445 
00446 ReadWritePart::ReadWritePart( QObject *parent, const char *name )
00447  : ReadOnlyPart( parent, name ), m_bModified( false ), m_bClosing( false )
00448 {
00449   m_bReadWrite = true;
00450 }
00451 
00452 ReadWritePart::~ReadWritePart()
00453 {
00454   // parent destructor will delete temp file
00455   // we can't call our own closeURL() here, because
00456   // "cancel" wouldn't cancel anything. We have to assume
00457   // the app called closeURL() before destroying us.
00458 }
00459 
00460 void ReadWritePart::setReadWrite( bool readwrite )
00461 {
00462   // Perhaps we should check isModified here and issue a warning if true
00463   m_bReadWrite = readwrite;
00464 }
00465 
00466 void ReadWritePart::setModified( bool modified )
00467 {
00468   kdDebug(1000) << "ReadWritePart::setModified( " << (modified ? "true" : "false") << ")" << endl;
00469   if ( !m_bReadWrite && modified )
00470   {
00471       kdError(1000) << "Can't set a read-only document to 'modified' !" << endl;
00472       return;
00473   }
00474   m_bModified = modified;
00475 }
00476 
00477 void ReadWritePart::setModified()
00478 {
00479   setModified( true );
00480 }
00481 
00482 bool ReadWritePart::queryClose()
00483 {
00484   if ( !isReadWrite() || !isModified() )
00485     return true;
00486 
00487   QString docName = url().fileName();
00488   if (docName.isEmpty()) docName = i18n( "Untitled" );
00489 
00490   int res = KMessageBox::warningYesNoCancel( widget(),
00491           i18n( "The document \"%1\" has been modified.\n"
00492                 "Do you want to save your changes or discard them?" ).arg( docName ),
00493           i18n( "Close Document" ), KStdGuiItem::save(), KStdGuiItem::discard() );
00494 
00495   bool abortClose=false;
00496   bool handled=false;
00497 
00498   switch(res) {
00499   case KMessageBox::Yes :
00500     sigQueryClose(&handled,&abortClose);
00501     if (!handled)
00502     {
00503       if (m_url.isEmpty())
00504       {
00505           KURL url = KFileDialog::getSaveURL();
00506           if (url.isEmpty())
00507             return false;
00508 
00509           saveAs( url );
00510       }
00511       else
00512       {
00513           save();
00514       }
00515     } else if (abortClose) return false;
00516     return waitSaveComplete();
00517   case KMessageBox::No :
00518     return true;
00519   default : // case KMessageBox::Cancel :
00520     return false;
00521   }
00522 }
00523 
00524 bool ReadWritePart::closeURL()
00525 {
00526   abortLoad(); //just in case
00527   if ( isReadWrite() && isModified() )
00528   {
00529     if (!queryClose())
00530        return false;
00531   }
00532   // Not modified => ok and delete temp file.
00533   return ReadOnlyPart::closeURL();
00534 }
00535 
00536 bool ReadWritePart::closeURL( bool promptToSave )
00537 {
00538   return promptToSave ? closeURL() : ReadOnlyPart::closeURL();
00539 }
00540 
00541 bool ReadWritePart::save()
00542 {
00543   d->m_saveOk = false;
00544   if ( m_file.isEmpty() ) // document was created empty
00545       prepareSaving();
00546   if( saveFile() )
00547     return saveToURL();
00548   else
00549     emit canceled(QString::null);
00550   return false;
00551 }
00552 
00553 bool ReadWritePart::saveAs( const KURL & kurl )
00554 {
00555   if (!kurl.isValid())
00556   {
00557       kdError(1000) << "saveAs: Malformed URL " << kurl.url() << endl;
00558       return false;
00559   }
00560   d->m_duringSaveAs = true;
00561   d->m_originalURL = m_url;
00562   m_url = kurl; // Store where to upload in saveToURL
00563   prepareSaving();
00564   bool result = save(); // Save local file and upload local file
00565   if (result)
00566     emit setWindowCaption( m_url.prettyURL() );
00567   else
00568   {
00569     m_url = d->m_originalURL;
00570     d->m_duringSaveAs = false;
00571     d->m_originalURL = KURL();
00572   }
00573 
00574   return result;
00575 }
00576 
00577 // Set m_file correctly for m_url
00578 void ReadWritePart::prepareSaving()
00579 {
00580   // Local file
00581   if ( m_url.isLocalFile() )
00582   {
00583     if ( m_bTemp ) // get rid of a possible temp file first
00584     {              // (happens if previous url was remote)
00585       unlink( QFile::encodeName(m_file) );
00586       m_bTemp = false;
00587     }
00588     m_file = m_url.path();
00589   }
00590   else
00591   { // Remote file
00592     // We haven't saved yet, or we did but locally - provide a temp file
00593     if ( m_file.isEmpty() || !m_bTemp )
00594     {
00595       KTempFile tempFile;
00596       m_file = tempFile.name();
00597       m_bTemp = true;
00598     }
00599     // otherwise, we already had a temp file
00600   }
00601 }
00602 
00603 bool ReadWritePart::saveToURL()
00604 {
00605   if ( m_url.isLocalFile() )
00606   {
00607     setModified( false );
00608     emit completed();
00609     // if m_url is a local file there won't be a temp file -> nothing to remove
00610     assert( !m_bTemp );
00611     d->m_saveOk = true;
00612     d->m_duringSaveAs = false;
00613     d->m_originalURL = KURL();
00614     return true; // Nothing to do
00615   }
00616   else
00617   {
00618     if (d->m_uploadJob)
00619     {
00620        unlink(QFile::encodeName(d->m_uploadJob->srcURL().path()));
00621        d->m_uploadJob->kill();
00622        d->m_uploadJob = 0;
00623     }
00624     KTempFile tempFile;
00625     QString uploadFile = tempFile.name();
00626     KURL uploadUrl;
00627     uploadUrl.setPath( uploadFile );
00628     tempFile.unlink();
00629     // Create hardlink
00630     if (::link(QFile::encodeName(m_file), QFile::encodeName(uploadFile)) != 0)
00631     {
00632        // Uh oh, some error happened.
00633        return false;
00634     }
00635     d->m_uploadJob = KIO::file_move( uploadUrl, m_url, -1, true /*overwrite*/ );
00636     d->m_uploadJob->setWindow( widget() ? widget()->topLevelWidget() : 0 );
00637     connect( d->m_uploadJob, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotUploadFinished (KIO::Job *) ) );
00638     return true;
00639   }
00640 }
00641 
00642 void ReadWritePart::slotUploadFinished( KIO::Job * )
00643 {
00644   if (d->m_uploadJob->error())
00645   {
00646     unlink(QFile::encodeName(d->m_uploadJob->srcURL().path()));
00647     QString error = d->m_uploadJob->errorString();
00648     d->m_uploadJob = 0;
00649     if (d->m_duringSaveAs)
00650       m_url = d->m_originalURL;
00651     emit canceled( error );
00652   }
00653   else
00654   {
00655     KDirNotify_stub allDirNotify("*", "KDirNotify*");
00656     KURL dirUrl( m_url );
00657     dirUrl.setPath( dirUrl.directory() );
00658     allDirNotify.FilesAdded( dirUrl );
00659 
00660     d->m_uploadJob = 0;
00661     setModified( false );
00662     emit completed();
00663     d->m_saveOk = true;
00664   }
00665   d->m_duringSaveAs = false;
00666   d->m_originalURL = KURL();
00667   if (d->m_waitForSave)
00668   {
00669      qApp->exit_loop();
00670   }
00671 }
00672 
00673 // Trolls: Nothing to see here, please step away.
00674 void qt_enter_modal( QWidget *widget );
00675 void qt_leave_modal( QWidget *widget );
00676 
00677 bool ReadWritePart::waitSaveComplete()
00678 {
00679   if (!d->m_uploadJob)
00680      return d->m_saveOk;
00681 
00682   d->m_waitForSave = true;
00683 
00684   QWidget dummy(0,0,WType_Dialog | WShowModal);
00685   dummy.setFocusPolicy( QWidget::NoFocus );
00686   qt_enter_modal(&dummy);
00687   qApp->enter_loop();
00688   qt_leave_modal(&dummy);
00689 
00690   d->m_waitForSave = false;
00691 
00692   return d->m_saveOk;
00693 }
00694 
00695 #include "part.moc"
00696 
00697 // vim:sw=2:ts=8:et
KDE Logo
This file is part of the documentation for kparts Library Version 3.4.2.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Aug 2 12:05:33 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003