/*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * 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 "core/ktlconfig.h" #include "docmanager.h" #include "document.h" #include "language.h" #include "languagemanager.h" #include "ktechlab.h" #include "microselectwidget.h" #include "programmerdlg.h" #include "projectdlgs.h" #include "projectmanager.h" #include "recentfilesaction.h" #include #include #include #include #include #include #include #include #include #include #include #include //BEGIN class LinkerOptions LinkerOptions::LinkerOptions() { m_hexFormat = HexFormat::inhx32; m_bOutputMapFile = false; } TQDomElement LinkerOptions::toDomElement( TQDomDocument & doc, const KURL & baseURL ) const { TQDomElement node = doc.createElement("linker"); node.setAttribute( "hex-format", hexFormatToString(hexFormat()) ); node.setAttribute( "output-map-file", outputMapFile() ); node.setAttribute( "library-dir", libraryDir() ); node.setAttribute( "linker-script", linkerScript() ); node.setAttribute( "other", linkerOther() ); TQStringList::const_iterator end = m_linkedInternal.end(); for ( TQStringList::const_iterator it = m_linkedInternal.begin(); it != end; ++it ) { TQDomElement child = doc.createElement("linked-internal"); node.appendChild(child); child.setAttribute( "url", KURL::relativeURL( baseURL, *it ) ); } end = m_linkedExternal.end(); for ( TQStringList::const_iterator it = m_linkedExternal.begin(); it != end; ++it ) { TQDomElement child = doc.createElement("linked-external"); node.appendChild(child); child.setAttribute( "url", *it ); } return node; } void LinkerOptions::domElementToLinkerOptions( const TQDomElement & element, const KURL & baseURL ) { setHexFormat( stringToHexFormat( element.attribute( "hex-format", TQString() ) ) ); setOutputMapFile( element.attribute( "output-map-file", "0" ).toInt() ); setLibraryDir( element.attribute( "library-dir", TQString() ) ); setLinkerScript( element.attribute( "linker-script", TQString() ) ); setLinkerOther( element.attribute( "other", TQString() ) ); m_linkedInternal.clear(); m_linkedExternal.clear(); TQDomNode node = element.firstChild(); while ( !node.isNull() ) { TQDomElement childElement = node.toElement(); if ( !childElement.isNull() ) { const TQString tagName = childElement.tagName(); if ( tagName == "linked-internal" ) m_linkedInternal << KURL( baseURL, childElement.attribute( "url", TQString() ) ).url(); else if ( tagName == "linked-external" ) m_linkedExternal << childElement.attribute( "url", TQString() ); else kdError() << k_funcinfo << "Unrecognised element tag name: "<deleteLater(); m_children.clear(); delete m_pILVItem; m_pILVItem = 0l; } void ProjectItem::setILVItem( ILVItem * ilvItem ) { m_pILVItem = ilvItem; ilvItem->setOpen(true); ilvItem->setText( 0, name() ); ilvItem->setProjectItem(this); updateILVItemPixmap(); } void ProjectItem::updateILVItemPixmap() { if ( !m_pILVItem ) return; switch ( type() ) { case ProjectType: { // ?! - We shouldn't have an ilvitem for this. break; } case ProgramType: { TQPixmap pm; pm.load( locate( "appdata", "icons/project_program.png" ) ); m_pILVItem->setPixmap( 0, pm ); break; } case LibraryType: { TQPixmap pm; pm.load( locate( "appdata", "icons/project_library.png" ) ); m_pILVItem->setPixmap( 0, pm ); break; } case FileType: { KMimeType::Ptr m = KMimeType::findByPath( url().path() ); m_pILVItem->setPixmap( 0, m->pixmap( TDEIcon::Small ) ); break; } } } void ProjectItem::addChild( ProjectItem * child ) { if ( !child || m_children.contains(child) ) return; m_children << child; child->setILVItem( m_pILVItem ? new ILVItem( m_pILVItem, child->name() ) : new ILVItem( m_pProjectManager, name() ) ); updateControlChildMicroIDs(); } void ProjectItem::updateControlChildMicroIDs() { bool control = false; switch ( type() ) { case ProjectItem::ProjectType: case ProjectItem::LibraryType: case ProjectItem::ProgramType: control = !microID().isEmpty(); break; case ProjectItem::FileType: control = true; break; } m_children.remove( (ProjectItem*)0l ); ProjectItemList::iterator end = m_children.end(); for ( ProjectItemList::iterator it = m_children.begin(); it != end; ++it ) (*it)->setUseParentMicroID( control ); } void ProjectItem::setName( const TQString & name ) { m_name = name; if (m_pILVItem) m_pILVItem->setText( 0, name ); } void ProjectItem::setURL( const KURL & url ) { m_url = url; if ( m_name.isEmpty() ) setName( url.fileName() ); if ( type() != FileType ) { // The output url *is* our url setOutputURL(url); } else if ( outputURL().isEmpty() ) { // Try and guess what the output url should be... TQString newExtension; switch ( outputType() ) { case ProgramOutput: newExtension = ".hex"; break; case ObjectOutput: newExtension = ".o"; break; case LibraryOutput: newExtension = ".o"; break; case UnknownOutput: break; } if ( !newExtension.isEmpty() ) { const TQString fileName = url.url(); TQString extension = fileName.right( fileName.length() - fileName.findRev('.') ); setOutputURL( TQString(fileName).replace( extension, newExtension ) ); } } updateILVItemPixmap(); } TQString ProjectItem::microID() const { if ( !m_bUseParentMicroID ) return m_microID; return m_pParent ? m_pParent->microID() : TQString(); } void ProjectItem::setMicroID( const TQString & id ) { ProcessingOptions::setMicroID(id); updateControlChildMicroIDs(); } ProjectItem::OutputType ProjectItem::outputType() const { if ( !m_pParent ) return UnknownOutput; switch ( m_pParent->type() ) { case ProjectItem::ProjectType: { // We're a top level build target, so look at our own type switch ( type() ) { case ProjectItem::ProjectType: kdWarning() << k_funcinfo << "Parent item and this item are both project items" << endl; return UnknownOutput; case ProjectItem::FileType: case ProjectItem::ProgramType: return ProgramOutput; case ProjectItem::LibraryType: return LibraryOutput; } return UnknownOutput; } case ProjectItem::FileType: { kdWarning() << k_funcinfo << "Don't know how to handle parent item being a file" << endl; return UnknownOutput; } case ProjectItem::ProgramType: case ProjectItem::LibraryType: return ObjectOutput; } return UnknownOutput; } bool ProjectItem::build( ProcessOptionsList * pol ) { if ( !pol ) return false; // Check to see that we aren't already in the ProcessOptionstList; ProcessOptionsList::iterator polEnd = pol->end(); for ( ProcessOptionsList::iterator it = pol->begin(); it != polEnd; ++it ) { if ( (*it).targetFile() == outputURL().path() ) return true; } ProjectInfo * projectInfo = ProjectManager::self()->currentProject(); assert(projectInfo); if ( outputURL().isEmpty() ) { KMessageBox::sorry( 0l, i18n("Don't know how to build \"%1\" (output url is empty).").arg(name()) ); return false; } // Build all internal libraries that we depend on TQStringList::iterator send = m_linkedInternal.end(); for ( TQStringList::iterator it = m_linkedInternal.begin(); it != send; ++it ) { ProjectItem * lib = projectInfo->findItem( projectInfo->directory() + *it ); if ( !lib ) { KMessageBox::sorry( 0l, i18n("Don't know how to build \"%1\" (library does not exist in project).").arg(*it) ); return false; } if ( !lib->build(pol) ) return false; } // Build all children m_children.remove( (ProjectItem*)0l ); ProjectItemList::iterator cend = m_children.end(); for ( ProjectItemList::iterator it = m_children.begin(); it != cend; ++it ) { if ( ! (*it)->build(pol) ) return false; } // Now build ourself ProcessOptions po; po.b_addToProject = false; po.setTargetFile( outputURL().path() ); po.m_picID = microID(); ProcessOptions::ProcessPath::MediaType typeTo; switch ( outputType() ) { case UnknownOutput: KMessageBox::sorry( 0l, i18n("Don't know how to build \"%1\" (unknown output type).").arg(name()) ); return false; case ProgramOutput: typeTo = ProcessOptions::ProcessPath::Program; break; case ObjectOutput: typeTo = ProcessOptions::ProcessPath::Object; break; case LibraryOutput: typeTo = ProcessOptions::ProcessPath::Library; break; } switch ( type() ) { case ProjectType: // Nothing to do return true; case FileType: po.setInputFiles( url().path() ); po.setProcessPath( ProcessOptions::ProcessPath::path( ProcessOptions::guessMediaType( url().url() ), typeTo ) ); break; case ProgramType: case LibraryType: // Build up a list of input urls TQStringList inputFiles; // Link child objects m_children.remove( (ProjectItem*)0l ); ProjectItemList::iterator cend = m_children.end(); for ( ProjectItemList::iterator it = m_children.begin(); it != cend; ++it ) inputFiles << (*it)->outputURL().path(); po.setInputFiles(inputFiles); po.setProcessPath( ProcessOptions::ProcessPath::path( ProcessOptions::ProcessPath::Object, typeTo ) ); break; } po.m_hexFormat = hexFormatToString( hexFormat() ); po.m_bOutputMapFile = outputMapFile(); po.m_libraryDir = libraryDir(); po.m_linkerScript = linkerScript(); po.m_linkOther = linkerOther(); // Link against libraries TQStringList::iterator lend = m_linkedInternal.end(); for ( TQStringList::iterator it = m_linkedInternal.begin(); it != lend; ++it ) po.m_linkLibraries += projectInfo->directory() + *it; lend = m_linkedExternal.end(); for ( TQStringList::iterator it = m_linkedExternal.begin(); it != lend; ++it ) po.m_linkLibraries += *it; // Save our working file (if open) and append to the build list Document * currentDoc = DocManager::self()->findDocument( url() ); if (currentDoc) currentDoc->fileSave(); pol->append(po); return true; } void ProjectItem::upload( ProcessOptionsList * pol ) { build( pol ); ProgrammerDlg * dlg = new ProgrammerDlg( microID(), (TQWidget*)p_ktechlab, "Programmer Dlg" ); dlg->exec(); if ( !dlg->isAccepted() ) { dlg->deleteLater(); return; } ProcessOptions po; dlg->initOptions( & po ); po.b_addToProject = false; po.setInputFiles( outputURL().path() ); po.setProcessPath( ProcessOptions::ProcessPath::Program_PIC ); pol->append( po ); dlg->deleteLater(); } TQDomElement ProjectItem::toDomElement( TQDomDocument & doc, const KURL & baseURL ) const { TQDomElement node = doc.createElement("item"); node.setAttribute( "type", typeToString() ); node.setAttribute( "name", m_name ); node.setAttribute( "url", KURL::relativeURL( baseURL, m_url.url() ) ); node.appendChild( LinkerOptions::toDomElement( doc, baseURL ) ); node.appendChild( ProcessingOptions::toDomElement( doc, baseURL ) ); ProjectItemList::const_iterator end = m_children.end(); for ( ProjectItemList::const_iterator it = m_children.begin(); it != end; ++it ) { if (*it) node.appendChild( (*it)->toDomElement( doc, baseURL ) ); } return node; } KURL::List ProjectItem::childOutputURLs( unsigned types, unsigned outputTypes ) const { KURL::List urls; ProjectItemList::const_iterator end = m_children.end(); for ( ProjectItemList::const_iterator it = m_children.begin(); it != end; ++it ) { if (!*it) continue; if ( ((*it)->type() & types) && ((*it)->outputType() & outputTypes) ) urls += (*it)->outputURL().prettyURL(); urls += (*it)->childOutputURLs(types); } return urls; } ProjectItem * ProjectItem::findItem( const KURL & url ) { if ( this->url() == url ) return this; ProjectItemList::const_iterator end = m_children.end(); for ( ProjectItemList::const_iterator it = m_children.begin(); it != end; ++it ) { if (!*it) continue; ProjectItem * found = (*it)->findItem(url); if (found) return found; } return 0l; } bool ProjectItem::closeOpenFiles() { Document * doc = DocManager::self()->findDocument(m_url); if ( doc && !doc->fileClose() ) return false; m_children.remove( (ProjectItem*)0l ); ProjectItemList::iterator end = m_children.end(); for ( ProjectItemList::iterator it = m_children.begin(); it != end; ++it ) { if ( !(*it)->closeOpenFiles() ) return false; } return true; } void ProjectItem::addFiles() { KURL::List urls = p_ktechlab->getFileURLs(); const KURL::List::iterator end = urls.end(); for ( KURL::List::iterator it = urls.begin(); it != end; ++ it) addFile(*it); } void ProjectItem::addCurrentFile() { Document *document = DocManager::self()->getFocusedDocument(); if (!document) return; // If the file isn't saved yet, we must do that // before it is added to the project. if( document->url().isEmpty() ) { document->fileSaveAs(); // If the user pressed cancel then just give up, // otherwise the file can now be added. } if( !document->url().isEmpty() ) addFile( document->url() ); } void ProjectItem::addFile( const KURL & url ) { if ( url.isEmpty() ) return; m_children.remove( (ProjectItem*)0l ); ProjectItemList::iterator end = m_children.end(); for ( ProjectItemList::iterator it = m_children.begin(); it != end; ++it ) { if ( (*it)->type() == FileType && (*it)->url() == url ) return; } ProjectItem * item = new ProjectItem( this, FileType, m_pProjectManager, p_ktechlab ); item->setURL(url); addChild(item); } TQString ProjectItem::typeToString() const { switch (m_type) { case ProjectType: return "Project"; case FileType: return "File"; case ProgramType: return "Program"; case LibraryType: return "Library"; } return TQString(); } ProjectItem::Type ProjectItem::stringToType( const TQString & type ) { if ( type == "Project" ) return ProjectType; if ( type == "File" ) return FileType; if ( type == "Program" ) return ProgramType; if ( type == "Library" ) return LibraryType; return FileType; } void ProjectItem::domElementToItem( const TQDomElement & element, const KURL & baseURL ) { Type type = stringToType( element.attribute( "type", TQString() ) ); TQString name = element.attribute( "name", TQString() ); KURL url( baseURL, element.attribute( "url", TQString() ) ); ProjectItem * createdItem = new ProjectItem( this, type, m_pProjectManager, p_ktechlab ); createdItem->setName( name ); createdItem->setURL( url ); addChild( createdItem ); TQDomNode node = element.firstChild(); while ( !node.isNull() ) { TQDomElement childElement = node.toElement(); if ( !childElement.isNull() ) { const TQString tagName = childElement.tagName(); if ( tagName == "linker" ) createdItem->domElementToLinkerOptions( childElement, baseURL ); else if ( tagName == "processing" ) createdItem->domElementToProcessingOptions( childElement, baseURL ); else if ( tagName == "item" ) createdItem->domElementToItem( childElement, baseURL ); else kdError() << k_funcinfo << "Unrecognised element tag name: "<toDomElement( doc, m_url ) ); TQTextStream stream(&file); stream << doc.toString(); file.close(); (static_cast(p_ktechlab->action("project_open_recent")))->addURL(m_url); return true; } bool ProjectInfo::saveAndClose() { if (!save()) return false; if (!closeOpenFiles()) return false; return true; } //END class ProjectInfo //BEGIN class ProjectManager ProjectManager * ProjectManager::m_pSelf = 0l; ProjectManager * ProjectManager::self( KTechlab * ktl, KateMDI::ToolView * parent ) { if ( !m_pSelf ) { assert(ktl); assert(parent); m_pSelf = new ProjectManager( ktl, parent ); } return m_pSelf; } ProjectManager::ProjectManager( KTechlab * ktl, KateMDI::ToolView * parent ) : ItemSelector( parent, "Project Manager" ), m_pCurrentProject(0l), p_ktechlab(ktl) { TQWhatsThis::add( this, 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") ); setListCaption( i18n("File") ); setCaption( i18n("Project Manager") ); connect( this, TQT_SIGNAL(clicked(TQListViewItem*)), this, TQT_SLOT(slotItemClicked(TQListViewItem*)) ); } ProjectManager::~ProjectManager() { } void ProjectManager::slotNewProject() { if ( !slotCloseProject() ) return; NewProjectDlg *newProjectDlg = new NewProjectDlg(this); newProjectDlg->exec(); if ( newProjectDlg->accepted() ) { m_pCurrentProject = new ProjectInfo( this, p_ktechlab ); m_pCurrentProject->setName( newProjectDlg->projectName() ); m_pCurrentProject->setURL( newProjectDlg->location() + m_pCurrentProject->name().lower() + ".ktechlab" ); TQDir dir; if ( !dir.mkdir( m_pCurrentProject->directory() ) ) kdDebug() << "Error in creating directory " << m_pCurrentProject->directory() << endl; m_pCurrentProject->save(); updateActions(); emit projectCreated(); } delete newProjectDlg; } void ProjectManager::slotProjectOptions() { } void ProjectManager::slotOpenProject() { KURL url = KFileDialog::getOpenURL(TQString(), "*.ktechlab|KTechlab Project(*.ktechlab)\n*|All Files", this, i18n("Open Location")); if ( url.isEmpty() ) return; slotOpenProject(url); } void ProjectManager::slotOpenProject( const KURL & url ) { if ( m_pCurrentProject && m_pCurrentProject->url() == url ) return; if ( !slotCloseProject() ) return; m_pCurrentProject = new ProjectInfo( this, p_ktechlab ); if ( !m_pCurrentProject->open(url) ) { m_pCurrentProject->deleteLater(); m_pCurrentProject = 0l; return; } RecentFilesAction * rfa = static_cast(p_ktechlab->action("project_open_recent")); rfa->addURL( m_pCurrentProject->url() ); if ( KTLConfig::raiseItemSelectors() ) p_ktechlab->showToolView( p_ktechlab->toolView( toolViewIdentifier() ) ); updateActions(); emit projectOpened(); } bool ProjectManager::slotCloseProject() { if ( !m_pCurrentProject ) return true; if ( !m_pCurrentProject->saveAndClose() ) return false; m_pCurrentProject->deleteLater(); m_pCurrentProject = 0l; updateActions(); emit projectClosed(); return true; } void ProjectManager::slotCreateSubproject() { if ( !currentProject() ) return; CreateSubprojectDlg * dlg = new CreateSubprojectDlg(this); dlg->exec(); if ( dlg->accepted() ) { ProjectItem::Type type = ProjectItem::ProgramType; switch ( dlg->type() ) { case CreateSubprojectDlg::ProgramType: type = ProjectItem::ProgramType; break; case CreateSubprojectDlg::LibraryType: type = ProjectItem::LibraryType; break; } ProjectItem * subproject = new ProjectItem( currentProject(), type, this, p_ktechlab ); subproject->setURL( dlg->targetFile() ); currentProject()->addChild(subproject); currentProject()->save(); emit subprojectCreated(); } delete dlg; } void ProjectManager::updateActions() { bool projectIsOpen = m_pCurrentProject; p_ktechlab->action("project_create_subproject")->setEnabled( projectIsOpen ); p_ktechlab->action("project_export_makefile")->setEnabled( projectIsOpen ); p_ktechlab->action("subproject_add_existing_file")->setEnabled( projectIsOpen ); p_ktechlab->action("subproject_add_current_file")->setEnabled( projectIsOpen ); // p_ktechlab->action("project_options")->setEnabled( projectIsOpen ); p_ktechlab->action("project_close")->setEnabled( projectIsOpen ); p_ktechlab->action("project_add_existing_file")->setEnabled( projectIsOpen ); p_ktechlab->action("project_add_current_file")->setEnabled( projectIsOpen ); } void ProjectManager::slotAddFile() { if ( !currentProject() ) return; currentProject()->addFiles(); emit filesAdded(); } void ProjectManager::slotAddCurrentFile() { if ( !currentProject() ) return; currentProject()->addCurrentFile(); emit filesAdded(); } void ProjectManager::slotSubprojectAddExistingFile() { ILVItem * currentItem = dynamic_cast(selectedItem()); if ( !currentItem || !currentItem->projectItem() ) return; currentItem->projectItem()->addFiles(); emit filesAdded(); } void ProjectManager::slotSubprojectAddCurrentFile() { ILVItem * currentItem = dynamic_cast(selectedItem()); if ( !currentItem || !currentItem->projectItem() ) return; currentItem->projectItem()->addCurrentFile(); emit filesAdded(); } void ProjectManager::slotItemBuild() { ILVItem * currentItem = dynamic_cast(selectedItem()); if ( !currentItem || !currentItem->projectItem() ) return; ProcessOptionsList pol; currentItem->projectItem()->build(&pol); LanguageManager::self()->compile(pol); } void ProjectManager::slotItemUpload() { ILVItem * currentItem = dynamic_cast(selectedItem()); if ( !currentItem || !currentItem->projectItem() ) return; ProcessOptionsList pol; currentItem->projectItem()->upload(&pol); LanguageManager::self()->compile(pol); } void ProjectManager::slotRemoveSelected() { ILVItem *currentItem = dynamic_cast(selectedItem()); if ( !currentItem ) return; int choice = KMessageBox::questionYesNo( this, i18n("Do you really want to remove \"%1\"?").arg( currentItem->text(0) ), i18n("Remove Project File?"), KGuiItem(i18n("Remove")), KGuiItem(i18n("Cancel")) ); if ( choice == KMessageBox::No ) return; currentItem->projectItem()->deleteLater(); emit filesRemoved(); } void ProjectManager::slotExportToMakefile() { } void ProjectManager::slotSubprojectLinkerOptions() { ILVItem * currentItem = dynamic_cast(selectedItem()); if ( !currentItem || !currentItem->projectItem() ) return; LinkerOptionsDlg * dlg = new LinkerOptionsDlg( currentItem->projectItem(), this ); dlg->exec(); currentProject()->save(); // The dialog sets the options for us if it was accepted, so we don't need to do anything delete dlg; } void ProjectManager::slotItemProcessingOptions() { ILVItem * currentItem = dynamic_cast(selectedItem()); if ( !currentItem || !currentItem->projectItem() ) return; ProcessingOptionsDlg * dlg = new ProcessingOptionsDlg( currentItem->projectItem(), this ); dlg->exec(); currentProject()->save(); // The dialog sets the options for us if it was accepted, so we don't need to do anything delete dlg; } void ProjectManager::slotItemClicked( TQListViewItem * item ) { ILVItem * ilvItem = dynamic_cast(item); if ( !ilvItem ) return; ProjectItem * projectItem = ilvItem->projectItem(); if ( !projectItem || projectItem->type() != ProjectItem::FileType ) return; DocManager::self()->openURL( projectItem->url() ); } void ProjectManager::slotContextMenuRequested( TQListViewItem * item, const TQPoint& pos, int /*col*/ ) { TQString popupName; ILVItem * ilvItem = dynamic_cast(item); TDEAction * linkerOptionsAct = p_ktechlab->action("project_item_linker_options"); linkerOptionsAct->setEnabled(false); if ( !m_pCurrentProject ) popupName = "project_none_popup"; else if ( !ilvItem ) popupName = "project_blank_popup"; else { ProcessOptions::ProcessPath::MediaType mediaType = ProcessOptions::guessMediaType( ilvItem->projectItem()->url().url() ); switch ( ilvItem->projectItem()->type() ) { case ProjectItem::FileType: if ( mediaType == ProcessOptions::ProcessPath::Unknown ) popupName = "project_file_other_popup"; else popupName = "project_file_popup"; break; case ProjectItem::ProgramType: popupName = "project_program_popup"; break; case ProjectItem::LibraryType: popupName = "project_library_popup"; break; case ProjectItem::ProjectType: return; } switch ( ilvItem->projectItem()->outputType() ) { case ProjectItem::ProgramOutput: linkerOptionsAct->setEnabled(true); break; case ProjectItem::ObjectOutput: case ProjectItem::LibraryOutput: case ProjectItem::UnknownOutput: linkerOptionsAct->setEnabled(false); break; } // Only have linking options for SDCC files linkerOptionsAct->setEnabled( mediaType == ProcessOptions::ProcessPath::C ); } bool haveFocusedDocument = DocManager::self()->getFocusedDocument(); p_ktechlab->action("subproject_add_current_file")->setEnabled( haveFocusedDocument ); p_ktechlab->action("project_add_current_file")->setEnabled( haveFocusedDocument ); TQPopupMenu *pop = static_cast(p_ktechlab->factory()->container( popupName, p_ktechlab )); if (pop) pop->popup(pos); } //END class ProjectManager #include "projectmanager.moc"