File indexing completed on 2024-05-12 16:39:27
0001 /* This file is part of the KDE project 0002 Copyright (C) 2009, 2012 Dag Andersen <danders@get2net.dk> 0003 Copyright (C) 2019 Dag Andersen <danders@get2net.dk> 0004 0005 This library is free software; you can redistribute it and/or 0006 modify it under the terms of the GNU Library General Public 0007 License as published by the Free Software Foundation; either 0008 version 2 of the License, or (at your option) any later version. 0009 0010 This library is distributed in the hope that it will be useful, 0011 but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0013 Library General Public License for more details. 0014 0015 You should have received a copy of the GNU Library General Public License 0016 along with this library; see the file COPYING.LIB. If not, write to 0017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0018 Boston, MA 02110-1301, USA. 0019 */ 0020 0021 // clazy:excludeall=qstring-arg 0022 #include "workpackage.h" 0023 0024 #include "KPlatoXmlLoader.h" //NOTE: this file should probably be moved 0025 0026 #include "part.h" 0027 #include "kptglobal.h" 0028 #include "kptnode.h" 0029 #include "kptproject.h" 0030 #include "kptdocuments.h" 0031 #include "kptcommand.h" 0032 #include "kptxmlloaderobject.h" 0033 #include "XmlSaveContext.h" 0034 #include "kptconfigbase.h" 0035 #include "kptcommonstrings.h" 0036 0037 #include <KoStore.h> 0038 #include <KoXmlReader.h> 0039 #include <KoStoreDevice.h> 0040 #include <KoResourcePaths.h> 0041 0042 #include <QDir> 0043 #include <QUrl> 0044 #include <QTimer> 0045 #include <QDateTime> 0046 #include <QDomDocument> 0047 0048 #include <kmessagebox.h> 0049 0050 0051 #include "debugarea.h" 0052 0053 using namespace KPlato; 0054 0055 namespace KPlatoWork 0056 { 0057 0058 WorkPackage::WorkPackage(bool fromProjectStore) 0059 : m_project(new Project()), 0060 m_fromProjectStore(fromProjectStore), 0061 m_modified(false) 0062 { 0063 m_project->setConfig(&m_config); 0064 } 0065 0066 WorkPackage::WorkPackage(Project *project, bool fromProjectStore) 0067 : m_project(project), 0068 m_fromProjectStore(fromProjectStore), 0069 m_modified(false) 0070 { 0071 Q_ASSERT(project); 0072 Q_ASSERT (project->childNode(0)); 0073 0074 m_project->setConfig(&m_config); 0075 0076 if (! project->scheduleManagers().isEmpty()) { 0077 // should be only one manager, so just get the first 0078 const QList<ScheduleManager*> &lst = m_project->scheduleManagers(); 0079 project->setCurrentSchedule(lst.first()->scheduleId()); 0080 } 0081 connect(project, &KPlato::Project::projectChanged, this, &WorkPackage::projectChanged); 0082 0083 } 0084 0085 WorkPackage::~WorkPackage() 0086 { 0087 delete m_project; 0088 qDeleteAll(m_childdocs); 0089 } 0090 0091 void WorkPackage::setSettings(const WorkPackageSettings &settings) 0092 { 0093 if (m_settings != settings) { 0094 m_settings = settings; 0095 setModified(true); 0096 } 0097 } 0098 0099 //TODO find a way to know when changes are undone 0100 void WorkPackage::projectChanged() 0101 { 0102 debugPlanWork; 0103 setModified(true); 0104 } 0105 0106 bool WorkPackage::addChild(Part */*part*/, const Document *doc) 0107 { 0108 DocumentChild *ch = findChild(doc); 0109 if (ch) { 0110 if (ch->isOpen()) { 0111 KMessageBox::error(0, i18n("Document is already open")); 0112 return false; 0113 } 0114 } else { 0115 ch = new DocumentChild(this); 0116 if (! ch->setDoc(doc)) { 0117 delete ch; 0118 return false; 0119 } 0120 } 0121 if (! ch->editDoc()) { 0122 delete ch; 0123 return false; 0124 } 0125 if (! m_childdocs.contains(ch)) { 0126 m_childdocs.append(ch); 0127 connect(ch, &DocumentChild::fileModified, this, &WorkPackage::slotChildModified); 0128 } 0129 return true; 0130 } 0131 0132 void WorkPackage::slotChildModified(bool mod) 0133 { 0134 debugPlanWork<<mod; 0135 emit modified(isModified()); 0136 emit saveWorkPackage(this); 0137 } 0138 0139 void WorkPackage::removeChild(DocumentChild *child) 0140 { 0141 disconnect(child, &DocumentChild::fileModified, this, &WorkPackage::slotChildModified); 0142 0143 int i = m_childdocs.indexOf(child); 0144 if (i != -1) { 0145 // TODO: process etc 0146 m_childdocs.removeAt(i); 0147 delete child; 0148 } else { 0149 warnPlanWork<<"Could not find document child"; 0150 } 0151 } 0152 0153 bool WorkPackage::contains(const Document *doc) const 0154 { 0155 return node() ? node()->documents().contains(doc) : false; 0156 } 0157 0158 DocumentChild *WorkPackage::findChild(const Document *doc) const 0159 { 0160 foreach (DocumentChild *c, m_childdocs) { 0161 if (c->doc() == doc) { 0162 return c; 0163 } 0164 } 0165 return 0; 0166 } 0167 0168 bool WorkPackage::loadXML(const KoXmlElement &element, XMLLoaderObject &status) 0169 { 0170 bool ok = false; 0171 QString wbsCode = "Unknown"; 0172 KoXmlNode n = element.firstChild(); 0173 for (; ! n.isNull(); n = n.nextSibling()) { 0174 if (! n.isElement()) { 0175 continue; 0176 } 0177 KoXmlElement e = n.toElement(); 0178 debugPlanWork<<e.tagName(); 0179 if (e.tagName() == "project") { 0180 status.setProject(m_project); 0181 debugPlanWork<<"loading new project"; 0182 if (! (ok = m_project->load(e, status))) { 0183 status.addMsg(XMLLoaderObject::Errors, "Loading of work package failed"); 0184 KMessageBox::error(0, i18n("Failed to load project: %1" , m_project->name())); 0185 } else { 0186 KoXmlElement te = e.namedItem("task").toElement(); 0187 if (!te.isNull()) { 0188 wbsCode = te.attribute("wbs", "empty"); 0189 } 0190 } 0191 } 0192 } 0193 if (ok) { 0194 KoXmlNode n = element.firstChild(); 0195 for (; ! n.isNull(); n = n.nextSibling()) { 0196 if (! n.isElement()) { 0197 continue; 0198 } 0199 KoXmlElement e = n.toElement(); 0200 debugPlanWork<<e.tagName(); 0201 if (e.tagName() == "workpackage") { 0202 Task *t = static_cast<Task*>(m_project->childNode(0)); 0203 t->workPackage().setOwnerName(e.attribute("owner")); 0204 t->workPackage().setOwnerId(e.attribute("owner-id")); 0205 m_sendUrl = QUrl(e.attribute("save-url")); 0206 m_fetchUrl = QUrl(e.attribute("load-url")); 0207 m_wbsCode = wbsCode; 0208 0209 Resource *r = m_project->findResource(t->workPackage().ownerId()); 0210 if (r == 0) { 0211 debugPlanWork<<"Cannot find resource id!!"<<t->workPackage().ownerId()<<t->workPackage().ownerName(); 0212 } 0213 debugPlanWork<<"is this me?"<<t->workPackage().ownerName(); 0214 KoXmlNode ch = e.firstChild(); 0215 for (; ! ch.isNull(); ch = ch.nextSibling()) { 0216 if (! ch.isElement()) { 0217 continue; 0218 } 0219 KoXmlElement el = ch.toElement(); 0220 debugPlanWork<<el.tagName(); 0221 if (el.tagName() == "settings") { 0222 m_settings.loadXML(el); 0223 } 0224 } 0225 } 0226 } 0227 } 0228 if (! m_project->scheduleManagers().isEmpty()) { 0229 // should be only one manager 0230 const QList<ScheduleManager*> &lst = m_project->scheduleManagers(); 0231 m_project->setCurrentSchedule(lst.first()->scheduleId()); 0232 } 0233 return ok; 0234 } 0235 0236 bool WorkPackage::loadKPlatoXML(const KoXmlElement &element, XMLLoaderObject &status) 0237 { 0238 bool ok = false; 0239 KoXmlNode n = element.firstChild(); 0240 for (; ! n.isNull(); n = n.nextSibling()) { 0241 if (! n.isElement()) { 0242 continue; 0243 } 0244 KoXmlElement e = n.toElement(); 0245 debugPlanWork<<e.tagName(); 0246 if (e.tagName() == "project") { 0247 status.setProject(m_project); 0248 KPlatoXmlLoader loader(status, m_project); 0249 debugPlanWork<<"loading new project"; 0250 if (! (ok = loader.load(m_project, e, status))) { 0251 status.addMsg(XMLLoaderObject::Errors, "Loading of work package failed"); 0252 KMessageBox::error(0, i18n("Failed to load project: %1" , m_project->name())); 0253 } 0254 } 0255 } 0256 if (ok) { 0257 KoXmlNode n = element.firstChild(); 0258 for (; ! n.isNull(); n = n.nextSibling()) { 0259 if (! n.isElement()) { 0260 continue; 0261 } 0262 KoXmlElement e = n.toElement(); 0263 debugPlanWork<<e.tagName(); 0264 if (e.tagName() == "workpackage") { 0265 Task *t = static_cast<Task*>(m_project->childNode(0)); 0266 t->workPackage().setOwnerName(e.attribute("owner")); 0267 t->workPackage().setOwnerId(e.attribute("owner-id")); 0268 0269 Resource *r = m_project->findResource(t->workPackage().ownerId()); 0270 if (r == 0) { 0271 debugPlanWork<<"Cannot find resource id!!"<<t->workPackage().ownerId()<<t->workPackage().ownerName(); 0272 } 0273 debugPlanWork<<"is this me?"<<t->workPackage().ownerName(); 0274 KoXmlNode ch = e.firstChild(); 0275 for (; ! ch.isNull(); ch = ch.nextSibling()) { 0276 if (! ch.isElement()) { 0277 continue; 0278 } 0279 KoXmlElement el = ch.toElement(); 0280 debugPlanWork<<el.tagName(); 0281 if (el.tagName() == "settings") { 0282 m_settings.loadXML(el); 0283 } 0284 } 0285 } 0286 } 0287 } 0288 if (! m_project->scheduleManagers().isEmpty()) { 0289 // should be only one manager 0290 const QList<ScheduleManager*> &lst = m_project->scheduleManagers(); 0291 m_project->setCurrentSchedule(lst.first()->scheduleId()); 0292 } 0293 return ok; 0294 } 0295 0296 bool WorkPackage::saveToStream(QIODevice * dev) 0297 { 0298 QDomDocument doc = saveXML(); 0299 // Save to buffer 0300 QByteArray s = doc.toByteArray(); // utf8 already 0301 dev->open(QIODevice::WriteOnly); 0302 int nwritten = dev->write(s.data(), s.size()); 0303 if (nwritten != (int)s.size()) 0304 warnPlanWork << "wrote " << nwritten << "- expected" << s.size(); 0305 return nwritten == (int)s.size(); 0306 } 0307 0308 bool WorkPackage::saveNativeFormat(Part */*part*/, const QString &path) 0309 { 0310 if (path.isEmpty()) { 0311 KMessageBox::error(0, i18n("Cannot save to empty filename")); 0312 return false; 0313 } 0314 debugPlanWork<<node()->name()<<path; 0315 KoStore* store = KoStore::createStore(path, KoStore::Write, "application/x-vnd.kde.plan.work", KoStore::Auto); 0316 if (store->bad()) { 0317 KMessageBox::error(0, i18n("Could not create the file for saving")); 0318 delete store; 0319 return false; 0320 } 0321 if (store->open("root")) { 0322 KoStoreDevice dev(store); 0323 if (! saveToStream(&dev) || ! store->close()) { 0324 debugPlanWork << "saveToStream failed"; 0325 delete store; 0326 return false; 0327 } 0328 } else { 0329 KMessageBox::error(0, i18n("Not able to write '%1'. Partition full?", QString("maindoc.xml"))); 0330 delete store; 0331 return false; 0332 } 0333 0334 if (!completeSaving(store)) { 0335 delete store; 0336 return false; 0337 } 0338 if (!store->finalize()) { 0339 delete store; 0340 return false; 0341 } 0342 // Success 0343 delete store; 0344 m_modified = false; 0345 return true; 0346 } 0347 0348 bool WorkPackage::completeSaving(KoStore *store) 0349 { 0350 debugPlanWork; 0351 KoStore *oldstore = KoStore::createStore(filePath(), KoStore::Read, "", KoStore::Zip); 0352 if (oldstore->bad()) { 0353 KMessageBox::error(0, i18n("Failed to open store:\n %1", filePath())); 0354 return false; 0355 } 0356 if (oldstore->hasFile("documentinfo.xml")) { 0357 copyFile(oldstore, store, "documentinfo.xml"); 0358 } 0359 if (oldstore->hasFile("preview.png")) { 0360 copyFile(oldstore, store, "preview.png"); 0361 } 0362 0363 // First get all open documents 0364 debugPlanWork<<m_childdocs.count(); 0365 foreach (DocumentChild *cd, m_childdocs) { 0366 if (! cd->saveToStore(store)) { 0367 } 0368 } 0369 // Then get new files 0370 foreach (const Document *doc, node()->documents().documents()) { 0371 if (m_newdocs.contains(doc)) { 0372 store->addLocalFile(m_newdocs[ doc ].path(), doc->url().fileName()); 0373 m_newdocs.remove(doc); 0374 // TODO remove temp file ?? 0375 } 0376 } 0377 // Then get files from the old store copied to the new store 0378 foreach (Document *doc, node()->documents().documents()) { 0379 if (doc->sendAs() != Document::SendAs_Copy) { 0380 continue; 0381 } 0382 if (! store->hasFile(doc->url().fileName())) { 0383 copyFile(oldstore, store, doc->url().fileName()); 0384 } 0385 } 0386 return true; 0387 } 0388 0389 QString WorkPackage::fileName(const Part *part) const 0390 { 0391 Q_UNUSED(part); 0392 if (m_project == 0) { 0393 warnPlanWork<<"No project in this package"; 0394 return QString(); 0395 } 0396 Node *n = node(); 0397 if (n == 0) { 0398 warnPlanWork<<"No node in this project"; 0399 return QString(); 0400 } 0401 QString projectName = m_project->name().remove(' '); 0402 // FIXME: workaround: KoResourcePaths::saveLocation("projects", projectName + '/'); 0403 const QString path = KoResourcePaths::saveLocation("appdata", "projects/" + projectName + '/'); 0404 QString wpName = n->name(); 0405 wpName = QString(wpName.remove(' ').replace('/', '_') + '_' + n->id() + ".planwork"); 0406 return path + wpName; 0407 } 0408 0409 void WorkPackage::removeFile() 0410 { 0411 QFile file(m_filePath); 0412 if (! file.exists()) { 0413 warnPlanWork<<"No project in this package"; 0414 return; 0415 } 0416 file.remove(); 0417 } 0418 0419 void WorkPackage::saveToProjects(Part *part) 0420 { 0421 debugPlanWork; 0422 QString path = fileName(part); 0423 debugPlanWork<<node()->name(); 0424 if (saveNativeFormat(part, path)) { 0425 m_fromProjectStore = true; 0426 m_filePath = path; 0427 } else { 0428 KMessageBox::error(0, i18n("Cannot save to projects store:\n%1" , path)); 0429 } 0430 return; 0431 } 0432 0433 bool WorkPackage::isModified() const 0434 { 0435 if (m_modified) { 0436 return true; 0437 } 0438 foreach (DocumentChild *ch, m_childdocs) { 0439 if (ch->isModified() || ch->isFileModified()) { 0440 return true; 0441 } 0442 } 0443 return false; 0444 } 0445 0446 QString WorkPackage::name() const 0447 { 0448 Task *t = task(); 0449 return t ? t->name() : QString(); 0450 } 0451 0452 Node *WorkPackage::node() const 0453 { 0454 return m_project == 0 ? 0 : m_project->childNode(0); 0455 } 0456 0457 Task *WorkPackage::task() const 0458 { 0459 Task *task = qobject_cast<Task*>(node()); 0460 Q_ASSERT(task); 0461 return task; 0462 } 0463 0464 bool WorkPackage::removeDocument(Part *part, Document *doc) 0465 { 0466 Node *n = node(); 0467 if (n == 0) { 0468 return false; 0469 } 0470 part->addCommand(new DocumentRemoveCmd(n->documents(), doc, UndoText::removeDocument())); 0471 return true; 0472 } 0473 0474 bool WorkPackage::copyFile(KoStore *from, KoStore *to, const QString &filename) 0475 { 0476 QByteArray data; 0477 if (! from->extractFile(filename , data)) { 0478 KMessageBox::error(0, i18n("Failed read file:\n %1", filename)); 0479 return false; 0480 } 0481 if (! to->addDataToFile(data, filename)) { 0482 KMessageBox::error(0, i18n("Failed write file:\n %1", filename)); 0483 return false; 0484 } 0485 debugPlanWork<<"Copied file:"<<filename; 0486 return true; 0487 } 0488 0489 QDomDocument WorkPackage::saveXML() 0490 { 0491 debugPlanWork; 0492 QDomDocument document("plan-workpackage"); 0493 0494 document.appendChild(document.createProcessingInstruction( 0495 "xml", 0496 "version=\"1.0\" encoding=\"UTF-8\"")); 0497 0498 QDomElement doc = document.createElement("planwork"); 0499 doc.setAttribute("editor", "PlanWork"); 0500 doc.setAttribute("mime", "application/x-vnd.kde.plan.work"); 0501 doc.setAttribute("version", PLANWORK_FILE_SYNTAX_VERSION); 0502 doc.setAttribute("plan-version", PLAN_FILE_SYNTAX_VERSION); 0503 document.appendChild(doc); 0504 0505 // Work package info 0506 QDomElement wp = document.createElement("workpackage"); 0507 wp.setAttribute("time-tag", QDateTime::currentDateTime().toString(Qt::ISODate)); 0508 m_settings.saveXML(wp); 0509 Task *t = qobject_cast<Task*>(node()); 0510 if (t) { 0511 wp.setAttribute("owner", t->workPackage().ownerName()); 0512 wp.setAttribute("owner-id", t->workPackage().ownerId()); 0513 } 0514 doc.appendChild(wp); 0515 m_project->save(doc, XmlSaveContext()); 0516 return document; 0517 } 0518 0519 void WorkPackage::merge(Part *part, const WorkPackage *wp, KoStore *store) 0520 { 0521 debugPlanWork; 0522 const Node *from = wp->node(); 0523 Node *to = node(); 0524 0525 MacroCommand *m = new MacroCommand(kundo2_i18n("Merge data")); 0526 if (m_wbsCode != wp->wbsCode()) { 0527 m->addCommand(new ModifyWbsCodeCmd(this, wp->wbsCode())); 0528 } 0529 if (to->name() != from->name()) { 0530 m->addCommand(new NodeModifyNameCmd(*to, from->name())); 0531 } 0532 if (to->description() != from->description()) { 0533 m->addCommand(new NodeModifyDescriptionCmd(*to, from->description())); 0534 } 0535 if (to->startTime() != from->startTime() && from->startTime().isValid()) { 0536 m->addCommand(new NodeModifyStartTimeCmd(*to, from->startTime())); 0537 } 0538 if (to->endTime() != from->endTime() && from->endTime().isValid()) { 0539 m->addCommand(new NodeModifyEndTimeCmd(*to, from->endTime())); 0540 } 0541 if (to->leader() != from->leader()) { 0542 m->addCommand(new NodeModifyLeaderCmd(*to, from->leader())); 0543 } 0544 0545 if (from->type() == Node::Type_Task && from->type() == Node::Type_Task) { 0546 if (static_cast<Task*>(to)->workPackage().ownerId() != static_cast<const Task*>(from)->workPackage().ownerId()) { 0547 debugPlanWork<<"merge:"<<"different owners"<<static_cast<const Task*>(from)->workPackage().ownerName()<<static_cast<Task*>(to)->workPackage().ownerName(); 0548 if (static_cast<Task*>(to)->workPackage().ownerId().isEmpty()) { 0549 //TODO cmd 0550 static_cast<Task*>(to)->workPackage().setOwnerId(static_cast<const Task*>(from)->workPackage().ownerId()); 0551 static_cast<Task*>(to)->workPackage().setOwnerName(static_cast<const Task*>(from)->workPackage().ownerName()); 0552 } 0553 } 0554 foreach (Document *doc, from->documents().documents()) { 0555 Document *org = to->documents().findDocument(doc->url()); 0556 if (org) { 0557 // TODO: also handle modified type, sendas 0558 // update ? what if open, modified ... 0559 if (doc->type() == Document::Type_Product) { 0560 //### FIXME. user feedback 0561 warnPlanWork<<"We do not update existing deliverables (except name change)"; 0562 if (doc->name() != org->name()) { 0563 m->addCommand(new DocumentModifyNameCmd(org, doc->name())); 0564 } 0565 } else { 0566 if (doc->name() != org->name()) { 0567 m->addCommand(new DocumentModifyNameCmd(org, doc->name())); 0568 } 0569 if (doc->sendAs() != org->sendAs()) { 0570 m->addCommand(new DocumentModifySendAsCmd(org, doc->sendAs())); 0571 } 0572 if (doc->sendAs() == Document::SendAs_Copy) { 0573 debugPlanWork<<"Update existing doc:"<<org->url(); 0574 openNewDocument(org, store); 0575 } 0576 } 0577 } else { 0578 debugPlanWork<<"new document:"<<doc->typeToString(doc->type())<<doc->url(); 0579 Document *newdoc = new Document(*doc); 0580 m->addCommand(new DocumentAddCmd(to->documents(), newdoc)); 0581 if (doc->sendAs() == Document::SendAs_Copy) { 0582 debugPlanWork<<"Copy file"; 0583 openNewDocument(newdoc, store); 0584 } 0585 } 0586 } 0587 } 0588 const Project *fromProject = wp->project(); 0589 Project *toProject = m_project; 0590 const ScheduleManager *fromSm = fromProject->scheduleManagers().value(0); 0591 Q_ASSERT(fromSm); 0592 ScheduleManager *toSm = toProject->scheduleManagers().value(0); 0593 Q_ASSERT(toSm); 0594 if (fromSm->managerId() != toSm->managerId() || fromSm->scheduleId() != toSm->scheduleId()) { 0595 // rescheduled, update schedules 0596 m->addCommand(new CopySchedulesCmd(*fromProject, *toProject)); 0597 } 0598 if (m->isEmpty()) { 0599 delete m; 0600 } else { 0601 part->addCommand(m); 0602 } 0603 } 0604 0605 void WorkPackage::openNewDocument(const Document *doc, KoStore *store) 0606 { 0607 const QUrl url = extractFile(doc, store); 0608 if (url.url().isEmpty()) { 0609 KMessageBox::error(0, i18n("Could not extract document from storage:<br>%1", doc->url().path())); 0610 return; 0611 } 0612 if (! url.isValid()) { 0613 KMessageBox::error(0, i18n("Invalid URL:<br>%1", url.path())); 0614 return; 0615 } 0616 m_newdocs.insert(doc, url); 0617 } 0618 0619 int WorkPackage::queryClose(Part *part) 0620 { 0621 debugPlanWork<<isModified(); 0622 QString name = node()->name(); 0623 QStringList lst; 0624 if (! m_childdocs.isEmpty()) { 0625 foreach (DocumentChild *ch, m_childdocs) { 0626 if (ch->isOpen() && ch->doc()->sendAs() == Document::SendAs_Copy) { 0627 lst << ch->doc()->url().fileName(); 0628 } 0629 } 0630 } 0631 if (! lst.isEmpty()) { 0632 KMessageBox::ButtonCode result = KMessageBox::warningContinueCancelList(0, 0633 i18np( 0634 "<p>The work package <b>'%2'</b> has an open document.</p><p>Data may be lost if you continue.</p>", 0635 "<p>The work package <b>'%2'</b> has open documents.</p><p>Data may be lost if you continue.</p>", 0636 lst.count(), 0637 name), 0638 lst); 0639 0640 switch (result) { 0641 case KMessageBox::Continue: { 0642 debugPlanWork<<"Continue"; 0643 break; 0644 } 0645 default: // case KMessageBox::Cancel : 0646 debugPlanWork<<"Cancel"; 0647 return KMessageBox::Cancel; 0648 break; 0649 } 0650 } 0651 if (! isModified()) { 0652 return KMessageBox::Yes; 0653 } 0654 KMessageBox::ButtonCode res = KMessageBox::warningYesNoCancel(0, 0655 i18n("<p>The work package <b>'%1'</b> has been modified.</p><p>Do you want to save it?</p>", name), 0656 QString(), 0657 KStandardGuiItem::save(), 0658 KStandardGuiItem::discard()); 0659 0660 switch (res) { 0661 case KMessageBox::Yes: { 0662 debugPlanWork<<"Yes"; 0663 saveToProjects(part); 0664 break; 0665 } 0666 case KMessageBox::No: 0667 debugPlanWork<<"No"; 0668 break; 0669 default: // case KMessageBox::Cancel : 0670 debugPlanWork<<"Cancel"; 0671 break; 0672 } 0673 return res; 0674 } 0675 0676 QUrl WorkPackage::extractFile(const Document *doc) 0677 { 0678 KoStore *store = KoStore::createStore(m_filePath, KoStore::Read, "", KoStore::Zip); 0679 if (store->bad()) 0680 { 0681 KMessageBox::error(0, i18n("<p>Work package <b>'%1'</b></p><p>Could not open store:</p><p>%2</p>", node()->name(), m_filePath)); 0682 delete store; 0683 return QUrl(); 0684 } 0685 const QUrl url = extractFile(doc, store); 0686 delete store; 0687 return url; 0688 } 0689 0690 QUrl WorkPackage::extractFile(const Document *doc, KoStore *store) 0691 { 0692 //FIXME: should use a special tmp dir 0693 QString tmp = QDir::tempPath() + QLatin1Char('/') + doc->url().fileName(); 0694 const QUrl url = QUrl::fromLocalFile(tmp); 0695 debugPlanWork<<"Extract: "<<doc->url().fileName()<<" -> "<<url.path(); 0696 if (! store->extractFile(doc->url().fileName(), url.path())) { 0697 KMessageBox::error(0, i18n("<p>Work package <b>'%1'</b></p><p>Could not extract file:</p><p>%2</p>", node()->name(), doc->url().fileName())); 0698 return QUrl(); 0699 } 0700 return url; 0701 } 0702 0703 QString WorkPackage::id() const 0704 { 0705 QString id; 0706 if (node()) { 0707 id = m_project->id() + node()->id(); 0708 } 0709 return id; 0710 } 0711 0712 //-------------------------------- 0713 PackageRemoveCmd::PackageRemoveCmd(Part *part, WorkPackage *value, const KUndo2MagicString& name) 0714 : NamedCommand(name), 0715 m_part(part), 0716 m_value(value), 0717 m_mine(false) 0718 { 0719 } 0720 PackageRemoveCmd::~PackageRemoveCmd() 0721 { 0722 if (m_mine) { 0723 m_value->removeFile(); 0724 delete m_value; 0725 } 0726 } 0727 void PackageRemoveCmd::execute() 0728 { 0729 m_part->removeWorkPackage(m_value); 0730 m_mine = true; 0731 } 0732 void PackageRemoveCmd::unexecute() 0733 { 0734 m_part->addWorkPackage(m_value); 0735 m_mine = false; 0736 } 0737 0738 //--------------------- 0739 CopySchedulesCmd::CopySchedulesCmd(const Project &fromProject, Project &toProject, const KUndo2MagicString &name) 0740 : NamedCommand(name), 0741 m_project(toProject) 0742 { 0743 QDomDocument olddoc; 0744 QDomElement e = olddoc.createElement("old"); 0745 olddoc.appendChild(e); 0746 toProject.save(e, XmlSaveContext()); 0747 m_olddoc = olddoc.toString(); 0748 0749 QDomDocument newdoc; 0750 e = newdoc.createElement("new"); 0751 newdoc.appendChild(e); 0752 fromProject.save(e, XmlSaveContext()); 0753 m_newdoc = newdoc.toString(); 0754 } 0755 void CopySchedulesCmd::execute() 0756 { 0757 load(m_newdoc); 0758 } 0759 void CopySchedulesCmd::unexecute() 0760 { 0761 load(m_olddoc); 0762 } 0763 0764 void CopySchedulesCmd::load(const QString &doc) 0765 { 0766 clearSchedules(); 0767 0768 KoXmlDocument d; 0769 d.setContent(doc); 0770 KoXmlElement proj = d.documentElement().namedItem("project").toElement(); 0771 Q_ASSERT(! proj.isNull()); 0772 KoXmlElement task = proj.namedItem("task").toElement(); 0773 Q_ASSERT(! task.isNull()); 0774 KoXmlElement ts = task.namedItem("schedules").namedItem("schedule").toElement(); 0775 Q_ASSERT(! ts.isNull()); 0776 KoXmlElement ps = proj.namedItem("schedules").namedItem("plan").toElement(); 0777 Q_ASSERT(! ps.isNull()); 0778 0779 XMLLoaderObject status; 0780 status.setProject(&m_project); 0781 status.setVersion(PLAN_FILE_SYNTAX_VERSION); 0782 // task first 0783 NodeSchedule *ns = new NodeSchedule(); 0784 if (ns->loadXML(ts, status)) { 0785 debugPlanWork<<ns->name()<<ns->type()<<ns->id(); 0786 ns->setNode(m_project.childNode(0)); 0787 m_project.childNode(0)->addSchedule(ns); 0788 } else { 0789 Q_ASSERT(false); 0790 delete ns; 0791 } 0792 // schedule manager next (includes main schedule and resource schedule) 0793 ScheduleManager *sm = new ScheduleManager(m_project); 0794 if (sm->loadXML(ps, status)) { 0795 m_project.addScheduleManager(sm); 0796 } else { 0797 Q_ASSERT(false); 0798 delete sm; 0799 } 0800 if (sm) { 0801 m_project.setCurrentSchedule(sm->scheduleId()); 0802 } 0803 m_project.childNode(0)->changed(); 0804 } 0805 0806 void CopySchedulesCmd::clearSchedules() 0807 { 0808 foreach (Schedule *s, m_project.schedules()) { 0809 m_project.takeSchedule(s); 0810 } 0811 foreach (Schedule *s, m_project.childNode(0)->schedules()) { 0812 foreach (Appointment *a, s->appointments()) { 0813 if (a->resource() && a->resource()->resource()) { 0814 a->resource()->resource()->takeSchedule(a->resource()); 0815 } 0816 } 0817 m_project.childNode(0)->takeSchedule(s); 0818 } 0819 foreach (ScheduleManager *sm, m_project.scheduleManagers()) { 0820 m_project.takeScheduleManager(sm); 0821 delete sm; 0822 } 0823 } 0824 0825 //--------------------- 0826 ModifyWbsCodeCmd::ModifyWbsCodeCmd(WorkPackage *wp, QString wbsCode, const KUndo2MagicString &name) 0827 : NamedCommand(name) 0828 , m_wp(wp) 0829 , m_old(wp->wbsCode()) 0830 , m_new(wbsCode) 0831 { 0832 } 0833 0834 void ModifyWbsCodeCmd::execute() 0835 { 0836 m_wp->setWbsCode(m_new); 0837 } 0838 0839 void ModifyWbsCodeCmd::unexecute() 0840 { 0841 m_wp->setWbsCode(m_old); 0842 } 0843 0844 } //KPlatoWork namespace 0845 0846 QDebug operator<<(QDebug dbg, const KPlatoWork::WorkPackage *wp) 0847 { 0848 if (!wp) { 0849 return dbg.noquote() << "WorkPackage[0x0]"; 0850 } 0851 return dbg << *wp; 0852 } 0853 0854 QDebug operator<<(QDebug dbg, const KPlatoWork::WorkPackage &wp) 0855 { 0856 dbg.noquote() << "WorkPackage["; 0857 dbg << wp.id(); 0858 dbg << wp.name(); 0859 dbg << ']'; 0860 return dbg; 0861 }