diff -r 000000000000 -r de9c2efb9d02 MultiProject/MultiProject.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MultiProject/MultiProject.py Mon Dec 28 16:03:33 2009 +0000 @@ -0,0 +1,975 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2008 - 2009 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing the multi project management functionality. +""" + +import os +import sys +import cStringIO + +from PyQt4.QtCore import * +from PyQt4.QtGui import * + +from E4Gui.E4Application import e4App + +from Globals import recentNameMultiProject + +from PropertiesDialog import PropertiesDialog +from AddProjectDialog import AddProjectDialog + +from E4XML.XMLUtilities import make_parser +from E4XML.XMLErrorHandler import XMLErrorHandler, XMLFatalParseError +from E4XML.XMLEntityResolver import XMLEntityResolver + +from E4XML.MultiProjectHandler import MultiProjectHandler +from E4XML.MultiProjectWriter import MultiProjectWriter + +import UI.PixmapCache + +from E4Gui.E4Action import E4Action, createActionGroup + +import Preferences +import Utilities + +class MultiProject(QObject): + """ + Class implementing the project management functionality. + + @signal dirty(int) emitted when the dirty state changes + @signal newMultiProject() emitted after a new multi project was generated + @signal multiProjectOpened() emitted after a multi project file was read + @signal multiProjectClosed() emitted after a multi project was closed + @signal multiProjectPropertiesChanged() emitted after the multi project + properties were changed + @signal showMenu(string, QMenu) emitted when a menu is about to be shown. + The name of the menu and a reference to the menu are given. + @signal projectDataChanged(project data dict) emitted after a project entry + has been changed + @signal projectAdded(project data dict) emitted after a project entry + has been added + @signal projectRemoved(project data dict) emitted after a project entry + has been removed + @signal projectOpened(filename) emitted after the project has been opened + """ + def __init__(self, project, parent = None, filename = None): + """ + Constructor + + @param project reference to the project object (Project.Project) + @param parent parent widget (usually the ui object) (QWidget) + @param filename optional filename of a multi project file to open (string) + """ + QObject.__init__(self, parent) + + self.ui = parent + self.projectObject = project + + self.__initData() + + self.recent = [] + self.__loadRecent() + + if filename is not None: + self.openMultiProject(filename) + + def __initData(self): + """ + Private method to initialize the multi project data part. + """ + self.loaded = False # flag for the loaded status + self.dirty = False # dirty flag + self.pfile = "" # name of the multi project file + self.ppath = "" # name of the multi project directory + self.description = "" # description of the multi project + self.name = "" + self.opened = False + self.projects = [] # list of project info; each info entry is a dictionary + # 'name' : Name of the project + # 'file' : project filename + # 'master' : flag indicating the master project + # 'description' : description of the project + + def __loadRecent(self): + """ + Private method to load the recently opened multi project filenames. + """ + self.recent = [] + Preferences.Prefs.rsettings.sync() + rp = Preferences.Prefs.rsettings.value(recentNameMultiProject) + if rp.isValid(): + for f in rp.toStringList(): + if QFileInfo(f).exists(): + self.recent.append(f) + + def __saveRecent(self): + """ + Private method to save the list of recently opened filenames. + """ + Preferences.Prefs.rsettings.setValue(recentNameMultiProject, + QVariant(self.recent)) + Preferences.Prefs.rsettings.sync() + + def getMostRecent(self): + """ + Public method to get the most recently opened multiproject. + + @return path of the most recently opened multiproject (string) + """ + if len(self.recent): + return self.recent[0] + else: + return None + + def setDirty(self, b): + """ + Public method to set the dirty state. + + It emits the signal dirty(int). + + @param b dirty state (boolean) + """ + self.dirty = b + self.saveAct.setEnabled(b) + self.emit(SIGNAL("dirty"), bool(b)) + + def isDirty(self): + """ + Public method to return the dirty state. + + @return dirty state (boolean) + """ + return self.dirty + + def isOpen(self): + """ + Public method to return the opened state. + + @return open state (boolean) + """ + return self.opened + + def getMultiProjectPath(self): + """ + Public method to get the multi project path. + + @return multi project path (string) + """ + return self.ppath + + def getMultiProjectFile(self): + """ + Public method to get the path of the multi project file. + + @return path of the multi project file (string) + """ + return self.pfile + + def __checkFilesExist(self): + """ + Private method to check, if the files in a list exist. + + The project files are checked for existance in the + filesystem. Non existant projects are removed from the list and the + dirty state of the multi project is changed accordingly. + """ + removelist = [] + for project in self.projects: + if not os.path.exists(project['file']): + removelist.append(project) + + if removelist: + for project in removelist: + self.projects.remove(project) + self.setDirty(True) + + def __readMultiProject(self, fn): + """ + Private method to read in a multi project (.e4m, .e4mz) file. + + @param fn filename of the multi project file to be read (string) + @return flag indicating success + """ + try: + if fn.lower().endswith("e4mz"): + try: + import gzip + except ImportError: + QApplication.restoreOverrideCursor() + QMessageBox.critical(None, + self.trUtf8("Read multiproject file"), + self.trUtf8("""Compressed multiproject files not supported.""" + """ The compression library is missing.""")) + return False + f = gzip.open(fn, "rb") + else: + f = open(fn, "rb") + line = f.readline() + dtdLine = f.readline() + f.close() + except EnvironmentError: + QApplication.restoreOverrideCursor() + QMessageBox.critical(None, + self.trUtf8("Read multiproject file"), + self.trUtf8("<p>The multiproject file <b>{0}</b> could not be read.</p>")\ + .format(fn)) + return False + + self.pfile = os.path.abspath(fn) + self.ppath = os.path.abspath(os.path.dirname(fn)) + + # now read the file + if not line.startswith('<?xml'): + QApplication.restoreOverrideCursor() + QMessageBox.critical(None, + self.trUtf8("Read multiproject file"), + self.trUtf8("<p>The multiproject file <b>{0}</b> has an unsupported" + " format.</p>").format(fn)) + return False + + # insert filename into list of recently opened multi projects + self.__syncRecent() + + res = self.__readXMLMultiProject(fn, dtdLine.startswith("<!DOCTYPE")) + if res: + self.name = os.path.splitext(os.path.basename(fn))[0] + + # check, if the files of the multi project still exist + self.__checkFilesExist() + + return res + + def __readXMLMultiProject(self, fn, validating): + """ + Private method to read the multi project data from an XML file. + + @param fn filename of the multi project file to be read (string) + @param validating flag indicating a validation of the XML file is + requested (boolean) + @return flag indicating success + """ + if fn.lower().endswith("e4mz"): + # work around for a bug in xmlproc + validating = False + + parser = make_parser(validating) + handler = MultiProjectHandler(self) + er = XMLEntityResolver() + eh = XMLErrorHandler() + + parser.setContentHandler(handler) + parser.setEntityResolver(er) + parser.setErrorHandler(eh) + + try: + if fn.lower().endswith("e4mz"): + try: + import gzip + except ImportError: + QApplication.restoreOverrideCursor() + QMessageBox.critical(None, + self.trUtf8("Read multiproject file"), + self.trUtf8("""Compressed multiproject files not supported.""" + """ The compression library is missing.""")) + return False + f = gzip.open(fn, "rb") + else: + f = open(fn, "rb") + try: + try: + parser.parse(f) + except UnicodeEncodeError: + f.seek(0) + buf = cStringIO.StringIO(f.read()) + parser.parse(buf) + finally: + f.close() + except IOError: + QApplication.restoreOverrideCursor() + QMessageBox.critical(None, + self.trUtf8("Read multiproject file"), + self.trUtf8("<p>The multiproject file <b>{0}</b> could not be read.</p>")\ + .format(fn)) + return False + except XMLFatalParseError: + QApplication.restoreOverrideCursor() + QMessageBox.critical(None, + self.trUtf8("Read multiproject file"), + self.trUtf8("<p>The multiproject file <b>{0}</b> has invalid " + "contents.</p>").format(fn)) + eh.showParseMessages() + return False + + QApplication.restoreOverrideCursor() + eh.showParseMessages() + QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) + QApplication.processEvents() + return True + + def __writeMultiProject(self, fn = None): + """ + Private method to save the multi project infos to a multi project file. + + @param fn optional filename of the multi project file to be written. + If fn is None, the filename stored in the multi project object + is used. This is the 'save' action. If fn is given, this filename + is used instead of the one in the multi project object. This is the + 'save as' action. + @return flag indicating success + """ + if fn is None: + fn = self.pfile + + res = self.__writeXMLMultiProject(fn) + + if res: + self.pfile = os.path.abspath(fn) + self.ppath = os.path.abspath(os.path.dirname(fn)) + self.name = os.path.splitext(os.path.basename(fn))[0] + self.setDirty(False) + + # insert filename into list of recently opened projects + self.__syncRecent() + + return res + + def __writeXMLMultiProject(self, fn = None): + """ + Private method to write the multi project data to an XML file. + + @param fn the filename of the multi project file (string) + """ + try: + if fn.lower().endswith("e4mz"): + try: + import gzip + except ImportError: + QMessageBox.critical(None, + self.trUtf8("Save multiproject file"), + self.trUtf8("""Compressed multiproject files not supported.""" + """ The compression library is missing.""")) + return False + f = gzip.open(fn, "wb") + else: + f = open(fn, "wb") + + MultiProjectWriter(self, f, os.path.splitext(os.path.basename(fn))[0])\ + .writeXML() + + f.close() + + except IOError: + QMessageBox.critical(None, + self.trUtf8("Save multiproject file"), + self.trUtf8("<p>The multiproject file <b>{0}</b> could not be " + "written.</p>").format(fn)) + return False + + return True + + def addProject(self, startdir = None): + """ + Public slot used to add files to the project. + + @param startdir start directory for the selection dialog (string) + """ + if startdir is None: + startdir = self.ppath + dlg = AddProjectDialog(self.ui, startdir = startdir) + if dlg.exec_() == QDialog.Accepted: + name, filename, isMaster, description = dlg.getData() + + # step 1: check, if project was already added + for project in self.projects: + if project['file'] == filename: + return + + # step 2: check, if master should be changed + if isMaster: + for project in self.projects: + if project['master']: + project['master'] = False + self.emit(SIGNAL("projectDataChanged"), project) + self.setDirty(True) + break + + # step 3: add the project entry + project = { + 'name' : name, + 'file' : filename, + 'master' : isMaster, + 'description' : description, + } + self.projects.append(project) + self.emit(SIGNAL("projectAdded"), project) + self.setDirty(True) + + def changeProjectProperties(self, pro): + """ + Public method to change the data of a project entry. + + @param pro dictionary with the project data (string) + """ + # step 1: check, if master should be changed + if pro['master']: + for project in self.projects: + if project['master']: + if project['file'] != pro['file']: + project['master'] = False + self.emit(SIGNAL("projectDataChanged"), project) + self.setDirty(True) + break + + # step 2: change the entry + for project in self.projects: + if project['file'] == pro['file']: + # project filename is not changeable via interface + project['name'] = pro['name'] + project['master'] = pro['master'] + project['description'] = pro['description'] + self.emit(SIGNAL("projectDataChanged"), project) + self.setDirty(True) + + def getProjects(self): + """ + Public method to get all project entries. + + @return list of all project entries (list of dictionaries) + """ + return self.projects + + def getProject(self, fn): + """ + Public method to get a reference to a project entry. + + @param fn filename of the project (string) + @return dictionary containing the project data + """ + for project in self.projects: + if project['file'] == fn: + return project + + return None + + def removeProject(self, fn): + """ + Public slot to remove a project from the multi project. + + @param fn filename of the project to be removed from the multi project (string) + """ + for project in self.projects: + if project['file'] == fn: + self.projects.remove(project) + self.emit(SIGNAL("projectRemoved"), project) + self.setDirty(True) + break + + def newMultiProject(self): + """ + Public slot to build a new multi project. + + This method displays the new multi project dialog and initializes + the multi project object with the data entered. + """ + if not self.checkDirty(): + return + + dlg = PropertiesDialog(self, True) + if dlg.exec_() == QDialog.Accepted: + self.closeMultiProject() + dlg.storeData() + self.opened = True + self.setDirty(True) + self.closeAct.setEnabled(True) + self.saveasAct.setEnabled(True) + self.addProjectAct.setEnabled(True) + self.propsAct.setEnabled(True) + self.emit(SIGNAL('newMultiProject')) + + def __showProperties(self): + """ + Private slot to display the properties dialog. + """ + dlg = PropertiesDialog(self, False) + if dlg.exec_() == QDialog.Accepted: + dlg.storeData() + self.setDirty(True) + self.emit(SIGNAL('multiProjectPropertiesChanged')) + + def openMultiProject(self, fn = None, openMaster = True): + """ + Public slot to open a multi project. + + @param fn optional filename of the multi project file to be read (string) + @param openMaster flag indicating, that the master project + should be opened depending on the configuration (boolean) + """ + if not self.checkDirty(): + return + + if fn is None: + fn = QFileDialog.getOpenFileName(\ + self.parent(), + self.trUtf8("Open multiproject"), + "", + self.trUtf8("Multiproject Files (*.e4m *.e4mz)")) + + if fn == "": + fn = None + + QApplication.processEvents() + + if fn is not None: + QApplication.setOverrideCursor(QCursor(Qt.WaitCursor)) + QApplication.processEvents() + self.closeMultiProject() + if self.__readMultiProject(fn): + self.opened = True + QApplication.restoreOverrideCursor() + QApplication.processEvents() + + self.closeAct.setEnabled(True) + self.saveasAct.setEnabled(True) + self.addProjectAct.setEnabled(True) + self.propsAct.setEnabled(True) + + self.emit(SIGNAL('multiProjectOpened')) + + if openMaster and Preferences.getMultiProject("OpenMasterAutomatically"): + self.__openMasterProject(False) + else: + QApplication.restoreOverrideCursor() + + def saveMultiProject(self): + """ + Public slot to save the current multi project. + + @return flag indicating success (boolean) + """ + if self.isDirty(): + if len(self.pfile) > 0: + ok = self.__writeMultiProject() + else: + ok = self.saveMultiProjectAs() + else: + ok = True + return ok + + def saveMultiProjectAs(self): + """ + Public slot to save the current multi project to a different file. + + @return flag indicating success (boolean) + """ + if Preferences.getProject("CompressedProjectFiles"): + defaultFilter = self.trUtf8("Compressed Multiproject Files (*.e4mz)") + else: + defaultFilter = self.trUtf8("Multiproject Files (*.e4m)") + fn, selectedFilter = QFileDialog.getSaveFileNameAndFilter( + self.parent(), + self.trUtf8("Save multiproject as"), + self.ppath, + self.trUtf8("Multiproject Files (*.e4m);;" + "Compressed Multiproject Files (*.e4mz)"), + defaultFilter, + QFileDialog.Options(QFileDialog.DontConfirmOverwrite)) + + if fn: + ext = QFileInfo(fn).suffix() + if not ext: + ex = selectedFilter.split("(*")[1].split(")")[0] + if ex: + fn += ex + if QFileInfo(fn).exists(): + res = QMessageBox.warning(None, + self.trUtf8("Save File"), + self.trUtf8("""<p>The file <b>{0}</b> already exists.</p>""") + .format(fn), + QMessageBox.StandardButtons(\ + QMessageBox.Abort | \ + QMessageBox.Save), + QMessageBox.Abort) + if res != QMessageBox.Save: + return False + + self.name = QFileInfo(fn).baseName() + ok = self.__writeMultiProject(fn) + + self.emit(SIGNAL('multiProjectClosed')) + self.emit(SIGNAL('multiProjectOpened')) + return True + else: + return False + + def checkDirty(self): + """ + Public method to check the dirty status and open a message window. + + @return flag indicating whether this operation was successful (boolean) + """ + if self.isDirty(): + res = QMessageBox.warning(self.parent(), + self.trUtf8("Close Multiproject"), + self.trUtf8("The current multiproject has unsaved changes."), + QMessageBox.StandardButtons(\ + QMessageBox.Abort | \ + QMessageBox.Discard | \ + QMessageBox.Save), + QMessageBox.Save) + if res == QMessageBox.Save: + return self.saveMultiProject() + elif res == QMessageBox.Discard: + self.setDirty(False) + return True + elif res == QMessageBox.Abort: + return False + + return True + + def closeMultiProject(self): + """ + Public slot to close the current multi project. + + @return flag indicating success (boolean) + """ + # save the list of recently opened projects + self.__saveRecent() + + if not self.isOpen(): + return True + + if not self.checkDirty(): + return False + + # now close the current project, if it belongs to the multi project + pfile = self.projectObject.getProjectFile() + if pfile: + for project in self.projects: + if project['file'] == pfile: + if not self.projectObject.closeProject(): + return False + break + + self.__initData() + self.closeAct.setEnabled(False) + self.saveasAct.setEnabled(False) + self.saveAct.setEnabled(False) + self.addProjectAct.setEnabled(False) + self.propsAct.setEnabled(False) + + self.emit(SIGNAL('multiProjectClosed')) + + return True + + def initActions(self): + """ + Public slot to initialize the multi project related actions. + """ + self.actions = [] + + self.actGrp1 = createActionGroup(self) + + act = E4Action(self.trUtf8('New multiproject'), + UI.PixmapCache.getIcon("multiProjectNew.png"), + self.trUtf8('&New...'), 0, 0, + self.actGrp1,'multi_project_new') + act.setStatusTip(self.trUtf8('Generate a new multiproject')) + act.setWhatsThis(self.trUtf8( + """<b>New...</b>""" + """<p>This opens a dialog for entering the info for a""" + """ new multiproject.</p>""" + )) + self.connect(act, SIGNAL('triggered()'), self.newMultiProject) + self.actions.append(act) + + act = E4Action(self.trUtf8('Open multiproject'), + UI.PixmapCache.getIcon("multiProjectOpen.png"), + self.trUtf8('&Open...'), 0, 0, + self.actGrp1,'multi_project_open') + act.setStatusTip(self.trUtf8('Open an existing multiproject')) + act.setWhatsThis(self.trUtf8( + """<b>Open...</b>""" + """<p>This opens an existing multiproject.</p>""" + )) + self.connect(act, SIGNAL('triggered()'), self.openMultiProject) + self.actions.append(act) + + self.closeAct = E4Action(self.trUtf8('Close multiproject'), + UI.PixmapCache.getIcon("multiProjectClose.png"), + self.trUtf8('&Close'), 0, 0, self, 'multi_project_close') + self.closeAct.setStatusTip(self.trUtf8('Close the current multiproject')) + self.closeAct.setWhatsThis(self.trUtf8( + """<b>Close</b>""" + """<p>This closes the current multiproject.</p>""" + )) + self.connect(self.closeAct, SIGNAL('triggered()'), self.closeMultiProject) + self.actions.append(self.closeAct) + + self.saveAct = E4Action(self.trUtf8('Save multiproject'), + UI.PixmapCache.getIcon("multiProjectSave.png"), + self.trUtf8('&Save'), 0, 0, self, 'multi_project_save') + self.saveAct.setStatusTip(self.trUtf8('Save the current multiproject')) + self.saveAct.setWhatsThis(self.trUtf8( + """<b>Save</b>""" + """<p>This saves the current multiproject.</p>""" + )) + self.connect(self.saveAct, SIGNAL('triggered()'), self.saveMultiProject) + self.actions.append(self.saveAct) + + self.saveasAct = E4Action(self.trUtf8('Save multiproject as'), + UI.PixmapCache.getIcon("multiProjectSaveAs.png"), + self.trUtf8('Save &as...'), 0, 0, self, 'multi_project_save_as') + self.saveasAct.setStatusTip(self.trUtf8( + 'Save the current multiproject to a new file')) + self.saveasAct.setWhatsThis(self.trUtf8( + """<b>Save as</b>""" + """<p>This saves the current multiproject to a new file.</p>""" + )) + self.connect(self.saveasAct, SIGNAL('triggered()'), self.saveMultiProjectAs) + self.actions.append(self.saveasAct) + + self.addProjectAct = E4Action(self.trUtf8('Add project to multiproject'), + UI.PixmapCache.getIcon("fileProject.png"), + self.trUtf8('Add &project...'), 0, 0, + self,'multi_project_add_project') + self.addProjectAct.setStatusTip(self.trUtf8( + 'Add a project to the current multiproject')) + self.addProjectAct.setWhatsThis(self.trUtf8( + """<b>Add project...</b>""" + """<p>This opens a dialog for adding a project""" + """ to the current multiproject.</p>""" + )) + self.connect(self.addProjectAct, SIGNAL('triggered()'), self.addProject) + self.actions.append(self.addProjectAct) + + self.propsAct = E4Action(self.trUtf8('Multiproject properties'), + UI.PixmapCache.getIcon("multiProjectProps.png"), + self.trUtf8('&Properties...'), 0, 0, self, 'multi_project_properties') + self.propsAct.setStatusTip(self.trUtf8('Show the multiproject properties')) + self.propsAct.setWhatsThis(self.trUtf8( + """<b>Properties...</b>""" + """<p>This shows a dialog to edit the multiproject properties.</p>""" + )) + self.connect(self.propsAct, SIGNAL('triggered()'), self.__showProperties) + self.actions.append(self.propsAct) + + self.closeAct.setEnabled(False) + self.saveAct.setEnabled(False) + self.saveasAct.setEnabled(False) + self.addProjectAct.setEnabled(False) + self.propsAct.setEnabled(False) + + def initMenu(self): + """ + Public slot to initialize the multi project menu. + + @return the menu generated (QMenu) + """ + menu = QMenu(self.trUtf8('&Multiproject'), self.parent()) + self.recentMenu = QMenu(self.trUtf8('Open &Recent Multiprojects'), menu) + + self.__menus = { + "Main" : menu, + "Recent" : self.recentMenu, + } + + # connect the aboutToShow signals + self.connect(self.recentMenu, SIGNAL('aboutToShow()'), + self.__showContextMenuRecent) + self.connect(self.recentMenu, SIGNAL('triggered(QAction *)'), + self.__openRecent) + self.connect(menu, SIGNAL('aboutToShow()'), self.__showMenu) + + # build the main menu + menu.setTearOffEnabled(True) + menu.addActions(self.actGrp1.actions()) + self.menuRecentAct = menu.addMenu(self.recentMenu) + menu.addSeparator() + menu.addAction(self.closeAct) + menu.addSeparator() + menu.addAction(self.saveAct) + menu.addAction(self.saveasAct) + menu.addSeparator() + menu.addAction(self.addProjectAct) + menu.addSeparator() + menu.addAction(self.propsAct) + + self.menu = menu + return menu + + def initToolbar(self, toolbarManager): + """ + Public slot to initialize the multi project toolbar. + + @param toolbarManager reference to a toolbar manager object (E4ToolBarManager) + @return the toolbar generated (QToolBar) + """ + tb = QToolBar(self.trUtf8("Multiproject"), self.parent()) + tb.setIconSize(UI.Config.ToolBarIconSize) + tb.setObjectName("MultiProjectToolbar") + tb.setToolTip(self.trUtf8('Multiproject')) + + tb.addActions(self.actGrp1.actions()) + tb.addAction(self.closeAct) + tb.addSeparator() + tb.addAction(self.saveAct) + tb.addAction(self.saveasAct) + + toolbarManager.addToolBar(tb, tb.windowTitle()) + toolbarManager.addAction(self.addProjectAct, tb.windowTitle()) + toolbarManager.addAction(self.propsAct, tb.windowTitle()) + + return tb + + def __showMenu(self): + """ + Private method to set up the multi project menu. + """ + self.menuRecentAct.setEnabled(len(self.recent) > 0) + + self.emit(SIGNAL("showMenu"), "Main", self.__menus["Main"]) + + def __syncRecent(self): + """ + Private method to synchronize the list of recently opened multi projects + with the central store. + """ + if self.pfile in self.recent: + self.recent.remove(self.pfile) + self.recent.insert(0, self.pfile) + maxRecent = Preferences.getProject("RecentNumber") + if len(self.recent) > maxRecent: + self.recent = self.recent[:maxRecent] + self.__saveRecent() + + def __showContextMenuRecent(self): + """ + Private method to set up the recent multi projects menu. + """ + self.__loadRecent() + + self.recentMenu.clear() + + idx = 1 + for rp in self.recent: + if idx < 10: + formatStr = '&%d. %s' + else: + formatStr = '%d. %s' + act = self.recentMenu.addAction(\ + formatStr % (idx, + Utilities.compactPath(rp, self.ui.maxMenuFilePathLen))) + act.setData(QVariant(rp)) + act.setEnabled(QFileInfo(rp).exists()) + idx += 1 + + self.recentMenu.addSeparator() + self.recentMenu.addAction(self.trUtf8('&Clear'), self.__clearRecent) + + def __openRecent(self, act): + """ + Private method to open a multi project from the list of rencently + opened multi projects. + + @param act reference to the action that triggered (QAction) + """ + file = act.data().toString() + if file: + self.openMultiProject(file) + + def __clearRecent(self): + """ + Private method to clear the recent multi projects menu. + """ + self.recent = [] + + def getActions(self): + """ + Public method to get a list of all actions. + + @return list of all actions (list of E4Action) + """ + return self.actions[:] + + def addE4Actions(self, actions): + """ + Public method to add actions to the list of actions. + + @param actions list of actions (list of E4Action) + """ + self.actions.extend(actions) + + def removeE4Actions(self, actions): + """ + Public method to remove actions from the list of actions. + + @param actions list of actions (list of E4Action) + """ + for act in actions: + try: + self.actions.remove(act) + except ValueError: + pass + + def getMenu(self, menuName): + """ + Public method to get a reference to the main menu or a submenu. + + @param menuName name of the menu (string) + @return reference to the requested menu (QMenu) or None + """ + try: + return self.__menus[menuName] + except KeyError: + return None + + def openProject(self, filename): + """ + Public slot to open a project. + + @param filename filename of the project file (string) + """ + self.projectObject.openProject(filename) + self.emit(SIGNAL('projectOpened'), filename) + + def __openMasterProject(self, reopen = True): + """ + Public slot to open the master project. + + @param reopen flag indicating, that the master project should be + reopened, if it has been opened already (boolean) + """ + for project in self.projects: + if project['master']: + if reopen or \ + not self.projectObject.isOpen() or \ + self.projectObject.getProjectFile() != project['file']: + self.openProject(project['file']) + return + + def getMasterProjectFile(self): + """ + Public method to get the filename of the master project. + + @return name of the master project file (string) + """ + for project in self.projects: + if project['master']: + return project['file'] + + return None + + def getDependantProjectFiles(self): + """ + Public method to get the filenames of the dependant projects. + + @return names of the dependant project files (list of strings) + """ + files = [] + for project in self.projects: + if not project['master']: + files.append(project['file']) + return files