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., 51 Franklin Street, Fifth Floor, 00018 Boston, MA 02110-1301, 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 <tqapplication.h> 00028 #include <tqfile.h> 00029 #include <tqpoint.h> 00030 #include <tqpointarray.h> 00031 #include <tqpainter.h> 00032 #include <tqtextstream.h> 00033 #include <tqfileinfo.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 TQPtrList<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( TQObject *obj ) 00096 { 00097 m_obj = obj; 00098 } 00099 00100 TQObject *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 + TQString::fromLatin1( inst->instanceName() ) + '/' ); 00118 if ( bLoadPlugins ) 00119 loadPlugins( m_obj, this, instance() ); 00120 } 00121 00122 void PartBase::loadPlugins( TQObject *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( TQObject *parent, const char* name ) 00134 : TQObject( 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, TQT_SIGNAL( destroyed() ), 00150 this, TQT_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 (TQWidget*) m_widget; 00160 } 00161 00162 delete d; 00163 } 00164 00165 void Part::embed( TQWidget * parentWidget ) 00166 { 00167 if ( widget() ) 00168 widget()->reparent( parentWidget, 0, TQPoint( 0, 0 ), true ); 00169 } 00170 00171 TQWidget *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( TQWidget *widget, const TQPoint & ) 00187 { 00188 if ( (TQWidget *)m_widget != widget ) 00189 return 0L; 00190 00191 return this; 00192 } 00193 00194 void Part::setWidget( TQWidget *widget ) 00195 { 00196 assert ( !m_widget ); // otherwise we get two connects 00197 m_widget = widget; 00198 connect( m_widget, TQT_SIGNAL( destroyed() ), 00199 this, TQT_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( TQCustomEvent *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 TQObject::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 TQWidget *Part::hostContainer( const TQString &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; // for saveAs 00295 TQString m_originalFilePath; // for saveAs 00296 bool m_showProgressInfo : 1; 00297 bool m_saveOk : 1; 00298 bool m_waitForSave : 1; 00299 bool m_duringSaveAs : 1; 00300 }; 00301 00302 } 00303 00304 ReadOnlyPart::ReadOnlyPart( TQObject *parent, const char *name ) 00305 : Part( parent, name ), m_bTemp( false ) 00306 { 00307 d = new ReadOnlyPartPrivate; 00308 } 00309 00310 ReadOnlyPart::~ReadOnlyPart() 00311 { 00312 ReadOnlyPart::closeURL(); 00313 delete d; 00314 } 00315 00316 void ReadOnlyPart::setProgressInfoEnabled( bool show ) 00317 { 00318 d->m_showProgressInfo = show; 00319 } 00320 00321 bool ReadOnlyPart::isProgressInfoEnabled() const 00322 { 00323 return d->m_showProgressInfo; 00324 } 00325 00326 #ifndef KDE_NO_COMPAT 00327 void ReadOnlyPart::showProgressInfo( bool show ) 00328 { 00329 d->m_showProgressInfo = show; 00330 } 00331 #endif 00332 00333 bool ReadOnlyPart::openURL( const KURL &url ) 00334 { 00335 if ( !url.isValid() ) 00336 return false; 00337 if ( !closeURL() ) 00338 return false; 00339 m_url = url; 00340 if ( m_url.isLocalFile() ) 00341 { 00342 emit started( 0 ); 00343 m_file = m_url.path(); 00344 bool ret = openFile(); 00345 if (ret) 00346 { 00347 emit completed(); 00348 emit setWindowCaption( m_url.prettyURL() ); 00349 }; 00350 return ret; 00351 } 00352 else 00353 { 00354 m_bTemp = true; 00355 // Use same extension as remote file. This is important for mimetype-determination (e.g. koffice) 00356 TQString fileName = url.fileName(); 00357 TQFileInfo fileInfo(fileName); 00358 TQString ext = fileInfo.extension(); 00359 TQString extension; 00360 if ( !ext.isEmpty() && url.query().isNull() ) // not if the URL has a query, e.g. cgi.pl?something 00361 extension = "."+ext; // keep the '.' 00362 KTempFile tempFile( TQString::null, extension ); 00363 m_file = tempFile.name(); 00364 00365 KURL destURL; 00366 destURL.setPath( m_file ); 00367 d->m_job = KIO::file_copy( m_url, destURL, 0600, true, false, d->m_showProgressInfo ); 00368 d->m_job->setWindow( widget() ? widget()->topLevelWidget() : 0 ); 00369 emit started( d->m_job ); 00370 connect( d->m_job, TQT_SIGNAL( result( KIO::Job * ) ), this, TQT_SLOT( slotJobFinished ( KIO::Job * ) ) ); 00371 return true; 00372 } 00373 } 00374 00375 void ReadOnlyPart::abortLoad() 00376 { 00377 if ( d->m_job ) 00378 { 00379 //kdDebug(1000) << "Aborting job " << d->m_job << endl; 00380 d->m_job->kill(); 00381 d->m_job = 0; 00382 } 00383 } 00384 00385 bool ReadOnlyPart::closeURL() 00386 { 00387 abortLoad(); //just in case 00388 00389 if ( m_bTemp ) 00390 { 00391 unlink( TQFile::encodeName(m_file) ); 00392 m_bTemp = false; 00393 } 00394 // It always succeeds for a read-only part, 00395 // but the return value exists for reimplementations 00396 // (e.g. pressing cancel for a modified read-write part) 00397 return true; 00398 } 00399 00400 void ReadOnlyPart::slotJobFinished( KIO::Job * job ) 00401 { 00402 kdDebug(1000) << "ReadOnlyPart::slotJobFinished" << endl; 00403 assert( job == d->m_job ); 00404 d->m_job = 0; 00405 if (job->error()) 00406 emit canceled( job->errorString() ); 00407 else 00408 { 00409 if ( openFile() ) 00410 emit setWindowCaption( m_url.prettyURL() ); 00411 emit completed(); 00412 } 00413 } 00414 00415 void ReadOnlyPart::guiActivateEvent( GUIActivateEvent * event ) 00416 { 00417 if (event->activated()) 00418 { 00419 if (!m_url.isEmpty()) 00420 { 00421 kdDebug(1000) << "ReadOnlyPart::guiActivateEvent -> " << m_url.prettyURL() << endl; 00422 emit setWindowCaption( m_url.prettyURL() ); 00423 } else emit setWindowCaption( "" ); 00424 } 00425 } 00426 00427 bool ReadOnlyPart::openStream( const TQString& mimeType, const KURL& url ) 00428 { 00429 if ( !closeURL() ) 00430 return false; 00431 m_url = url; 00432 return doOpenStream( mimeType ); 00433 } 00434 00435 bool ReadOnlyPart::writeStream( const TQByteArray& data ) 00436 { 00437 return doWriteStream( data ); 00438 } 00439 00440 bool ReadOnlyPart::closeStream() 00441 { 00442 return doCloseStream(); 00443 } 00444 00446 00447 ReadWritePart::ReadWritePart( TQObject *parent, const char *name ) 00448 : ReadOnlyPart( parent, name ), m_bModified( false ), m_bClosing( false ) 00449 { 00450 m_bReadWrite = true; 00451 } 00452 00453 ReadWritePart::~ReadWritePart() 00454 { 00455 // parent destructor will delete temp file 00456 // we can't call our own closeURL() here, because 00457 // "cancel" wouldn't cancel anything. We have to assume 00458 // the app called closeURL() before destroying us. 00459 } 00460 00461 void ReadWritePart::setReadWrite( bool readwrite ) 00462 { 00463 // Perhaps we should check isModified here and issue a warning if true 00464 m_bReadWrite = readwrite; 00465 } 00466 00467 void ReadWritePart::setModified( bool modified ) 00468 { 00469 kdDebug(1000) << "ReadWritePart::setModified( " << (modified ? "true" : "false") << ")" << endl; 00470 if ( !m_bReadWrite && modified ) 00471 { 00472 kdError(1000) << "Can't set a read-only document to 'modified' !" << endl; 00473 return; 00474 } 00475 m_bModified = modified; 00476 } 00477 00478 void ReadWritePart::setModified() 00479 { 00480 setModified( true ); 00481 } 00482 00483 bool ReadWritePart::queryClose() 00484 { 00485 if ( !isReadWrite() || !isModified() ) 00486 return true; 00487 00488 TQString docName = url().fileName(); 00489 if (docName.isEmpty()) docName = i18n( "Untitled" ); 00490 00491 int res = KMessageBox::warningYesNoCancel( widget(), 00492 i18n( "The document \"%1\" has been modified.\n" 00493 "Do you want to save your changes or discard them?" ).arg( docName ), 00494 i18n( "Close Document" ), KStdGuiItem::save(), KStdGuiItem::discard() ); 00495 00496 bool abortClose=false; 00497 bool handled=false; 00498 00499 switch(res) { 00500 case KMessageBox::Yes : 00501 sigQueryClose(&handled,&abortClose); 00502 if (!handled) 00503 { 00504 if (m_url.isEmpty()) 00505 { 00506 KURL url = KFileDialog::getSaveURL(); 00507 if (url.isEmpty()) 00508 return false; 00509 00510 saveAs( url ); 00511 } 00512 else 00513 { 00514 save(); 00515 } 00516 } else if (abortClose) return false; 00517 return waitSaveComplete(); 00518 case KMessageBox::No : 00519 return true; 00520 default : // case KMessageBox::Cancel : 00521 return false; 00522 } 00523 } 00524 00525 bool ReadWritePart::closeURL() 00526 { 00527 abortLoad(); //just in case 00528 if ( isReadWrite() && isModified() ) 00529 { 00530 if (!queryClose()) 00531 return false; 00532 } 00533 // Not modified => ok and delete temp file. 00534 return ReadOnlyPart::closeURL(); 00535 } 00536 00537 bool ReadWritePart::closeURL( bool promptToSave ) 00538 { 00539 return promptToSave ? closeURL() : ReadOnlyPart::closeURL(); 00540 } 00541 00542 bool ReadWritePart::save() 00543 { 00544 d->m_saveOk = false; 00545 if ( m_file.isEmpty() ) // document was created empty 00546 prepareSaving(); 00547 if( saveFile() ) 00548 return saveToURL(); 00549 else 00550 emit canceled(TQString::null); 00551 return false; 00552 } 00553 00554 bool ReadWritePart::saveAs( const KURL & kurl ) 00555 { 00556 if (!kurl.isValid()) 00557 { 00558 kdError(1000) << "saveAs: Malformed URL " << kurl.url() << endl; 00559 return false; 00560 } 00561 d->m_duringSaveAs = true; 00562 d->m_originalURL = m_url; 00563 d->m_originalFilePath = m_file; 00564 m_url = kurl; // Store where to upload in saveToURL 00565 prepareSaving(); 00566 bool result = save(); // Save local file and upload local file 00567 if (result) 00568 emit setWindowCaption( m_url.prettyURL() ); 00569 else 00570 { 00571 m_url = d->m_originalURL; 00572 m_file = d->m_originalFilePath; 00573 d->m_duringSaveAs = false; 00574 d->m_originalURL = KURL(); 00575 d->m_originalFilePath = TQString::null; 00576 } 00577 00578 return result; 00579 } 00580 00581 // Set m_file correctly for m_url 00582 void ReadWritePart::prepareSaving() 00583 { 00584 // Local file 00585 if ( m_url.isLocalFile() ) 00586 { 00587 if ( m_bTemp ) // get rid of a possible temp file first 00588 { // (happens if previous url was remote) 00589 unlink( TQFile::encodeName(m_file) ); 00590 m_bTemp = false; 00591 } 00592 m_file = m_url.path(); 00593 } 00594 else 00595 { // Remote file 00596 // We haven't saved yet, or we did but locally - provide a temp file 00597 if ( m_file.isEmpty() || !m_bTemp ) 00598 { 00599 KTempFile tempFile; 00600 m_file = tempFile.name(); 00601 m_bTemp = true; 00602 } 00603 // otherwise, we already had a temp file 00604 } 00605 } 00606 00607 bool ReadWritePart::saveToURL() 00608 { 00609 if ( m_url.isLocalFile() ) 00610 { 00611 setModified( false ); 00612 emit completed(); 00613 // if m_url is a local file there won't be a temp file -> nothing to remove 00614 assert( !m_bTemp ); 00615 d->m_saveOk = true; 00616 d->m_duringSaveAs = false; 00617 d->m_originalURL = KURL(); 00618 d->m_originalFilePath = TQString::null; 00619 return true; // Nothing to do 00620 } 00621 else 00622 { 00623 if (d->m_uploadJob) 00624 { 00625 unlink(TQFile::encodeName(d->m_uploadJob->srcURL().path())); 00626 d->m_uploadJob->kill(); 00627 d->m_uploadJob = 0; 00628 } 00629 KTempFile tempFile; 00630 TQString uploadFile = tempFile.name(); 00631 KURL uploadUrl; 00632 uploadUrl.setPath( uploadFile ); 00633 tempFile.unlink(); 00634 // Create hardlink 00635 if (::link(TQFile::encodeName(m_file), TQFile::encodeName(uploadFile)) != 0) 00636 { 00637 // Uh oh, some error happened. 00638 return false; 00639 } 00640 d->m_uploadJob = KIO::file_move( uploadUrl, m_url, -1, true /*overwrite*/ ); 00641 d->m_uploadJob->setWindow( widget() ? widget()->topLevelWidget() : 0 ); 00642 connect( d->m_uploadJob, TQT_SIGNAL( result( KIO::Job * ) ), this, TQT_SLOT( slotUploadFinished (KIO::Job *) ) ); 00643 return true; 00644 } 00645 } 00646 00647 void ReadWritePart::slotUploadFinished( KIO::Job * ) 00648 { 00649 if (d->m_uploadJob->error()) 00650 { 00651 unlink(TQFile::encodeName(d->m_uploadJob->srcURL().path())); 00652 TQString error = d->m_uploadJob->errorString(); 00653 d->m_uploadJob = 0; 00654 if (d->m_duringSaveAs) { 00655 m_url = d->m_originalURL; 00656 m_file = d->m_originalFilePath; 00657 } 00658 emit canceled( error ); 00659 } 00660 else 00661 { 00662 KDirNotify_stub allDirNotify("*", "KDirNotify*"); 00663 KURL dirUrl( m_url ); 00664 dirUrl.setPath( dirUrl.directory() ); 00665 allDirNotify.FilesAdded( dirUrl ); 00666 00667 d->m_uploadJob = 0; 00668 setModified( false ); 00669 emit completed(); 00670 d->m_saveOk = true; 00671 } 00672 d->m_duringSaveAs = false; 00673 d->m_originalURL = KURL(); 00674 d->m_originalFilePath = TQString::null; 00675 if (d->m_waitForSave) 00676 { 00677 tqApp->exit_loop(); 00678 } 00679 } 00680 00681 // Trolls: Nothing to see here, please step away. 00682 void qt_enter_modal( TQWidget *widget ); 00683 void qt_leave_modal( TQWidget *widget ); 00684 00685 bool ReadWritePart::waitSaveComplete() 00686 { 00687 if (!d->m_uploadJob) 00688 return d->m_saveOk; 00689 00690 d->m_waitForSave = true; 00691 00692 TQWidget dummy(0,0,(WFlags)(WType_Dialog | WShowModal)); 00693 dummy.setFocusPolicy( TQ_NoFocus ); 00694 qt_enter_modal(&dummy); 00695 tqApp->enter_loop(); 00696 qt_leave_modal(&dummy); 00697 00698 d->m_waitForSave = false; 00699 00700 return d->m_saveOk; 00701 } 00702 00703 #include "part.moc" 00704 00705 // vim:sw=2:ts=8:et