1 /* This file is part of the KDE project
2 Copyright (C) 1998, 1999, 2000 Torben Weis <weis@kde.org>
3 Copyright (C) 2004 - 2009 Dag Andersen <danders@get2net.dk>
4 Copyright (C) 2006 Raphael Langerhorst <raphael.langerhorst@kdemail.net>
5 Copyright (C) 2007 Thorsten Zachmann <zachmann@kde.org>
6 Copyright (C) 2007 - 2009, 2012 Dag Andersen <danders@get2net.dk>
7
8 This library is free software; you can redistribute it and/or
9 modify it under the terms of the GNU Library General Public
10 License as published by the Free Software Foundation; either
11 version 2 of the License, or (at your option) any later version.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Library General Public License for more details.
17
18 You should have received a copy of the GNU Library General Public License
19 along with this library; see the file COPYING.LIB. If not, write to
20 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24 // clazy:excludeall=qstring-arg
25 #include "part.h"
26 #include "view.h"
27 #include "factory.h"
28 #include "mainwindow.h"
29 #include "workpackage.h"
30 #include "calligraplanworksettings.h"
31
32 #include "KPlatoXmlLoader.h" //NB!
33
34 #include "kptglobal.h"
35 #include "kptnode.h"
36 #include "kptproject.h"
37 #include "kpttask.h"
38 #include "kptdocuments.h"
39 #include "kptcommand.h"
40
41 #include <KoXmlReader.h>
42 #include <KoStore.h>
43 #include <KoDocumentInfo.h>
44 #include <KoResourcePaths.h>
45 #include <KoComponentData.h>
46
47 #include <QPainter>
48 #include <QFileInfo>
49 #include <QDir>
50 #include <QTimer>
51 #include <QFileSystemWatcher>
52 #include <kundo2qstack.h>
53 #include <QPointer>
54 #include <QUrl>
55 #include <QMimeDatabase>
56 #include <QApplication>
57
58 #include <KLocalizedString>
59 #include <kmessagebox.h>
60 #include <kparts/partmanager.h>
61 #include <kopenwithdialog.h>
62 #include <kmimetypetrader.h>
63 //#include <kserviceoffer.h>
64 #include <KIO/DesktopExecParser>
65 #include <krun.h>
66 #include <kprocess.h>
67 #include <kactioncollection.h>
68
69 #include "debugarea.h"
70
71 using namespace KPlato;
72
73 namespace KPlatoWork
74 {
75
76 //-------------------------------
DocumentChild(WorkPackage * parent)77 DocumentChild::DocumentChild(WorkPackage *parent)
78 : QObject(parent),
79 m_doc(0),
80 m_type(Type_Unknown),
81 m_copy(false),
82 m_process(0),
83 m_editor(0),
84 m_editormodified(false),
85 m_filemodified(false),
86 m_fileSystemWatcher(new QFileSystemWatcher(this))
87
88 {
89 }
90
91 // DocumentChild::DocumentChild(KParts::ReadWritePart *editor, const QUrl &url, const Document *doc, Part *parent)
92 // : KoDocumentChild(parent),
93 // m_doc(doc),
94 // m_type(Type_Unknown),
95 // m_copy(true),
96 // m_process(0),
97 // m_editor(editor),
98 // m_editormodified(false),
99 // m_filemodified(false)
100 // {
101 // setFileInfo(url);
102 // if (dynamic_cast<KoDocument*>(editor)) {
103 // debugPlanWork<<"Creating Calligra doc";
104 // m_type = Type_Calligra;
105 // connect(static_cast<KoDocument*>(editor), SIGNAL(modified(bool)), this, SLOT(setModified(bool)));
106 // } else {
107 // debugPlanWork<<"Creating KParts doc";
108 // m_type = Type_KParts;
109 // slotUpdateModified();
110 // }
111 // }
112
~DocumentChild()113 DocumentChild::~DocumentChild()
114 {
115 debugPlanWork;
116 disconnect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, this, &DocumentChild::slotDirty);
117 m_fileSystemWatcher->removePath(filePath());
118
119 if (m_type == Type_Calligra || m_type == Type_KParts) {
120 delete m_editor;
121 }
122 }
123
parentPackage() const124 WorkPackage *DocumentChild::parentPackage() const
125 {
126 return static_cast<WorkPackage*>(parent());
127 }
128
setFileInfo(const QUrl & url)129 void DocumentChild::setFileInfo(const QUrl &url)
130 {
131 m_fileinfo.setFile(url.path());
132 //debugPlanWork<<url;
133 bool res = connect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, this, &DocumentChild::slotDirty);
134 //debugPlanWork<<res<<filePath();
135 #ifndef NDEBUG
136 Q_ASSERT(res);
137 #else
138 Q_UNUSED(res);
139 #endif
140 m_fileSystemWatcher->addPath(filePath());
141 }
142
setModified(bool mod)143 void DocumentChild::setModified(bool mod)
144 {
145 debugPlanWork<<mod<<filePath();
146 if (m_editormodified != mod) {
147 m_editormodified = mod;
148 emit modified(mod);
149 }
150 }
151
slotDirty(const QString & file)152 void DocumentChild::slotDirty(const QString &file)
153 {
154 //debugPlanWork<<filePath()<<file<<m_filemodified;
155 if (file == filePath() && ! m_filemodified) {
156 debugPlanWork<<file<<"is modified";
157 m_filemodified = true;
158 emit fileModified(true);
159 }
160 }
161
slotUpdateModified()162 void DocumentChild::slotUpdateModified()
163 {
164 if (m_type == Type_KParts && m_editor && (m_editor->isModified() != m_editormodified)) {
165 setModified(m_editor->isModified());
166 }
167 QTimer::singleShot(500, this, &DocumentChild::slotUpdateModified);
168 }
169
setDoc(const Document * doc)170 bool DocumentChild::setDoc(const Document *doc)
171 {
172 Q_ASSERT (m_doc == 0);
173 if (isOpen()) {
174 KMessageBox::error(0, i18n("Document is already open:<br>%1", doc->url().url()));
175 return false;
176 }
177 m_doc = doc;
178 QUrl url;
179 if (parentPackage()->newDocuments().contains(doc)) {
180 url = parentPackage()->newDocuments().value(doc);
181 Q_ASSERT(url.isValid());
182 parentPackage()->removeNewDocument(doc);
183 } else if (doc->sendAs() == Document::SendAs_Copy) {
184 url = parentPackage()->extractFile(doc);
185 if (url.url().isEmpty()) {
186 KMessageBox::error(0, i18n("Could not extract document from storage:<br>%1", doc->url().url()));
187 return false;
188 }
189 m_copy = true;
190 } else {
191 url = doc->url();
192 }
193 if (! url.isValid()) {
194 KMessageBox::error(0, i18n("Invalid URL:<br>%1", url.url()));
195 return false;
196 }
197 setFileInfo(url);
198 return true;
199 }
200
openDoc(const Document * doc,KoStore * store)201 bool DocumentChild::openDoc(const Document *doc, KoStore *store)
202 {
203 Q_ASSERT (m_doc == 0);
204 if (isOpen()) {
205 KMessageBox::error(0, i18n("Document is already open:<br>%1", doc->url().path()));
206 return false;
207 }
208 m_doc = doc;
209 QUrl url;
210 if (doc->sendAs() == Document::SendAs_Copy) {
211 url = parentPackage()->extractFile(doc, store);
212 if (url.url().isEmpty()) {
213 KMessageBox::error(0, i18n("Could not extract document from storage:<br>%1", doc->url().path()));
214 return false;
215 }
216 m_copy = true;
217 } else {
218 url = doc->url();
219 }
220 if (! url.isValid()) {
221 KMessageBox::error(0, i18n("Invalid URL:<br>%1", url.url()));
222 return false;
223 }
224 setFileInfo(url);
225 return true;
226 }
227
editDoc()228 bool DocumentChild::editDoc()
229 {
230 Q_ASSERT(m_doc != 0);
231 debugPlanWork<<"file:"<<filePath();
232 if (isOpen()) {
233 KMessageBox::error(0, i18n("Document is already open:<br> %1", m_doc->url().path()));
234 return false;
235 }
236 if (! m_fileinfo.exists()) {
237 KMessageBox::error(0, i18n("File does not exist:<br>%1", fileName()));
238 return false;
239 }
240 QUrl filename = QUrl::fromLocalFile(filePath());
241 const QMimeType mimetype = QMimeDatabase().mimeTypeForUrl(filename);
242 KService::Ptr service = KMimeTypeTrader::self()->preferredService(mimetype.name());
243 bool editing = startProcess(service, filename);
244 if (editing) {
245 m_type = Type_Other; // FIXME: try to be more specific
246 }
247 return editing;
248 }
249
startProcess(KService::Ptr service,const QUrl & url)250 bool DocumentChild::startProcess(KService::Ptr service, const QUrl &url)
251 {
252 QStringList args;
253 QList<QUrl> files;
254 if (url.isValid()) {
255 files << url;
256 }
257 if (service) {
258 KIO::DesktopExecParser parser(*service, files);
259 parser.setUrlsAreTempFiles(false);
260 args = parser.resultingArguments();
261 } else {
262 QList<QUrl> list;
263 QPointer<KOpenWithDialog> dlg = new KOpenWithDialog(list, i18n("Edit with:"), QString(), 0);
264 if (dlg->exec() == QDialog::Accepted && dlg){
265 args << dlg->text();
266 }
267 if (args.isEmpty()) {
268 debugPlanWork<<"No executable selected";
269 return false;
270 }
271 args << url.url();
272 delete dlg;
273 }
274 debugPlanWork<<args;
275 m_process = new KProcess();
276 m_process->setProgram(args);
277 connect(m_process, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(slotEditFinished(int,QProcess::ExitStatus)));
278 connect(m_process, SIGNAL(error(QProcess::ProcessError)), SLOT(slotEditError(QProcess::ProcessError)));
279 m_process->start();
280 //debugPlanWork<<m_process->pid()<<m_process->program();
281 return true;
282 }
283
isModified() const284 bool DocumentChild::isModified() const
285 {
286 return m_editormodified;
287 }
288
isFileModified() const289 bool DocumentChild::isFileModified() const
290 {
291 return m_filemodified;
292 }
293
slotEditFinished(int,QProcess::ExitStatus)294 void DocumentChild::slotEditFinished(int /*par*/, QProcess::ExitStatus)
295 {
296 //debugPlanWork<<par<<filePath();
297 delete m_process;
298 m_process = 0;
299 }
300
slotEditError(QProcess::ProcessError status)301 void DocumentChild::slotEditError(QProcess::ProcessError status)
302 {
303 debugPlanWork<<status;
304 if (status == QProcess::FailedToStart || status == QProcess::Crashed) {
305 m_process->deleteLater();
306 m_process = 0;
307 } else debugPlanWork<<"Error="<<status<<" what to do?";
308 }
309
saveToStore(KoStore * store)310 bool DocumentChild::saveToStore(KoStore *store)
311 {
312 debugPlanWork<<filePath();
313 m_fileSystemWatcher->removePath(filePath());
314 bool ok = false;
315 bool wasmod = m_filemodified;
316 if (m_type == Type_Calligra || m_type == Type_KParts) {
317 if (m_editor->isModified()) {
318 ok = m_editor->save(); // hmmmm
319 } else {
320 ok = true;
321 }
322 } else if (m_type == Type_Other) {
323 if (isOpen()) {
324 warnPlanWork<<"External editor open";
325 }
326 ok = true;
327 } else {
328 errorPlanWork<<"Unknown document type";
329 }
330 if (ok) {
331 debugPlanWork<<"Add to store:"<<fileName();
332 store->addLocalFile(filePath(), fileName());
333 m_filemodified = false;
334 if (wasmod != m_filemodified) {
335 emit fileModified(m_filemodified);
336 }
337 }
338 m_fileSystemWatcher->addPath(filePath());
339 return ok;
340 }
341
342
343 //------------------------------------
Part(QWidget * parentWidget,QObject * parent,const QVariantList &)344 Part::Part(QWidget *parentWidget, QObject *parent, const QVariantList & /*args*/)
345 : KParts::ReadWritePart(parent),
346 m_xmlLoader(),
347 m_modified(false),
348 m_loadingFromProjectStore(false),
349 m_undostack(new KUndo2QStack(this))
350 {
351 debugPlanWork;
352 setComponentName(Factory::global().componentName(), Factory::global().componentDisplayName());
353 if (isReadWrite()) {
354 setXMLFile("calligraplanwork.rc");
355 } else {
356 setXMLFile("calligraplanwork_readonly.rc");
357 }
358
359 m_view = new View(this, parentWidget, actionCollection());
360 setWidget(m_view);
361 connect(m_view, &View::viewDocument, this, &Part::viewWorkpackageDocument);
362
363 loadWorkPackages();
364
365 connect(m_undostack, &KUndo2QStack::cleanChanged, this, &Part::setDocumentClean);
366
367 }
368
~Part()369 Part::~Part()
370 {
371 debugPlanWork;
372 // m_config.save();
373 // views must be deleted before packages
374 delete m_view;
375 qDeleteAll(m_packageMap);
376 PlanWorkSettings::self()->save();
377 }
378
addCommand(KUndo2Command * cmd)379 void Part::addCommand(KUndo2Command *cmd)
380 {
381 if (cmd) {
382 m_undostack->push(cmd);
383 }
384 }
385
setWorkPackage(WorkPackage * wp,KoStore * store)386 bool Part::setWorkPackage(WorkPackage *wp, KoStore *store)
387 {
388 //debugPlanWork;
389 QString id = wp->id();
390 if (m_packageMap.contains(id)) {
391 if (KMessageBox::warningYesNo(0, i18n("<p>The work package already exists in the projects store.</p>"
392 "<p>Project: %1<br>Task: %2</p>"
393 "<p>Do you want to update the existing package with data from the new?</p>",
394 wp->project()->name(), wp->node()->name())) == KMessageBox::No) {
395 delete wp;
396 return false;
397 }
398 m_packageMap[ id ]->merge(this, wp, store);
399 delete wp;
400 return true;
401 }
402 wp->setFilePath(m_loadingFromProjectStore ? wp->fileName(this) : localFilePath());
403 m_packageMap[ id ] = wp;
404 if (! m_loadingFromProjectStore) {
405 wp->saveToProjects(this);
406 }
407 connect(wp->project(), SIGNAL(projectChanged()), wp, SLOT(projectChanged()));
408 connect (wp, SIGNAL(modified(bool)), this, SLOT(setModified(bool)));
409 emit workPackageAdded(wp, indexOf(wp));
410 connect(wp, &WorkPackage::saveWorkPackage, this, &Part::saveWorkPackage);
411 return true;
412 }
413
removeWorkPackage(Node * node,MacroCommand * m)414 void Part::removeWorkPackage(Node *node, MacroCommand *m)
415 {
416 debugPlanWork<<node->name();
417 WorkPackage *wp = findWorkPackage(node);
418 if (wp == 0) {
419 KMessageBox::error(0, i18n("Remove failed. Cannot find work package"));
420 return;
421 }
422 PackageRemoveCmd *cmd = new PackageRemoveCmd(this, wp, kundo2_i18n("Remove work package"));
423 if (m) {
424 m->addCommand(cmd);
425 } else {
426 addCommand(cmd);
427 }
428 }
429
removeWorkPackages(const QList<Node * > & nodes)430 void Part::removeWorkPackages(const QList<Node*> &nodes)
431 {
432 debugPlanWork<<nodes;
433 MacroCommand *m = new MacroCommand(kundo2_i18np("Remove work package", "Remove work packages", nodes.count()));
434 foreach (Node *n, nodes) {
435 removeWorkPackage(n, m);
436 }
437 if (m->isEmpty()) {
438 delete m;
439 } else {
440 addCommand(m);
441 }
442 }
443
removeWorkPackage(WorkPackage * wp)444 void Part::removeWorkPackage(WorkPackage *wp)
445 {
446 //debugPlanWork;
447 int row = indexOf(wp);
448 if (row >= 0) {
449 const QList<QString> &lst = m_packageMap.keys();
450 const QString &key = lst.value(row);
451 m_packageMap.remove(key);
452 emit workPackageRemoved(wp, row);
453 }
454 }
455
addWorkPackage(WorkPackage * wp)456 void Part::addWorkPackage(WorkPackage *wp)
457 {
458 //debugPlanWork;
459 QString id = wp->id();
460 Q_ASSERT(! m_packageMap.contains(id));
461 m_packageMap[ id ] = wp;
462 emit workPackageAdded(wp, indexOf(wp));
463 }
464
loadWorkPackages()465 bool Part::loadWorkPackages()
466 {
467 m_loadingFromProjectStore = true;
468 const QStringList lst = KoResourcePaths::findAllResources("projects", "*.planwork", KoResourcePaths::Recursive | KoResourcePaths::NoDuplicates);
469 debugPlanWork<<lst;
470 foreach (const QString &file, lst) {
471 if (! loadNativeFormatFromStore(file)) {
472 KMessageBox::information(0, i18n("Failed to load file:<br>%1" , file));
473 }
474 }
475 m_loadingFromProjectStore = false;
476 return true;
477
478 }
479
loadNativeFormatFromStore(const QString & file)480 bool Part::loadNativeFormatFromStore(const QString& file)
481 {
482 debugPlanWork<<file;
483 KoStore * store = KoStore::createStore(file, KoStore::Read, "", KoStore::Auto);
484
485 if (store->bad()) {
486 KMessageBox::error(0, i18n("Not a valid work package file:<br>%1", file));
487 delete store;
488 QApplication::restoreOverrideCursor();
489 return false;
490 }
491
492 const bool success = loadNativeFormatFromStoreInternal(store);
493
494 delete store;
495
496 return success;
497 }
498
loadNativeFormatFromStoreInternal(KoStore * store)499 bool Part::loadNativeFormatFromStoreInternal(KoStore * store)
500 {
501 if (store->hasFile("root")) {
502 KoXmlDocument doc;
503 bool ok = loadAndParse(store, "root", doc);
504 if (ok) {
505 ok = loadXML(doc, store);
506 }
507 if (!ok) {
508 QApplication::restoreOverrideCursor();
509 return false;
510 }
511
512 } else {
513 errorPlanWork << "ERROR: No maindoc.xml" << endl;
514 KMessageBox::error(0, i18n("Invalid document. The document does not contain 'maindoc.xml'."));
515 QApplication::restoreOverrideCursor();
516 return false;
517 }
518 // if (store->hasFile("documentinfo.xml")) {
519 // KoXmlDocument doc;
520 // if (oldLoadAndParse(store, "documentinfo.xml", doc)) {
521 // d->m_docInfo->load(doc);
522 // }
523 // } else {
524 // //debugPlanWork <<"cannot open document info";
525 // delete d->m_docInfo;
526 // d->m_docInfo = new KoDocumentInfo(this);
527 // }
528
529 bool res = completeLoading(store);
530 QApplication::restoreOverrideCursor();
531 return res;
532 }
533
loadAndParse(KoStore * store,const QString & filename,KoXmlDocument & doc)534 bool Part::loadAndParse(KoStore* store, const QString& filename, KoXmlDocument& doc)
535 {
536 //debugPlanWork <<"Trying to open" << filename;
537
538 if (!store->open(filename)) {
539 warnPlanWork << "Entry " << filename << " not found!";
540 KMessageBox::error(0, i18n("Failed to open file: %1", filename));
541 return false;
542 }
543 // Error variables for QDomDocument::setContent
544 QString errorMsg;
545 int errorLine, errorColumn;
546 bool ok = doc.setContent(store->device(), &errorMsg, &errorLine, &errorColumn);
547 store->close();
548 if (!ok) {
549 errorPlanWork << "Parsing error in " << filename << "! Aborting!" << endl
550 << " In line: " << errorLine << ", column: " << errorColumn << endl
551 << " Error message: " << errorMsg;
552 KMessageBox::error(0, i18n("Parsing error in file '%1' at line %2, column %3<br>Error message: %4", filename , errorLine, errorColumn ,
553 QCoreApplication::translate("QXml", errorMsg.toUtf8(), 0)));
554 return false;
555 }
556 return true;
557 }
558
loadXML(const KoXmlDocument & document,KoStore * store)559 bool Part::loadXML(const KoXmlDocument &document, KoStore* store)
560 {
561 debugPlanWork;
562 QString value;
563 KoXmlElement plan = document.documentElement();
564
565 // Check if this is the right app
566 value = plan.attribute("mime", QString());
567 if (value.isEmpty()) {
568 errorPlanWork << "No mime type specified!" << endl;
569 KMessageBox::error(0, i18n("Invalid document. No mimetype specified."));
570 return false;
571 } else if (value == "application/x-vnd.kde.kplato.work") {
572 return loadKPlatoXML(document, store);
573 } else if (value != "application/x-vnd.kde.plan.work") {
574 errorPlanWork << "Unknown mime type " << value;
575 KMessageBox::error(0, i18n("Invalid document. Expected mimetype application/x-vnd.kde.plan.work, got %1", value));
576 return false;
577 }
578 QString syntaxVersion = plan.attribute("version", PLANWORK_FILE_SYNTAX_VERSION);
579 m_xmlLoader.setWorkVersion(syntaxVersion);
580 if (syntaxVersion > PLANWORK_FILE_SYNTAX_VERSION) {
581 KMessageBox::ButtonCode ret = KMessageBox::warningContinueCancel(
582 0, i18n("This document is a newer version than supported by PlanWork (syntax version: %1)<br>"
583 "Opening it in this version of PlanWork will lose some information.", syntaxVersion),
584 i18n("File-Format Mismatch"), KGuiItem(i18n("Continue")));
585 if (ret == KMessageBox::Cancel) {
586 return false;
587 }
588 }
589 m_xmlLoader.setVersion(plan.attribute("plan-version", PLAN_FILE_SYNTAX_VERSION));
590 m_xmlLoader.startLoad();
591 WorkPackage *wp = new WorkPackage(m_loadingFromProjectStore);
592 wp->loadXML(plan, m_xmlLoader);
593 m_xmlLoader.stopLoad();
594 if (! setWorkPackage(wp, store)) {
595 // rejected, so nothing changed...
596 return true;
597 }
598 emit changed();
599 return true;
600 }
601
loadKPlatoXML(const KoXmlDocument & document,KoStore *)602 bool Part::loadKPlatoXML(const KoXmlDocument &document, KoStore*)
603 {
604 debugPlanWork;
605 QString value;
606 KoXmlElement plan = document.documentElement();
607
608 // Check if this is the right app
609 value = plan.attribute("mime", QString());
610 if (value.isEmpty()) {
611 errorPlanWork << "No mime type specified!" << endl;
612 KMessageBox::error(0, i18n("Invalid document. No mimetype specified."));
613 return false;
614 } else if (value != "application/x-vnd.kde.kplato.work") {
615 errorPlanWork << "Unknown mime type " << value;
616 KMessageBox::error(0, i18n("Invalid document. Expected mimetype application/x-vnd.kde.kplato.work, got %1", value));
617 return false;
618 }
619 QString syntaxVersion = plan.attribute("version", KPLATOWORK_MAX_FILE_SYNTAX_VERSION);
620 m_xmlLoader.setWorkVersion(syntaxVersion);
621 if (syntaxVersion > KPLATOWORK_MAX_FILE_SYNTAX_VERSION) {
622 KMessageBox::ButtonCode ret = KMessageBox::warningContinueCancel(
623 0, i18n("This document is a newer version than supported by PlanWork (syntax version: %1)<br>"
624 "Opening it in this version of PlanWork will lose some information.", syntaxVersion),
625 i18n("File-Format Mismatch"), KGuiItem(i18n("Continue")));
626 if (ret == KMessageBox::Cancel) {
627 return false;
628 }
629 }
630 m_xmlLoader.setMimetype(value);
631 m_xmlLoader.setVersion(plan.attribute("kplato-version", KPLATO_MAX_FILE_SYNTAX_VERSION));
632 m_xmlLoader.startLoad();
633 WorkPackage *wp = new WorkPackage(m_loadingFromProjectStore);
634 wp->loadKPlatoXML(plan, m_xmlLoader);
635 m_xmlLoader.stopLoad();
636 if (! setWorkPackage(wp)) {
637 // rejected, so nothing changed...
638 return true;
639 }
640 emit changed();
641 return true;
642 }
643
completeLoading(KoStore *)644 bool Part::completeLoading(KoStore *)
645 {
646 return true;
647 }
648
extractFile(const Document * doc)649 QUrl Part::extractFile(const Document *doc)
650 {
651 WorkPackage *wp = findWorkPackage(doc);
652 return wp == 0 ? QUrl() : wp->extractFile(doc);
653 }
654
docType(const Document * doc) const655 int Part::docType(const Document *doc) const
656 {
657 DocumentChild *ch = findChild(doc);
658 if (ch == 0) {
659 return DocumentChild::Type_Unknown;
660 }
661 return ch->type();
662 }
663
findChild(const Document * doc) const664 DocumentChild *Part::findChild(const Document *doc) const
665 {
666 foreach (const WorkPackage *wp, m_packageMap) {
667 DocumentChild *c = wp->findChild(doc);
668 if (c) {
669 return c;
670 }
671 }
672 return 0;
673 }
674
findWorkPackage(const Document * doc) const675 WorkPackage *Part::findWorkPackage(const Document *doc) const
676 {
677 foreach (const WorkPackage *wp, m_packageMap) {
678 if (wp->contains(doc)) {
679 return const_cast<WorkPackage*>(wp);
680 }
681 }
682 return 0;
683 }
684
findWorkPackage(const DocumentChild * child) const685 WorkPackage *Part::findWorkPackage(const DocumentChild *child) const
686 {
687 foreach (const WorkPackage *wp, m_packageMap) {
688 if (wp->contains(child)) {
689 return const_cast<WorkPackage*>(wp);
690 }
691 }
692 return 0;
693 }
694
findWorkPackage(const Node * node) const695 WorkPackage *Part::findWorkPackage(const Node *node) const
696 {
697 return m_packageMap.value(node->projectNode()->id() + node->id());
698 }
699
editWorkpackageDocument(const Document * doc)700 bool Part::editWorkpackageDocument(const Document *doc)
701 {
702 //debugPlanWork<<doc<<doc->url();
703 // start in any suitable application
704 return editOtherDocument(doc);
705 }
706
editOtherDocument(const Document * doc)707 bool Part::editOtherDocument(const Document *doc)
708 {
709 Q_ASSERT(doc != 0);
710 //debugPlanWork<<doc->url();
711 WorkPackage *wp = findWorkPackage(doc);
712 if (wp == 0) {
713 KMessageBox::error(0, i18n("Edit failed. Cannot find a work package."));
714 return false;
715 }
716 return wp->addChild(this, doc);
717 }
718
viewWorkpackageDocument(Document * doc)719 void Part::viewWorkpackageDocument(Document *doc)
720 {
721 debugPlanWork<<doc;
722 if (doc == 0) {
723 return;
724 }
725 QUrl filename;
726 if (doc->sendAs() == Document::SendAs_Copy) {
727 filename = extractFile(doc);
728 } else {
729 filename = doc->url();
730 }
731 // open for view
732 viewDocument(filename);
733 }
734
removeDocument(Document * doc)735 bool Part::removeDocument(Document *doc)
736 {
737 if (doc == 0) {
738 return false;
739 }
740 WorkPackage *wp = findWorkPackage(doc);
741 if (wp == 0) {
742 return false;
743 }
744 return wp->removeDocument(this, doc);
745 }
746
viewDocument(const QUrl & filename)747 bool Part::viewDocument(const QUrl &filename)
748 {
749 debugPlanWork<<"url:"<<filename;
750 if (! filename.isValid()) {
751 //KMessageBox::error(0, i18n("Cannot open document. Invalid url: %1", filename.pathOrUrl()));
752 return false;
753 }
754 KRun *run = new KRun(filename, 0);
755 Q_UNUSED(run); // XXX: shouldn't run be deleted?
756 return true;
757 }
758
setDocumentClean(bool clean)759 void Part::setDocumentClean(bool clean)
760 {
761 debugPlanWork<<clean;
762 setModified(! clean);
763 if (! clean) {
764 saveModifiedWorkPackages();
765 return;
766 }
767 }
768
setModified(bool mod)769 void Part::setModified(bool mod)
770 {
771 KParts::ReadWritePart::setModified(mod);
772 emit captionChanged(QString(), mod);
773 }
774
saveAs(const QUrl &)775 bool Part::saveAs(const QUrl &/*url*/)
776 {
777 return false;
778 }
779
saveModifiedWorkPackages()780 void Part::saveModifiedWorkPackages()
781 {
782 foreach (WorkPackage *wp, m_packageMap) {
783 if (wp->isModified()) {
784 saveWorkPackage(wp);
785 }
786 }
787 m_undostack->setClean();
788 }
789
saveWorkPackage(WorkPackage * wp)790 void Part::saveWorkPackage(WorkPackage *wp)
791 {
792 wp->saveToProjects(this);
793 }
794
saveWorkPackages(bool silent)795 bool Part::saveWorkPackages(bool silent)
796 {
797 debugPlanWork<<silent;
798 foreach (WorkPackage *wp, m_packageMap) {
799 wp->saveToProjects(this);
800 }
801 m_undostack->setClean();
802 return true;
803 }
804
completeSaving(KoStore *)805 bool Part::completeSaving(KoStore */*store*/)
806 {
807 return true;
808 }
809
saveXML()810 QDomDocument Part::saveXML()
811 {
812 debugPlanWork;
813 return QDomDocument();
814 }
815
queryClose()816 bool Part::queryClose()
817 {
818 debugPlanWork;
819 QList<WorkPackage*> modifiedList;
820 foreach (WorkPackage *wp, m_packageMap) {
821 switch (wp->queryClose(this)) {
822 case KMessageBox::No:
823 modifiedList << wp;
824 break;
825 case KMessageBox::Cancel:
826 debugPlanWork<<"Cancel";
827 return false;
828 }
829 }
830 // closeEvent calls queryClose so modified must be reset or else wps are queried all over again
831 foreach (WorkPackage *wp, modifiedList) {
832 wp->setModified(false);
833 }
834 setModified(false);
835 return true;
836 }
837
openFile()838 bool Part::openFile()
839 {
840 debugPlanWork<<localFilePath();
841 return loadNativeFormatFromStore(localFilePath());
842 }
843
saveFile()844 bool Part::saveFile()
845 {
846 return false;
847 }
848
849 } //KPlatoWork namespace
850