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 | |
52 | using namespace KParts; |
53 | |
54 | namespace KParts |
55 | { |
56 | |
57 | class PartBasePrivate |
58 | { |
59 | public: |
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 | |
79 | class PartPrivate: public PartBasePrivate |
80 | { |
81 | public: |
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 | |
108 | PartBase::PartBase() |
109 | : d_ptr(new PartBasePrivate(this)) |
110 | { |
111 | } |
112 | |
113 | PartBase::PartBase(PartBasePrivate &dd) |
114 | : d_ptr(&dd) |
115 | { |
116 | } |
117 | |
118 | PartBase::~PartBase() |
119 | { |
120 | delete d_ptr; |
121 | } |
122 | |
123 | void PartBase::setPartObject( QObject *obj ) |
124 | { |
125 | Q_D(PartBase); |
126 | |
127 | d->m_obj = obj; |
128 | } |
129 | |
130 | QObject *PartBase::partObject() const |
131 | { |
132 | Q_D(const PartBase); |
133 | |
134 | return d->m_obj; |
135 | } |
136 | |
137 | void PartBase::setComponentData(const KComponentData &componentData) |
138 | { |
139 | setComponentData(componentData, true); |
140 | } |
141 | |
142 | void 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 | |
156 | void 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 | |
164 | void PartBase::setPluginLoadingMode( PluginLoadingMode loadingMode ) |
165 | { |
166 | Q_D(PartBase); |
167 | |
168 | d->m_pluginLoadingMode = loadingMode; |
169 | } |
170 | |
171 | void KParts::PartBase::setPluginInterfaceVersion( int version ) |
172 | { |
173 | Q_D(PartBase); |
174 | |
175 | d->m_pluginInterfaceVersion = version; |
176 | } |
177 | |
178 | Part::Part( QObject *parent ) |
179 | : QObject( parent ), PartBase( *new PartPrivate(this) ) |
180 | { |
181 | PartBase::setPartObject( this ); |
182 | } |
183 | |
184 | Part::Part(PartPrivate &dd, QObject *parent) |
185 | : QObject( parent ), PartBase( dd ) |
186 | { |
187 | PartBase::setPartObject( this ); |
188 | } |
189 | |
190 | Part::~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 | |
215 | void 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 | |
225 | QWidget *Part::widget() |
226 | { |
227 | Q_D(Part); |
228 | |
229 | return d->m_widget; |
230 | } |
231 | |
232 | void Part::setAutoDeleteWidget(bool autoDeleteWidget) |
233 | { |
234 | Q_D(Part); |
235 | d->m_autoDeleteWidget = autoDeleteWidget; |
236 | } |
237 | |
238 | void Part::setAutoDeletePart(bool autoDeletePart) |
239 | { |
240 | Q_D(Part); |
241 | d->m_autoDeletePart = autoDeletePart; |
242 | } |
243 | |
244 | |
245 | |
246 | KIconLoader* 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 | |
257 | void Part::setManager( PartManager *manager ) |
258 | { |
259 | Q_D(Part); |
260 | |
261 | d->m_manager = manager; |
262 | } |
263 | |
264 | PartManager *Part::manager() const |
265 | { |
266 | Q_D(const Part); |
267 | |
268 | return d->m_manager; |
269 | } |
270 | |
271 | Part *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 | |
281 | void 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 | |
289 | void Part::setSelectable( bool selectable ) |
290 | { |
291 | Q_D(Part); |
292 | |
293 | d->m_bSelectable = selectable; |
294 | } |
295 | |
296 | bool Part::isSelectable() const |
297 | { |
298 | Q_D(const Part); |
299 | |
300 | return d->m_bSelectable; |
301 | } |
302 | |
303 | void 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 | |
326 | void Part::partActivateEvent( PartActivateEvent * ) |
327 | { |
328 | } |
329 | |
330 | void Part::partSelectEvent( PartSelectEvent * ) |
331 | { |
332 | } |
333 | |
334 | void Part::guiActivateEvent( GUIActivateEvent * ) |
335 | { |
336 | } |
337 | |
338 | QWidget *Part::hostContainer( const QString &containerName ) |
339 | { |
340 | if ( !factory() ) |
341 | return 0; |
342 | |
343 | return factory()->container( containerName, this ); |
344 | } |
345 | |
346 | void 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 | |
357 | void Part::loadPlugins() |
358 | { |
359 | PartBase::loadPlugins(this, this, componentData()); |
360 | } |
361 | |
362 | ////////////////////////////////////////////////// |
363 | |
364 | namespace KParts |
365 | { |
366 | |
367 | class ReadOnlyPartPrivate: public PartPrivate |
368 | { |
369 | public: |
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 | |
426 | class ReadWritePartPrivate: public ReadOnlyPartPrivate |
427 | { |
428 | public: |
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 | |
450 | ReadOnlyPart::ReadOnlyPart( QObject *parent ) |
451 | : Part( *new ReadOnlyPartPrivate(this), parent ) |
452 | { |
453 | } |
454 | |
455 | ReadOnlyPart::ReadOnlyPart( ReadOnlyPartPrivate &dd, QObject *parent ) |
456 | : Part( dd, parent ) |
457 | { |
458 | } |
459 | |
460 | ReadOnlyPart::~ReadOnlyPart() |
461 | { |
462 | ReadOnlyPart::closeUrl(); |
463 | } |
464 | |
465 | KUrl ReadOnlyPart::url() const |
466 | { |
467 | Q_D(const ReadOnlyPart); |
468 | |
469 | return d->m_url; |
470 | } |
471 | |
472 | void ReadOnlyPart::setUrl(const KUrl &url) |
473 | { |
474 | Q_D(ReadOnlyPart); |
475 | |
476 | d->m_url = url; |
477 | emit urlChanged( url ); |
478 | } |
479 | |
480 | QString ReadOnlyPart::localFilePath() const |
481 | { |
482 | Q_D(const ReadOnlyPart); |
483 | |
484 | return d->m_file; |
485 | } |
486 | |
487 | void ReadOnlyPart::setLocalFilePath( const QString &localFilePath ) |
488 | { |
489 | Q_D(ReadOnlyPart); |
490 | |
491 | d->m_file = localFilePath; |
492 | } |
493 | |
494 | #ifndef KDE_NO_DEPRECATED |
495 | bool ReadOnlyPart::isLocalFileTemporary() const |
496 | { |
497 | Q_D(const ReadOnlyPart); |
498 | |
499 | return d->m_bTemp; |
500 | } |
501 | #endif |
502 | |
503 | #ifndef KDE_NO_DEPRECATED |
504 | void ReadOnlyPart::setLocalFileTemporary( bool temp ) |
505 | { |
506 | Q_D(ReadOnlyPart); |
507 | |
508 | d->m_bTemp = temp; |
509 | } |
510 | #endif |
511 | |
512 | void ReadOnlyPart::setProgressInfoEnabled( bool show ) |
513 | { |
514 | Q_D(ReadOnlyPart); |
515 | |
516 | d->m_showProgressInfo = show; |
517 | } |
518 | |
519 | bool ReadOnlyPart::isProgressInfoEnabled() const |
520 | { |
521 | Q_D(const ReadOnlyPart); |
522 | |
523 | return d->m_showProgressInfo; |
524 | } |
525 | |
526 | #ifndef KDE_NO_COMPAT |
527 | void ReadOnlyPart::showProgressInfo( bool show ) |
528 | { |
529 | Q_D(ReadOnlyPart); |
530 | |
531 | d->m_showProgressInfo = show; |
532 | } |
533 | #endif |
534 | |
535 | bool 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 | |
569 | bool 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 | |
576 | bool 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 | |
601 | void 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 | |
630 | void 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 | |
646 | bool 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 | |
665 | void 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 | |
684 | void 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 | |
701 | void 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 | |
712 | void 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 | |
726 | bool 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 | |
738 | bool ReadOnlyPart::writeStream( const QByteArray& data ) |
739 | { |
740 | return doWriteStream( data ); |
741 | } |
742 | |
743 | bool ReadOnlyPart::closeStream() |
744 | { |
745 | return doCloseStream(); |
746 | } |
747 | |
748 | BrowserExtension* ReadOnlyPart::browserExtension() const |
749 | { |
750 | return findChild<KParts::BrowserExtension *>(); |
751 | } |
752 | |
753 | void 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 | |
760 | OpenUrlArguments KParts::ReadOnlyPart::arguments() const |
761 | { |
762 | Q_D(const ReadOnlyPart); |
763 | return d->m_arguments; |
764 | } |
765 | |
766 | ////////////////////////////////////////////////// |
767 | |
768 | |
769 | ReadWritePart::ReadWritePart( QObject *parent ) |
770 | : ReadOnlyPart( *new ReadWritePartPrivate(this), parent ) |
771 | { |
772 | } |
773 | |
774 | ReadWritePart::~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 | |
782 | void 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 | |
790 | void 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 | |
803 | void ReadWritePart::setModified() |
804 | { |
805 | setModified( true ); |
806 | } |
807 | |
808 | bool 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 | |
855 | bool 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 | |
867 | bool ReadWritePart::closeUrl( bool promptToSave ) |
868 | { |
869 | return promptToSave ? closeUrl() : ReadOnlyPart::closeUrl(); |
870 | } |
871 | |
872 | bool 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 | |
886 | bool 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 |
916 | void 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 | |
943 | bool 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 | |
986 | void 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 | |
1020 | bool ReadWritePart::isReadWrite() const |
1021 | { |
1022 | Q_D(const ReadWritePart); |
1023 | |
1024 | return d->m_bReadWrite; |
1025 | } |
1026 | |
1027 | bool ReadWritePart::isModified() const |
1028 | { |
1029 | Q_D(const ReadWritePart); |
1030 | |
1031 | return d->m_bModified; |
1032 | } |
1033 | |
1034 | bool 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 | |
1052 | class KParts::OpenUrlArgumentsPrivate : public QSharedData |
1053 | { |
1054 | public: |
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 | |
1071 | KParts::OpenUrlArguments::OpenUrlArguments() |
1072 | : d(new OpenUrlArgumentsPrivate) |
1073 | { |
1074 | } |
1075 | |
1076 | KParts::OpenUrlArguments::OpenUrlArguments(const OpenUrlArguments &other) |
1077 | : d(other.d) |
1078 | { |
1079 | } |
1080 | |
1081 | KParts::OpenUrlArguments & KParts::OpenUrlArguments::operator=( const OpenUrlArguments &other) |
1082 | { |
1083 | d = other.d; |
1084 | return *this; |
1085 | } |
1086 | |
1087 | KParts::OpenUrlArguments::~OpenUrlArguments() |
1088 | { |
1089 | } |
1090 | |
1091 | bool KParts::OpenUrlArguments::reload() const |
1092 | { |
1093 | return d->reload; |
1094 | } |
1095 | |
1096 | void KParts::OpenUrlArguments::setReload(bool b) |
1097 | { |
1098 | d->reload = b; |
1099 | } |
1100 | |
1101 | int KParts::OpenUrlArguments::xOffset() const |
1102 | { |
1103 | return d->xOffset; |
1104 | } |
1105 | |
1106 | void KParts::OpenUrlArguments::setXOffset(int x) |
1107 | { |
1108 | d->xOffset = x; |
1109 | } |
1110 | |
1111 | int KParts::OpenUrlArguments::yOffset() const |
1112 | { |
1113 | return d->yOffset; |
1114 | } |
1115 | |
1116 | void KParts::OpenUrlArguments::setYOffset(int y) |
1117 | { |
1118 | d->yOffset = y; |
1119 | } |
1120 | |
1121 | QString KParts::OpenUrlArguments::mimeType() const |
1122 | { |
1123 | return d->mimeType; |
1124 | } |
1125 | |
1126 | void KParts::OpenUrlArguments::setMimeType(const QString& mime) |
1127 | { |
1128 | d->mimeType = mime; |
1129 | } |
1130 | |
1131 | QMap<QString, QString> & KParts::OpenUrlArguments::metaData() |
1132 | { |
1133 | return d->metaData; |
1134 | } |
1135 | |
1136 | const QMap<QString, QString> & KParts::OpenUrlArguments::metaData() const |
1137 | { |
1138 | return d->metaData; |
1139 | } |
1140 | |
1141 | bool KParts::OpenUrlArguments::actionRequestedByUser() const |
1142 | { |
1143 | return d->actionRequestedByUser; |
1144 | } |
1145 | |
1146 | void KParts::OpenUrlArguments::setActionRequestedByUser(bool userRequested) |
1147 | { |
1148 | d->actionRequestedByUser = userRequested; |
1149 | } |
1150 | |
1151 | #include "part.moc" |
1152 | |