File indexing completed on 2025-02-02 12:46:21
0001 /*************************************************************************** 0002 * Copyright (C) 2003-2005 by David Saxton * 0003 * david@bluehaze.org * 0004 * * 0005 * This program is free software; you can redistribute it and/or modify * 0006 * it under the terms of the GNU General Public License as published by * 0007 * the Free Software Foundation; either version 2 of the License, or * 0008 * (at your option) any later version. * 0009 ***************************************************************************/ 0010 0011 #include "projectmanager.h" 0012 #include "docmanager.h" 0013 #include "document.h" 0014 #include "ktechlab.h" 0015 #include "language.h" 0016 #include "languagemanager.h" 0017 #include "microselectwidget.h" 0018 #include "programmerdlg.h" 0019 #include "projectdlgs.h" 0020 0021 #include <KIO/FileCopyJob> 0022 #include <KJobWidgets> 0023 #include <KLocalizedString> 0024 #include <KMessageBox> 0025 #include <KRecentFilesAction> 0026 #include <KXMLGUIFactory> 0027 0028 #include <QDomDocument> 0029 #include <QDomElement> 0030 #include <QFileDialog> 0031 // #include <q3popupmenu.h> 0032 #include <QDir> 0033 #include <QMenu> 0034 #include <QMimeDatabase> 0035 #include <QMimeType> 0036 #include <QScopedPointer> 0037 #include <QStandardPaths> 0038 #include <QTemporaryFile> 0039 0040 #include <cassert> 0041 0042 #include <ktlconfig.h> 0043 #include <ktechlab_debug.h> 0044 0045 static QString relativeUrl(const QUrl &baseDirUrl, const QUrl &url) 0046 { 0047 if (baseDirUrl.scheme() == url.scheme() && baseDirUrl.host() == url.host() && baseDirUrl.port() == url.port() && baseDirUrl.userInfo() == url.userInfo()) { 0048 return QDir(baseDirUrl.path()).relativeFilePath(url.path()); 0049 } 0050 0051 return url.toDisplayString(QUrl::PreferLocalFile); 0052 } 0053 0054 static QString resolvedLocalFile(const QString &baseDir, const QString &path) 0055 { 0056 Q_ASSERT(baseDir.endsWith(QLatin1Char('/'))); 0057 if (QDir::isAbsolutePath(path)) 0058 return path; 0059 0060 return QDir::cleanPath(baseDir + path); 0061 } 0062 0063 // BEGIN class LinkerOptions 0064 LinkerOptions::LinkerOptions() 0065 { 0066 m_hexFormat = HexFormat::inhx32; 0067 m_bOutputMapFile = false; 0068 } 0069 0070 QDomElement LinkerOptions::toDomElement(QDomDocument &doc, const QUrl &baseDirUrl) const 0071 { 0072 QDomElement node = doc.createElement("linker"); 0073 0074 node.setAttribute("hex-format", hexFormatToString(hexFormat())); 0075 node.setAttribute("output-map-file", outputMapFile()); 0076 node.setAttribute("library-dir", libraryDir()); 0077 node.setAttribute("linker-script", linkerScript()); 0078 node.setAttribute("other", linkerOther()); 0079 0080 // internal are always local files, like the project base dir 0081 // so can always get relative path from a QDir 0082 const QDir baseDir(baseDirUrl.toLocalFile()); 0083 QStringList::const_iterator end = m_linkedInternal.end(); 0084 for (QStringList::const_iterator it = m_linkedInternal.begin(); it != end; ++it) { 0085 QDomElement child = doc.createElement("linked-internal"); 0086 node.appendChild(child); 0087 child.setAttribute("url", baseDir.relativeFilePath(*it)); 0088 } 0089 0090 end = m_linkedExternal.end(); 0091 for (QStringList::const_iterator it = m_linkedExternal.begin(); it != end; ++it) { 0092 QDomElement child = doc.createElement("linked-external"); 0093 node.appendChild(child); 0094 child.setAttribute("url", *it); 0095 } 0096 0097 return node; 0098 } 0099 0100 void LinkerOptions::domElementToLinkerOptions(const QDomElement &element, const QUrl &baseDirUrl) 0101 { 0102 setHexFormat(stringToHexFormat(element.attribute("hex-format", QString()))); 0103 setOutputMapFile(element.attribute("output-map-file", "0").toInt()); 0104 setLibraryDir(element.attribute("library-dir", QString())); 0105 setLinkerScript(element.attribute("linker-script", QString())); 0106 setLinkerOther(element.attribute("other", QString())); 0107 0108 m_linkedInternal.clear(); 0109 m_linkedExternal.clear(); 0110 0111 QString baseDir = baseDirUrl.toLocalFile(); 0112 if (!baseDir.endsWith(QLatin1Char('/'))) { 0113 baseDir.append(QLatin1Char('/')); 0114 } 0115 QDomNode node = element.firstChild(); 0116 while (!node.isNull()) { 0117 QDomElement childElement = node.toElement(); 0118 if (!childElement.isNull()) { 0119 const QString tagName = childElement.tagName(); 0120 0121 if (tagName == "linked-internal") 0122 m_linkedInternal << ::resolvedLocalFile(baseDir, childElement.attribute("url", QString())); 0123 else if (tagName == "linked-external") 0124 m_linkedExternal << childElement.attribute("url", QString()); 0125 0126 else 0127 qCCritical(KTL_LOG) << "Unrecognised element tag name: " << tagName; 0128 } 0129 0130 node = node.nextSibling(); 0131 } 0132 } 0133 0134 QString LinkerOptions::hexFormatToString(HexFormat::type hexFormat) 0135 { 0136 switch (hexFormat) { 0137 case HexFormat::inhx32: 0138 return "inhx32"; 0139 0140 case HexFormat::inhx8m: 0141 return "inhx8m"; 0142 0143 case HexFormat::inhx8s: 0144 return "inhx8s"; 0145 0146 case HexFormat::inhx16: 0147 return "inhx16"; 0148 } 0149 0150 // Default hex format is inhx32 0151 return "inhx32"; 0152 } 0153 0154 LinkerOptions::HexFormat::type LinkerOptions::stringToHexFormat(const QString &hexFormat) 0155 { 0156 if (hexFormat == "inhx8m") 0157 return HexFormat::inhx8m; 0158 0159 if (hexFormat == "inhx8s") 0160 return HexFormat::inhx8s; 0161 0162 if (hexFormat == "inhx16") 0163 return HexFormat::inhx16; 0164 0165 return HexFormat::inhx32; 0166 } 0167 // END class LinkerOptions 0168 0169 // BEGIN class ProcessingOptions 0170 ProcessingOptions::ProcessingOptions() 0171 { 0172 m_bUseParentMicroID = false; 0173 m_microID = "P16F84"; 0174 } 0175 0176 ProcessingOptions::~ProcessingOptions() 0177 { 0178 } 0179 0180 QDomElement ProcessingOptions::toDomElement(QDomDocument &doc, const QUrl &baseDirUrl) const 0181 { 0182 QDomElement node = doc.createElement("processing"); 0183 0184 node.setAttribute("output", ::relativeUrl(baseDirUrl, outputURL())); 0185 node.setAttribute("micro", m_microID); 0186 0187 return node; 0188 } 0189 0190 void ProcessingOptions::domElementToProcessingOptions(const QDomElement &element, const QUrl &baseDirUrl) 0191 { 0192 setOutputURL(baseDirUrl.resolved(QUrl(element.attribute("output", QString())))); 0193 setMicroID(element.attribute("micro", QString())); 0194 } 0195 // END class ProcessingOptions 0196 0197 // BEGIN class ProjectItem 0198 ProjectItem::ProjectItem(ProjectItem *parent, Type type, ProjectManager *projectManager) 0199 : QObject() 0200 { 0201 m_pParent = parent; 0202 m_pILVItem = nullptr; 0203 m_pProjectManager = projectManager; 0204 m_type = type; 0205 } 0206 0207 ProjectItem::~ProjectItem() 0208 { 0209 m_children.removeAll(static_cast<ProjectItem *>(nullptr)); 0210 ProjectItemList::iterator end = m_children.end(); 0211 for (ProjectItemList::iterator it = m_children.begin(); it != end; ++it) 0212 (*it)->deleteLater(); 0213 m_children.clear(); 0214 0215 delete m_pILVItem; 0216 } 0217 0218 void ProjectItem::setILVItem(ILVItem *ilvItem) 0219 { 0220 m_pILVItem = ilvItem; 0221 ilvItem->setExpanded(true); 0222 ilvItem->setText(0, name()); 0223 ilvItem->setProjectItem(this); 0224 updateILVItemPixmap(); 0225 } 0226 0227 void ProjectItem::updateILVItemPixmap() 0228 { 0229 if (!m_pILVItem) 0230 return; 0231 0232 switch (type()) { 0233 case ProjectType: { 0234 // ?! - We shouldn't have an ilvitem for this. 0235 break; 0236 } 0237 0238 case ProgramType: { 0239 QPixmap pm; 0240 pm.load(QStandardPaths::locate(QStandardPaths::AppDataLocation, "icons/project_program.png")); 0241 m_pILVItem->setIcon(0, QIcon(pm)); 0242 break; 0243 } 0244 0245 case LibraryType: { 0246 QPixmap pm; 0247 pm.load(QStandardPaths::locate(QStandardPaths::AppDataLocation, "icons/project_library.png")); 0248 m_pILVItem->setIcon(0, QIcon(pm)); 0249 break; 0250 } 0251 0252 case FileType: { 0253 QMimeType m = QMimeDatabase().mimeTypeForFile(url().path()); 0254 // m_pILVItem->setPixmap( 0, m->pixmap( KIconLoader::Small ) ); 0255 m_pILVItem->setIcon(0, QIcon::fromTheme(m.iconName())); 0256 break; 0257 } 0258 } 0259 } 0260 0261 void ProjectItem::addChild(ProjectItem *child) 0262 { 0263 if (!child || m_children.contains(child)) 0264 return; 0265 0266 m_children << child; 0267 0268 child->setILVItem(m_pILVItem ? new ILVItem(m_pILVItem, child->name()) : new ILVItem(m_pProjectManager, name())); 0269 0270 updateControlChildMicroIDs(); 0271 } 0272 0273 void ProjectItem::updateControlChildMicroIDs() 0274 { 0275 bool control = false; 0276 switch (type()) { 0277 case ProjectItem::ProjectType: 0278 case ProjectItem::LibraryType: 0279 case ProjectItem::ProgramType: 0280 control = !microID().isEmpty(); 0281 break; 0282 0283 case ProjectItem::FileType: 0284 control = true; 0285 break; 0286 } 0287 0288 m_children.removeAll(static_cast<ProjectItem *>(nullptr)); 0289 ProjectItemList::iterator end = m_children.end(); 0290 for (ProjectItemList::iterator it = m_children.begin(); it != end; ++it) 0291 (*it)->setUseParentMicroID(control); 0292 } 0293 0294 void ProjectItem::setObjectName(const QString &name) 0295 { 0296 m_name = name; 0297 if (m_pILVItem) 0298 m_pILVItem->setText(0, name); 0299 } 0300 0301 void ProjectItem::setURL(const QUrl &url) 0302 { 0303 m_url = url; 0304 0305 if (m_name.isEmpty()) 0306 setObjectName(url.fileName()); 0307 0308 if (type() != FileType) { 0309 // The output url *is* our url 0310 setOutputURL(url); 0311 } else if (outputURL().isEmpty()) { 0312 // Try and guess what the output url should be... 0313 QString newExtension; 0314 0315 switch (outputType()) { 0316 case ProgramOutput: 0317 newExtension = ".hex"; 0318 break; 0319 0320 case ObjectOutput: 0321 newExtension = ".o"; 0322 break; 0323 0324 case LibraryOutput: 0325 newExtension = ".o"; 0326 break; 0327 0328 case UnknownOutput: 0329 break; 0330 } 0331 0332 if (!newExtension.isEmpty()) { 0333 QString fileName = url.path(); 0334 fileName.chop(fileName.length() - fileName.lastIndexOf('.')); 0335 fileName.append(newExtension); 0336 QUrl newUrl(url); 0337 newUrl.setPath(fileName); 0338 setOutputURL(newUrl); 0339 } 0340 } 0341 0342 updateILVItemPixmap(); 0343 } 0344 0345 QString ProjectItem::microID() const 0346 { 0347 if (!m_bUseParentMicroID) 0348 return m_microID; 0349 0350 return m_pParent ? m_pParent->microID() : QString(); 0351 } 0352 0353 void ProjectItem::setMicroID(const QString &id) 0354 { 0355 ProcessingOptions::setMicroID(id); 0356 updateControlChildMicroIDs(); 0357 } 0358 0359 ProjectItem::OutputType ProjectItem::outputType() const 0360 { 0361 if (!m_pParent) 0362 return UnknownOutput; 0363 0364 switch (m_pParent->type()) { 0365 case ProjectItem::ProjectType: { 0366 // We're a top level build target, so look at our own type 0367 switch (type()) { 0368 case ProjectItem::ProjectType: 0369 qCWarning(KTL_LOG) << "Parent item and this item are both project items"; 0370 return UnknownOutput; 0371 0372 case ProjectItem::FileType: 0373 case ProjectItem::ProgramType: 0374 return ProgramOutput; 0375 0376 case ProjectItem::LibraryType: 0377 return LibraryOutput; 0378 } 0379 return UnknownOutput; 0380 } 0381 0382 case ProjectItem::FileType: { 0383 qCWarning(KTL_LOG) << "Don't know how to handle parent item being a file"; 0384 return UnknownOutput; 0385 } 0386 0387 case ProjectItem::ProgramType: 0388 case ProjectItem::LibraryType: 0389 return ObjectOutput; 0390 } 0391 0392 return UnknownOutput; 0393 } 0394 0395 bool ProjectItem::build(ProcessOptionsList *pol) 0396 { 0397 if (!pol) 0398 return false; 0399 0400 // Check to see that we aren't already in the ProcessOptionstList; 0401 ProcessOptionsList::iterator polEnd = pol->end(); 0402 for (ProcessOptionsList::iterator it = pol->begin(); it != polEnd; ++it) { 0403 if ((*it).targetFile() == outputURL().toLocalFile()) 0404 return true; 0405 } 0406 0407 ProjectInfo *projectInfo = ProjectManager::self()->currentProject(); 0408 assert(projectInfo); 0409 0410 if (outputURL().isEmpty()) { 0411 KMessageBox::error(nullptr, i18n("Do not know how to build \"%1\" (output URL is empty).", name())); 0412 return false; 0413 } 0414 0415 // Build all internal libraries that we depend on 0416 QStringList::iterator send = m_linkedInternal.end(); 0417 for (QStringList::iterator it = m_linkedInternal.begin(); it != send; ++it) { 0418 ProjectItem *lib = projectInfo->findItem(QUrl::fromLocalFile(projectInfo->directory() + *it)); 0419 if (!lib) { 0420 KMessageBox::error(nullptr, i18n("Do not know how to build \"%1\" (library does not exist in project).", *it)); 0421 return false; 0422 } 0423 0424 if (!lib->build(pol)) 0425 return false; 0426 } 0427 0428 // Build all children 0429 m_children.removeAll(static_cast<ProjectItem *>(nullptr)); 0430 ProjectItemList::iterator cend = m_children.end(); 0431 for (ProjectItemList::iterator it = m_children.begin(); it != cend; ++it) { 0432 if (!(*it)->build(pol)) 0433 return false; 0434 } 0435 0436 // Now build ourself 0437 ProcessOptions po; 0438 po.b_addToProject = false; 0439 po.setTargetFile(outputURL().toLocalFile()); 0440 po.m_picID = microID(); 0441 0442 ProcessOptions::ProcessPath::MediaType typeTo = ProcessOptions::ProcessPath::Unknown; 0443 0444 switch (outputType()) { 0445 case UnknownOutput: 0446 KMessageBox::error(nullptr, i18n("Do not know how to build \"%1\" (unknown output type).", name())); 0447 return false; 0448 0449 case ProgramOutput: 0450 typeTo = ProcessOptions::ProcessPath::Program; 0451 break; 0452 0453 case ObjectOutput: 0454 typeTo = ProcessOptions::ProcessPath::Object; 0455 break; 0456 0457 case LibraryOutput: 0458 typeTo = ProcessOptions::ProcessPath::Library; 0459 break; 0460 } 0461 0462 switch (type()) { 0463 case ProjectType: 0464 // Nothing to do 0465 return true; 0466 0467 case FileType: { 0468 const QString fileName = url().toLocalFile(); 0469 po.setInputFiles(QStringList(fileName)); 0470 po.setProcessPath(ProcessOptions::ProcessPath::path(ProcessOptions::guessMediaType(fileName), typeTo)); 0471 break; 0472 } 0473 case ProgramType: 0474 case LibraryType: 0475 // Build up a list of input urls 0476 QStringList inputFiles; 0477 0478 // Link child objects 0479 m_children.removeAll(static_cast<ProjectItem *>(nullptr)); 0480 ProjectItemList::iterator cend = m_children.end(); 0481 for (ProjectItemList::iterator it = m_children.begin(); it != cend; ++it) 0482 inputFiles << (*it)->outputURL().toLocalFile(); 0483 0484 po.setInputFiles(inputFiles); 0485 po.setProcessPath(ProcessOptions::ProcessPath::path(ProcessOptions::ProcessPath::Object, typeTo)); 0486 break; 0487 } 0488 0489 po.m_hexFormat = hexFormatToString(hexFormat()); 0490 po.m_bOutputMapFile = outputMapFile(); 0491 po.m_libraryDir = libraryDir(); 0492 po.m_linkerScript = linkerScript(); 0493 po.m_linkOther = linkerOther(); 0494 0495 // Link against libraries 0496 QStringList::iterator lend = m_linkedInternal.end(); 0497 for (QStringList::iterator it = m_linkedInternal.begin(); it != lend; ++it) 0498 po.m_linkLibraries += projectInfo->directory() + *it; 0499 lend = m_linkedExternal.end(); 0500 for (QStringList::iterator it = m_linkedExternal.begin(); it != lend; ++it) 0501 po.m_linkLibraries += *it; 0502 0503 // Save our working file (if open) and append to the build list 0504 Document *currentDoc = DocManager::self()->findDocument(url()); 0505 if (currentDoc) 0506 currentDoc->fileSave(); 0507 pol->append(po); 0508 0509 return true; 0510 } 0511 0512 void ProjectItem::upload(ProcessOptionsList *pol) 0513 { 0514 build(pol); 0515 0516 ProgrammerDlg *dlg = new ProgrammerDlg(microID(), static_cast<QWidget *>(KTechlab::self())); 0517 dlg->setObjectName("Programmer Dlg"); 0518 0519 const int accepted = dlg->exec(); 0520 if (accepted != QDialog::Accepted) { 0521 dlg->deleteLater(); 0522 return; 0523 } 0524 0525 ProcessOptions po; 0526 dlg->initOptions(&po); 0527 po.b_addToProject = false; 0528 po.setInputFiles(QStringList(outputURL().toLocalFile())); 0529 po.setProcessPath(ProcessOptions::ProcessPath::Program_PIC); 0530 0531 pol->append(po); 0532 0533 dlg->deleteLater(); 0534 } 0535 0536 QDomElement ProjectItem::toDomElement(QDomDocument &doc, const QUrl &baseDirUrl) const 0537 { 0538 QDomElement node = doc.createElement("item"); 0539 0540 node.setAttribute("type", typeToString()); 0541 node.setAttribute("name", m_name); 0542 node.setAttribute("url", ::relativeUrl(baseDirUrl, m_url)); 0543 0544 node.appendChild(LinkerOptions::toDomElement(doc, baseDirUrl)); 0545 node.appendChild(ProcessingOptions::toDomElement(doc, baseDirUrl)); 0546 0547 ProjectItemList::const_iterator end = m_children.end(); 0548 for (ProjectItemList::const_iterator it = m_children.begin(); it != end; ++it) { 0549 if (*it) 0550 node.appendChild((*it)->toDomElement(doc, baseDirUrl)); 0551 } 0552 0553 return node; 0554 } 0555 0556 QList<QUrl> ProjectItem::childOutputURLs(unsigned types, unsigned outputTypes) const 0557 { 0558 QList<QUrl> urls; 0559 0560 ProjectItemList::const_iterator end = m_children.end(); 0561 for (ProjectItemList::const_iterator it = m_children.begin(); it != end; ++it) { 0562 if (!*it) 0563 continue; 0564 0565 if (((*it)->type() & types) && ((*it)->outputType() & outputTypes)) 0566 urls += (*it)->outputURL(); 0567 0568 urls += (*it)->childOutputURLs(types); 0569 } 0570 0571 return urls; 0572 } 0573 0574 ProjectItem *ProjectItem::findItem(const QUrl &url) 0575 { 0576 if (this->url() == url) 0577 return this; 0578 0579 ProjectItemList::const_iterator end = m_children.end(); 0580 for (ProjectItemList::const_iterator it = m_children.begin(); it != end; ++it) { 0581 if (!*it) 0582 continue; 0583 0584 ProjectItem *found = (*it)->findItem(url); 0585 if (found) 0586 return found; 0587 } 0588 0589 return nullptr; 0590 } 0591 0592 bool ProjectItem::closeOpenFiles() 0593 { 0594 Document *doc = DocManager::self()->findDocument(m_url); 0595 if (doc && !doc->fileClose()) 0596 return false; 0597 0598 m_children.removeAll(static_cast<ProjectItem *>(nullptr)); 0599 ProjectItemList::iterator end = m_children.end(); 0600 for (ProjectItemList::iterator it = m_children.begin(); it != end; ++it) { 0601 if (!(*it)->closeOpenFiles()) 0602 return false; 0603 } 0604 0605 return true; 0606 } 0607 0608 void ProjectItem::addFiles() 0609 { 0610 const QList<QUrl> urls = KTechlab::self()->getFileURLs(); 0611 for (const QUrl &url : urls) 0612 addFile(url); 0613 } 0614 0615 void ProjectItem::addCurrentFile() 0616 { 0617 Document *document = DocManager::self()->getFocusedDocument(); 0618 if (!document) 0619 return; 0620 0621 // If the file isn't saved yet, we must do that 0622 // before it is added to the project. 0623 if (document->url().isEmpty()) { 0624 document->fileSaveAs(); 0625 // If the user pressed cancel then just give up, 0626 // otherwise the file can now be added. 0627 } 0628 0629 if (!document->url().isEmpty()) 0630 addFile(document->url()); 0631 } 0632 0633 void ProjectItem::addFile(const QUrl &url) 0634 { 0635 if (url.isEmpty()) 0636 return; 0637 0638 m_children.removeAll(static_cast<ProjectItem *>(nullptr)); 0639 ProjectItemList::iterator end = m_children.end(); 0640 for (ProjectItemList::iterator it = m_children.begin(); it != end; ++it) { 0641 if ((*it)->type() == FileType && (*it)->url() == url) 0642 return; 0643 } 0644 0645 ProjectItem *item = new ProjectItem(this, FileType, m_pProjectManager); 0646 item->setURL(url); 0647 addChild(item); 0648 } 0649 0650 QString ProjectItem::typeToString() const 0651 { 0652 switch (m_type) { 0653 case ProjectType: 0654 return "Project"; 0655 0656 case FileType: 0657 return "File"; 0658 0659 case ProgramType: 0660 return "Program"; 0661 0662 case LibraryType: 0663 return "Library"; 0664 } 0665 return QString(); 0666 } 0667 0668 ProjectItem::Type ProjectItem::stringToType(const QString &type) 0669 { 0670 if (type == "Project") 0671 return ProjectType; 0672 0673 if (type == "File") 0674 return FileType; 0675 0676 if (type == "Program") 0677 return ProgramType; 0678 0679 if (type == "Library") 0680 return LibraryType; 0681 0682 return FileType; 0683 } 0684 0685 void ProjectItem::domElementToItem(const QDomElement &element, const QUrl &baseDirUrl) 0686 { 0687 Type type = stringToType(element.attribute("type", QString())); 0688 QString name = element.attribute("name", QString()); 0689 QUrl url = baseDirUrl.resolved(QUrl(element.attribute("url", QString()))); 0690 0691 ProjectItem *createdItem = new ProjectItem(this, type, m_pProjectManager); 0692 createdItem->setObjectName(name); 0693 createdItem->setURL(url); 0694 0695 addChild(createdItem); 0696 0697 QDomNode node = element.firstChild(); 0698 while (!node.isNull()) { 0699 QDomElement childElement = node.toElement(); 0700 if (!childElement.isNull()) { 0701 const QString tagName = childElement.tagName(); 0702 0703 if (tagName == "linker") 0704 createdItem->domElementToLinkerOptions(childElement, baseDirUrl); 0705 0706 else if (tagName == "processing") 0707 createdItem->domElementToProcessingOptions(childElement, baseDirUrl); 0708 0709 else if (tagName == "item") 0710 createdItem->domElementToItem(childElement, baseDirUrl); 0711 0712 else 0713 qCCritical(KTL_LOG) << "Unrecognised element tag name: " << tagName; 0714 } 0715 0716 node = node.nextSibling(); 0717 } 0718 } 0719 // END class ProjectItem 0720 0721 // BEGIN class ProjectInfo 0722 ProjectInfo::ProjectInfo(ProjectManager *projectManager) 0723 : ProjectItem(nullptr, ProjectItem::ProjectType, projectManager) 0724 { 0725 m_microID = QString(); 0726 } 0727 0728 ProjectInfo::~ProjectInfo() 0729 { 0730 } 0731 0732 bool ProjectInfo::open(const QUrl &url) 0733 { 0734 QScopedPointer<QFile> file; 0735 if (!url.isLocalFile()) { 0736 QScopedPointer<QTemporaryFile> downloadedFile(new QTemporaryFile()); 0737 downloadedFile->open(); 0738 KIO::FileCopyJob *job = KIO::file_copy(url, QUrl::fromLocalFile(downloadedFile->fileName())); 0739 KJobWidgets::setWindow(job, nullptr); 0740 if (!job->exec()) { 0741 KMessageBox::error(nullptr, job->errorString()); 0742 return false; 0743 } 0744 file.reset(downloadedFile.take()); 0745 } else { 0746 QScopedPointer<QFile> localFile(new QFile(url.toLocalFile())); 0747 if (!localFile->open(QIODevice::ReadOnly)) { 0748 KMessageBox::error(nullptr, i18n("Could not open %1 for reading", localFile->fileName())); 0749 return false; 0750 } 0751 file.reset(localFile.take()); 0752 } 0753 0754 m_url = url; 0755 0756 QString xml; 0757 QTextStream textStream(file.data()); 0758 while (!textStream.atEnd()) // was: eof() 0759 xml += textStream.readLine() + '\n'; 0760 0761 QDomDocument doc("KTechlab"); 0762 QString errorMessage; 0763 if (!doc.setContent(xml, &errorMessage)) { 0764 KMessageBox::error(nullptr, i18n("Could not parse XML:\n%1", errorMessage)); 0765 return false; 0766 } 0767 0768 QDomElement root = doc.documentElement(); 0769 0770 QDomNode node = root.firstChild(); 0771 while (!node.isNull()) { 0772 QDomElement element = node.toElement(); 0773 if (!element.isNull()) { 0774 const QString tagName = element.tagName(); 0775 0776 if (tagName == "linker") 0777 domElementToLinkerOptions(element, m_url); 0778 0779 else if (tagName == "processing") 0780 domElementToProcessingOptions(element, m_url); 0781 0782 else if (tagName == "file" || tagName == "item") 0783 domElementToItem(element, m_url); 0784 0785 else 0786 qCWarning(KTL_LOG) << "Unrecognised element tag name: " << tagName; 0787 } 0788 0789 node = node.nextSibling(); 0790 } 0791 0792 updateControlChildMicroIDs(); 0793 return true; 0794 } 0795 0796 bool ProjectInfo::save() 0797 { 0798 QFile file(m_url.toLocalFile()); 0799 if (file.open(QIODevice::WriteOnly) == false) { 0800 KMessageBox::error(nullptr, i18n("Project could not be saved to \"%1\"", file.fileName()), i18n("Saving Project")); 0801 return false; 0802 } 0803 0804 QDomDocument doc("KTechlab"); 0805 0806 QDomElement root = doc.createElement("project"); 0807 doc.appendChild(root); 0808 0809 m_children.removeAll(static_cast<ProjectItem *>(nullptr)); 0810 const QUrl baseDirUrl = m_url.adjusted(QUrl::RemoveFilename); 0811 ProjectItemList::const_iterator end = m_children.end(); 0812 for (ProjectItemList::const_iterator it = m_children.begin(); it != end; ++it) 0813 root.appendChild((*it)->toDomElement(doc, baseDirUrl)); 0814 0815 QTextStream stream(&file); 0816 stream << doc.toString(); 0817 file.close(); 0818 0819 { 0820 KRecentFilesAction *rfa = static_cast<KRecentFilesAction *>(KTechlab::self()->actionByName("project_open_recent")); 0821 if (rfa) { 0822 KSharedConfigPtr config = KSharedConfig::openConfig(); 0823 rfa->addUrl(m_url); 0824 rfa->saveEntries(config->group("Recent Projects")); 0825 config->sync(); 0826 } else { 0827 qCWarning(KTL_LOG) << "there is no project_open_recent action in application"; 0828 } 0829 } 0830 0831 return true; 0832 } 0833 0834 bool ProjectInfo::saveAndClose() 0835 { 0836 if (!save()) 0837 return false; 0838 0839 if (!closeOpenFiles()) 0840 return false; 0841 0842 return true; 0843 } 0844 // END class ProjectInfo 0845 0846 // BEGIN class ProjectManager 0847 ProjectManager *ProjectManager::m_pSelf = nullptr; 0848 0849 ProjectManager *ProjectManager::self(KateMDI::ToolView *parent) 0850 { 0851 if (!m_pSelf) { 0852 assert(parent); 0853 m_pSelf = new ProjectManager(parent); 0854 m_pSelf->setObjectName("Project Manager"); 0855 } 0856 return m_pSelf; 0857 } 0858 0859 ProjectManager::ProjectManager(KateMDI::ToolView *parent) 0860 : ItemSelector(parent) 0861 , m_pCurrentProject(nullptr) 0862 { 0863 setWhatsThis(i18n("Displays the list of files in the project.\nTo open or close a project, use the \"Project\" menu. Right click on a file to remove it from the project")); 0864 0865 setListCaption(i18n("File")); 0866 setWindowTitle(i18n("Project Manager")); 0867 0868 connect(this, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this, SLOT(slotItemClicked(QTreeWidgetItem *, int))); 0869 } 0870 0871 ProjectManager::~ProjectManager() 0872 { 0873 } 0874 0875 void ProjectManager::slotNewProject() 0876 { 0877 if (!slotCloseProject()) 0878 return; 0879 0880 NewProjectDlg *newProjectDlg = new NewProjectDlg(this); 0881 const int accepted = newProjectDlg->exec(); 0882 0883 if (accepted == QDialog::Accepted) { 0884 m_pCurrentProject = new ProjectInfo(this); 0885 m_pCurrentProject->setObjectName(newProjectDlg->projectName()); 0886 m_pCurrentProject->setURL(QUrl::fromLocalFile(newProjectDlg->location() + m_pCurrentProject->name().toLower() + ".ktechlab")); 0887 0888 QDir dir; 0889 if (!dir.mkdir(m_pCurrentProject->directory())) 0890 qCDebug(KTL_LOG) << "Error in creating directory " << m_pCurrentProject->directory(); 0891 0892 m_pCurrentProject->save(); 0893 updateActions(); 0894 0895 emit projectCreated(); 0896 } 0897 0898 delete newProjectDlg; 0899 } 0900 0901 void ProjectManager::slotProjectOptions() 0902 { 0903 } 0904 0905 void ProjectManager::slotOpenProject() 0906 { 0907 QString filter; 0908 filter = QString("*.ktechlab|%1 (*.ktechlab)\n*|%2").arg(i18n("KTechlab Project")).arg(i18n("All Files")); 0909 0910 QUrl url = QFileDialog::getOpenFileUrl(this, i18n("Open Location"), QUrl(), filter); 0911 0912 if (url.isEmpty()) 0913 return; 0914 0915 slotOpenProject(url); 0916 } 0917 0918 void ProjectManager::slotOpenProject(const QUrl &url) 0919 { 0920 if (m_pCurrentProject && m_pCurrentProject->url() == url) 0921 return; 0922 0923 if (!slotCloseProject()) 0924 return; 0925 0926 m_pCurrentProject = new ProjectInfo(this); 0927 0928 if (!m_pCurrentProject->open(url)) { 0929 m_pCurrentProject->deleteLater(); 0930 m_pCurrentProject = nullptr; 0931 return; 0932 } 0933 { 0934 KRecentFilesAction *rfa = static_cast<KRecentFilesAction *>(KTechlab::self()->actionByName("project_open_recent")); 0935 if (rfa) { 0936 KSharedConfigPtr config = KSharedConfig::openConfig(); 0937 rfa->addUrl(m_pCurrentProject->url()); 0938 rfa->saveEntries(config->group("Recent Projects")); 0939 config->sync(); 0940 } else { 0941 qCWarning(KTL_LOG) << "there is no project_open_recent action in application"; 0942 } 0943 } 0944 0945 if (KTLConfig::raiseItemSelectors()) 0946 KTechlab::self()->showToolView(KTechlab::self()->toolView(toolViewIdentifier())); 0947 0948 updateActions(); 0949 emit projectOpened(); 0950 } 0951 0952 bool ProjectManager::slotCloseProject() 0953 { 0954 if (!m_pCurrentProject) 0955 return true; 0956 0957 if (!m_pCurrentProject->saveAndClose()) 0958 return false; 0959 0960 m_pCurrentProject->deleteLater(); 0961 m_pCurrentProject = nullptr; 0962 updateActions(); 0963 emit projectClosed(); 0964 return true; 0965 } 0966 0967 void ProjectManager::slotCreateSubproject() 0968 { 0969 if (!currentProject()) 0970 return; 0971 0972 CreateSubprojectDlg *dlg = new CreateSubprojectDlg(this); 0973 const int accepted = dlg->exec(); 0974 0975 if (accepted == QDialog::Accepted) { 0976 ProjectItem::Type type = ProjectItem::ProgramType; 0977 switch (dlg->type()) { 0978 case CreateSubprojectDlg::ProgramType: 0979 type = ProjectItem::ProgramType; 0980 break; 0981 0982 case CreateSubprojectDlg::LibraryType: 0983 type = ProjectItem::LibraryType; 0984 break; 0985 } 0986 0987 ProjectItem *subproject = new ProjectItem(currentProject(), type, this); 0988 subproject->setURL(QUrl::fromLocalFile(dlg->targetFile())); 0989 0990 currentProject()->addChild(subproject); 0991 currentProject()->save(); 0992 0993 emit subprojectCreated(); 0994 } 0995 0996 delete dlg; 0997 } 0998 0999 void ProjectManager::updateActions() 1000 { 1001 bool projectIsOpen = m_pCurrentProject; 1002 1003 KTechlab::self()->actionByName("project_create_subproject")->setEnabled(projectIsOpen); 1004 KTechlab::self()->actionByName("project_export_makefile")->setEnabled(projectIsOpen); 1005 KTechlab::self()->actionByName("subproject_add_existing_file")->setEnabled(projectIsOpen); 1006 KTechlab::self()->actionByName("subproject_add_current_file")->setEnabled(projectIsOpen); 1007 // KTechlab::self()->actionByName("project_options")->setEnabled( projectIsOpen ); 1008 KTechlab::self()->actionByName("project_close")->setEnabled(projectIsOpen); 1009 KTechlab::self()->actionByName("project_add_existing_file")->setEnabled(projectIsOpen); 1010 KTechlab::self()->actionByName("project_add_current_file")->setEnabled(projectIsOpen); 1011 } 1012 1013 void ProjectManager::slotAddFile() 1014 { 1015 if (!currentProject()) 1016 return; 1017 1018 currentProject()->addFiles(); 1019 emit filesAdded(); 1020 } 1021 1022 void ProjectManager::slotAddCurrentFile() 1023 { 1024 if (!currentProject()) 1025 return; 1026 currentProject()->addCurrentFile(); 1027 emit filesAdded(); 1028 } 1029 1030 void ProjectManager::slotSubprojectAddExistingFile() 1031 { 1032 ILVItem *currentItem = dynamic_cast<ILVItem *>(selectedItem()); 1033 if (!currentItem || !currentItem->projectItem()) 1034 return; 1035 1036 currentItem->projectItem()->addFiles(); 1037 emit filesAdded(); 1038 } 1039 1040 void ProjectManager::slotSubprojectAddCurrentFile() 1041 { 1042 ILVItem *currentItem = dynamic_cast<ILVItem *>(selectedItem()); 1043 if (!currentItem || !currentItem->projectItem()) 1044 return; 1045 1046 currentItem->projectItem()->addCurrentFile(); 1047 emit filesAdded(); 1048 } 1049 1050 void ProjectManager::slotItemBuild() 1051 { 1052 ILVItem *currentItem = dynamic_cast<ILVItem *>(selectedItem()); 1053 if (!currentItem || !currentItem->projectItem()) 1054 return; 1055 1056 ProcessOptionsList pol; 1057 currentItem->projectItem()->build(&pol); 1058 LanguageManager::self()->compile(pol); 1059 } 1060 1061 void ProjectManager::slotItemUpload() 1062 { 1063 ILVItem *currentItem = dynamic_cast<ILVItem *>(selectedItem()); 1064 if (!currentItem || !currentItem->projectItem()) 1065 return; 1066 1067 ProcessOptionsList pol; 1068 currentItem->projectItem()->upload(&pol); 1069 LanguageManager::self()->compile(pol); 1070 } 1071 1072 void ProjectManager::slotRemoveSelected() 1073 { 1074 ILVItem *currentItem = dynamic_cast<ILVItem *>(selectedItem()); 1075 if (!currentItem) 1076 return; 1077 1078 int choice = KMessageBox::questionYesNo(this, i18n("Do you really want to remove \"%1\"?", currentItem->text(0)), i18n("Remove Project File?"), KGuiItem(i18n("Remove")), KGuiItem(i18n("Cancel"))); 1079 1080 if (choice == KMessageBox::No) 1081 return; 1082 1083 currentItem->projectItem()->deleteLater(); 1084 emit filesRemoved(); 1085 } 1086 1087 void ProjectManager::slotExportToMakefile() 1088 { 1089 } 1090 1091 void ProjectManager::slotSubprojectLinkerOptions() 1092 { 1093 ILVItem *currentItem = dynamic_cast<ILVItem *>(selectedItem()); 1094 if (!currentItem || !currentItem->projectItem()) 1095 return; 1096 1097 LinkerOptionsDlg *dlg = new LinkerOptionsDlg(currentItem->projectItem(), this); 1098 dlg->exec(); 1099 currentProject()->save(); 1100 1101 // The dialog sets the options for us if it was accepted, so we don't need to do anything 1102 delete dlg; 1103 } 1104 1105 void ProjectManager::slotItemProcessingOptions() 1106 { 1107 ILVItem *currentItem = dynamic_cast<ILVItem *>(selectedItem()); 1108 if (!currentItem || !currentItem->projectItem()) 1109 return; 1110 1111 ProcessingOptionsDlg *dlg = new ProcessingOptionsDlg(currentItem->projectItem(), this); 1112 dlg->exec(); 1113 currentProject()->save(); 1114 1115 // The dialog sets the options for us if it was accepted, so we don't need to do anything 1116 delete dlg; 1117 } 1118 1119 void ProjectManager::slotItemClicked(QTreeWidgetItem *item, int) 1120 { 1121 ILVItem *ilvItem = dynamic_cast<ILVItem *>(item); 1122 if (!ilvItem) 1123 return; 1124 1125 ProjectItem *projectItem = ilvItem->projectItem(); 1126 if (!projectItem || projectItem->type() != ProjectItem::FileType) 1127 return; 1128 1129 DocManager::self()->openURL(projectItem->url()); 1130 } 1131 1132 void ProjectManager::slotContextMenuRequested(const QPoint &pos) 1133 { 1134 QTreeWidgetItem *item = itemAt(pos); 1135 QString popupName; 1136 ILVItem *ilvItem = dynamic_cast<ILVItem *>(item); 1137 QAction *linkerOptionsAct = KTechlab::self()->actionByName("project_item_linker_options"); 1138 linkerOptionsAct->setEnabled(false); 1139 1140 if (!m_pCurrentProject) { 1141 popupName = "project_none_popup"; 1142 1143 } else if (!ilvItem) { 1144 popupName = "project_blank_popup"; 1145 1146 } else { 1147 ProcessOptions::ProcessPath::MediaType mediaType = ProcessOptions::guessMediaType(ilvItem->projectItem()->url().toLocalFile()); 1148 1149 switch (ilvItem->projectItem()->type()) { 1150 case ProjectItem::FileType: 1151 if (mediaType == ProcessOptions::ProcessPath::Unknown) 1152 popupName = "project_file_other_popup"; 1153 else 1154 popupName = "project_file_popup"; 1155 break; 1156 1157 case ProjectItem::ProgramType: 1158 popupName = "project_program_popup"; 1159 break; 1160 1161 case ProjectItem::LibraryType: 1162 popupName = "project_library_popup"; 1163 break; 1164 1165 case ProjectItem::ProjectType: 1166 return; 1167 } 1168 switch (ilvItem->projectItem()->outputType()) { 1169 case ProjectItem::ProgramOutput: 1170 linkerOptionsAct->setEnabled(true); 1171 break; 1172 1173 case ProjectItem::ObjectOutput: 1174 case ProjectItem::LibraryOutput: 1175 case ProjectItem::UnknownOutput: 1176 linkerOptionsAct->setEnabled(false); 1177 break; 1178 } 1179 1180 // Only have linking options for SDCC files 1181 linkerOptionsAct->setEnabled(mediaType == ProcessOptions::ProcessPath::C); 1182 } 1183 1184 bool haveFocusedDocument = DocManager::self()->getFocusedDocument(); 1185 KTechlab::self()->actionByName("subproject_add_current_file")->setEnabled(haveFocusedDocument); 1186 KTechlab::self()->actionByName("project_add_current_file")->setEnabled(haveFocusedDocument); 1187 1188 QMenu *pop = static_cast<QMenu *>(KTechlab::self()->factory()->container(popupName, KTechlab::self())); 1189 if (pop) { 1190 QPoint globalPos = mapToGlobal(pos); 1191 pop->popup(globalPos); 1192 } 1193 } 1194 // END class ProjectManager 1195 1196 #include "moc_projectmanager.cpp"