File indexing completed on 2024-05-19 09:39:48

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"