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( saveFile() ) 00545 return saveToURL(); 00546 else 00547 emit canceled(QString::null); 00548 return false; 00549 } 00550 00551 bool ReadWritePart::saveAs( const KURL & kurl ) 00552 { 00553 if (!kurl.isValid()) 00554 { 00555 kdError(1000) << "saveAs: Malformed URL" << kurl.url() << endl; 00556 return false; 00557 } 00558 d->m_duringSaveAs = true; 00559 d->m_originalURL = m_url; 00560 m_url = kurl; // Store where to upload in saveToURL 00561 // Local file 00562 if ( m_url.isLocalFile() ) 00563 { 00564 if ( m_bTemp ) // get rid of a possible temp file first 00565 { // (happens if previous url was remote) 00566 unlink( QFile::encodeName(m_file) ); 00567 m_bTemp = false; 00568 } 00569 m_file = m_url.path(); 00570 } 00571 else 00572 { // Remote file 00573 // We haven't saved yet, or we did but locally - provide a temp file 00574 if ( m_file.isEmpty() || !m_bTemp ) 00575 { 00576 KTempFile tempFile; 00577 m_file = tempFile.name(); 00578 m_bTemp = true; 00579 } 00580 // otherwise, we already had a temp file 00581 } 00582 bool result = save(); // Save local file and upload local file 00583 if (result) 00584 emit setWindowCaption( m_url.prettyURL() ); 00585 else 00586 { 00587 m_url = d->m_originalURL; 00588 d->m_duringSaveAs = false; 00589 d->m_originalURL = KURL(); 00590 } 00591 00592 return result; 00593 } 00594 00595 bool ReadWritePart::saveToURL() 00596 { 00597 if ( m_url.isLocalFile() ) 00598 { 00599 setModified( false ); 00600 emit completed(); 00601 // if m_url is a local file there won't be a temp file -> nothing to remove 00602 assert( !m_bTemp ); 00603 d->m_saveOk = true; 00604 d->m_duringSaveAs = false; 00605 d->m_originalURL = KURL(); 00606 return true; // Nothing to do 00607 } 00608 else 00609 { 00610 if (d->m_uploadJob) 00611 { 00612 unlink(QFile::encodeName(d->m_uploadJob->srcURL().path())); 00613 d->m_uploadJob->kill(); 00614 d->m_uploadJob = 0; 00615 } 00616 KTempFile tempFile; 00617 QString uploadFile = tempFile.name(); 00618 KURL uploadUrl; 00619 uploadUrl.setPath( uploadFile ); 00620 tempFile.unlink(); 00621 // Create hardlink 00622 if (::link(QFile::encodeName(m_file), QFile::encodeName(uploadFile)) != 0) 00623 { 00624 // Uh oh, some error happened. 00625 return false; 00626 } 00627 d->m_uploadJob = KIO::file_move( uploadUrl, m_url, -1, true /*overwrite*/ ); 00628 d->m_uploadJob->setWindow( widget() ? widget()->topLevelWidget() : 0 ); 00629 connect( d->m_uploadJob, SIGNAL( result( KIO::Job * ) ), this, SLOT( slotUploadFinished (KIO::Job *) ) ); 00630 return true; 00631 } 00632 } 00633 00634 void ReadWritePart::slotUploadFinished( KIO::Job * ) 00635 { 00636 if (d->m_uploadJob->error()) 00637 { 00638 unlink(QFile::encodeName(d->m_uploadJob->srcURL().path())); 00639 QString error = d->m_uploadJob->errorString(); 00640 d->m_uploadJob = 0; 00641 if (d->m_duringSaveAs) 00642 m_url = d->m_originalURL; 00643 emit canceled( error ); 00644 } 00645 else 00646 { 00647 KDirNotify_stub allDirNotify("*", "KDirNotify*"); 00648 KURL dirUrl( m_url ); 00649 dirUrl.setPath( dirUrl.directory() ); 00650 allDirNotify.FilesAdded( dirUrl ); 00651 00652 d->m_uploadJob = 0; 00653 setModified( false ); 00654 emit completed(); 00655 d->m_saveOk = true; 00656 } 00657 d->m_duringSaveAs = false; 00658 d->m_originalURL = KURL(); 00659 if (d->m_waitForSave) 00660 { 00661 qApp->exit_loop(); 00662 } 00663 } 00664 00665 // Trolls: Nothing to see here, please step away. 00666 void qt_enter_modal( QWidget *widget ); 00667 void qt_leave_modal( QWidget *widget ); 00668 00669 bool ReadWritePart::waitSaveComplete() 00670 { 00671 if (!d->m_uploadJob) 00672 return d->m_saveOk; 00673 00674 d->m_waitForSave = true; 00675 00676 QWidget dummy(0,0,WType_Dialog | WShowModal); 00677 dummy.setFocusPolicy( QWidget::NoFocus ); 00678 qt_enter_modal(&dummy); 00679 qApp->enter_loop(); 00680 qt_leave_modal(&dummy); 00681 00682 d->m_waitForSave = false; 00683 00684 return d->m_saveOk; 00685 } 00686 00687 #include "part.moc" 00688 00689 // vim:sw=2:ts=8:et
KDE Logo
This file is part of the documentation for kparts Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Sep 29 09:44:10 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003