KParts
part.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE project 00002 Copyright (C) 1999 Simon Hausmann <hausmann@kde.org> 00003 (C) 1999-2005 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 "part.h" 00022 #include <kprotocolinfo.h> 00023 #include "event.h" 00024 #include "plugin.h" 00025 #include "mainwindow.h" 00026 #include "partmanager.h" 00027 #include "browserextension.h" 00028 00029 #include <QtGui/QApplication> 00030 #include <QtCore/QFile> 00031 #include <QtCore/QFileInfo> 00032 #include <QtGui/QPainter> 00033 #include <QtCore/QPoint> 00034 00035 #include <kdirnotify.h> 00036 #include <kfiledialog.h> 00037 #include <kcomponentdata.h> 00038 #include <kio/job.h> 00039 #include <kio/jobuidelegate.h> 00040 #include <klocale.h> 00041 #include <kmessagebox.h> 00042 #include <kstandarddirs.h> 00043 #include <ktemporaryfile.h> 00044 #include <kxmlguifactory.h> 00045 00046 #include <stdio.h> 00047 #include <unistd.h> 00048 #include <assert.h> 00049 #include <kdebug.h> 00050 #include <kiconloader.h> 00051 00052 using namespace KParts; 00053 00054 namespace KParts 00055 { 00056 00057 class PartBasePrivate 00058 { 00059 public: 00060 Q_DECLARE_PUBLIC(PartBase) 00061 00062 PartBasePrivate(PartBase *q): q_ptr(q) 00063 { 00064 m_pluginLoadingMode = PartBase::LoadPlugins; 00065 m_pluginInterfaceVersion = 0; 00066 m_obj = 0; 00067 } 00068 00069 virtual ~PartBasePrivate() 00070 { 00071 } 00072 00073 PartBase *q_ptr; 00074 PartBase::PluginLoadingMode m_pluginLoadingMode; 00075 int m_pluginInterfaceVersion; 00076 QObject *m_obj; 00077 }; 00078 00079 class PartPrivate: public PartBasePrivate 00080 { 00081 public: 00082 Q_DECLARE_PUBLIC(Part) 00083 00084 PartPrivate(Part *q) 00085 : PartBasePrivate(q), 00086 m_iconLoader(0), 00087 m_bSelectable(true), 00088 m_autoDeleteWidget(true), 00089 m_autoDeletePart(true), 00090 m_manager(0) 00091 { 00092 } 00093 00094 ~PartPrivate() 00095 { 00096 } 00097 00098 KIconLoader* m_iconLoader; 00099 bool m_bSelectable; 00100 bool m_autoDeleteWidget; 00101 bool m_autoDeletePart; 00102 PartManager * m_manager; 00103 QPointer<QWidget> m_widget; 00104 }; 00105 00106 } 00107 00108 PartBase::PartBase() 00109 : d_ptr(new PartBasePrivate(this)) 00110 { 00111 } 00112 00113 PartBase::PartBase(PartBasePrivate &dd) 00114 : d_ptr(&dd) 00115 { 00116 } 00117 00118 PartBase::~PartBase() 00119 { 00120 delete d_ptr; 00121 } 00122 00123 void PartBase::setPartObject( QObject *obj ) 00124 { 00125 Q_D(PartBase); 00126 00127 d->m_obj = obj; 00128 } 00129 00130 QObject *PartBase::partObject() const 00131 { 00132 Q_D(const PartBase); 00133 00134 return d->m_obj; 00135 } 00136 00137 void PartBase::setComponentData(const KComponentData &componentData) 00138 { 00139 setComponentData(componentData, true); 00140 } 00141 00142 void PartBase::setComponentData(const KComponentData &componentData, bool bLoadPlugins) 00143 { 00144 Q_D(PartBase); 00145 00146 KXMLGUIClient::setComponentData(componentData); 00147 KGlobal::locale()->insertCatalog(componentData.catalogName()); 00148 // install 'instancename'data resource type 00149 KGlobal::dirs()->addResourceType(QString(componentData.componentName() + "data").toUtf8(), 00150 "data", componentData.componentName()); 00151 if (bLoadPlugins) { 00152 loadPlugins(d->m_obj, this, componentData); 00153 } 00154 } 00155 00156 void PartBase::loadPlugins(QObject *parent, KXMLGUIClient *parentGUIClient, const KComponentData &instance) 00157 { 00158 Q_D(PartBase); 00159 00160 if( d->m_pluginLoadingMode != DoNotLoadPlugins ) 00161 Plugin::loadPlugins( parent, parentGUIClient, instance, d->m_pluginLoadingMode == LoadPlugins, d->m_pluginInterfaceVersion ); 00162 } 00163 00164 void PartBase::setPluginLoadingMode( PluginLoadingMode loadingMode ) 00165 { 00166 Q_D(PartBase); 00167 00168 d->m_pluginLoadingMode = loadingMode; 00169 } 00170 00171 void KParts::PartBase::setPluginInterfaceVersion( int version ) 00172 { 00173 Q_D(PartBase); 00174 00175 d->m_pluginInterfaceVersion = version; 00176 } 00177 00178 Part::Part( QObject *parent ) 00179 : QObject( parent ), PartBase( *new PartPrivate(this) ) 00180 { 00181 PartBase::setPartObject( this ); 00182 } 00183 00184 Part::Part(PartPrivate &dd, QObject *parent) 00185 : QObject( parent ), PartBase( dd ) 00186 { 00187 PartBase::setPartObject( this ); 00188 } 00189 00190 Part::~Part() 00191 { 00192 Q_D(Part); 00193 00194 //kDebug(1000) << this; 00195 00196 if ( d->m_widget ) 00197 { 00198 // We need to disconnect first, to avoid calling it ! 00199 disconnect( d->m_widget, SIGNAL( destroyed() ), 00200 this, SLOT( slotWidgetDestroyed() ) ); 00201 } 00202 00203 if ( d->m_manager ) 00204 d->m_manager->removePart(this); 00205 00206 if ( d->m_widget && d->m_autoDeleteWidget ) 00207 { 00208 kDebug(1000) << "deleting widget" << d->m_widget << d->m_widget->objectName(); 00209 delete static_cast<QWidget*>(d->m_widget); 00210 } 00211 00212 delete d->m_iconLoader; 00213 } 00214 00215 void Part::embed( QWidget * parentWidget ) 00216 { 00217 if ( widget() ) 00218 { 00219 widget()->setParent( parentWidget, 0 ); 00220 widget()->setGeometry( 0, 0, widget()->width(), widget()->height() ); 00221 widget()->show(); 00222 } 00223 } 00224 00225 QWidget *Part::widget() 00226 { 00227 Q_D(Part); 00228 00229 return d->m_widget; 00230 } 00231 00232 void Part::setAutoDeleteWidget(bool autoDeleteWidget) 00233 { 00234 Q_D(Part); 00235 d->m_autoDeleteWidget = autoDeleteWidget; 00236 } 00237 00238 void Part::setAutoDeletePart(bool autoDeletePart) 00239 { 00240 Q_D(Part); 00241 d->m_autoDeletePart = autoDeletePart; 00242 } 00243 00244 00245 00246 KIconLoader* Part::iconLoader() 00247 { 00248 Q_D(Part); 00249 00250 if (!d->m_iconLoader) { 00251 Q_ASSERT(componentData().isValid()); 00252 d->m_iconLoader = new KIconLoader( componentData() ); 00253 } 00254 return d->m_iconLoader; 00255 } 00256 00257 void Part::setManager( PartManager *manager ) 00258 { 00259 Q_D(Part); 00260 00261 d->m_manager = manager; 00262 } 00263 00264 PartManager *Part::manager() const 00265 { 00266 Q_D(const Part); 00267 00268 return d->m_manager; 00269 } 00270 00271 Part *Part::hitTest( QWidget *widget, const QPoint & ) 00272 { 00273 Q_D(Part); 00274 00275 if ( (QWidget *)d->m_widget != widget ) 00276 return 0; 00277 00278 return this; 00279 } 00280 00281 void Part::setWidget( QWidget *widget ) 00282 { 00283 Q_D(Part); 00284 00285 assert ( !d->m_widget ); // otherwise we get two connects 00286 d->m_widget = widget; 00287 connect( d->m_widget, SIGNAL( destroyed() ), 00288 this, SLOT( slotWidgetDestroyed() ) ); 00289 } 00290 00291 void Part::setSelectable( bool selectable ) 00292 { 00293 Q_D(Part); 00294 00295 d->m_bSelectable = selectable; 00296 } 00297 00298 bool Part::isSelectable() const 00299 { 00300 Q_D(const Part); 00301 00302 return d->m_bSelectable; 00303 } 00304 00305 void Part::customEvent( QEvent *ev ) 00306 { 00307 if ( PartActivateEvent::test( ev ) ) 00308 { 00309 partActivateEvent( static_cast<PartActivateEvent *>(ev) ); 00310 return; 00311 } 00312 00313 if ( PartSelectEvent::test( ev ) ) 00314 { 00315 partSelectEvent( static_cast<PartSelectEvent *>(ev) ); 00316 return; 00317 } 00318 00319 if ( GUIActivateEvent::test( ev ) ) 00320 { 00321 guiActivateEvent( static_cast<GUIActivateEvent *>(ev) ); 00322 return; 00323 } 00324 00325 QObject::customEvent( ev ); 00326 } 00327 00328 void Part::partActivateEvent( PartActivateEvent * ) 00329 { 00330 } 00331 00332 void Part::partSelectEvent( PartSelectEvent * ) 00333 { 00334 } 00335 00336 void Part::guiActivateEvent( GUIActivateEvent * ) 00337 { 00338 } 00339 00340 QWidget *Part::hostContainer( const QString &containerName ) 00341 { 00342 if ( !factory() ) 00343 return 0; 00344 00345 return factory()->container( containerName, this ); 00346 } 00347 00348 void Part::slotWidgetDestroyed() 00349 { 00350 Q_D(Part); 00351 00352 d->m_widget = 0; 00353 if (d->m_autoDeletePart) { 00354 kDebug(1000) << "deleting part" << objectName(); 00355 delete this; // ouch, this should probably be deleteLater() 00356 } 00357 } 00358 00359 void Part::loadPlugins() 00360 { 00361 PartBase::loadPlugins(this, this, componentData()); 00362 } 00363 00365 00366 namespace KParts 00367 { 00368 00369 class ReadOnlyPartPrivate: public PartPrivate 00370 { 00371 public: 00372 Q_DECLARE_PUBLIC(ReadOnlyPart) 00373 00374 ReadOnlyPartPrivate(ReadOnlyPart *q): PartPrivate(q) 00375 { 00376 m_job = 0; 00377 m_statJob = 0; 00378 m_uploadJob = 0; 00379 m_showProgressInfo = true; 00380 m_saveOk = false; 00381 m_waitForSave = false; 00382 m_duringSaveAs = false; 00383 m_bTemp = false; 00384 m_bAutoDetectedMime = false; 00385 } 00386 00387 ~ReadOnlyPartPrivate() 00388 { 00389 } 00390 00391 void _k_slotJobFinished( KJob * job ); 00392 void _k_slotStatJobFinished(KJob * job); 00393 void _k_slotGotMimeType(KIO::Job *job, const QString &mime); 00394 bool openLocalFile(); 00395 void openRemoteFile(); 00396 00397 KIO::FileCopyJob * m_job; 00398 KIO::StatJob * m_statJob; 00399 KIO::FileCopyJob * m_uploadJob; 00400 KUrl m_originalURL; // for saveAs 00401 QString m_originalFilePath; // for saveAs 00402 bool m_showProgressInfo : 1; 00403 bool m_saveOk : 1; 00404 bool m_waitForSave : 1; 00405 bool m_duringSaveAs : 1; 00406 00410 bool m_bTemp: 1; 00411 00412 // whether the mimetype in the arguments was detected by the part itself 00413 bool m_bAutoDetectedMime : 1; 00414 00418 KUrl m_url; 00419 00423 QString m_file; 00424 00425 OpenUrlArguments m_arguments; 00426 }; 00427 00428 class ReadWritePartPrivate: public ReadOnlyPartPrivate 00429 { 00430 public: 00431 Q_DECLARE_PUBLIC(ReadWritePart) 00432 00433 ReadWritePartPrivate(ReadWritePart *q): ReadOnlyPartPrivate(q) 00434 { 00435 m_bModified = false; 00436 m_bReadWrite = true; 00437 m_bClosing = false; 00438 } 00439 00440 void _k_slotUploadFinished( KJob * job ); 00441 00442 void prepareSaving(); 00443 00444 bool m_bModified; 00445 bool m_bReadWrite; 00446 bool m_bClosing; 00447 QEventLoop m_eventLoop; 00448 }; 00449 00450 } 00451 00452 ReadOnlyPart::ReadOnlyPart( QObject *parent ) 00453 : Part( *new ReadOnlyPartPrivate(this), parent ) 00454 { 00455 } 00456 00457 ReadOnlyPart::ReadOnlyPart( ReadOnlyPartPrivate &dd, QObject *parent ) 00458 : Part( dd, parent ) 00459 { 00460 } 00461 00462 ReadOnlyPart::~ReadOnlyPart() 00463 { 00464 ReadOnlyPart::closeUrl(); 00465 } 00466 00467 KUrl ReadOnlyPart::url() const 00468 { 00469 Q_D(const ReadOnlyPart); 00470 00471 return d->m_url; 00472 } 00473 00474 void ReadOnlyPart::setUrl(const KUrl &url) 00475 { 00476 Q_D(ReadOnlyPart); 00477 00478 d->m_url = url; 00479 } 00480 00481 QString ReadOnlyPart::localFilePath() const 00482 { 00483 Q_D(const ReadOnlyPart); 00484 00485 return d->m_file; 00486 } 00487 00488 void ReadOnlyPart::setLocalFilePath( const QString &localFilePath ) 00489 { 00490 Q_D(ReadOnlyPart); 00491 00492 d->m_file = localFilePath; 00493 } 00494 00495 #ifndef KDE_NO_DEPRECATED 00496 bool ReadOnlyPart::isLocalFileTemporary() const 00497 { 00498 Q_D(const ReadOnlyPart); 00499 00500 return d->m_bTemp; 00501 } 00502 #endif 00503 00504 #ifndef KDE_NO_DEPRECATED 00505 void ReadOnlyPart::setLocalFileTemporary( bool temp ) 00506 { 00507 Q_D(ReadOnlyPart); 00508 00509 d->m_bTemp = temp; 00510 } 00511 #endif 00512 00513 void ReadOnlyPart::setProgressInfoEnabled( bool show ) 00514 { 00515 Q_D(ReadOnlyPart); 00516 00517 d->m_showProgressInfo = show; 00518 } 00519 00520 bool ReadOnlyPart::isProgressInfoEnabled() const 00521 { 00522 Q_D(const ReadOnlyPart); 00523 00524 return d->m_showProgressInfo; 00525 } 00526 00527 #ifndef KDE_NO_COMPAT 00528 void ReadOnlyPart::showProgressInfo( bool show ) 00529 { 00530 Q_D(ReadOnlyPart); 00531 00532 d->m_showProgressInfo = show; 00533 } 00534 #endif 00535 00536 bool ReadOnlyPart::openUrl( const KUrl &url ) 00537 { 00538 Q_D(ReadOnlyPart); 00539 00540 if ( !url.isValid() ) 00541 return false; 00542 if (d->m_bAutoDetectedMime) { 00543 d->m_arguments.setMimeType(QString()); 00544 d->m_bAutoDetectedMime = false; 00545 } 00546 OpenUrlArguments args = d->m_arguments; 00547 if ( !closeUrl() ) 00548 return false; 00549 d->m_arguments = args; 00550 d->m_url = url; 00551 00552 d->m_file.clear(); 00553 00554 if (d->m_url.isLocalFile()) { 00555 d->m_file = d->m_url.toLocalFile(); 00556 return d->openLocalFile(); 00557 } else if (KProtocolInfo::protocolClass(url.protocol()) == ":local") { 00558 // Maybe we can use a "local path", to avoid a temp copy? 00559 KIO::JobFlags flags = d->m_showProgressInfo ? KIO::DefaultFlags : KIO::HideProgressInfo; 00560 d->m_statJob = KIO::mostLocalUrl(d->m_url, flags); 00561 d->m_statJob->ui()->setWindow( widget() ? widget()->topLevelWidget() : 0 ); 00562 connect(d->m_statJob, SIGNAL(result(KJob*)), this, SLOT(_k_slotStatJobFinished(KJob*))); 00563 return true; 00564 } else { 00565 d->openRemoteFile(); 00566 return true; 00567 } 00568 } 00569 00570 bool ReadOnlyPart::openFile() 00571 { 00572 kWarning(1000) << "Default implementation of ReadOnlyPart::openFile called!" 00573 << metaObject()->className() << "should reimplement either openUrl or openFile."; 00574 return false; 00575 } 00576 00577 bool ReadOnlyPartPrivate::openLocalFile() 00578 { 00579 Q_Q(ReadOnlyPart); 00580 emit q->started( 0 ); 00581 m_bTemp = false; 00582 // set the mimetype only if it was not already set (for example, by the host application) 00583 if (m_arguments.mimeType().isEmpty()) { 00584 // get the mimetype of the file 00585 // using findByUrl() to avoid another string -> url conversion 00586 KMimeType::Ptr mime = KMimeType::findByUrl(m_url, 0, true /* local file*/); 00587 if (mime) { 00588 m_arguments.setMimeType(mime->name()); 00589 m_bAutoDetectedMime = true; 00590 } 00591 } 00592 const bool ret = q->openFile(); 00593 if (ret) { 00594 emit q->setWindowCaption(m_url.prettyUrl()); 00595 emit q->completed(); 00596 } else { 00597 emit q->canceled(QString()); 00598 } 00599 return ret; 00600 } 00601 00602 void ReadOnlyPartPrivate::openRemoteFile() 00603 { 00604 Q_Q(ReadOnlyPart); 00605 m_bTemp = true; 00606 // Use same extension as remote file. This is important for mimetype-determination (e.g. koffice) 00607 QString fileName = m_url.fileName(); 00608 QFileInfo fileInfo(fileName); 00609 QString ext = fileInfo.completeSuffix(); 00610 QString extension; 00611 if (!ext.isEmpty() && m_url.query().isNull()) // not if the URL has a query, e.g. cgi.pl?something 00612 extension = '.'+ext; // keep the '.' 00613 KTemporaryFile tempFile; 00614 tempFile.setSuffix(extension); 00615 tempFile.setAutoRemove(false); 00616 tempFile.open(); 00617 m_file = tempFile.fileName(); 00618 00619 KUrl destURL; 00620 destURL.setPath( m_file ); 00621 KIO::JobFlags flags = m_showProgressInfo ? KIO::DefaultFlags : KIO::HideProgressInfo; 00622 flags |= KIO::Overwrite; 00623 m_job = KIO::file_copy(m_url, destURL, 0600, flags); 00624 m_job->ui()->setWindow(q->widget() ? q->widget()->topLevelWidget() : 0); 00625 emit q->started(m_job); 00626 QObject::connect(m_job, SIGNAL(result(KJob*)), q, SLOT(_k_slotJobFinished(KJob*))); 00627 QObject::connect(m_job, SIGNAL(mimetype(KIO::Job*, QString)), 00628 q, SLOT(_k_slotGotMimeType(KIO::Job*,QString))); 00629 } 00630 00631 void ReadOnlyPart::abortLoad() 00632 { 00633 Q_D(ReadOnlyPart); 00634 00635 if ( d->m_statJob ) { 00636 //kDebug(1000) << "Aborting job" << d->m_statJob; 00637 d->m_statJob->kill(); 00638 d->m_statJob = 0; 00639 } 00640 if ( d->m_job ) { 00641 //kDebug(1000) << "Aborting job" << d->m_job; 00642 d->m_job->kill(); 00643 d->m_job = 0; 00644 } 00645 } 00646 00647 bool ReadOnlyPart::closeUrl() 00648 { 00649 Q_D(ReadOnlyPart); 00650 00651 abortLoad(); //just in case 00652 00653 d->m_arguments = KParts::OpenUrlArguments(); 00654 00655 if ( d->m_bTemp ) 00656 { 00657 QFile::remove( d->m_file ); 00658 d->m_bTemp = false; 00659 } 00660 // It always succeeds for a read-only part, 00661 // but the return value exists for reimplementations 00662 // (e.g. pressing cancel for a modified read-write part) 00663 return true; 00664 } 00665 00666 void ReadOnlyPartPrivate::_k_slotStatJobFinished(KJob * job) 00667 { 00668 Q_ASSERT(job == m_statJob); 00669 m_statJob = 0; 00670 00671 // We could emit canceled on error, but we haven't even emitted started yet, 00672 // this could maybe confuse some apps? So for now we'll just fallback to KIO::get 00673 // and error again. Well, maybe this even helps with wrong stat results. 00674 if (!job->error()) { 00675 const KUrl localUrl = static_cast<KIO::StatJob*>(job)->mostLocalUrl(); 00676 if (localUrl.isLocalFile()) { 00677 m_file = localUrl.toLocalFile(); 00678 (void)openLocalFile(); 00679 return; 00680 } 00681 } 00682 openRemoteFile(); 00683 } 00684 00685 void ReadOnlyPartPrivate::_k_slotJobFinished( KJob * job ) 00686 { 00687 Q_Q(ReadOnlyPart); 00688 00689 assert( job == m_job ); 00690 m_job = 0; 00691 if (job->error()) 00692 emit q->canceled( job->errorString() ); 00693 else 00694 { 00695 if ( q->openFile() ) { 00696 emit q->setWindowCaption( m_url.prettyUrl() ); 00697 emit q->completed(); 00698 } else emit q->canceled(QString()); 00699 } 00700 } 00701 00702 void ReadOnlyPartPrivate::_k_slotGotMimeType(KIO::Job *job, const QString &mime) 00703 { 00704 kDebug(1000) << mime; 00705 Q_ASSERT(job == m_job); Q_UNUSED(job) 00706 // set the mimetype only if it was not already set (for example, by the host application) 00707 if (m_arguments.mimeType().isEmpty()) { 00708 m_arguments.setMimeType(mime); 00709 m_bAutoDetectedMime = true; 00710 } 00711 } 00712 00713 void ReadOnlyPart::guiActivateEvent( GUIActivateEvent * event ) 00714 { 00715 Q_D(ReadOnlyPart); 00716 00717 if (event->activated()) 00718 { 00719 if (!d->m_url.isEmpty()) 00720 { 00721 kDebug(1000) << d->m_url; 00722 emit setWindowCaption( d->m_url.prettyUrl() ); 00723 } else emit setWindowCaption( "" ); 00724 } 00725 } 00726 00727 bool ReadOnlyPart::openStream( const QString& mimeType, const KUrl& url ) 00728 { 00729 Q_D(ReadOnlyPart); 00730 00731 OpenUrlArguments args = d->m_arguments; 00732 if ( !closeUrl() ) 00733 return false; 00734 d->m_arguments = args; 00735 d->m_url = url; 00736 return doOpenStream( mimeType ); 00737 } 00738 00739 bool ReadOnlyPart::writeStream( const QByteArray& data ) 00740 { 00741 return doWriteStream( data ); 00742 } 00743 00744 bool ReadOnlyPart::closeStream() 00745 { 00746 return doCloseStream(); 00747 } 00748 00749 BrowserExtension* ReadOnlyPart::browserExtension() const 00750 { 00751 return findChild<KParts::BrowserExtension *>(); 00752 } 00753 00754 void KParts::ReadOnlyPart::setArguments(const OpenUrlArguments& arguments) 00755 { 00756 Q_D(ReadOnlyPart); 00757 d->m_arguments = arguments; 00758 d->m_bAutoDetectedMime = arguments.mimeType().isEmpty(); 00759 } 00760 00761 OpenUrlArguments KParts::ReadOnlyPart::arguments() const 00762 { 00763 Q_D(const ReadOnlyPart); 00764 return d->m_arguments; 00765 } 00766 00768 00769 00770 ReadWritePart::ReadWritePart( QObject *parent ) 00771 : ReadOnlyPart( *new ReadWritePartPrivate(this), parent ) 00772 { 00773 } 00774 00775 ReadWritePart::~ReadWritePart() 00776 { 00777 // parent destructor will delete temp file 00778 // we can't call our own closeUrl() here, because 00779 // "cancel" wouldn't cancel anything. We have to assume 00780 // the app called closeUrl() before destroying us. 00781 } 00782 00783 void ReadWritePart::setReadWrite( bool readwrite ) 00784 { 00785 Q_D(ReadWritePart); 00786 00787 // Perhaps we should check isModified here and issue a warning if true 00788 d->m_bReadWrite = readwrite; 00789 } 00790 00791 void ReadWritePart::setModified( bool modified ) 00792 { 00793 Q_D(ReadWritePart); 00794 00795 kDebug(1000) << "setModified(" << (modified ? "true" : "false") << ")"; 00796 if ( !d->m_bReadWrite && modified ) 00797 { 00798 kError(1000) << "Can't set a read-only document to 'modified' !" << endl; 00799 return; 00800 } 00801 d->m_bModified = modified; 00802 } 00803 00804 void ReadWritePart::setModified() 00805 { 00806 setModified( true ); 00807 } 00808 00809 bool ReadWritePart::queryClose() 00810 { 00811 Q_D(ReadWritePart); 00812 00813 if ( !isReadWrite() || !isModified() ) 00814 return true; 00815 00816 QString docName = url().fileName(); 00817 if (docName.isEmpty()) docName = i18n( "Untitled" ); 00818 00819 QWidget *parentWidget=widget(); 00820 if(!parentWidget) parentWidget=QApplication::activeWindow(); 00821 00822 int res = KMessageBox::warningYesNoCancel( parentWidget, 00823 i18n( "The document \"%1\" has been modified.\n" 00824 "Do you want to save your changes or discard them?" , docName ), 00825 i18n( "Close Document" ), KStandardGuiItem::save(), KStandardGuiItem::discard() ); 00826 00827 bool abortClose=false; 00828 bool handled=false; 00829 00830 switch(res) { 00831 case KMessageBox::Yes : 00832 sigQueryClose(&handled,&abortClose); 00833 if (!handled) 00834 { 00835 if (d->m_url.isEmpty()) 00836 { 00837 KUrl url = KFileDialog::getSaveUrl(KUrl(), QString(), parentWidget); 00838 if (url.isEmpty()) 00839 return false; 00840 00841 saveAs( url ); 00842 } 00843 else 00844 { 00845 save(); 00846 } 00847 } else if (abortClose) return false; 00848 return waitSaveComplete(); 00849 case KMessageBox::No : 00850 return true; 00851 default : // case KMessageBox::Cancel : 00852 return false; 00853 } 00854 } 00855 00856 bool ReadWritePart::closeUrl() 00857 { 00858 abortLoad(); //just in case 00859 if ( isReadWrite() && isModified() ) 00860 { 00861 if (!queryClose()) 00862 return false; 00863 } 00864 // Not modified => ok and delete temp file. 00865 return ReadOnlyPart::closeUrl(); 00866 } 00867 00868 bool ReadWritePart::closeUrl( bool promptToSave ) 00869 { 00870 return promptToSave ? closeUrl() : ReadOnlyPart::closeUrl(); 00871 } 00872 00873 bool ReadWritePart::save() 00874 { 00875 Q_D(ReadWritePart); 00876 00877 d->m_saveOk = false; 00878 if ( d->m_file.isEmpty() ) // document was created empty 00879 d->prepareSaving(); 00880 if( saveFile() ) 00881 return saveToUrl(); 00882 else 00883 emit canceled(QString()); 00884 return false; 00885 } 00886 00887 bool ReadWritePart::saveAs( const KUrl & kurl ) 00888 { 00889 Q_D(ReadWritePart); 00890 00891 if (!kurl.isValid()) 00892 { 00893 kError(1000) << "saveAs: Malformed URL " << kurl.url() << endl; 00894 return false; 00895 } 00896 d->m_duringSaveAs = true; 00897 d->m_originalURL = d->m_url; 00898 d->m_originalFilePath = d->m_file; 00899 d->m_url = kurl; // Store where to upload in saveToURL 00900 d->prepareSaving(); 00901 bool result = save(); // Save local file and upload local file 00902 if (result) { 00903 emit setWindowCaption( d->m_url.prettyUrl() ); 00904 } else { 00905 d->m_url = d->m_originalURL; 00906 d->m_file = d->m_originalFilePath; 00907 d->m_duringSaveAs = false; 00908 d->m_originalURL = KUrl(); 00909 d->m_originalFilePath.clear(); 00910 } 00911 00912 return result; 00913 } 00914 00915 // Set m_file correctly for m_url 00916 void ReadWritePartPrivate::prepareSaving() 00917 { 00918 // Local file 00919 if ( m_url.isLocalFile() ) 00920 { 00921 if ( m_bTemp ) // get rid of a possible temp file first 00922 { // (happens if previous url was remote) 00923 QFile::remove( m_file ); 00924 m_bTemp = false; 00925 } 00926 m_file = m_url.toLocalFile(); 00927 } 00928 else 00929 { // Remote file 00930 // We haven't saved yet, or we did but locally - provide a temp file 00931 if ( m_file.isEmpty() || !m_bTemp ) 00932 { 00933 KTemporaryFile tempFile; 00934 tempFile.setAutoRemove(false); 00935 tempFile.open(); 00936 m_file = tempFile.fileName(); 00937 m_bTemp = true; 00938 } 00939 // otherwise, we already had a temp file 00940 } 00941 } 00942 00943 bool ReadWritePart::saveToUrl() 00944 { 00945 Q_D(ReadWritePart); 00946 00947 if ( d->m_url.isLocalFile() ) 00948 { 00949 setModified( false ); 00950 emit completed(); 00951 // if m_url is a local file there won't be a temp file -> nothing to remove 00952 assert( !d->m_bTemp ); 00953 d->m_saveOk = true; 00954 d->m_duringSaveAs = false; 00955 d->m_originalURL = KUrl(); 00956 d->m_originalFilePath.clear(); 00957 return true; // Nothing to do 00958 } 00959 else 00960 { 00961 if (d->m_uploadJob) 00962 { 00963 QFile::remove(d->m_uploadJob->srcUrl().toLocalFile()); 00964 d->m_uploadJob->kill(); 00965 d->m_uploadJob = 0; 00966 } 00967 KTemporaryFile *tempFile = new KTemporaryFile(); 00968 tempFile->open(); 00969 QString uploadFile = tempFile->fileName(); 00970 delete tempFile; 00971 KUrl uploadUrl; 00972 uploadUrl.setPath( uploadFile ); 00973 // Create hardlink 00974 if (::link(QFile::encodeName(d->m_file), QFile::encodeName(uploadFile)) != 0) 00975 { 00976 // Uh oh, some error happened. 00977 return false; 00978 } 00979 d->m_uploadJob = KIO::file_move( uploadUrl, d->m_url, -1, KIO::Overwrite ); 00980 d->m_uploadJob->ui()->setWindow( widget() ? widget()->topLevelWidget() : 0 ); 00981 connect( d->m_uploadJob, SIGNAL( result( KJob * ) ), this, SLOT( _k_slotUploadFinished (KJob *) ) ); 00982 return true; 00983 } 00984 } 00985 00986 void ReadWritePartPrivate::_k_slotUploadFinished( KJob * ) 00987 { 00988 Q_Q(ReadWritePart); 00989 00990 if (m_uploadJob->error()) 00991 { 00992 QFile::remove(m_uploadJob->srcUrl().toLocalFile()); 00993 QString error = m_uploadJob->errorString(); 00994 m_uploadJob = 0; 00995 if (m_duringSaveAs) { 00996 m_url = m_originalURL; 00997 m_file = m_originalFilePath; 00998 } 00999 emit q->canceled( error ); 01000 } 01001 else 01002 { 01003 KUrl dirUrl( m_url ); 01004 dirUrl.setPath( dirUrl.directory() ); 01005 ::org::kde::KDirNotify::emitFilesAdded( dirUrl.url() ); 01006 01007 m_uploadJob = 0; 01008 q->setModified( false ); 01009 emit q->completed(); 01010 m_saveOk = true; 01011 } 01012 m_duringSaveAs = false; 01013 m_originalURL = KUrl(); 01014 m_originalFilePath.clear(); 01015 if (m_waitForSave) { 01016 m_eventLoop.quit(); 01017 } 01018 } 01019 01020 bool ReadWritePart::isReadWrite() const 01021 { 01022 Q_D(const ReadWritePart); 01023 01024 return d->m_bReadWrite; 01025 } 01026 01027 bool ReadWritePart::isModified() const 01028 { 01029 Q_D(const ReadWritePart); 01030 01031 return d->m_bModified; 01032 } 01033 01034 bool ReadWritePart::waitSaveComplete() 01035 { 01036 Q_D(ReadWritePart); 01037 01038 if (!d->m_uploadJob) 01039 return d->m_saveOk; 01040 01041 d->m_waitForSave = true; 01042 01043 d->m_eventLoop.exec(QEventLoop::ExcludeUserInputEvents); 01044 01045 d->m_waitForSave = false; 01046 01047 return d->m_saveOk; 01048 } 01049 01051 01052 class KParts::OpenUrlArgumentsPrivate : public QSharedData 01053 { 01054 public: 01055 OpenUrlArgumentsPrivate() 01056 : reload(false), 01057 actionRequestedByUser(true), 01058 xOffset(0), 01059 yOffset(0), 01060 mimeType(), 01061 metaData() 01062 {} 01063 bool reload; 01064 bool actionRequestedByUser; 01065 int xOffset; 01066 int yOffset; 01067 QString mimeType; 01068 QMap<QString, QString> metaData; 01069 }; 01070 01071 KParts::OpenUrlArguments::OpenUrlArguments() 01072 : d(new OpenUrlArgumentsPrivate) 01073 { 01074 } 01075 01076 KParts::OpenUrlArguments::OpenUrlArguments(const OpenUrlArguments &other) 01077 : d(other.d) 01078 { 01079 } 01080 01081 KParts::OpenUrlArguments & KParts::OpenUrlArguments::operator=( const OpenUrlArguments &other) 01082 { 01083 d = other.d; 01084 return *this; 01085 } 01086 01087 KParts::OpenUrlArguments::~OpenUrlArguments() 01088 { 01089 } 01090 01091 bool KParts::OpenUrlArguments::reload() const 01092 { 01093 return d->reload; 01094 } 01095 01096 void KParts::OpenUrlArguments::setReload(bool b) 01097 { 01098 d->reload = b; 01099 } 01100 01101 int KParts::OpenUrlArguments::xOffset() const 01102 { 01103 return d->xOffset; 01104 } 01105 01106 void KParts::OpenUrlArguments::setXOffset(int x) 01107 { 01108 d->xOffset = x; 01109 } 01110 01111 int KParts::OpenUrlArguments::yOffset() const 01112 { 01113 return d->yOffset; 01114 } 01115 01116 void KParts::OpenUrlArguments::setYOffset(int y) 01117 { 01118 d->yOffset = y; 01119 } 01120 01121 QString KParts::OpenUrlArguments::mimeType() const 01122 { 01123 return d->mimeType; 01124 } 01125 01126 void KParts::OpenUrlArguments::setMimeType(const QString& mime) 01127 { 01128 d->mimeType = mime; 01129 } 01130 01131 QMap<QString, QString> & KParts::OpenUrlArguments::metaData() 01132 { 01133 return d->metaData; 01134 } 01135 01136 const QMap<QString, QString> & KParts::OpenUrlArguments::metaData() const 01137 { 01138 return d->metaData; 01139 } 01140 01141 bool KParts::OpenUrlArguments::actionRequestedByUser() const 01142 { 01143 return d->actionRequestedByUser; 01144 } 01145 01146 void KParts::OpenUrlArguments::setActionRequestedByUser(bool userRequested) 01147 { 01148 d->actionRequestedByUser = userRequested; 01149 } 01150 01151 #include "part.moc"
KDE 4.6 API Reference