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