1 /* This file is part of the KDE project
2 Copyright (C) 1999 Simon Hausmann <hausmann@kde.org>
3 (C) 1999-2005 David Faure <faure@kde.org>
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
24 #include "mainwindow.h"
25 #include "partmanager.h"
26 #include "browserextension.h"
28 #include <QtGui/QApplication>
29 #include <QtCore/QFile>
30 #include <QtCore/QFileInfo>
31 #include <QtGui/QPainter>
32 #include <QtCore/QPoint>
34 #include <kdirnotify.h>
35 #include <kfiledialog.h>
36 #include <kcomponentdata.h>
38 #include <kio/jobuidelegate.h>
40 #include <kmessagebox.h>
41 #include <kstandarddirs.h>
42 #include <ktemporaryfile.h>
43 #include <kxmlguifactory.h>
49 #include <kiconloader.h>
51 using namespace KParts
;
59 Q_DECLARE_PUBLIC(PartBase
)
61 PartBasePrivate(PartBase
*q
): q_ptr(q
)
63 m_pluginLoadingMode
= PartBase::LoadPlugins
;
64 m_pluginInterfaceVersion
= 0;
68 virtual ~PartBasePrivate()
73 PartBase::PluginLoadingMode m_pluginLoadingMode
;
74 int m_pluginInterfaceVersion
;
78 class PartPrivate
: public PartBasePrivate
81 Q_DECLARE_PUBLIC(Part
)
87 m_autoDeleteWidget(true),
88 m_autoDeletePart(true),
97 KIconLoader
* m_iconLoader
;
99 bool m_autoDeleteWidget
;
100 bool m_autoDeletePart
;
101 PartManager
* m_manager
;
102 QPointer
<QWidget
> m_widget
;
108 : d_ptr(new PartBasePrivate(this))
112 PartBase::PartBase(PartBasePrivate
&dd
)
117 PartBase::~PartBase()
122 void PartBase::setPartObject( QObject
*obj
)
129 QObject
*PartBase::partObject() const
136 void PartBase::setComponentData(const KComponentData
&componentData
)
138 setComponentData(componentData
, true);
141 void PartBase::setComponentData(const KComponentData
&componentData
, bool bLoadPlugins
)
145 KXMLGUIClient::setComponentData(componentData
);
146 KGlobal::locale()->insertCatalog(componentData
.catalogName());
147 // install 'instancename'data resource type
148 KGlobal::dirs()->addResourceType((componentData
.componentName() + "data").toUtf8(),
149 "data", componentData
.componentName());
151 loadPlugins(d
->m_obj
, this, componentData
);
155 void PartBase::loadPlugins(QObject
*parent
, KXMLGUIClient
*parentGUIClient
, const KComponentData
&instance
)
159 if( d
->m_pluginLoadingMode
!= DoNotLoadPlugins
)
160 Plugin::loadPlugins( parent
, parentGUIClient
, instance
, d
->m_pluginLoadingMode
== LoadPlugins
, d
->m_pluginInterfaceVersion
);
163 void PartBase::setPluginLoadingMode( PluginLoadingMode loadingMode
)
167 d
->m_pluginLoadingMode
= loadingMode
;
170 void KParts::PartBase::setPluginInterfaceVersion( int version
)
174 d
->m_pluginInterfaceVersion
= version
;
177 Part::Part( QObject
*parent
)
178 : QObject( parent
), PartBase( *new PartPrivate(this) )
180 PartBase::setPartObject( this );
183 Part::Part(PartPrivate
&dd
, QObject
*parent
)
184 : QObject( parent
), PartBase( dd
)
186 PartBase::setPartObject( this );
193 //kDebug(1000) << "Part::~Part " << this;
197 // We need to disconnect first, to avoid calling it !
198 disconnect( d
->m_widget
, SIGNAL( destroyed() ),
199 this, SLOT( slotWidgetDestroyed() ) );
203 d
->m_manager
->removePart(this);
205 if ( d
->m_widget
&& d
->m_autoDeleteWidget
)
207 kDebug(1000) << "deleting widget " << d
->m_widget
<< " " << d
->m_widget
->objectName();
208 delete static_cast<QWidget
*>(d
->m_widget
);
211 delete d
->m_iconLoader
;
214 void Part::embed( QWidget
* parentWidget
)
218 widget()->setParent( parentWidget
, 0 );
219 widget()->setGeometry( 0, 0, widget()->width(), widget()->height() );
224 QWidget
*Part::widget()
231 void Part::setAutoDeleteWidget(bool autoDeleteWidget
)
234 d
->m_autoDeleteWidget
= autoDeleteWidget
;
237 void Part::setAutoDeletePart(bool autoDeletePart
)
240 d
->m_autoDeletePart
= autoDeletePart
;
245 KIconLoader
* Part::iconLoader()
249 if (!d
->m_iconLoader
) {
250 Q_ASSERT(componentData().isValid());
251 d
->m_iconLoader
= new KIconLoader( componentData() );
253 return d
->m_iconLoader
;
256 void Part::setManager( PartManager
*manager
)
260 d
->m_manager
= manager
;
263 PartManager
*Part::manager() const
270 Part
*Part::hitTest( QWidget
*widget
, const QPoint
& )
274 if ( (QWidget
*)d
->m_widget
!= widget
)
280 void Part::setWidget( QWidget
*widget
)
284 assert ( !d
->m_widget
); // otherwise we get two connects
285 d
->m_widget
= widget
;
286 connect( d
->m_widget
, SIGNAL( destroyed() ),
287 this, SLOT( slotWidgetDestroyed() ) );
290 void Part::setSelectable( bool selectable
)
294 d
->m_bSelectable
= selectable
;
297 bool Part::isSelectable() const
301 return d
->m_bSelectable
;
304 void Part::customEvent( QEvent
*ev
)
306 if ( PartActivateEvent::test( ev
) )
308 partActivateEvent( static_cast<PartActivateEvent
*>(ev
) );
312 if ( PartSelectEvent::test( ev
) )
314 partSelectEvent( static_cast<PartSelectEvent
*>(ev
) );
318 if ( GUIActivateEvent::test( ev
) )
320 guiActivateEvent( static_cast<GUIActivateEvent
*>(ev
) );
324 QObject::customEvent( ev
);
327 void Part::partActivateEvent( PartActivateEvent
* )
331 void Part::partSelectEvent( PartSelectEvent
* )
335 void Part::guiActivateEvent( GUIActivateEvent
* )
339 QWidget
*Part::hostContainer( const QString
&containerName
)
344 return factory()->container( containerName
, this );
347 void Part::slotWidgetDestroyed()
352 if (d
->m_autoDeletePart
) {
353 kDebug(1000) << "KPart::slotWidgetDestroyed(), deleting part " << objectName();
354 delete this; // ouch, this should probably be deleteLater()
358 void Part::loadPlugins()
360 PartBase::loadPlugins(this, this, componentData());
363 //////////////////////////////////////////////////
368 class ReadOnlyPartPrivate
: public PartPrivate
371 Q_DECLARE_PUBLIC(ReadOnlyPart
)
373 ReadOnlyPartPrivate(ReadOnlyPart
*q
): PartPrivate(q
)
377 m_showProgressInfo
= true;
379 m_waitForSave
= false;
380 m_duringSaveAs
= false;
382 m_bAutoDetectedMime
= false;
385 ~ReadOnlyPartPrivate()
389 void _k_slotJobFinished( KJob
* job
);
390 void _k_slotGotMimeType(KIO::Job
*job
, const QString
&mime
);
392 KIO::FileCopyJob
* m_job
;
393 KIO::FileCopyJob
* m_uploadJob
;
394 KUrl m_originalURL
; // for saveAs
395 QString m_originalFilePath
; // for saveAs
396 bool m_showProgressInfo
: 1;
398 bool m_waitForSave
: 1;
399 bool m_duringSaveAs
: 1;
402 * If @p true, @p m_file is a temporary file that needs to be deleted later.
406 // whether the mimetype in the arguments was detected by the part itself
407 bool m_bAutoDetectedMime
: 1;
410 * Remote (or local) url - the one displayed to the user.
415 * Local file - the only one the part implementation should deal with.
419 OpenUrlArguments m_arguments
;
422 class ReadWritePartPrivate
: public ReadOnlyPartPrivate
425 Q_DECLARE_PUBLIC(ReadWritePart
)
427 ReadWritePartPrivate(ReadWritePart
*q
): ReadOnlyPartPrivate(q
)
434 void _k_slotUploadFinished( KJob
* job
);
436 void prepareSaving();
441 QEventLoop m_eventLoop
;
446 ReadOnlyPart::ReadOnlyPart( QObject
*parent
)
447 : Part( *new ReadOnlyPartPrivate(this), parent
)
451 ReadOnlyPart::ReadOnlyPart( ReadOnlyPartPrivate
&dd
, QObject
*parent
)
456 ReadOnlyPart::~ReadOnlyPart()
458 ReadOnlyPart::closeUrl();
461 KUrl
ReadOnlyPart::url() const
463 Q_D(const ReadOnlyPart
);
468 void ReadOnlyPart::setUrl(const KUrl
&url
)
475 QString
ReadOnlyPart::localFilePath() const
477 Q_D(const ReadOnlyPart
);
482 void ReadOnlyPart::setLocalFilePath( const QString
&localFilePath
)
486 d
->m_file
= localFilePath
;
489 bool ReadOnlyPart::isLocalFileTemporary() const
491 Q_D(const ReadOnlyPart
);
496 void ReadOnlyPart::setLocalFileTemporary( bool temp
)
503 void ReadOnlyPart::setProgressInfoEnabled( bool show
)
507 d
->m_showProgressInfo
= show
;
510 bool ReadOnlyPart::isProgressInfoEnabled() const
512 Q_D(const ReadOnlyPart
);
514 return d
->m_showProgressInfo
;
517 #ifndef KDE_NO_COMPAT
518 void ReadOnlyPart::showProgressInfo( bool show
)
522 d
->m_showProgressInfo
= show
;
526 bool ReadOnlyPart::openUrl( const KUrl
&url
)
530 if ( !url
.isValid() )
532 if (d
->m_bAutoDetectedMime
) {
533 d
->m_arguments
.setMimeType(QString());
534 d
->m_bAutoDetectedMime
= false;
536 OpenUrlArguments args
= d
->m_arguments
;
539 d
->m_arguments
= args
;
541 if ( d
->m_url
.isLocalFile() )
544 d
->m_file
= d
->m_url
.toLocalFile();
546 // set the mimetype only if it was not already set (for example, by the host application)
547 if (d
->m_arguments
.mimeType().isEmpty())
549 // get the mimetype of the file
550 // using findByUrl() to avoid another string -> url conversion
551 KMimeType::Ptr mime
= KMimeType::findByUrl(d
->m_url
, 0, true /* local file*/);
553 d
->m_arguments
.setMimeType(mime
->name());
554 d
->m_bAutoDetectedMime
= true;
557 bool ret
= openFile();
559 emit
setWindowCaption( d
->m_url
.prettyUrl() );
561 } else emit
canceled(QString());
567 // Use same extension as remote file. This is important for mimetype-determination (e.g. koffice)
568 QString fileName
= url
.fileName();
569 QFileInfo
fileInfo(fileName
);
570 QString ext
= fileInfo
.completeSuffix();
572 if ( !ext
.isEmpty() && url
.query().isNull() ) // not if the URL has a query, e.g. cgi.pl?something
573 extension
= '.'+ext
; // keep the '.'
574 KTemporaryFile tempFile
;
575 tempFile
.setSuffix(extension
);
576 tempFile
.setAutoRemove(false);
578 d
->m_file
= tempFile
.fileName();
581 destURL
.setPath( d
->m_file
);
582 KIO::JobFlags flags
= d
->m_showProgressInfo
? KIO::DefaultFlags
: KIO::HideProgressInfo
;
583 flags
|= KIO::Overwrite
;
584 d
->m_job
= KIO::file_copy( d
->m_url
, destURL
, 0600, flags
);
585 d
->m_job
->ui()->setWindow( widget() ? widget()->topLevelWidget() : 0 );
586 emit
started( d
->m_job
);
587 connect( d
->m_job
, SIGNAL( result( KJob
* ) ), this, SLOT( _k_slotJobFinished ( KJob
* ) ) );
588 connect(d
->m_job
, SIGNAL(mimetype(KIO::Job
*, const QString
&)),
589 this, SLOT(_k_slotGotMimeType(KIO::Job
*, const QString
&)));
594 void ReadOnlyPart::abortLoad()
600 //kDebug(1000) << "Aborting job " << d->m_job;
606 bool ReadOnlyPart::closeUrl()
610 abortLoad(); //just in case
612 d
->m_arguments
= KParts::OpenUrlArguments();
616 QFile::remove( d
->m_file
);
619 // It always succeeds for a read-only part,
620 // but the return value exists for reimplementations
621 // (e.g. pressing cancel for a modified read-write part)
625 void ReadOnlyPartPrivate::_k_slotJobFinished( KJob
* job
)
629 kDebug(1000) << "ReadOnlyPart::slotJobFinished";
630 assert( job
== m_job
);
633 emit q
->canceled( job
->errorString() );
636 if ( q
->openFile() ) {
637 emit q
->setWindowCaption( m_url
.prettyUrl() );
639 } else emit q
->canceled(QString());
643 void ReadOnlyPartPrivate::_k_slotGotMimeType(KIO::Job
*job
, const QString
&mime
)
645 kDebug(1000) << "ReadOnlyPart::slotJobFinished:" << mime
;
646 Q_ASSERT(job
== m_job
);
647 // set the mimetype only if it was not already set (for example, by the host application)
648 if (m_arguments
.mimeType().isEmpty()) {
649 m_arguments
.setMimeType(mime
);
650 m_bAutoDetectedMime
= true;
654 void ReadOnlyPart::guiActivateEvent( GUIActivateEvent
* event
)
658 if (event
->activated())
660 if (!d
->m_url
.isEmpty())
662 kDebug(1000) << "ReadOnlyPart::guiActivateEvent -> " << d
->m_url
.prettyUrl();
663 emit
setWindowCaption( d
->m_url
.prettyUrl() );
664 } else emit
setWindowCaption( "" );
668 bool ReadOnlyPart::openStream( const QString
& mimeType
, const KUrl
& url
)
672 OpenUrlArguments args
= d
->m_arguments
;
675 d
->m_arguments
= args
;
677 return doOpenStream( mimeType
);
680 bool ReadOnlyPart::writeStream( const QByteArray
& data
)
682 return doWriteStream( data
);
685 bool ReadOnlyPart::closeStream()
687 return doCloseStream();
690 BrowserExtension
* ReadOnlyPart::browserExtension() const
692 return findChild
<KParts::BrowserExtension
*>();
695 void KParts::ReadOnlyPart::setArguments(const OpenUrlArguments
& arguments
)
698 d
->m_arguments
= arguments
;
699 d
->m_bAutoDetectedMime
= arguments
.mimeType().isEmpty();
702 OpenUrlArguments
KParts::ReadOnlyPart::arguments() const
704 Q_D(const ReadOnlyPart
);
705 return d
->m_arguments
;
708 //////////////////////////////////////////////////
711 ReadWritePart::ReadWritePart( QObject
*parent
)
712 : ReadOnlyPart( *new ReadWritePartPrivate(this), parent
)
716 ReadWritePart::~ReadWritePart()
718 // parent destructor will delete temp file
719 // we can't call our own closeUrl() here, because
720 // "cancel" wouldn't cancel anything. We have to assume
721 // the app called closeUrl() before destroying us.
724 void ReadWritePart::setReadWrite( bool readwrite
)
728 // Perhaps we should check isModified here and issue a warning if true
729 d
->m_bReadWrite
= readwrite
;
732 void ReadWritePart::setModified( bool modified
)
736 kDebug(1000) << "ReadWritePart::setModified( " << (modified
? "true" : "false") << ")";
737 if ( !d
->m_bReadWrite
&& modified
)
739 kError(1000) << "Can't set a read-only document to 'modified' !" << endl
;
742 d
->m_bModified
= modified
;
745 void ReadWritePart::setModified()
750 bool ReadWritePart::queryClose()
754 if ( !isReadWrite() || !isModified() )
757 QString docName
= url().fileName();
758 if (docName
.isEmpty()) docName
= i18n( "Untitled" );
760 QWidget
*parentWidget
=widget();
761 if(!parentWidget
) parentWidget
=QApplication::activeWindow();
763 int res
= KMessageBox::warningYesNoCancel( parentWidget
,
764 i18n( "The document \"%1\" has been modified.\n"
765 "Do you want to save your changes or discard them?" , docName
),
766 i18n( "Close Document" ), KStandardGuiItem::save(), KStandardGuiItem::discard() );
768 bool abortClose
=false;
772 case KMessageBox::Yes
:
773 sigQueryClose(&handled
,&abortClose
);
776 if (d
->m_url
.isEmpty())
778 KUrl url
= KFileDialog::getSaveUrl(KUrl(), QString(), parentWidget
);
788 } else if (abortClose
) return false;
789 return waitSaveComplete();
790 case KMessageBox::No
:
792 default : // case KMessageBox::Cancel :
797 bool ReadWritePart::closeUrl()
799 abortLoad(); //just in case
800 if ( isReadWrite() && isModified() )
805 // Not modified => ok and delete temp file.
806 return ReadOnlyPart::closeUrl();
809 bool ReadWritePart::closeUrl( bool promptToSave
)
811 return promptToSave
? closeUrl() : ReadOnlyPart::closeUrl();
814 bool ReadWritePart::save()
819 if ( d
->m_file
.isEmpty() ) // document was created empty
824 emit
canceled(QString());
828 bool ReadWritePart::saveAs( const KUrl
& kurl
)
834 kError(1000) << "saveAs: Malformed URL " << kurl
.url() << endl
;
837 d
->m_duringSaveAs
= true;
838 d
->m_originalURL
= d
->m_url
;
839 d
->m_originalFilePath
= d
->m_file
;
840 d
->m_url
= kurl
; // Store where to upload in saveToURL
842 bool result
= save(); // Save local file and upload local file
844 emit
setWindowCaption( d
->m_url
.prettyUrl() );
846 d
->m_url
= d
->m_originalURL
;
847 d
->m_file
= d
->m_originalFilePath
;
848 d
->m_duringSaveAs
= false;
849 d
->m_originalURL
= KUrl();
850 d
->m_originalFilePath
.clear();
856 // Set m_file correctly for m_url
857 void ReadWritePartPrivate::prepareSaving()
860 if ( m_url
.isLocalFile() )
862 if ( m_bTemp
) // get rid of a possible temp file first
863 { // (happens if previous url was remote)
864 QFile::remove( m_file
);
867 m_file
= m_url
.toLocalFile();
871 // We haven't saved yet, or we did but locally - provide a temp file
872 if ( m_file
.isEmpty() || !m_bTemp
)
874 KTemporaryFile tempFile
;
875 tempFile
.setAutoRemove(false);
877 m_file
= tempFile
.fileName();
880 // otherwise, we already had a temp file
884 bool ReadWritePart::saveToUrl()
888 if ( d
->m_url
.isLocalFile() )
890 setModified( false );
892 // if m_url is a local file there won't be a temp file -> nothing to remove
893 assert( !d
->m_bTemp
);
895 d
->m_duringSaveAs
= false;
896 d
->m_originalURL
= KUrl();
897 d
->m_originalFilePath
.clear();
898 return true; // Nothing to do
904 QFile::remove(d
->m_uploadJob
->srcUrl().toLocalFile());
905 d
->m_uploadJob
->kill();
908 KTemporaryFile
*tempFile
= new KTemporaryFile();
910 QString uploadFile
= tempFile
->fileName();
913 uploadUrl
.setPath( uploadFile
);
915 if (::link(QFile::encodeName(d
->m_file
), QFile::encodeName(uploadFile
)) != 0)
917 // Uh oh, some error happened.
920 d
->m_uploadJob
= KIO::file_move( uploadUrl
, d
->m_url
, -1, KIO::Overwrite
);
921 d
->m_uploadJob
->ui()->setWindow( widget() ? widget()->topLevelWidget() : 0 );
922 connect( d
->m_uploadJob
, SIGNAL( result( KJob
* ) ), this, SLOT( _k_slotUploadFinished (KJob
*) ) );
927 void ReadWritePartPrivate::_k_slotUploadFinished( KJob
* )
931 if (m_uploadJob
->error())
933 QFile::remove(m_uploadJob
->srcUrl().toLocalFile());
934 QString error
= m_uploadJob
->errorString();
936 if (m_duringSaveAs
) {
937 m_url
= m_originalURL
;
938 m_file
= m_originalFilePath
;
940 emit q
->canceled( error
);
944 KUrl
dirUrl( m_url
);
945 dirUrl
.setPath( dirUrl
.directory() );
946 ::org::kde::KDirNotify::emitFilesAdded( dirUrl
.url() );
949 q
->setModified( false );
953 m_duringSaveAs
= false;
954 m_originalURL
= KUrl();
955 m_originalFilePath
.clear();
961 bool ReadWritePart::isReadWrite() const
963 Q_D(const ReadWritePart
);
965 return d
->m_bReadWrite
;
968 bool ReadWritePart::isModified() const
970 Q_D(const ReadWritePart
);
972 return d
->m_bModified
;
975 bool ReadWritePart::waitSaveComplete()
982 d
->m_waitForSave
= true;
984 d
->m_eventLoop
.exec(QEventLoop::ExcludeUserInputEvents
);
986 d
->m_waitForSave
= false;
993 class KParts::OpenUrlArgumentsPrivate
: public QSharedData
996 OpenUrlArgumentsPrivate()
998 actionRequestedByUser(true),
1005 bool actionRequestedByUser
;
1009 QMap
<QString
, QString
> metaData
;
1012 KParts::OpenUrlArguments::OpenUrlArguments()
1013 : d(new OpenUrlArgumentsPrivate
)
1017 KParts::OpenUrlArguments::OpenUrlArguments(const OpenUrlArguments
&other
)
1022 KParts::OpenUrlArguments
& KParts::OpenUrlArguments::operator=( const OpenUrlArguments
&other
)
1028 KParts::OpenUrlArguments::~OpenUrlArguments()
1032 bool KParts::OpenUrlArguments::reload() const
1037 void KParts::OpenUrlArguments::setReload(bool b
)
1042 int KParts::OpenUrlArguments::xOffset() const
1047 void KParts::OpenUrlArguments::setXOffset(int x
)
1052 int KParts::OpenUrlArguments::yOffset() const
1057 void KParts::OpenUrlArguments::setYOffset(int y
)
1062 QString
KParts::OpenUrlArguments::mimeType() const
1067 void KParts::OpenUrlArguments::setMimeType(const QString
& mime
)
1072 QMap
<QString
, QString
> & KParts::OpenUrlArguments::metaData()
1077 const QMap
<QString
, QString
> & KParts::OpenUrlArguments::metaData() const
1082 bool KParts::OpenUrlArguments::actionRequestedByUser() const
1084 return d
->actionRequestedByUser
;
1087 void KParts::OpenUrlArguments::setActionRequestedByUser(bool userRequested
)
1089 d
->actionRequestedByUser
= userRequested
;