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>
4
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.
9
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.
14
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.
19*/
20
21#include "part.h"
22#include <kprotocolinfo.h>
23#include "event.h"
24#include "plugin.h"
25#include "mainwindow.h"
26#include "partmanager.h"
27#include "browserextension.h"
28
29#include <QtGui/QApplication>
30#include <QtCore/QFile>
31#include <QtCore/QFileInfo>
32#include <QtGui/QPainter>
33#include <QtCore/QPoint>
34
35#include <kdirnotify.h>
36#include <kfiledialog.h>
37#include <kcomponentdata.h>
38#include <kio/job.h>
39#include <kio/jobuidelegate.h>
40#include <klocale.h>
41#include <kmessagebox.h>
42#include <kstandarddirs.h>
43#include <ktemporaryfile.h>
44#include <kxmlguifactory.h>
45
46#include <stdio.h>
47#include <unistd.h>
48#include <assert.h>
49#include <kdebug.h>
50#include <kiconloader.h>
51
52using namespace KParts;
53
54namespace KParts
55{
56
57class PartBasePrivate
58{
59public:
60 Q_DECLARE_PUBLIC(PartBase)
61
62 PartBasePrivate(PartBase *q): q_ptr(q)
63 {
64 m_pluginLoadingMode = PartBase::LoadPlugins;
65 m_pluginInterfaceVersion = 0;
66 m_obj = 0;
67 }
68
69 virtual ~PartBasePrivate()
70 {
71 }
72
73 PartBase *q_ptr;
74 PartBase::PluginLoadingMode m_pluginLoadingMode;
75 int m_pluginInterfaceVersion;
76 QObject *m_obj;
77};
78
79class PartPrivate: public PartBasePrivate
80{
81public:
82 Q_DECLARE_PUBLIC(Part)
83
84 PartPrivate(Part *q)
85 : PartBasePrivate(q),
86 m_iconLoader(0),
87 m_bSelectable(true),
88 m_autoDeleteWidget(true),
89 m_autoDeletePart(true),
90 m_manager(0)
91 {
92 }
93
94 ~PartPrivate()
95 {
96 }
97
98 KIconLoader* m_iconLoader;
99 bool m_bSelectable;
100 bool m_autoDeleteWidget;
101 bool m_autoDeletePart;
102 PartManager * m_manager;
103 QPointer<QWidget> m_widget;
104};
105
106}
107
108PartBase::PartBase()
109 : d_ptr(new PartBasePrivate(this))
110{
111}
112
113PartBase::PartBase(PartBasePrivate &dd)
114 : d_ptr(&dd)
115{
116}
117
118PartBase::~PartBase()
119{
120 delete d_ptr;
121}
122
123void PartBase::setPartObject( QObject *obj )
124{
125 Q_D(PartBase);
126
127 d->m_obj = obj;
128}
129
130QObject *PartBase::partObject() const
131{
132 Q_D(const PartBase);
133
134 return d->m_obj;
135}
136
137void PartBase::setComponentData(const KComponentData &componentData)
138{
139 setComponentData(componentData, true);
140}
141
142void PartBase::setComponentData(const KComponentData &componentData, bool bLoadPlugins)
143{
144 Q_D(PartBase);
145
146 KXMLGUIClient::setComponentData(componentData);
147 KGlobal::locale()->insertCatalog(componentData.catalogName());
148 // install 'instancename'data resource type
149 KGlobal::dirs()->addResourceType(QString(componentData.componentName() + "data").toUtf8(),
150 "data", componentData.componentName());
151 if (bLoadPlugins) {
152 loadPlugins(d->m_obj, this, componentData);
153 }
154}
155
156void PartBase::loadPlugins(QObject *parent, KXMLGUIClient *parentGUIClient, const KComponentData &instance)
157{
158 Q_D(PartBase);
159
160 if( d->m_pluginLoadingMode != DoNotLoadPlugins )
161 Plugin::loadPlugins( parent, parentGUIClient, instance, d->m_pluginLoadingMode == LoadPlugins, d->m_pluginInterfaceVersion );
162}
163
164void PartBase::setPluginLoadingMode( PluginLoadingMode loadingMode )
165{
166 Q_D(PartBase);
167
168 d->m_pluginLoadingMode = loadingMode;
169}
170
171void KParts::PartBase::setPluginInterfaceVersion( int version )
172{
173 Q_D(PartBase);
174
175 d->m_pluginInterfaceVersion = version;
176}
177
178Part::Part( QObject *parent )
179 : QObject( parent ), PartBase( *new PartPrivate(this) )
180{
181 PartBase::setPartObject( this );
182}
183
184Part::Part(PartPrivate &dd, QObject *parent)
185 : QObject( parent ), PartBase( dd )
186{
187 PartBase::setPartObject( this );
188}
189
190Part::~Part()
191{
192 Q_D(Part);
193
194 //kDebug(1000) << this;
195
196 if ( d->m_widget )
197 {
198 // We need to disconnect first, to avoid calling it !
199 disconnect( d->m_widget, SIGNAL(destroyed()),
200 this, SLOT(slotWidgetDestroyed()) );
201 }
202
203 if ( d->m_manager )
204 d->m_manager->removePart(this);
205
206 if ( d->m_widget && d->m_autoDeleteWidget )
207 {
208 kDebug(1000) << "deleting widget" << d->m_widget << d->m_widget->objectName();
209 delete static_cast<QWidget*>(d->m_widget);
210 }
211
212 delete d->m_iconLoader;
213}
214
215void Part::embed( QWidget * parentWidget )
216{
217 if ( widget() )
218 {
219 widget()->setParent( parentWidget, 0 );
220 widget()->setGeometry( 0, 0, widget()->width(), widget()->height() );
221 widget()->show();
222 }
223}
224
225QWidget *Part::widget()
226{
227 Q_D(Part);
228
229 return d->m_widget;
230}
231
232void Part::setAutoDeleteWidget(bool autoDeleteWidget)
233{
234 Q_D(Part);
235 d->m_autoDeleteWidget = autoDeleteWidget;
236}
237
238void Part::setAutoDeletePart(bool autoDeletePart)
239{
240 Q_D(Part);
241 d->m_autoDeletePart = autoDeletePart;
242}
243
244
245
246KIconLoader* Part::iconLoader()
247{
248 Q_D(Part);
249
250 if (!d->m_iconLoader) {
251 Q_ASSERT(componentData().isValid());
252 d->m_iconLoader = new KIconLoader( componentData() );
253 }
254 return d->m_iconLoader;
255}
256
257void Part::setManager( PartManager *manager )
258{
259 Q_D(Part);
260
261 d->m_manager = manager;
262}
263
264PartManager *Part::manager() const
265{
266 Q_D(const Part);
267
268 return d->m_manager;
269}
270
271Part *Part::hitTest( QWidget *widget, const QPoint & )
272{
273 Q_D(Part);
274
275 if ( (QWidget *)d->m_widget != widget )
276 return 0;
277
278 return this;
279}
280
281void Part::setWidget( QWidget *widget )
282{
283 Q_D(Part);
284 d->m_widget = widget;
285 connect( d->m_widget, SIGNAL(destroyed()),
286 this, SLOT(slotWidgetDestroyed()), Qt::UniqueConnection );
287}
288
289void Part::setSelectable( bool selectable )
290{
291 Q_D(Part);
292
293 d->m_bSelectable = selectable;
294}
295
296bool Part::isSelectable() const
297{
298 Q_D(const Part);
299
300 return d->m_bSelectable;
301}
302
303void Part::customEvent( QEvent *ev )
304{
305 if ( PartActivateEvent::test( ev ) )
306 {
307 partActivateEvent( static_cast<PartActivateEvent *>(ev) );
308 return;
309 }
310
311 if ( PartSelectEvent::test( ev ) )
312 {
313 partSelectEvent( static_cast<PartSelectEvent *>(ev) );
314 return;
315 }
316
317 if ( GUIActivateEvent::test( ev ) )
318 {
319 guiActivateEvent( static_cast<GUIActivateEvent *>(ev) );
320 return;
321 }
322
323 QObject::customEvent( ev );
324}
325
326void Part::partActivateEvent( PartActivateEvent * )
327{
328}
329
330void Part::partSelectEvent( PartSelectEvent * )
331{
332}
333
334void Part::guiActivateEvent( GUIActivateEvent * )
335{
336}
337
338QWidget *Part::hostContainer( const QString &containerName )
339{
340 if ( !factory() )
341 return 0;
342
343 return factory()->container( containerName, this );
344}
345
346void Part::slotWidgetDestroyed()
347{
348 Q_D(Part);
349
350 d->m_widget = 0;
351 if (d->m_autoDeletePart) {
352 kDebug(1000) << "deleting part" << objectName();
353 delete this; // ouch, this should probably be deleteLater()
354 }
355}
356
357void Part::loadPlugins()
358{
359 PartBase::loadPlugins(this, this, componentData());
360}
361
362//////////////////////////////////////////////////
363
364namespace KParts
365{
366
367class ReadOnlyPartPrivate: public PartPrivate
368{
369public:
370 Q_DECLARE_PUBLIC(ReadOnlyPart)
371
372 ReadOnlyPartPrivate(ReadOnlyPart *q): PartPrivate(q)
373 {
374 m_job = 0;
375 m_statJob = 0;
376 m_uploadJob = 0;
377 m_showProgressInfo = true;
378 m_saveOk = false;
379 m_waitForSave = false;
380 m_duringSaveAs = false;
381 m_bTemp = false;
382 m_bAutoDetectedMime = false;
383 }
384
385 ~ReadOnlyPartPrivate()
386 {
387 }
388
389 void _k_slotJobFinished( KJob * job );
390 void _k_slotStatJobFinished(KJob * job);
391 void _k_slotGotMimeType(KIO::Job *job, const QString &mime);
392 bool openLocalFile();
393 void openRemoteFile();
394
395 KIO::FileCopyJob * m_job;
396 KIO::StatJob * m_statJob;
397 KIO::FileCopyJob * m_uploadJob;
398 KUrl m_originalURL; // for saveAs
399 QString m_originalFilePath; // for saveAs
400 bool m_showProgressInfo : 1;
401 bool m_saveOk : 1;
402 bool m_waitForSave : 1;
403 bool m_duringSaveAs : 1;
404
405 /**
406 * If @p true, @p m_file is a temporary file that needs to be deleted later.
407 */
408 bool m_bTemp: 1;
409
410 // whether the mimetype in the arguments was detected by the part itself
411 bool m_bAutoDetectedMime : 1;
412
413 /**
414 * Remote (or local) url - the one displayed to the user.
415 */
416 KUrl m_url;
417
418 /**
419 * Local file - the only one the part implementation should deal with.
420 */
421 QString m_file;
422
423 OpenUrlArguments m_arguments;
424};
425
426class ReadWritePartPrivate: public ReadOnlyPartPrivate
427{
428public:
429 Q_DECLARE_PUBLIC(ReadWritePart)
430
431 ReadWritePartPrivate(ReadWritePart *q): ReadOnlyPartPrivate(q)
432 {
433 m_bModified = false;
434 m_bReadWrite = true;
435 m_bClosing = false;
436 }
437
438 void _k_slotUploadFinished( KJob * job );
439
440 void prepareSaving();
441
442 bool m_bModified;
443 bool m_bReadWrite;
444 bool m_bClosing;
445 QEventLoop m_eventLoop;
446};
447
448}
449
450ReadOnlyPart::ReadOnlyPart( QObject *parent )
451 : Part( *new ReadOnlyPartPrivate(this), parent )
452{
453}
454
455ReadOnlyPart::ReadOnlyPart( ReadOnlyPartPrivate &dd, QObject *parent )
456 : Part( dd, parent )
457{
458}
459
460ReadOnlyPart::~ReadOnlyPart()
461{
462 ReadOnlyPart::closeUrl();
463}
464
465KUrl ReadOnlyPart::url() const
466{
467 Q_D(const ReadOnlyPart);
468
469 return d->m_url;
470}
471
472void ReadOnlyPart::setUrl(const KUrl &url)
473{
474 Q_D(ReadOnlyPart);
475
476 d->m_url = url;
477 emit urlChanged( url );
478}
479
480QString ReadOnlyPart::localFilePath() const
481{
482 Q_D(const ReadOnlyPart);
483
484 return d->m_file;
485}
486
487void ReadOnlyPart::setLocalFilePath( const QString &localFilePath )
488{
489 Q_D(ReadOnlyPart);
490
491 d->m_file = localFilePath;
492}
493
494#ifndef KDE_NO_DEPRECATED
495bool ReadOnlyPart::isLocalFileTemporary() const
496{
497 Q_D(const ReadOnlyPart);
498
499 return d->m_bTemp;
500}
501#endif
502
503#ifndef KDE_NO_DEPRECATED
504void ReadOnlyPart::setLocalFileTemporary( bool temp )
505{
506 Q_D(ReadOnlyPart);
507
508 d->m_bTemp = temp;
509}
510#endif
511
512void ReadOnlyPart::setProgressInfoEnabled( bool show )
513{
514 Q_D(ReadOnlyPart);
515
516 d->m_showProgressInfo = show;
517}
518
519bool ReadOnlyPart::isProgressInfoEnabled() const
520{
521 Q_D(const ReadOnlyPart);
522
523 return d->m_showProgressInfo;
524}
525
526#ifndef KDE_NO_COMPAT
527void ReadOnlyPart::showProgressInfo( bool show )
528{
529 Q_D(ReadOnlyPart);
530
531 d->m_showProgressInfo = show;
532}
533#endif
534
535bool ReadOnlyPart::openUrl( const KUrl &url )
536{
537 Q_D(ReadOnlyPart);
538
539 if ( !url.isValid() )
540 return false;
541 if (d->m_bAutoDetectedMime) {
542 d->m_arguments.setMimeType(QString());
543 d->m_bAutoDetectedMime = false;
544 }
545 OpenUrlArguments args = d->m_arguments;
546 if ( !closeUrl() )
547 return false;
548 d->m_arguments = args;
549 setUrl(url);
550
551 d->m_file.clear();
552
553 if (d->m_url.isLocalFile()) {
554 d->m_file = d->m_url.toLocalFile();
555 return d->openLocalFile();
556 } else if (KProtocolInfo::protocolClass(url.protocol()) == ":local") {
557 // Maybe we can use a "local path", to avoid a temp copy?
558 KIO::JobFlags flags = d->m_showProgressInfo ? KIO::DefaultFlags : KIO::HideProgressInfo;
559 d->m_statJob = KIO::mostLocalUrl(d->m_url, flags);
560 d->m_statJob->ui()->setWindow( widget() ? widget()->topLevelWidget() : 0 );
561 connect(d->m_statJob, SIGNAL(result(KJob*)), this, SLOT(_k_slotStatJobFinished(KJob*)));
562 return true;
563 } else {
564 d->openRemoteFile();
565 return true;
566 }
567}
568
569bool ReadOnlyPart::openFile()
570{
571 kWarning(1000) << "Default implementation of ReadOnlyPart::openFile called!"
572 << metaObject()->className() << "should reimplement either openUrl or openFile.";
573 return false;
574}
575
576bool ReadOnlyPartPrivate::openLocalFile()
577{
578 Q_Q(ReadOnlyPart);
579 emit q->started( 0 );
580 m_bTemp = false;
581 // set the mimetype only if it was not already set (for example, by the host application)
582 if (m_arguments.mimeType().isEmpty()) {
583 // get the mimetype of the file
584 // using findByUrl() to avoid another string -> url conversion
585 KMimeType::Ptr mime = KMimeType::findByUrl(m_url, 0, true /* local file*/);
586 if (mime) {
587 m_arguments.setMimeType(mime->name());
588 m_bAutoDetectedMime = true;
589 }
590 }
591 const bool ret = q->openFile();
592 if (ret) {
593 emit q->setWindowCaption(m_url.prettyUrl());
594 emit q->completed();
595 } else {
596 emit q->canceled(QString());
597 }
598 return ret;
599}
600
601void ReadOnlyPartPrivate::openRemoteFile()
602{
603 Q_Q(ReadOnlyPart);
604 m_bTemp = true;
605 // Use same extension as remote file. This is important for mimetype-determination (e.g. koffice)
606 QString fileName = m_url.fileName();
607 QFileInfo fileInfo(fileName);
608 QString ext = fileInfo.completeSuffix();
609 QString extension;
610 if (!ext.isEmpty() && m_url.query().isNull()) // not if the URL has a query, e.g. cgi.pl?something
611 extension = '.'+ext; // keep the '.'
612 KTemporaryFile tempFile;
613 tempFile.setSuffix(extension);
614 tempFile.setAutoRemove(false);
615 tempFile.open();
616 m_file = tempFile.fileName();
617
618 KUrl destURL;
619 destURL.setPath( m_file );
620 KIO::JobFlags flags = m_showProgressInfo ? KIO::DefaultFlags : KIO::HideProgressInfo;
621 flags |= KIO::Overwrite;
622 m_job = KIO::file_copy(m_url, destURL, 0600, flags);
623 m_job->ui()->setWindow(q->widget() ? q->widget()->topLevelWidget() : 0);
624 emit q->started(m_job);
625 QObject::connect(m_job, SIGNAL(result(KJob*)), q, SLOT(_k_slotJobFinished(KJob*)));
626 QObject::connect(m_job, SIGNAL(mimetype(KIO::Job*,QString)),
627 q, SLOT(_k_slotGotMimeType(KIO::Job*,QString)));
628}
629
630void ReadOnlyPart::abortLoad()
631{
632 Q_D(ReadOnlyPart);
633
634 if ( d->m_statJob ) {
635 //kDebug(1000) << "Aborting job" << d->m_statJob;
636 d->m_statJob->kill();
637 d->m_statJob = 0;
638 }
639 if ( d->m_job ) {
640 //kDebug(1000) << "Aborting job" << d->m_job;
641 d->m_job->kill();
642 d->m_job = 0;
643 }
644}
645
646bool ReadOnlyPart::closeUrl()
647{
648 Q_D(ReadOnlyPart);
649
650 abortLoad(); //just in case
651
652 d->m_arguments = KParts::OpenUrlArguments();
653
654 if ( d->m_bTemp )
655 {
656 QFile::remove( d->m_file );
657 d->m_bTemp = false;
658 }
659 // It always succeeds for a read-only part,
660 // but the return value exists for reimplementations
661 // (e.g. pressing cancel for a modified read-write part)
662 return true;
663}
664
665void ReadOnlyPartPrivate::_k_slotStatJobFinished(KJob * job)
666{
667 Q_ASSERT(job == m_statJob);
668 m_statJob = 0;
669
670 // We could emit canceled on error, but we haven't even emitted started yet,
671 // this could maybe confuse some apps? So for now we'll just fallback to KIO::get
672 // and error again. Well, maybe this even helps with wrong stat results.
673 if (!job->error()) {
674 const KUrl localUrl = static_cast<KIO::StatJob*>(job)->mostLocalUrl();
675 if (localUrl.isLocalFile()) {
676 m_file = localUrl.toLocalFile();
677 (void)openLocalFile();
678 return;
679 }
680 }
681 openRemoteFile();
682}
683
684void ReadOnlyPartPrivate::_k_slotJobFinished( KJob * job )
685{
686 Q_Q(ReadOnlyPart);
687
688 assert( job == m_job );
689 m_job = 0;
690 if (job->error())
691 emit q->canceled( job->errorString() );
692 else
693 {
694 if ( q->openFile() ) {
695 emit q->setWindowCaption( m_url.prettyUrl() );
696 emit q->completed();
697 } else emit q->canceled(QString());
698 }
699}
700
701void ReadOnlyPartPrivate::_k_slotGotMimeType(KIO::Job *job, const QString &mime)
702{
703 kDebug(1000) << mime;
704 Q_ASSERT(job == m_job); Q_UNUSED(job)
705 // set the mimetype only if it was not already set (for example, by the host application)
706 if (m_arguments.mimeType().isEmpty()) {
707 m_arguments.setMimeType(mime);
708 m_bAutoDetectedMime = true;
709 }
710}
711
712void ReadOnlyPart::guiActivateEvent( GUIActivateEvent * event )
713{
714 Q_D(ReadOnlyPart);
715
716 if (event->activated())
717 {
718 if (!d->m_url.isEmpty())
719 {
720 kDebug(1000) << d->m_url;
721 emit setWindowCaption( d->m_url.prettyUrl() );
722 } else emit setWindowCaption( "" );
723 }
724}
725
726bool ReadOnlyPart::openStream( const QString& mimeType, const KUrl& url )
727{
728 Q_D(ReadOnlyPart);
729
730 OpenUrlArguments args = d->m_arguments;
731 if ( !closeUrl() )
732 return false;
733 d->m_arguments = args;
734 setUrl( url );
735 return doOpenStream( mimeType );
736}
737
738bool ReadOnlyPart::writeStream( const QByteArray& data )
739{
740 return doWriteStream( data );
741}
742
743bool ReadOnlyPart::closeStream()
744{
745 return doCloseStream();
746}
747
748BrowserExtension* ReadOnlyPart::browserExtension() const
749{
750 return findChild<KParts::BrowserExtension *>();
751}
752
753void KParts::ReadOnlyPart::setArguments(const OpenUrlArguments& arguments)
754{
755 Q_D(ReadOnlyPart);
756 d->m_arguments = arguments;
757 d->m_bAutoDetectedMime = arguments.mimeType().isEmpty();
758}
759
760OpenUrlArguments KParts::ReadOnlyPart::arguments() const
761{
762 Q_D(const ReadOnlyPart);
763 return d->m_arguments;
764}
765
766//////////////////////////////////////////////////
767
768
769ReadWritePart::ReadWritePart( QObject *parent )
770 : ReadOnlyPart( *new ReadWritePartPrivate(this), parent )
771{
772}
773
774ReadWritePart::~ReadWritePart()
775{
776 // parent destructor will delete temp file
777 // we can't call our own closeUrl() here, because
778 // "cancel" wouldn't cancel anything. We have to assume
779 // the app called closeUrl() before destroying us.
780}
781
782void ReadWritePart::setReadWrite( bool readwrite )
783{
784 Q_D(ReadWritePart);
785
786 // Perhaps we should check isModified here and issue a warning if true
787 d->m_bReadWrite = readwrite;
788}
789
790void ReadWritePart::setModified( bool modified )
791{
792 Q_D(ReadWritePart);
793
794 kDebug(1000) << "setModified(" << (modified ? "true" : "false") << ")";
795 if ( !d->m_bReadWrite && modified )
796 {
797 kError(1000) << "Can't set a read-only document to 'modified' !" << endl;
798 return;
799 }
800 d->m_bModified = modified;
801}
802
803void ReadWritePart::setModified()
804{
805 setModified( true );
806}
807
808bool ReadWritePart::queryClose()
809{
810 Q_D(ReadWritePart);
811
812 if ( !isReadWrite() || !isModified() )
813 return true;
814
815 QString docName = url().fileName();
816 if (docName.isEmpty()) docName = i18n( "Untitled" );
817
818 QWidget *parentWidget=widget();
819 if(!parentWidget) parentWidget=QApplication::activeWindow();
820
821 int res = KMessageBox::warningYesNoCancel( parentWidget,
822 i18n( "The document \"%1\" has been modified.\n"
823 "Do you want to save your changes or discard them?" , docName ),
824 i18n( "Close Document" ), KStandardGuiItem::save(), KStandardGuiItem::discard() );
825
826 bool abortClose=false;
827 bool handled=false;
828
829 switch(res) {
830 case KMessageBox::Yes :
831 sigQueryClose(&handled,&abortClose);
832 if (!handled)
833 {
834 if (d->m_url.isEmpty())
835 {
836 KUrl url = KFileDialog::getSaveUrl(KUrl(), QString(), parentWidget);
837 if (url.isEmpty())
838 return false;
839
840 saveAs( url );
841 }
842 else
843 {
844 save();
845 }
846 } else if (abortClose) return false;
847 return waitSaveComplete();
848 case KMessageBox::No :
849 return true;
850 default : // case KMessageBox::Cancel :
851 return false;
852 }
853}
854
855bool ReadWritePart::closeUrl()
856{
857 abortLoad(); //just in case
858 if ( isReadWrite() && isModified() )
859 {
860 if (!queryClose())
861 return false;
862 }
863 // Not modified => ok and delete temp file.
864 return ReadOnlyPart::closeUrl();
865}
866
867bool ReadWritePart::closeUrl( bool promptToSave )
868{
869 return promptToSave ? closeUrl() : ReadOnlyPart::closeUrl();
870}
871
872bool ReadWritePart::save()
873{
874 Q_D(ReadWritePart);
875
876 d->m_saveOk = false;
877 if ( d->m_file.isEmpty() ) // document was created empty
878 d->prepareSaving();
879 if( saveFile() )
880 return saveToUrl();
881 else
882 emit canceled(QString());
883 return false;
884}
885
886bool ReadWritePart::saveAs( const KUrl & kurl )
887{
888 Q_D(ReadWritePart);
889
890 if (!kurl.isValid())
891 {
892 kError(1000) << "saveAs: Malformed URL " << kurl.url() << endl;
893 return false;
894 }
895 d->m_duringSaveAs = true;
896 d->m_originalURL = d->m_url;
897 d->m_originalFilePath = d->m_file;
898 d->m_url = kurl; // Store where to upload in saveToURL
899 d->prepareSaving();
900 bool result = save(); // Save local file and upload local file
901 if (result) {
902 emit urlChanged( d->m_url );
903 emit setWindowCaption( d->m_url.prettyUrl() );
904 } else {
905 d->m_url = d->m_originalURL;
906 d->m_file = d->m_originalFilePath;
907 d->m_duringSaveAs = false;
908 d->m_originalURL = KUrl();
909 d->m_originalFilePath.clear();
910 }
911
912 return result;
913}
914
915// Set m_file correctly for m_url
916void ReadWritePartPrivate::prepareSaving()
917{
918 // Local file
919 if ( m_url.isLocalFile() )
920 {
921 if ( m_bTemp ) // get rid of a possible temp file first
922 { // (happens if previous url was remote)
923 QFile::remove( m_file );
924 m_bTemp = false;
925 }
926 m_file = m_url.toLocalFile();
927 }
928 else
929 { // Remote file
930 // We haven't saved yet, or we did but locally - provide a temp file
931 if ( m_file.isEmpty() || !m_bTemp )
932 {
933 KTemporaryFile tempFile;
934 tempFile.setAutoRemove(false);
935 tempFile.open();
936 m_file = tempFile.fileName();
937 m_bTemp = true;
938 }
939 // otherwise, we already had a temp file
940 }
941}
942
943bool ReadWritePart::saveToUrl()
944{
945 Q_D(ReadWritePart);
946
947 if ( d->m_url.isLocalFile() )
948 {
949 setModified( false );
950 emit completed();
951 // if m_url is a local file there won't be a temp file -> nothing to remove
952 assert( !d->m_bTemp );
953 d->m_saveOk = true;
954 d->m_duringSaveAs = false;
955 d->m_originalURL = KUrl();
956 d->m_originalFilePath.clear();
957 return true; // Nothing to do
958 }
959 else
960 {
961 if (d->m_uploadJob)
962 {
963 QFile::remove(d->m_uploadJob->srcUrl().toLocalFile());
964 d->m_uploadJob->kill();
965 d->m_uploadJob = 0;
966 }
967 KTemporaryFile *tempFile = new KTemporaryFile();
968 tempFile->open();
969 QString uploadFile = tempFile->fileName();
970 delete tempFile;
971 KUrl uploadUrl;
972 uploadUrl.setPath( uploadFile );
973 // Create hardlink
974 if (::link(QFile::encodeName(d->m_file), QFile::encodeName(uploadFile)) != 0)
975 {
976 // Uh oh, some error happened.
977 return false;
978 }
979 d->m_uploadJob = KIO::file_move( uploadUrl, d->m_url, -1, KIO::Overwrite );
980 d->m_uploadJob->ui()->setWindow( widget() ? widget()->topLevelWidget() : 0 );
981 connect( d->m_uploadJob, SIGNAL(result(KJob*)), this, SLOT(_k_slotUploadFinished(KJob*)) );
982 return true;
983 }
984}
985
986void ReadWritePartPrivate::_k_slotUploadFinished( KJob * )
987{
988 Q_Q(ReadWritePart);
989
990 if (m_uploadJob->error())
991 {
992 QFile::remove(m_uploadJob->srcUrl().toLocalFile());
993 QString error = m_uploadJob->errorString();
994 m_uploadJob = 0;
995 if (m_duringSaveAs) {
996 q->setUrl(m_originalURL);
997 m_file = m_originalFilePath;
998 }
999 emit q->canceled( error );
1000 }
1001 else
1002 {
1003 KUrl dirUrl( m_url );
1004 dirUrl.setPath( dirUrl.directory() );
1005 ::org::kde::KDirNotify::emitFilesAdded( dirUrl.url() );
1006
1007 m_uploadJob = 0;
1008 q->setModified( false );
1009 emit q->completed();
1010 m_saveOk = true;
1011 }
1012 m_duringSaveAs = false;
1013 m_originalURL = KUrl();
1014 m_originalFilePath.clear();
1015 if (m_waitForSave) {
1016 m_eventLoop.quit();
1017 }
1018}
1019
1020bool ReadWritePart::isReadWrite() const
1021{
1022 Q_D(const ReadWritePart);
1023
1024 return d->m_bReadWrite;
1025}
1026
1027bool ReadWritePart::isModified() const
1028{
1029 Q_D(const ReadWritePart);
1030
1031 return d->m_bModified;
1032}
1033
1034bool ReadWritePart::waitSaveComplete()
1035{
1036 Q_D(ReadWritePart);
1037
1038 if (!d->m_uploadJob)
1039 return d->m_saveOk;
1040
1041 d->m_waitForSave = true;
1042
1043 d->m_eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
1044
1045 d->m_waitForSave = false;
1046
1047 return d->m_saveOk;
1048}
1049
1050////
1051
1052class KParts::OpenUrlArgumentsPrivate : public QSharedData
1053{
1054public:
1055 OpenUrlArgumentsPrivate()
1056 : reload(false),
1057 actionRequestedByUser(true),
1058 xOffset(0),
1059 yOffset(0),
1060 mimeType(),
1061 metaData()
1062 {}
1063 bool reload;
1064 bool actionRequestedByUser;
1065 int xOffset;
1066 int yOffset;
1067 QString mimeType;
1068 QMap<QString, QString> metaData;
1069};
1070
1071KParts::OpenUrlArguments::OpenUrlArguments()
1072 : d(new OpenUrlArgumentsPrivate)
1073{
1074}
1075
1076KParts::OpenUrlArguments::OpenUrlArguments(const OpenUrlArguments &other)
1077 : d(other.d)
1078{
1079}
1080
1081KParts::OpenUrlArguments & KParts::OpenUrlArguments::operator=( const OpenUrlArguments &other)
1082{
1083 d = other.d;
1084 return *this;
1085}
1086
1087KParts::OpenUrlArguments::~OpenUrlArguments()
1088{
1089}
1090
1091bool KParts::OpenUrlArguments::reload() const
1092{
1093 return d->reload;
1094}
1095
1096void KParts::OpenUrlArguments::setReload(bool b)
1097{
1098 d->reload = b;
1099}
1100
1101int KParts::OpenUrlArguments::xOffset() const
1102{
1103 return d->xOffset;
1104}
1105
1106void KParts::OpenUrlArguments::setXOffset(int x)
1107{
1108 d->xOffset = x;
1109}
1110
1111int KParts::OpenUrlArguments::yOffset() const
1112{
1113 return d->yOffset;
1114}
1115
1116void KParts::OpenUrlArguments::setYOffset(int y)
1117{
1118 d->yOffset = y;
1119}
1120
1121QString KParts::OpenUrlArguments::mimeType() const
1122{
1123 return d->mimeType;
1124}
1125
1126void KParts::OpenUrlArguments::setMimeType(const QString& mime)
1127{
1128 d->mimeType = mime;
1129}
1130
1131QMap<QString, QString> & KParts::OpenUrlArguments::metaData()
1132{
1133 return d->metaData;
1134}
1135
1136const QMap<QString, QString> & KParts::OpenUrlArguments::metaData() const
1137{
1138 return d->metaData;
1139}
1140
1141bool KParts::OpenUrlArguments::actionRequestedByUser() const
1142{
1143 return d->actionRequestedByUser;
1144}
1145
1146void KParts::OpenUrlArguments::setActionRequestedByUser(bool userRequested)
1147{
1148 d->actionRequestedByUser = userRequested;
1149}
1150
1151#include "part.moc"
1152