File indexing completed on 2024-05-12 16:39:25
0001 /* This file is part of the KDE project 0002 Copyright (C) 1998, 1999, 2000 Torben Weis <weis@kde.org> 0003 Copyright (C) 2004 - 2009 Dag Andersen <danders@get2net.dk> 0004 Copyright (C) 2006 Raphael Langerhorst <raphael.langerhorst@kdemail.net> 0005 Copyright (C) 2007 Thorsten Zachmann <zachmann@kde.org> 0006 Copyright (C) 2007 - 2009, 2012 Dag Andersen <danders@get2net.dk> 0007 0008 This library is free software; you can redistribute it and/or 0009 modify it under the terms of the GNU Library General Public 0010 License as published by the Free Software Foundation; either 0011 version 2 of the License, or (at your option) any later version. 0012 0013 This library is distributed in the hope that it will be useful, 0014 but WITHOUT ANY WARRANTY; without even the implied warranty of 0015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0016 Library General Public License for more details. 0017 0018 You should have received a copy of the GNU Library General Public License 0019 along with this library; see the file COPYING.LIB. If not, write to 0020 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0021 * Boston, MA 02110-1301, USA. 0022 */ 0023 0024 // clazy:excludeall=qstring-arg 0025 #include "part.h" 0026 #include "view.h" 0027 #include "factory.h" 0028 #include "mainwindow.h" 0029 #include "workpackage.h" 0030 #include "calligraplanworksettings.h" 0031 0032 #include "KPlatoXmlLoader.h" //NB! 0033 0034 #include "kptglobal.h" 0035 #include "kptnode.h" 0036 #include "kptproject.h" 0037 #include "kpttask.h" 0038 #include "kptdocuments.h" 0039 #include "kptcommand.h" 0040 0041 #include <KoXmlReader.h> 0042 #include <KoStore.h> 0043 #include <KoDocumentInfo.h> 0044 #include <KoResourcePaths.h> 0045 #include <KoComponentData.h> 0046 0047 #include <QPainter> 0048 #include <QFileInfo> 0049 #include <QDir> 0050 #include <QTimer> 0051 #include <QFileSystemWatcher> 0052 #include <kundo2qstack.h> 0053 #include <QPointer> 0054 #include <QUrl> 0055 #include <QMimeDatabase> 0056 #include <QApplication> 0057 0058 #include <KLocalizedString> 0059 #include <kmessagebox.h> 0060 #include <kparts/partmanager.h> 0061 #include <kopenwithdialog.h> 0062 #include <kmimetypetrader.h> 0063 //#include <kserviceoffer.h> 0064 #include <KIO/DesktopExecParser> 0065 #include <krun.h> 0066 #include <kprocess.h> 0067 #include <kactioncollection.h> 0068 0069 #include "debugarea.h" 0070 0071 using namespace KPlato; 0072 0073 namespace KPlatoWork 0074 { 0075 0076 //------------------------------- 0077 DocumentChild::DocumentChild(WorkPackage *parent) 0078 : QObject(parent), 0079 m_doc(0), 0080 m_type(Type_Unknown), 0081 m_copy(false), 0082 m_process(0), 0083 m_editor(0), 0084 m_editormodified(false), 0085 m_filemodified(false), 0086 m_fileSystemWatcher(new QFileSystemWatcher(this)) 0087 0088 { 0089 } 0090 0091 // DocumentChild::DocumentChild(KParts::ReadWritePart *editor, const QUrl &url, const Document *doc, Part *parent) 0092 // : KoDocumentChild(parent), 0093 // m_doc(doc), 0094 // m_type(Type_Unknown), 0095 // m_copy(true), 0096 // m_process(0), 0097 // m_editor(editor), 0098 // m_editormodified(false), 0099 // m_filemodified(false) 0100 // { 0101 // setFileInfo(url); 0102 // if (dynamic_cast<KoDocument*>(editor)) { 0103 // debugPlanWork<<"Creating Calligra doc"; 0104 // m_type = Type_Calligra; 0105 // connect(static_cast<KoDocument*>(editor), SIGNAL(modified(bool)), this, SLOT(setModified(bool))); 0106 // } else { 0107 // debugPlanWork<<"Creating KParts doc"; 0108 // m_type = Type_KParts; 0109 // slotUpdateModified(); 0110 // } 0111 // } 0112 0113 DocumentChild::~DocumentChild() 0114 { 0115 debugPlanWork; 0116 disconnect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, this, &DocumentChild::slotDirty); 0117 m_fileSystemWatcher->removePath(filePath()); 0118 0119 if (m_type == Type_Calligra || m_type == Type_KParts) { 0120 delete m_editor; 0121 } 0122 } 0123 0124 WorkPackage *DocumentChild::parentPackage() const 0125 { 0126 return static_cast<WorkPackage*>(parent()); 0127 } 0128 0129 void DocumentChild::setFileInfo(const QUrl &url) 0130 { 0131 m_fileinfo.setFile(url.path()); 0132 //debugPlanWork<<url; 0133 bool res = connect(m_fileSystemWatcher, &QFileSystemWatcher::fileChanged, this, &DocumentChild::slotDirty); 0134 //debugPlanWork<<res<<filePath(); 0135 #ifndef NDEBUG 0136 Q_ASSERT(res); 0137 #else 0138 Q_UNUSED(res); 0139 #endif 0140 m_fileSystemWatcher->addPath(filePath()); 0141 } 0142 0143 void DocumentChild::setModified(bool mod) 0144 { 0145 debugPlanWork<<mod<<filePath(); 0146 if (m_editormodified != mod) { 0147 m_editormodified = mod; 0148 emit modified(mod); 0149 } 0150 } 0151 0152 void DocumentChild::slotDirty(const QString &file) 0153 { 0154 //debugPlanWork<<filePath()<<file<<m_filemodified; 0155 if (file == filePath() && ! m_filemodified) { 0156 debugPlanWork<<file<<"is modified"; 0157 m_filemodified = true; 0158 emit fileModified(true); 0159 } 0160 } 0161 0162 void DocumentChild::slotUpdateModified() 0163 { 0164 if (m_type == Type_KParts && m_editor && (m_editor->isModified() != m_editormodified)) { 0165 setModified(m_editor->isModified()); 0166 } 0167 QTimer::singleShot(500, this, &DocumentChild::slotUpdateModified); 0168 } 0169 0170 bool DocumentChild::setDoc(const Document *doc) 0171 { 0172 Q_ASSERT (m_doc == 0); 0173 if (isOpen()) { 0174 KMessageBox::error(0, i18n("Document is already open:<br>%1", doc->url().url())); 0175 return false; 0176 } 0177 m_doc = doc; 0178 QUrl url; 0179 if (parentPackage()->newDocuments().contains(doc)) { 0180 url = parentPackage()->newDocuments().value(doc); 0181 Q_ASSERT(url.isValid()); 0182 parentPackage()->removeNewDocument(doc); 0183 } else if (doc->sendAs() == Document::SendAs_Copy) { 0184 url = parentPackage()->extractFile(doc); 0185 if (url.url().isEmpty()) { 0186 KMessageBox::error(0, i18n("Could not extract document from storage:<br>%1", doc->url().url())); 0187 return false; 0188 } 0189 m_copy = true; 0190 } else { 0191 url = doc->url(); 0192 } 0193 if (! url.isValid()) { 0194 KMessageBox::error(0, i18n("Invalid URL:<br>%1", url.url())); 0195 return false; 0196 } 0197 setFileInfo(url); 0198 return true; 0199 } 0200 0201 bool DocumentChild::openDoc(const Document *doc, KoStore *store) 0202 { 0203 Q_ASSERT (m_doc == 0); 0204 if (isOpen()) { 0205 KMessageBox::error(0, i18n("Document is already open:<br>%1", doc->url().path())); 0206 return false; 0207 } 0208 m_doc = doc; 0209 QUrl url; 0210 if (doc->sendAs() == Document::SendAs_Copy) { 0211 url = parentPackage()->extractFile(doc, store); 0212 if (url.url().isEmpty()) { 0213 KMessageBox::error(0, i18n("Could not extract document from storage:<br>%1", doc->url().path())); 0214 return false; 0215 } 0216 m_copy = true; 0217 } else { 0218 url = doc->url(); 0219 } 0220 if (! url.isValid()) { 0221 KMessageBox::error(0, i18n("Invalid URL:<br>%1", url.url())); 0222 return false; 0223 } 0224 setFileInfo(url); 0225 return true; 0226 } 0227 0228 bool DocumentChild::editDoc() 0229 { 0230 Q_ASSERT(m_doc != 0); 0231 debugPlanWork<<"file:"<<filePath(); 0232 if (isOpen()) { 0233 KMessageBox::error(0, i18n("Document is already open:<br> %1", m_doc->url().path())); 0234 return false; 0235 } 0236 if (! m_fileinfo.exists()) { 0237 KMessageBox::error(0, i18n("File does not exist:<br>%1", fileName())); 0238 return false; 0239 } 0240 QUrl filename = QUrl::fromLocalFile(filePath()); 0241 const QMimeType mimetype = QMimeDatabase().mimeTypeForUrl(filename); 0242 KService::Ptr service = KMimeTypeTrader::self()->preferredService(mimetype.name()); 0243 bool editing = startProcess(service, filename); 0244 if (editing) { 0245 m_type = Type_Other; // FIXME: try to be more specific 0246 } 0247 return editing; 0248 } 0249 0250 bool DocumentChild::startProcess(KService::Ptr service, const QUrl &url) 0251 { 0252 QStringList args; 0253 QList<QUrl> files; 0254 if (url.isValid()) { 0255 files << url; 0256 } 0257 if (service) { 0258 KIO::DesktopExecParser parser(*service, files); 0259 parser.setUrlsAreTempFiles(false); 0260 args = parser.resultingArguments(); 0261 } else { 0262 QList<QUrl> list; 0263 QPointer<KOpenWithDialog> dlg = new KOpenWithDialog(list, i18n("Edit with:"), QString(), 0); 0264 if (dlg->exec() == QDialog::Accepted && dlg){ 0265 args << dlg->text(); 0266 } 0267 if (args.isEmpty()) { 0268 debugPlanWork<<"No executable selected"; 0269 return false; 0270 } 0271 args << url.url(); 0272 delete dlg; 0273 } 0274 debugPlanWork<<args; 0275 m_process = new KProcess(); 0276 m_process->setProgram(args); 0277 connect(m_process, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(slotEditFinished(int,QProcess::ExitStatus))); 0278 connect(m_process, SIGNAL(error(QProcess::ProcessError)), SLOT(slotEditError(QProcess::ProcessError))); 0279 m_process->start(); 0280 //debugPlanWork<<m_process->pid()<<m_process->program(); 0281 return true; 0282 } 0283 0284 bool DocumentChild::isModified() const 0285 { 0286 return m_editormodified; 0287 } 0288 0289 bool DocumentChild::isFileModified() const 0290 { 0291 return m_filemodified; 0292 } 0293 0294 void DocumentChild::slotEditFinished(int /*par*/, QProcess::ExitStatus) 0295 { 0296 //debugPlanWork<<par<<filePath(); 0297 delete m_process; 0298 m_process = 0; 0299 } 0300 0301 void DocumentChild::slotEditError(QProcess::ProcessError status) 0302 { 0303 debugPlanWork<<status; 0304 if (status == QProcess::FailedToStart || status == QProcess::Crashed) { 0305 m_process->deleteLater(); 0306 m_process = 0; 0307 } else debugPlanWork<<"Error="<<status<<" what to do?"; 0308 } 0309 0310 bool DocumentChild::saveToStore(KoStore *store) 0311 { 0312 debugPlanWork<<filePath(); 0313 m_fileSystemWatcher->removePath(filePath()); 0314 bool ok = false; 0315 bool wasmod = m_filemodified; 0316 if (m_type == Type_Calligra || m_type == Type_KParts) { 0317 if (m_editor->isModified()) { 0318 ok = m_editor->save(); // hmmmm 0319 } else { 0320 ok = true; 0321 } 0322 } else if (m_type == Type_Other) { 0323 if (isOpen()) { 0324 warnPlanWork<<"External editor open"; 0325 } 0326 ok = true; 0327 } else { 0328 errorPlanWork<<"Unknown document type"; 0329 } 0330 if (ok) { 0331 debugPlanWork<<"Add to store:"<<fileName(); 0332 store->addLocalFile(filePath(), fileName()); 0333 m_filemodified = false; 0334 if (wasmod != m_filemodified) { 0335 emit fileModified(m_filemodified); 0336 } 0337 } 0338 m_fileSystemWatcher->addPath(filePath()); 0339 return ok; 0340 } 0341 0342 0343 //------------------------------------ 0344 Part::Part(QWidget *parentWidget, QObject *parent, const QVariantList & /*args*/) 0345 : KParts::ReadWritePart(parent), 0346 m_xmlLoader(), 0347 m_modified(false), 0348 m_loadingFromProjectStore(false), 0349 m_undostack(new KUndo2QStack(this)) 0350 { 0351 debugPlanWork; 0352 setComponentName(Factory::global().componentName(), Factory::global().componentDisplayName()); 0353 if (isReadWrite()) { 0354 setXMLFile("calligraplanwork.rc"); 0355 } else { 0356 setXMLFile("calligraplanwork_readonly.rc"); 0357 } 0358 0359 m_view = new View(this, parentWidget, actionCollection()); 0360 setWidget(m_view); 0361 connect(m_view, &View::viewDocument, this, &Part::viewWorkpackageDocument); 0362 0363 loadWorkPackages(); 0364 0365 connect(m_undostack, &KUndo2QStack::cleanChanged, this, &Part::setDocumentClean); 0366 0367 } 0368 0369 Part::~Part() 0370 { 0371 debugPlanWork; 0372 // m_config.save(); 0373 // views must be deleted before packages 0374 delete m_view; 0375 qDeleteAll(m_packageMap); 0376 PlanWorkSettings::self()->save(); 0377 } 0378 0379 void Part::addCommand(KUndo2Command *cmd) 0380 { 0381 if (cmd) { 0382 m_undostack->push(cmd); 0383 } 0384 } 0385 0386 bool Part::setWorkPackage(WorkPackage *wp, KoStore *store) 0387 { 0388 //debugPlanWork; 0389 QString id = wp->id(); 0390 if (m_packageMap.contains(id)) { 0391 if (KMessageBox::warningYesNo(0, i18n("<p>The work package already exists in the projects store.</p>" 0392 "<p>Project: %1<br>Task: %2</p>" 0393 "<p>Do you want to update the existing package with data from the new?</p>", 0394 wp->project()->name(), wp->node()->name())) == KMessageBox::No) { 0395 delete wp; 0396 return false; 0397 } 0398 m_packageMap[ id ]->merge(this, wp, store); 0399 delete wp; 0400 return true; 0401 } 0402 wp->setFilePath(m_loadingFromProjectStore ? wp->fileName(this) : localFilePath()); 0403 m_packageMap[ id ] = wp; 0404 if (! m_loadingFromProjectStore) { 0405 wp->saveToProjects(this); 0406 } 0407 connect(wp->project(), SIGNAL(projectChanged()), wp, SLOT(projectChanged())); 0408 connect (wp, SIGNAL(modified(bool)), this, SLOT(setModified(bool))); 0409 emit workPackageAdded(wp, indexOf(wp)); 0410 connect(wp, &WorkPackage::saveWorkPackage, this, &Part::saveWorkPackage); 0411 return true; 0412 } 0413 0414 void Part::removeWorkPackage(Node *node, MacroCommand *m) 0415 { 0416 debugPlanWork<<node->name(); 0417 WorkPackage *wp = findWorkPackage(node); 0418 if (wp == 0) { 0419 KMessageBox::error(0, i18n("Remove failed. Cannot find work package")); 0420 return; 0421 } 0422 PackageRemoveCmd *cmd = new PackageRemoveCmd(this, wp, kundo2_i18n("Remove work package")); 0423 if (m) { 0424 m->addCommand(cmd); 0425 } else { 0426 addCommand(cmd); 0427 } 0428 } 0429 0430 void Part::removeWorkPackages(const QList<Node*> &nodes) 0431 { 0432 debugPlanWork<<nodes; 0433 MacroCommand *m = new MacroCommand(kundo2_i18np("Remove work package", "Remove work packages", nodes.count())); 0434 foreach (Node *n, nodes) { 0435 removeWorkPackage(n, m); 0436 } 0437 if (m->isEmpty()) { 0438 delete m; 0439 } else { 0440 addCommand(m); 0441 } 0442 } 0443 0444 void Part::removeWorkPackage(WorkPackage *wp) 0445 { 0446 //debugPlanWork; 0447 int row = indexOf(wp); 0448 if (row >= 0) { 0449 const QList<QString> &lst = m_packageMap.keys(); 0450 const QString &key = lst.value(row); 0451 m_packageMap.remove(key); 0452 emit workPackageRemoved(wp, row); 0453 } 0454 } 0455 0456 void Part::addWorkPackage(WorkPackage *wp) 0457 { 0458 //debugPlanWork; 0459 QString id = wp->id(); 0460 Q_ASSERT(! m_packageMap.contains(id)); 0461 m_packageMap[ id ] = wp; 0462 emit workPackageAdded(wp, indexOf(wp)); 0463 } 0464 0465 bool Part::loadWorkPackages() 0466 { 0467 m_loadingFromProjectStore = true; 0468 const QStringList lst = KoResourcePaths::findAllResources("projects", "*.planwork", KoResourcePaths::Recursive | KoResourcePaths::NoDuplicates); 0469 debugPlanWork<<lst; 0470 foreach (const QString &file, lst) { 0471 if (! loadNativeFormatFromStore(file)) { 0472 KMessageBox::information(0, i18n("Failed to load file:<br>%1" , file)); 0473 } 0474 } 0475 m_loadingFromProjectStore = false; 0476 return true; 0477 0478 } 0479 0480 bool Part::loadNativeFormatFromStore(const QString& file) 0481 { 0482 debugPlanWork<<file; 0483 KoStore * store = KoStore::createStore(file, KoStore::Read, "", KoStore::Auto); 0484 0485 if (store->bad()) { 0486 KMessageBox::error(0, i18n("Not a valid work package file:<br>%1", file)); 0487 delete store; 0488 QApplication::restoreOverrideCursor(); 0489 return false; 0490 } 0491 0492 const bool success = loadNativeFormatFromStoreInternal(store); 0493 0494 delete store; 0495 0496 return success; 0497 } 0498 0499 bool Part::loadNativeFormatFromStoreInternal(KoStore * store) 0500 { 0501 if (store->hasFile("root")) { 0502 KoXmlDocument doc; 0503 bool ok = loadAndParse(store, "root", doc); 0504 if (ok) { 0505 ok = loadXML(doc, store); 0506 } 0507 if (!ok) { 0508 QApplication::restoreOverrideCursor(); 0509 return false; 0510 } 0511 0512 } else { 0513 errorPlanWork << "ERROR: No maindoc.xml" << endl; 0514 KMessageBox::error(0, i18n("Invalid document. The document does not contain 'maindoc.xml'.")); 0515 QApplication::restoreOverrideCursor(); 0516 return false; 0517 } 0518 // if (store->hasFile("documentinfo.xml")) { 0519 // KoXmlDocument doc; 0520 // if (oldLoadAndParse(store, "documentinfo.xml", doc)) { 0521 // d->m_docInfo->load(doc); 0522 // } 0523 // } else { 0524 // //debugPlanWork <<"cannot open document info"; 0525 // delete d->m_docInfo; 0526 // d->m_docInfo = new KoDocumentInfo(this); 0527 // } 0528 0529 bool res = completeLoading(store); 0530 QApplication::restoreOverrideCursor(); 0531 return res; 0532 } 0533 0534 bool Part::loadAndParse(KoStore* store, const QString& filename, KoXmlDocument& doc) 0535 { 0536 //debugPlanWork <<"Trying to open" << filename; 0537 0538 if (!store->open(filename)) { 0539 warnPlanWork << "Entry " << filename << " not found!"; 0540 KMessageBox::error(0, i18n("Failed to open file: %1", filename)); 0541 return false; 0542 } 0543 // Error variables for QDomDocument::setContent 0544 QString errorMsg; 0545 int errorLine, errorColumn; 0546 bool ok = doc.setContent(store->device(), &errorMsg, &errorLine, &errorColumn); 0547 store->close(); 0548 if (!ok) { 0549 errorPlanWork << "Parsing error in " << filename << "! Aborting!" << endl 0550 << " In line: " << errorLine << ", column: " << errorColumn << endl 0551 << " Error message: " << errorMsg; 0552 KMessageBox::error(0, i18n("Parsing error in file '%1' at line %2, column %3<br>Error message: %4", filename , errorLine, errorColumn , 0553 QCoreApplication::translate("QXml", errorMsg.toUtf8(), 0))); 0554 return false; 0555 } 0556 return true; 0557 } 0558 0559 bool Part::loadXML(const KoXmlDocument &document, KoStore* store) 0560 { 0561 debugPlanWork; 0562 QString value; 0563 KoXmlElement plan = document.documentElement(); 0564 0565 // Check if this is the right app 0566 value = plan.attribute("mime", QString()); 0567 if (value.isEmpty()) { 0568 errorPlanWork << "No mime type specified!" << endl; 0569 KMessageBox::error(0, i18n("Invalid document. No mimetype specified.")); 0570 return false; 0571 } else if (value == "application/x-vnd.kde.kplato.work") { 0572 return loadKPlatoXML(document, store); 0573 } else if (value != "application/x-vnd.kde.plan.work") { 0574 errorPlanWork << "Unknown mime type " << value; 0575 KMessageBox::error(0, i18n("Invalid document. Expected mimetype application/x-vnd.kde.plan.work, got %1", value)); 0576 return false; 0577 } 0578 QString syntaxVersion = plan.attribute("version", PLANWORK_FILE_SYNTAX_VERSION); 0579 m_xmlLoader.setWorkVersion(syntaxVersion); 0580 if (syntaxVersion > PLANWORK_FILE_SYNTAX_VERSION) { 0581 KMessageBox::ButtonCode ret = KMessageBox::warningContinueCancel( 0582 0, i18n("This document is a newer version than supported by PlanWork (syntax version: %1)<br>" 0583 "Opening it in this version of PlanWork will lose some information.", syntaxVersion), 0584 i18n("File-Format Mismatch"), KGuiItem(i18n("Continue"))); 0585 if (ret == KMessageBox::Cancel) { 0586 return false; 0587 } 0588 } 0589 m_xmlLoader.setVersion(plan.attribute("plan-version", PLAN_FILE_SYNTAX_VERSION)); 0590 m_xmlLoader.startLoad(); 0591 WorkPackage *wp = new WorkPackage(m_loadingFromProjectStore); 0592 wp->loadXML(plan, m_xmlLoader); 0593 m_xmlLoader.stopLoad(); 0594 if (! setWorkPackage(wp, store)) { 0595 // rejected, so nothing changed... 0596 return true; 0597 } 0598 emit changed(); 0599 return true; 0600 } 0601 0602 bool Part::loadKPlatoXML(const KoXmlDocument &document, KoStore*) 0603 { 0604 debugPlanWork; 0605 QString value; 0606 KoXmlElement plan = document.documentElement(); 0607 0608 // Check if this is the right app 0609 value = plan.attribute("mime", QString()); 0610 if (value.isEmpty()) { 0611 errorPlanWork << "No mime type specified!" << endl; 0612 KMessageBox::error(0, i18n("Invalid document. No mimetype specified.")); 0613 return false; 0614 } else if (value != "application/x-vnd.kde.kplato.work") { 0615 errorPlanWork << "Unknown mime type " << value; 0616 KMessageBox::error(0, i18n("Invalid document. Expected mimetype application/x-vnd.kde.kplato.work, got %1", value)); 0617 return false; 0618 } 0619 QString syntaxVersion = plan.attribute("version", KPLATOWORK_MAX_FILE_SYNTAX_VERSION); 0620 m_xmlLoader.setWorkVersion(syntaxVersion); 0621 if (syntaxVersion > KPLATOWORK_MAX_FILE_SYNTAX_VERSION) { 0622 KMessageBox::ButtonCode ret = KMessageBox::warningContinueCancel( 0623 0, i18n("This document is a newer version than supported by PlanWork (syntax version: %1)<br>" 0624 "Opening it in this version of PlanWork will lose some information.", syntaxVersion), 0625 i18n("File-Format Mismatch"), KGuiItem(i18n("Continue"))); 0626 if (ret == KMessageBox::Cancel) { 0627 return false; 0628 } 0629 } 0630 m_xmlLoader.setMimetype(value); 0631 m_xmlLoader.setVersion(plan.attribute("kplato-version", KPLATO_MAX_FILE_SYNTAX_VERSION)); 0632 m_xmlLoader.startLoad(); 0633 WorkPackage *wp = new WorkPackage(m_loadingFromProjectStore); 0634 wp->loadKPlatoXML(plan, m_xmlLoader); 0635 m_xmlLoader.stopLoad(); 0636 if (! setWorkPackage(wp)) { 0637 // rejected, so nothing changed... 0638 return true; 0639 } 0640 emit changed(); 0641 return true; 0642 } 0643 0644 bool Part::completeLoading(KoStore *) 0645 { 0646 return true; 0647 } 0648 0649 QUrl Part::extractFile(const Document *doc) 0650 { 0651 WorkPackage *wp = findWorkPackage(doc); 0652 return wp == 0 ? QUrl() : wp->extractFile(doc); 0653 } 0654 0655 int Part::docType(const Document *doc) const 0656 { 0657 DocumentChild *ch = findChild(doc); 0658 if (ch == 0) { 0659 return DocumentChild::Type_Unknown; 0660 } 0661 return ch->type(); 0662 } 0663 0664 DocumentChild *Part::findChild(const Document *doc) const 0665 { 0666 foreach (const WorkPackage *wp, m_packageMap) { 0667 DocumentChild *c = wp->findChild(doc); 0668 if (c) { 0669 return c; 0670 } 0671 } 0672 return 0; 0673 } 0674 0675 WorkPackage *Part::findWorkPackage(const Document *doc) const 0676 { 0677 foreach (const WorkPackage *wp, m_packageMap) { 0678 if (wp->contains(doc)) { 0679 return const_cast<WorkPackage*>(wp); 0680 } 0681 } 0682 return 0; 0683 } 0684 0685 WorkPackage *Part::findWorkPackage(const DocumentChild *child) const 0686 { 0687 foreach (const WorkPackage *wp, m_packageMap) { 0688 if (wp->contains(child)) { 0689 return const_cast<WorkPackage*>(wp); 0690 } 0691 } 0692 return 0; 0693 } 0694 0695 WorkPackage *Part::findWorkPackage(const Node *node) const 0696 { 0697 return m_packageMap.value(node->projectNode()->id() + node->id()); 0698 } 0699 0700 bool Part::editWorkpackageDocument(const Document *doc) 0701 { 0702 //debugPlanWork<<doc<<doc->url(); 0703 // start in any suitable application 0704 return editOtherDocument(doc); 0705 } 0706 0707 bool Part::editOtherDocument(const Document *doc) 0708 { 0709 Q_ASSERT(doc != 0); 0710 //debugPlanWork<<doc->url(); 0711 WorkPackage *wp = findWorkPackage(doc); 0712 if (wp == 0) { 0713 KMessageBox::error(0, i18n("Edit failed. Cannot find a work package.")); 0714 return false; 0715 } 0716 return wp->addChild(this, doc); 0717 } 0718 0719 void Part::viewWorkpackageDocument(Document *doc) 0720 { 0721 debugPlanWork<<doc; 0722 if (doc == 0) { 0723 return; 0724 } 0725 QUrl filename; 0726 if (doc->sendAs() == Document::SendAs_Copy) { 0727 filename = extractFile(doc); 0728 } else { 0729 filename = doc->url(); 0730 } 0731 // open for view 0732 viewDocument(filename); 0733 } 0734 0735 bool Part::removeDocument(Document *doc) 0736 { 0737 if (doc == 0) { 0738 return false; 0739 } 0740 WorkPackage *wp = findWorkPackage(doc); 0741 if (wp == 0) { 0742 return false; 0743 } 0744 return wp->removeDocument(this, doc); 0745 } 0746 0747 bool Part::viewDocument(const QUrl &filename) 0748 { 0749 debugPlanWork<<"url:"<<filename; 0750 if (! filename.isValid()) { 0751 //KMessageBox::error(0, i18n("Cannot open document. Invalid url: %1", filename.pathOrUrl())); 0752 return false; 0753 } 0754 KRun *run = new KRun(filename, 0); 0755 Q_UNUSED(run); // XXX: shouldn't run be deleted? 0756 return true; 0757 } 0758 0759 void Part::setDocumentClean(bool clean) 0760 { 0761 debugPlanWork<<clean; 0762 setModified(! clean); 0763 if (! clean) { 0764 saveModifiedWorkPackages(); 0765 return; 0766 } 0767 } 0768 0769 void Part::setModified(bool mod) 0770 { 0771 KParts::ReadWritePart::setModified(mod); 0772 emit captionChanged(QString(), mod); 0773 } 0774 0775 bool Part::saveAs(const QUrl &/*url*/) 0776 { 0777 return false; 0778 } 0779 0780 void Part::saveModifiedWorkPackages() 0781 { 0782 foreach (WorkPackage *wp, m_packageMap) { 0783 if (wp->isModified()) { 0784 saveWorkPackage(wp); 0785 } 0786 } 0787 m_undostack->setClean(); 0788 } 0789 0790 void Part::saveWorkPackage(WorkPackage *wp) 0791 { 0792 wp->saveToProjects(this); 0793 } 0794 0795 bool Part::saveWorkPackages(bool silent) 0796 { 0797 debugPlanWork<<silent; 0798 foreach (WorkPackage *wp, m_packageMap) { 0799 wp->saveToProjects(this); 0800 } 0801 m_undostack->setClean(); 0802 return true; 0803 } 0804 0805 bool Part::completeSaving(KoStore */*store*/) 0806 { 0807 return true; 0808 } 0809 0810 QDomDocument Part::saveXML() 0811 { 0812 debugPlanWork; 0813 return QDomDocument(); 0814 } 0815 0816 bool Part::queryClose() 0817 { 0818 debugPlanWork; 0819 QList<WorkPackage*> modifiedList; 0820 foreach (WorkPackage *wp, m_packageMap) { 0821 switch (wp->queryClose(this)) { 0822 case KMessageBox::No: 0823 modifiedList << wp; 0824 break; 0825 case KMessageBox::Cancel: 0826 debugPlanWork<<"Cancel"; 0827 return false; 0828 } 0829 } 0830 // closeEvent calls queryClose so modified must be reset or else wps are queried all over again 0831 foreach (WorkPackage *wp, modifiedList) { 0832 wp->setModified(false); 0833 } 0834 setModified(false); 0835 return true; 0836 } 0837 0838 bool Part::openFile() 0839 { 0840 debugPlanWork<<localFilePath(); 0841 return loadNativeFormatFromStore(localFilePath()); 0842 } 0843 0844 bool Part::saveFile() 0845 { 0846 return false; 0847 } 0848 0849 } //KPlatoWork namespace