/*************************************************************************** * Copyright (C) 2005-2006 Nicolas Hadacek * * Copyright (C) 2003 Alain Gibaud * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * ***************************************************************************/ #include "project_manager.h" #include #include #include #include #include #include #include #include #include #include "project.h" #include "project_wizard.h" #include "project_editor.h" #include "toplevel.h" #include "editor_manager.h" #include "object_view.h" #include "devices/list/device_list.h" #include "tools/list/compile_config.h" #include "register_view.h" #include "hex_editor.h" #include "main_global.h" #include "common/gui/purl_gui.h" #include "device_gui.h" //---------------------------------------------------------------------------- ProjectManager::View::View(QWidget *parent) : ListView(parent, "project_manager"), _project(0), _modified(false) { connect(this, SIGNAL(mouseButtonClicked(int, QListViewItem *, const QPoint &, int)), SLOT(clicked(int, QListViewItem *, const QPoint &, int))); connect(this, SIGNAL(contextMenuRequested(QListViewItem *, const QPoint &, int)), SLOT(contextMenu(QListViewItem *, const QPoint &, int))); connect(this, SIGNAL(itemRenamed(QListViewItem *, int, const QString &)), SLOT(renamed(QListViewItem *, int, const QString &))); connect(this, SIGNAL(moved()), SLOT(filesReordered())); header()->hide(); setSorting(-1); addColumn(QString::null, 170); setFullWidth(true); setRootIsDecorated(false); setAcceptDrops(true); setDragEnabled(true); setDropVisualizer(true); QTimer::singleShot(0, this, SLOT(init()));; } ProjectManager::View::~View() { delete _project; } void ProjectManager::View::init() { clear(); _rootItem = new RootItem(this); setCurrentItem(_rootItem); KListViewItem *item = new RegisterItem(headerItem(DeviceGroup)); ensureItemVisible(item); _deviceItem = new DeviceItem(headerItem(DeviceGroup)); ensureItemVisible(_deviceItem); _linkerScriptItem = 0; (void)headerItem(SourceGroup); } ProjectManager::HeaderItem *ProjectManager::View::findHeaderItem(Group group) const { QListViewItemIterator it(const_cast(this)); for(; it.current(); ++it) { if ( it.current()->rtti()!=HeaderRtti ) continue; HeaderItem *hi = static_cast(it.current()); if ( hi->group()==group ) return hi; } return 0; } ProjectManager::HeaderItem *ProjectManager::View::headerItem(Group group) { HeaderItem *item = findHeaderItem(group); if (item) return item; item = new HeaderItem(_rootItem, group); // reorder groups... HeaderItem *items[Nb_Groups]; for (uint i=0; itakeItem(items[i]); } for (int i=Nb_Groups-1; i>=0; i--) { if ( items[i]==0 ) continue; _rootItem->insertItem(items[i]); } return item; } ProjectManager::FileItem *ProjectManager::View::findFileItem(const PURL::Url &url) const { QListViewItemIterator it(const_cast(this)); for(; it.current(); ++it) { if ( it.current()->rtti()!=FileRtti ) continue; FileItem *fi = static_cast(it.current()); if ( fi->url()==url ) return fi; } return 0; } ProjectManager::FileItem *ProjectManager::View::findFileItem(PURL::FileType type) const { QListViewItemIterator it(const_cast(this)); for(; it.current(); ++it) { if ( it.current()->rtti()!=FileRtti ) continue; FileItem *fi = static_cast(it.current()); if ( fi->ftype()==type ) return fi; } return 0; } QListViewItem *ProjectManager::View::findItem(const QString &tag) const { QListViewItemIterator it(const_cast(this)); for(; it.current(); ++it) { switch (Rtti(it.current()->rtti())) { case RootRtti: case FileRtti: case LinkerScriptRtti: case DeviceRtti: continue; case HeaderRtti: case RegisterRtti: break; } if ( it.current()->text(0)==tag ) return it.current(); } return 0; } void ProjectManager::View::select(const Editor *e) { QListViewItem *item = 0; if ( e->url().isEmpty() ) item = findItem(e->tag()); else item = findFileItem(e->url()); if (item) setSelected(item, true); else clearSelection(); } void ProjectManager::View::contextMenu(QListViewItem *item, const QPoint &p, int) { if ( item==0 ) return; PopupMenu pop; Group group = Nb_Groups; if ( item->rtti()==HeaderRtti) group = static_cast(item)->group(); if ( item->rtti()==LinkerScriptRtti || group==LinkerScriptGroup ) { if ( _project==0 || Main::toolGroup().linkerScriptType()==PURL::Nb_FileTypes ) return; pop.insertTitle(i18n("Linker Script")); pop.insertItem("piklab_addfile", i18n("Set Custom...")); if ( !_project->customLinkerScript().isEmpty() ) pop.insertItem("editdelete", i18n("Set Default")); switch( pop.exec(p) ) { case 1: { PURL::Url url = PURL::getOpenUrl(":custom_linker_script", PURL::filter(Main::toolGroup().linkerScriptType()), this, i18n("Select Linker Script")); if ( !url.isEmpty() ) _project->setCustomLinkerScript(url); break; } case 2: _project->setCustomLinkerScript(PURL::Url()); break; } _linkerScriptItem->init(); } else if ( item->rtti()==RootRtti ) { RootItem *ri = static_cast(item); if ( _project==0 ) { if ( ri->url().isEmpty() ) { pop.insertItem("piklab_createproject", i18n("New Project..."), &Main::toplevel(), SLOT(newProject())); pop.insertItem("piklab_openproject", i18n("Open Project..."), &Main::toplevel(), SLOT(openProject())); pop.exec(p); } else { pop.insertTitle(i18n("Standalone File")); pop.insertItem("configure", i18n("Configure...")); if ( pop.exec(p)==1 ) Main::toplevel().configure(ConfigCenter::Standalone); } } else { pop.insertTitle(i18n("Project")); pop.insertItem("configure", i18n("Options..."), &Main::toplevel(), SLOT(configureProject())); pop.insertItem("find", i18n("Find Files..."), &Main::toplevel(), SLOT(runKfind())); pop.insertSeparator(); pop.insertItem("piklab_compile", i18n("Build Project"), &Main::toplevel(), SLOT(buildProject())); pop.insertItem("trashcan_empty", i18n("Clean Project"), &Main::toplevel(), SLOT(cleanBuild())); pop.insertSeparator(); pop.insertItem("filenew", i18n("New Source File..."), &Main::toplevel(), SLOT(newSourceFile())); pop.insertItem("piklab_addfile", i18n("Add Source Files..."), this, SLOT(insertSourceFiles())); pop.insertItem("piklab_addfile", i18n("Add Object Files..."), this, SLOT(insertObjectFiles())); if ( Main::currentEditor() ) pop.insertItem("piklab_addcurrentfile", i18n("Add Current File"), this, SLOT(insertCurrentFile())); pop.exec(p); } } else if ( item->rtti()==FileRtti ) { if ( _project==0 ) return; FileItem *fi = static_cast(item); if ( isExternalFile(fi->url()) ) return; pop.insertTitle(item->text(0)); pop.insertItem("editdelete", i18n("Remove From Project")); if ( pop.exec(p)==1 ) removeFile(fi->url()); } else if ( item->rtti()==HeaderRtti ) { if ( _project==0 ) return; if ( group==LinkerObjectGroup ) { pop.insertTitle(i18n("Objects")); pop.insertItem("piklab_addfile", i18n("Add Object Files..."), this, SLOT(insertObjectFiles())); pop.exec(p); } else if ( group==SourceGroup || group==HeaderGroup ) { pop.insertTitle(i18n("Sources")); pop.insertItem("filenew", i18n("New File..."), &Main::toplevel(), SLOT(newSourceFile())); pop.insertItem("piklab_addfile", i18n("Add Source Files..."), this, SLOT(insertSourceFiles())); pop.exec(p); } else if ( group==DeviceGroup ) { pop.insertItem("filenew", i18n("Select Device..."), &Main::toplevel(), SLOT(showDeviceInfo())); pop.exec(p); } } } void ProjectManager::View::closeProject() { if ( _project==0 && projectUrl().isEmpty() ) return; if (_project) { // save opened files PURL::UrlList files = Main::editorManager().files(); PURL::UrlList::const_iterator it = files.begin(); PURL::UrlList opened; for (; it!=files.end(); ++it) { if ( !isExternalFile(*it) ) opened += *it; Main::editorManager().closeEditor(*it); } _project->setOpenedFiles(opened); // save watched registers _project->setWatchedRegisters(Register::list().watched()); QString error; if ( !_project->save(error) ) MessageBox::detailedSorry(i18n("Could not save project file \"%1\".").arg(_project->url().pretty()), error, Log::Show); delete _project; _project = 0; } _modified = false; init(); } void ProjectManager::View::addExternalFiles() { const QMap &ext = projectData().externals; QMap::const_iterator it; for (it=ext.begin(); it!=ext.end(); ++it) { if ( !it.key().exists() ) continue; addFile(it.key(), it.key().fileType(), it.data()); } } void ProjectManager::View::setStandalone(const PURL::Url &url, PURL::FileType type) { if ( projectUrl()==url ) return; closeProject(); _rootItem->set(url, true); addFile(url, type, Intrinsic); _standaloneData[url].type = type; addExternalFiles(); } PURL::Url ProjectManager::View::standaloneGenerator(const PURL::Url &url, PURL::FileType &type) const { QMap::const_iterator it; for (it=_standaloneData.begin(); it!=_standaloneData.end(); ++it) { if ( !it.data().externals.contains(url) ) continue; if ( !it.key().exists() ) continue; type = it.data().type; return it.key(); } type = PURL::Nb_FileTypes; return url; } void ProjectManager::View::insertSourceFiles() { Q_ASSERT(_project); PURL::UrlList list = PURL::getOpenUrls(":", PURL::sourceFilter(PURL::CompleteFilter), this, i18n("Select Source File")); if ( list.isEmpty() ) return; PURL::UrlList::const_iterator it; for (it=list.begin(); it!=list.end(); ++it) insertFile(*it); } void ProjectManager::View::insertObjectFiles() { Q_ASSERT(_project); PURL::UrlList list = PURL::getOpenUrls(":", PURL::objectFilter(PURL::CompleteFilter), this, i18n("Select Object File")); if ( list.isEmpty() ) return; PURL::UrlList::const_iterator it; for (it=list.begin(); it!=list.end(); ++it) insertFile(*it); } void ProjectManager::View::insertFile(const PURL::Url &url) { if ( !url.exists() ) { MessageBox::detailedSorry(i18n("Could not find file."), i18n("File: %1").arg(url.pretty()), Log::Show); return; } PURL::Url purl = url; MessageBox::Result copy = MessageBox::No; if ( !url.isInto(_project->directory()) ) { copy = MessageBox::questionYesNoCancel(i18n("File \"%1\" is not inside the project directory. Do you want to copy the file to your project directory?").arg(url.pretty()), i18n("Copy and Add"), i18n("Add only")); if ( copy==MessageBox::Cancel ) return; if ( copy==MessageBox::Yes ) purl = PURL::Url(_project->directory(), url.filename()); } if ( _project->absoluteFiles().contains(purl) ) { MessageBox::detailedSorry(i18n("File is already in the project."), i18n("File: %1").arg(purl.pretty()), Log::Show); return; } if ( copy==MessageBox::Yes ) { Log::StringView sview; if ( !url.copyTo(purl, sview) ) { MessageBox::detailedSorry(i18n("Copying file to project directory failed."), i18n("File: %1\n").arg(url.pretty()) + sview.string(), Log::Show); return; } } _project->addFile(purl); addFile(purl, purl.fileType(), Intrinsic); } bool ProjectManager::View::openProject() { PURL::Url url = PURL::getOpenUrl(":open_project", PURL::projectFilter(PURL::CompleteFilter), this, i18n("Select Project file")); return openProject(url); } void ProjectManager::View::addExternalFile(const PURL::Url &url, FileOrigin origin) { Q_ASSERT( origin!=Intrinsic ); addFile(url, url.fileType(), origin); } const ProjectManager::View::ProjectData &ProjectManager::View::projectData() const { if ( _project==0 ) return _standaloneData[projectUrl()]; return _projectData; } ProjectManager::View::ProjectData &ProjectManager::View::projectData() { if ( _project==0 ) return _standaloneData[projectUrl()]; return _projectData; } void ProjectManager::View::addFile(const PURL::Url &url, PURL::FileType type, FileOrigin origin) { if ( contains(url) ) return; QMap &ext = projectData().externals; if ( type.data().group==PURL::LinkerScript && _linkerScriptItem ) { _linkerScriptItem->set(url); ext[url] = Included; return; } PURL::FileProperties properties = type.data().properties; Group grp = Nb_Groups; switch (origin) { case Intrinsic: grp = group(type); break; case Generated: grp = GeneratedGroup; break; case Included: grp = IncludedGroup; break; } HeaderItem *hitem = headerItem(grp); QListViewItem *item = new FileItem(hitem, type, url, origin!=Intrinsic); item->moveItem(hitem->lastChild()); ensureItemVisible(item); if ( origin!=Intrinsic ) ext[url] = origin; if ( type==PURL::Hex && _project==0 ) { QString extension = PURL::extension(PURL::AsmGPAsm); PURL::Url durl = PURL::Url::fromPathOrUrl("<" + (url.isEmpty() ? i18n("Disassembly") : url.appendExtension(extension).filename()) + ">"); if ( findFileItem(durl)==0 ) { (void)new FileItem(headerItem(ViewGroup), PURL::Coff, durl, true); ext[durl] = Generated; } } if ( _project && origin==Intrinsic ) _modified = true; } void ProjectManager::View::removeExternalFiles() { QMap &ext = projectData().externals; QMap::const_iterator it; for (it=ext.begin(); it!=ext.end(); ++it) { Main::editorManager().closeEditor(it.key()); removeFile(it.key()); } ext.clear(); if (_linkerScriptItem) _linkerScriptItem->init(); } void ProjectManager::View::removeFile(const PURL::Url &url) { if ( _project && !isExternalFile(url) ) _project->removeFile(url); FileItem *item = findFileItem(url); if ( item==0 ) return; HeaderItem *group = static_cast(item->parent()); delete item; if ( group->childCount()==0 ) delete group; _modified = true; emit guiChanged(); } void ProjectManager::View::clicked(int button, QListViewItem *item, const QPoint &, int) { if ( item==0 ) return; if ( button!=LeftButton ) return; const Device::Data *data = Main::deviceData(); Rtti rtti = Rtti(item->rtti()); if ( data==0 && rtti!=DeviceRtti && rtti!=RootRtti ) { MessageBox::sorry(i18n("Cannot open without device specified."), Log::Show); return; } Editor *e = 0; ::BusyCursor bc; switch (rtti) { case RootRtti: Main::toplevel().configureProject(); break; case HeaderRtti: { if ( static_cast(item)->group()!=DeviceGroup ) break; e = Main::editorManager().openEditor(EditorManager::DeviceEditor); break; } case RegisterRtti: e = Main::editorManager().openEditor(EditorManager::RegisterEditor); break; case DeviceRtti: return; case LinkerScriptRtti: { PURL::Url url = Main::projectManager().linkerScriptUrl(); if ( url.isEmpty() ) break; e = Main::editorManager().findEditor(url); if ( e==0 ) { e = Main::editorManager().createEditor(url.fileType(), url); if ( !e->open(url) ) { delete e; break; } if ( e && isExternalFile(url) ) e->setReadOnly(true); Main::editorManager().addEditor(e); } else Main::editorManager().showEditor(e); break; } case FileRtti: { FileItem *fi = static_cast(item); if ( !(fi->ftype().data().properties & PURL::Editable) ) break; e = Main::editorManager().findEditor(fi->url()); if ( e==0 ) { if ( fi->ftype()==PURL::Coff && _project==0 && !fi->url().exists() ) { PURL::Url url = findFileItem(PURL::Hex)->url(); if ( url.isEmpty() ) { HexEditor *he = ::qt_cast(Main::currentEditor()); if ( he==0 ) break; e = new DisassemblyEditor(*he, *data, this); } else e = new DisassemblyEditor(url, *data, this); addExternalFile(fi->url(), Generated); } else e = Main::editorManager().createEditor(fi->url().fileType(), fi->url()); if ( e==0 ) break; if ( !e->open(fi->url()) ) { delete e; break; } if ( isExternalFile(fi->url()) ) e->setReadOnly(true); Main::editorManager().addEditor(e); } else Main::editorManager().showEditor(e); break; } } cancelRenaming(); emit guiChanged(); } void ProjectManager::View::insertCurrentFile() { insertFile(Main::editorManager().currentEditor()->url()); } bool ProjectManager::View::editProject() { ProjectEditor dialog(*_project, this); if ( dialog.exec()!=QDialog::Accepted ) return false; _modified = true; if (_linkerScriptItem) _linkerScriptItem->init(); return true; } bool ProjectManager::View::newProject() { ProjectWizard wizard(this); if ( wizard.exec()==QDialog::Rejected ) return false; closeProject(); QString error; if ( !wizard.project()->save(error) ) { MessageBox::detailedSorry(i18n("Failed to create new project file"), error, Log::Show); return false; } openProject(wizard.url()); return true; } void ProjectManager::View::setProject(Project *project) { closeProject(); Main::editorManager().closeAllEditors(); _project = project; _rootItem->set(project->url(), false); if ( project && Main::toolGroup().linkerScriptType()!=PURL::Nb_FileTypes ) { _linkerScriptItem = new LinkerScriptItem(headerItem(LinkerScriptGroup)); ensureItemVisible(_linkerScriptItem); } } bool ProjectManager::View::openProject(const PURL::Url &url) { if ( url.isEmpty() ) return false; bool reload = ( _project && _project->url()==url ); if ( reload && !MessageBox::askContinue(i18n("Project already loaded. Reload?"), i18n("Reload")) ) return false; static_cast< KRecentFilesAction *>(Main::action("project_open_recent"))->removeURL(url.kurl()); Project *p = new Project(url); QString error; if ( !p->load(error) ) { MessageBox::detailedSorry(i18n("Could not open project file."), error, Log::Show); delete p; return false; } setProject(p); PURL::UrlList files = _project->absoluteFiles(); PURL::UrlList::const_iterator it; for(it=files.begin(); it!=files.end(); ++it) addFile(*it, (*it).fileType(), Intrinsic); _projectData.type = PURL::Project; _projectData.externals.clear(); static_cast(Main::action("project_open_recent"))->addURL(url.kurl()); files = _project->openedFiles(); for(it = files.begin(); it!=files.end(); ++it) Main::editorManager().openFile(*it); Register::list().init(); QValueList watched = _project->watchedRegisters(); QValueList::const_iterator wit; for (wit=watched.begin(); wit!=watched.end(); ++wit) Register::list().setWatched(*wit, true); return true; } bool ProjectManager::View::isExternalFile(const PURL::Url &url) const { if ( projectUrl().isEmpty() ) return false; return projectData().externals.contains(url); } void ProjectManager::View::modified(const PURL::Url &url) { FileItem *item = findFileItem(url); if ( item && !isExternalFile(url) ) _modified = true; } void ProjectManager::View::renamed(QListViewItem *item, int, const QString &text) { Q_ASSERT ( item->rtti()==DeviceRtti ); Main::toplevel().setDevice(text); } void ProjectManager::View::updateGUI() { _deviceItem->updateText(); } QDragObject *ProjectManager::View::dragObject() { if ( currentItem()==0 || currentItem()->rtti()!=FileRtti ) return 0; const FileItem *item = static_cast(currentItem()); const HeaderItem *hitem = static_cast(item->parent()); if ( hitem->group()!=SourceGroup ) return 0; QStrList uris; uris.append(QUriDrag::localFileToUri(item->url().filepath())); return new QUriDrag(uris, viewport()); } bool ProjectManager::View::acceptDrag(QDropEvent* e) const { if ( e->source()!=viewport() ) return false; const QListViewItem *item = itemAt(e->pos()); if ( item==0 || item->rtti()!=FileRtti ) return false; const HeaderItem *hitem = static_cast(item->parent()); return ( hitem->group()==SourceGroup ); } void ProjectManager::View::filesReordered() { if ( _project==0 ) return; _project->clearFiles(); QListViewItem *item = headerItem(SourceGroup)->firstChild(); for (;item; item=item->nextSibling()) _project->addFile(static_cast(item)->url()); } QString ProjectManager::View::tooltip(QListViewItem *item, int) const { switch (Rtti(item->rtti())) { case RootRtti: case FileRtti: case LinkerScriptRtti: return static_cast(item)->url().filepath(); case DeviceRtti: case RegisterRtti: case HeaderRtti: break; } return QString::null; } PURL::Url ProjectManager::View::linkerScriptUrl() const { QListViewItemIterator it(const_cast(this)); for(; it.current(); ++it) { if ( it.current()->rtti()!=LinkerScriptRtti ) continue; return static_cast(it.current())->url(); } return PURL::Url(); } bool ProjectManager::View::needsRecompile() const { // ### this could be perfected... PURL::Url output = projectUrl().toFileType(PURL::Hex); QDateTime outputLastModified; if ( !output.exists(&outputLastModified) ) return true; PURL::UrlList files; if ( Main::project() ) files = Main::project()->absoluteFiles(); else files.append(projectUrl()); PURL::UrlList::const_iterator it; for (it=files.begin(); it!=files.end(); ++it) { QDateTime lastModified; if ( !(*it).exists(&lastModified) ) continue; if ( lastModified>outputLastModified ) return true; } return false; } PURL::Url ProjectManager::View::selectedUrl() const { QListViewItem *item = currentItem(); if ( item==0 ) return PURL::Url(); Rtti rtti = Rtti(item->rtti()); if ( rtti!=FileRtti ) return PURL::Url(); return static_cast(item)->url(); }