CMS 3D CMS Logo

/data/doxygen/doxygen-1.7.3/gen/CMSSW_4_2_8/src/FWCore/GuiBrowsers/python/Vispa/Main/Application.py

Go to the documentation of this file.
00001 import os
00002 import sys
00003 import string
00004 import commands
00005 import platform
00006 import logging
00007 import logging.handlers
00008 import ConfigParser
00009 import optparse
00010 import webbrowser
00011 import subprocess
00012 
00013 from PyQt4.QtCore import SIGNAL,qVersion,QString,QVariant, Qt
00014 from PyQt4.QtGui import QApplication,QMenu,QPixmap,QAction,QFileDialog,QIcon,QMessageBox
00015 
00016 from Vispa.Main.Directories import logDirectory,pluginDirectory,baseDirectory,homeDirectory,iniFileName,applicationName,docDirectory,websiteUrl
00017 from Vispa.Main.MainWindow import MainWindow
00018 from Vispa.Main.AbstractTab import AbstractTab
00019 from Vispa.Main.Filetype import Filetype
00020 from Vispa.Main.Exceptions import *
00021 from Vispa.Main.AboutDialog import AboutDialog
00022 from Vispa.Main.RotatingIcon import RotatingIcon 
00023 #from PreferencesEditor import PreferencesEditor
00024 import Vispa.__init__
00025 
00026 import Resources
00027 
00028 class Application(QApplication):
00029 
00030     MAX_RECENT_FILES = 30
00031     MAX_VISIBLE_RECENT_FILES = 10
00032     MAX_VISIBLE_UNDO_EVENTS = 10
00033     FAILED_LOADING_PLUGINS_ERROR = "Errors while loading plugins. For details see error output or log file.\n\nThe following plugins won't work correctly:\n\n"
00034     TAB_PREMATURELY_CLOSED_WARNING = "Tab was closed before user request could be handled."
00035     NO_PROCESS_EVENTS = False
00036 
00037     def __init__(self, argv):
00038         QApplication.__init__(self, argv)
00039         self._version = None
00040         self._plugins = []
00041         self._closeAllFlag = False
00042         self._knownFiltersList = []
00043         self._knownExtensionsDictionary = {}
00044         self._pluginMenus = []
00045         self._pluginToolBars = []
00046         self._recentFiles = []
00047         self._ini = None
00048         self._iniFileName = iniFileName
00049         self._zoomToolBar = None
00050         self._undoToolBar = None
00051         self._messageId=0
00052         self._loadablePlugins = {}
00053         self._logFile = None
00054         
00055         self._initLogging()
00056 
00057         logging.debug('Running with Qt-Version ' + str(qVersion()))
00058 
00059         self._initCommandLineAttributes()
00060 
00061         self._loadIni()
00062 
00063         self.setVersion(Vispa.__init__.__version__)
00064 
00065         self._window = MainWindow(self, applicationName)
00066         self._window.show()
00067                 
00068         self._loadPlugins()
00069         
00070         self._fillFileMenu()
00071         self._fillEditMenu()
00072         self._fillHelpMenu()
00073         
00074         self.createUndoToolBar()
00075         self.createZoomToolBar()
00076         self.hidePluginMenus()
00077         self.hidePluginToolBars()
00078         self.createStatusBar()
00079         self.updateMenu()
00080     
00081         self._readCommandLineAttributes()
00082         self._connectSignals()
00083 
00084     def commandLineParser(self):
00085         return self._commandLineParser
00086         
00087     def commandLineOptions(self):
00088         return self._commandLineOptions
00089         
00090     def setVersion(self, version):
00091         self._version = version
00092         
00093     def version(self):
00094         """ Returns version string.
00095         """
00096         return self._version
00097         
00098     def atLeastQtVersion(self, versionString):
00099         """ Returns True if given versionString is newer than current used version of Qt.
00100         """
00101         [majorV, minorV, revisionV] = versionString.split(".")
00102         [majorQ, minorQ, revisionQ] = str(qVersion()).split(".")
00103         if majorV > majorQ:
00104             return True
00105         elif majorV < majorQ:
00106             return False
00107         elif majorV == majorQ:
00108             if minorV > minorQ:
00109                 return True
00110             elif minorV < minorQ:
00111                 return False
00112             elif minorV == minorQ:
00113                 if revisionV > revisionQ:
00114                     return True
00115                 elif revisionV < revisionQ:
00116                     return False
00117                 elif revisionV == revisionQ:
00118                     return True
00119         return False
00120 
00121     def _setCommandLineOptions(self):
00122         """ Set the available command line options.
00123         """
00124         self._commandLineParser.add_option("-f", "--file", dest="filename", help="open a FILE", metavar="FILE")
00125         self._commandLineParser.add_option("-l", "--loglevel", dest="loglevel", help="set LOGLEVEL to 10=DEBUG, 20=INFO, 30=WARNING, 40=ERROR, 50=CRITICAL", metavar="LOGLEVEL", type="int")
00126 
00127     def _initCommandLineAttributes(self):
00128         """ Initialize command line parser.
00129         
00130         After calling this function, plugins may add options.
00131         """
00132         class QuiteOptionParser(optparse.OptionParser):
00133             def __init__(self):
00134                 optparse.OptionParser.__init__(self,add_help_option=False)
00135             def error(self,message=""):
00136                 pass
00137         self._commandLineParser = QuiteOptionParser()
00138         self._setCommandLineOptions()
00139         (self._commandLineOptions, self._args) = self._commandLineParser.parse_args()
00140         if self._commandLineOptions.loglevel:
00141             logging.root.setLevel(self._commandLineOptions.loglevel)
00142         self._commandLineParser = optparse.OptionParser()
00143         self._setCommandLineOptions()
00144         
00145     def _readCommandLineAttributes(self):
00146         """ Analyzes the command line attributes and print usage summary if required.
00147         """
00148         (self._commandLineOptions, self._args) = self._commandLineParser.parse_args()
00149         if self._commandLineOptions.filename:
00150             self.mainWindow().setStartupScreenVisible(False)
00151             self.openFile(self._commandLineOptions.filename)
00152         if len(self._args) > 0:
00153             self.mainWindow().setStartupScreenVisible(False)
00154             self.openFile(self._args[0])
00155         
00156     def _checkFile(self, filename):
00157         """ Check if logfile is closed correctly
00158         """
00159         finished = True
00160         file = open(filename, "r")
00161         for line in file.readlines():
00162             if "INFO Start logging" in line:
00163                 finished = False
00164             if "INFO Stop logging" in line:
00165                 finished = True
00166         return finished
00167 
00168     def _initLogging(self):
00169         """ Add logging handlers for a log file as well as stderr.
00170         """ 
00171         instance = 0
00172         done = False
00173         while not done:
00174             # iterate name of log file for several instances of vispa
00175             instance += 1
00176             logfile = os.path.join(logDirectory, "log" + str(instance) + ".txt")
00177             # do not create more than 10 files
00178             if instance > 10:
00179                 instance = 1
00180                 logfile = os.path.join(logDirectory, "log" + str(instance) + ".txt")
00181                 done = True
00182                 break
00183             if not os.path.exists(logfile):
00184                 done = True
00185                 break
00186             done = self._checkFile(logfile)
00187         
00188         # clean up old logs
00189         nextlogfile = os.path.join(logDirectory, "log" + str(instance + 1) + ".txt")
00190         if os.path.exists(nextlogfile):
00191             if not self._checkFile(nextlogfile):
00192                 file = open(nextlogfile, "a")
00193                 file.write("Cleaning up logfile after abnormal termination: INFO Stop logging\n")
00194 
00195         if os.path.exists(logDirectory):
00196             handler1 = logging.handlers.RotatingFileHandler(logfile, maxBytes=100000, backupCount=1)
00197             formatter1 = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
00198             handler1.setFormatter(formatter1)
00199             self._logFile = logfile
00200 
00201         handler2 = logging.StreamHandler(sys.stderr)
00202         formatter2 = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
00203         handler2.setFormatter(formatter2)
00204         
00205         logging.root.handlers = []
00206         if os.path.exists(logDirectory):
00207             logging.root.addHandler(handler1)
00208         logging.root.addHandler(handler2)
00209         #logging.root.setLevel(logging.INFO)
00210 
00211         self._infologger = logging.getLogger("info")
00212         self._infologger.setLevel(logging.INFO)
00213         self._infologger.handlers = []
00214         if self._logFile:
00215             self._infologger.info("Start logging to " + self._logFile)
00216         
00217     def run(self):
00218         """ Show the MainWindow and run the application.
00219         """
00220         #logging.debug('Application: run()')
00221 
00222         self.exec_()
00223         if self._logFile:
00224             self._infologger.info("Stop logging to " + self._logFile)
00225 
00226     def _connectSignals(self):
00227         """ Connect signal to observe the TabWidget in the MainWindow.
00228         """
00229         logging.debug('Application: _connectSignals()')
00230         self.connect(self._window.tabWidget(), SIGNAL("currentChanged(int)"), self.tabChanged)
00231         self.connect(self._window, SIGNAL("windowActivated()"), self.tabChanged)
00232         self.connect(self._window.tabWidget(), SIGNAL("tabCloseRequested(int)"), self.tabCloseRequest)
00233         
00234     def _loadPlugins(self):
00235         """ Search all subfolders of the plugin directory for vispa plugins and registers them.
00236         """
00237         logging.debug('Application: _loadPlugins()')
00238         dirs = ["Vispa.Plugins." + str(f) for f in os.listdir(pluginDirectory)
00239                 if os.path.isdir(os.path.join(pluginDirectory, f)) and not f.startswith(".") and not f.startswith("CVS")]
00240         failedToLoad = []
00241         for di in dirs:
00242             try:
00243                 module = __import__(di, globals(), locals(), "Vispa.Plugins")
00244                 self._loadablePlugins[module.plugin.__name__] = module.plugin
00245             except ImportError:
00246                 logging.warning('Application: cannot load plugin ' + di + ': ' + exception_traceback())
00247                 failedToLoad.append(di)
00248             except PluginIgnoredException,e:
00249                 logging.info('Application: plugin ' + di + ' cannot be loaded and is ignored: ' + str(e))
00250             except AttributeError,e:
00251                 logging.info('Application: plugin ' + di + ' is deactivated (define plugin in __init__.py to activate): ' + str(e))
00252                 
00253         for pluginName in self._loadablePlugins.keys():
00254             # loop over all loadable plugins
00255             # this mechanism enables plugins to call initializePlugin() for plugins they depend on
00256             if not self.initializePlugin(pluginName):
00257                 failedToLoad.append(pluginName)
00258         
00259         if len(failedToLoad) > 0:
00260             self.errorMessage(self.FAILED_LOADING_PLUGINS_ERROR + "\n".join(failedToLoad))
00261                 
00262         self._collectFileExtensions()
00263         
00264     def initializePlugin(self, name):
00265         if name in [plugin.__class__.__name__ for plugin in self._plugins]:
00266             logging.info("%s: initalizePlugin(): Plugin '%s' already loaded. Aborting..." % (self.__class__.__name__, name))
00267             return True
00268         if not name in self._loadablePlugins.keys():
00269             logging.error("%s: initalizePlugin(): Unknown plugin '%s'. Aborting..." % (self.__class__.__name__, name))
00270             return False
00271         
00272         try:
00273             pluginObject = self._loadablePlugins[name](self)
00274             self._plugins.append(pluginObject)
00275             logging.debug('Application: added plugin ' + name)
00276             return True
00277         except ValueError:
00278             logging.warning('Application: ' + name + ' is not a plugin: ' + exception_traceback())
00279         return False
00280         
00281                     
00282     def plugins(self):
00283         return self._plugins
00284     
00285     def plugin(self, name):
00286         """ Returns plugin with given name or None if there is no such one.
00287         """
00288         if not name.endswith("Plugin"):
00289             name += "Plugin"
00290             
00291         for plugin in self._plugins:
00292             if name == plugin.__class__.__name__:
00293                 return plugin
00294         return None
00295 
00296     def tabControllers(self):
00297         controllers=[self._window.tabWidget().widget(i).controller() for i in range(0, self._window.tabWidget().count())]
00298         controllers+=[tab.controller() for tab in self.mainWindow().tabWidgets()]
00299         return controllers
00300     
00301     def setCurrentTabController(self, controller):
00302         if controller.tab().tabWidget():
00303             self._window.activateWindow()
00304             self._window.tabWidget().setCurrentIndex(self.tabControllers().index(controller))
00305         else:
00306             controller.tab().activateWindow()
00307     
00308     def currentTabController(self):
00309         """ Return the TabController that belongs to the tab selected in the MainWindow.
00310         """
00311         #logging.debug('Application: currentTabController()')
00312         if isinstance(self.activeWindow(),AbstractTab):
00313             return self.activeWindow().controller()
00314         else:
00315             currentWidget = self._window.tabWidget().currentWidget()
00316             if isinstance(currentWidget, AbstractTab):
00317                 return currentWidget.controller()
00318         raise NoCurrentTabControllerException
00319     
00320     def mainWindow(self):
00321         return self._window
00322 
00323     #def editPreferences(self):
00324     #    self.preferencesEditor = PreferencesEditor()
00325     #    self.preferencesEditor.show()
00326 
00327     def createAction(self, name, slot=None, shortcut=None, image=None, enabled=True):
00328         """ create an action with name and icon and connect it to a slot.
00329         """
00330         #logging.debug('Application: createAction() - ' + name)
00331         if image:
00332             image0 = QPixmap()
00333             image0.load(":/resources/" + image + ".svg")
00334             action = QAction(QIcon(image0), name, self._window)
00335         else:
00336             action = QAction(name, self._window)
00337         action.setEnabled(enabled)
00338         if slot:
00339             self.connect(action, SIGNAL("triggered()"), slot)
00340         if shortcut:
00341             if isinstance(shortcut, list):
00342                 action.setShortcuts(shortcut)
00343             else:
00344                 action.setShortcut(shortcut)
00345         return action
00346         
00347     def _fillFileMenu(self):
00348         """Called for the first time this function creates the file menu and fill it.
00349 
00350         The function is written in a way that it recreates the whole menu, if it
00351         is called again later during execution. So it is possible to aad new
00352         plugins and use them  (which means they appear in the menus) without
00353         restarting the program.
00354         """
00355         logging.debug('Application: _fillFileMenu()')
00356         self._fileMenuItems = {}
00357         if not self._window.fileMenu().isEmpty():
00358             self._window.fileMenu().clear()
00359         
00360         # New
00361         newFileActions = []
00362         for plugin in self._plugins:
00363             newFileActions += plugin.getNewFileActions()
00364         
00365         if len(newFileActions) == 1:
00366             newFileActions[0].setShortcut('Ctrl+N')
00367         
00368         self._window.fileMenu().addActions(newFileActions)               
00369         
00370         # Open
00371         openFileAction = self.createAction('&Open File', self.openFileDialog, 'Ctrl+O', "fileopen")
00372         self._window.fileMenu().addAction(openFileAction)
00373         self._window.fileToolBar().addAction(openFileAction)
00374 
00375         # Reload
00376         self._fileMenuItems['reloadFileAction'] = self.createAction('&Reload File', self.reloadFile, ['Ctrl+R', 'F5'], "reload")
00377         self._window.fileMenu().addAction(self._fileMenuItems['reloadFileAction'])
00378         #self._window.fileToolBar().addAction(self._fileMenuItems['reloadFileAction'])
00379         
00380         # Recent files
00381         if not hasattr(self, 'recentFilesMenu'):
00382             self._recentFilesMenu = QMenu('&Recent Files', self._window)
00383             self._recentFilesMenuActions = []
00384             for i in range(0, self.MAX_VISIBLE_RECENT_FILES):
00385                 action = self.createAction("recent file " + str(i), self.openRecentFileSlot)
00386                 action.setVisible(False)
00387                 self._recentFilesMenu.addAction(action)                
00388                 self._recentFilesMenuActions.append(action)
00389             self._recentFilesMenu.addSeparator()
00390             self._fileMenuItems['clearMissingRecentFilesAction'] = self.createAction("Clear missing files", self.clearMissingRecentFiles)
00391             self._recentFilesMenu.addAction(self._fileMenuItems['clearMissingRecentFilesAction'])
00392             self._fileMenuItems['clearRecentFilesAction'] = self.createAction("Clear list", self.clearRecentFiles)
00393             self._recentFilesMenu.addAction(self._fileMenuItems['clearRecentFilesAction'])
00394                 
00395         self._window.fileMenu().addMenu(self._recentFilesMenu)
00396 
00397         self._window.fileMenu().addSeparator()
00398         
00399         # Close
00400         self._fileMenuItems['closeFileAction'] = self.createAction('&Close', self.closeFile, 'Ctrl+W', "closefile")
00401         self._window.fileMenu().addAction(self._fileMenuItems['closeFileAction'])
00402         
00403         # Close all
00404         self._fileMenuItems['closeAllAction'] = self.createAction('Close All', self.closeAllFiles, 'Ctrl+Shift+W', "closefileall")      
00405         self._window.fileMenu().addAction(self._fileMenuItems['closeAllAction'])
00406         
00407         self._window.fileMenu().addSeparator()
00408         
00409         # Save
00410         self._fileMenuItems['saveFileAction'] = self.createAction('&Save', self.saveFile, 'Ctrl+S', "filesave")      
00411         self._window.fileMenu().addAction(self._fileMenuItems['saveFileAction'])
00412         self._window.fileToolBar().addAction(self._fileMenuItems['saveFileAction'])
00413         
00414         # Save as
00415         self._fileMenuItems['saveFileAsAction'] = self.createAction('Save As...', self.saveFileAsDialog, 'Ctrl+Shift+S', image="filesaveas")      
00416         self._window.fileMenu().addAction(self._fileMenuItems['saveFileAsAction'])
00417    
00418         # Save all
00419         self._fileMenuItems['saveAllFilesAction'] = self.createAction('Save &All', self.saveAllFiles, "Ctrl+Alt+S", "filesaveall")      
00420         self._window.fileMenu().addAction(self._fileMenuItems['saveAllFilesAction'])
00421        
00422         self._window.fileMenu().addSeparator()
00423         
00424         #editPreferencesAction = self.createAction('Preferences',self.editPreferences)
00425         #self._window.fileMenu().addAction(editPreferencesAction)
00426         # Exit
00427         exit = self.createAction('&Exit', self.exit, "Ctrl+Q", "exit")      
00428         self._window.fileMenu().addAction(exit)
00429 
00430     def _fillEditMenu(self):
00431         """Called for the first time this function creates the edit menu and fills it.
00432         
00433         The function is written in a way that it recreates the whole menu, if it
00434         is called again later during execution. So it is possible to aad new
00435         plugins and use them  (which means they appear in the menus) without
00436         restarting the program.
00437         """
00438         logging.debug('Application: _fillEditMenu()')
00439         self._editMenuItems = {}
00440         if not self._window.editMenu().isEmpty():
00441             self._window.editMenu().clear()
00442             
00443         # Undo / Redo
00444         self._editMenuItems["undoAction"] = self.createAction("Undo", self.undoEvent, "Ctrl+Z", "edit-undo")
00445         self._editMenuItems["redoAction"] = self.createAction("Redo", self.redoEvent, "Ctrl+Y", "edit-redo")
00446         self._editMenuItems["undoAction"].setData(QVariant(1))
00447         self._editMenuItems["redoAction"].setData(QVariant(1))
00448         self._editMenuItems["undoAction"].setEnabled(False)
00449         self._editMenuItems["redoAction"].setEnabled(False)
00450         self._window.editMenu().addAction(self._editMenuItems["undoAction"])
00451         self._window.editMenu().addAction(self._editMenuItems["redoAction"])
00452         #self._editMenuItems["undoAction"].menu().addAction(self.createAction("test"))
00453         #self._editMenuItems["undoAction"].menu().setEnabled(False)
00454         #self._editMenuItems["undoAction"].menu().setVisible(False)
00455         
00456         self._undoActionsMenu = QMenu(self._window)
00457         self._undoMenuActions = []
00458         for i in range(0, self.MAX_VISIBLE_UNDO_EVENTS):
00459             action = self.createAction("undo " + str(i), self.undoEvent)
00460             action.setVisible(False)
00461             self._undoActionsMenu.addAction(action)                
00462             self._undoMenuActions.append(action)
00463 
00464         self._redoActionsMenu  = QMenu(self._window)
00465         self._redoMenuActions = []
00466         for i in range(0, self.MAX_VISIBLE_UNDO_EVENTS):
00467             action = self.createAction("redo " + str(i), self.redoEvent)
00468             action.setVisible(False)
00469             self._redoActionsMenu.addAction(action)                
00470             self._redoMenuActions.append(action)
00471                     
00472         # Cut
00473         self._editMenuItems['cutAction'] = self.createAction('&Cut', self.cutEvent, 'Ctrl+X', 'editcut')      
00474         self._window.editMenu().addAction(self._editMenuItems['cutAction'])
00475         self._editMenuItems['cutAction'].setEnabled(False)
00476 
00477         # Copy
00478         self._editMenuItems['copyAction'] = self.createAction('C&opy', self.copyEvent, 'Ctrl+C', 'editcopy')      
00479         self._window.editMenu().addAction(self._editMenuItems['copyAction'])
00480         self._editMenuItems['copyAction'].setEnabled(False)
00481         
00482         # Paste
00483         self._editMenuItems['pasteAction'] = self.createAction('&Paste', self.pasteEvent, 'Ctrl+V', 'editpaste')      
00484         self._window.editMenu().addAction(self._editMenuItems['pasteAction'])
00485         self._editMenuItems['pasteAction'].setEnabled(False)
00486         
00487         # Select all
00488         self._editMenuItems['selectAllAction'] = self.createAction("Select &all", self.selectAllEvent, "Ctrl+A", "selectall")
00489         self._window.editMenu().addAction(self._editMenuItems['selectAllAction'])
00490         self._editMenuItems['selectAllAction'].setVisible(False)
00491 
00492         self._window.editMenu().addSeparator()
00493         
00494         # Find
00495         self._editMenuItems['findAction'] = self.createAction('&Find', self.findEvent, 'Ctrl+F', "edit-find")      
00496         self._window.editMenu().addAction(self._editMenuItems['findAction'])
00497         self._editMenuItems['findAction'].setEnabled(False)
00498 
00499     def _fillHelpMenu(self):
00500         logging.debug('Application: _fillHelpMenu()')
00501         self._helpMenuItems = {}
00502                     
00503         # About
00504         self._helpMenuItems['aboutAction'] = self.createAction('&About', self.aboutBoxSlot, 'F1')      
00505         self._window.helpMenu().addAction(self._helpMenuItems['aboutAction'])
00506         
00507         # open log file
00508         if self._logFile:
00509             self._helpMenuItems['openLogFile'] = self.createAction("Open log file", self.openLogFileSlot)      
00510             self._window.helpMenu().addAction(self._helpMenuItems['openLogFile'])
00511         
00512         # Offline Documentation
00513         if os.path.exists(os.path.join(docDirectory,"index.html")):
00514             self._window.helpMenu().addAction(self.createAction('Offline Documentation', self._openDocumentation, "CTRL+F1"))
00515         
00516         # Vispa Website
00517         self._window.helpMenu().addAction(self.createAction('Website', self._openWebsite, "Shift+F1"))
00518         
00519     def updateMenu(self):
00520         """ Update recent files and enable disable menu entries in file and edit menu.
00521         """
00522         #logging.debug('Application: updateMenu()')
00523         if self.mainWindow().startupScreen():
00524             self.updateStartupScreen()
00525         # Recent files
00526         num_recent_files = min(len(self._recentFiles), self.MAX_VISIBLE_RECENT_FILES)
00527         for i in range(0, num_recent_files):
00528             filename = self._recentFiles[i]
00529             self._recentFilesMenuActions[i].setText(os.path.basename(filename))
00530             self._recentFilesMenuActions[i].setToolTip(filename)
00531             self._recentFilesMenuActions[i].setStatusTip(filename)
00532             self._recentFilesMenuActions[i].setData(QVariant(filename))
00533             self._recentFilesMenuActions[i].setVisible(True)
00534             
00535         for i in range(num_recent_files, self.MAX_VISIBLE_RECENT_FILES):
00536             self._recentFilesMenuActions[i].setVisible(False)
00537             
00538         if num_recent_files == 0:
00539             self._fileMenuItems['clearRecentFilesAction'].setEnabled(False)
00540             self._fileMenuItems['clearMissingRecentFilesAction'].setEnabled(False)
00541         else:
00542             self._fileMenuItems['clearRecentFilesAction'].setEnabled(True)
00543             self._fileMenuItems['clearMissingRecentFilesAction'].setEnabled(True)
00544             
00545         # Enabled / disable menu entries depending on number of open files
00546         at_least_one_flag = False
00547         at_least_two_flag = False
00548         if len(self.tabControllers()) > 1:
00549             at_least_one_flag = True
00550             at_least_two_flag = True
00551         elif len(self.tabControllers()) > 0:
00552             at_least_one_flag = True
00553         
00554         self._fileMenuItems['saveFileAction'].setEnabled(at_least_one_flag)
00555         self._fileMenuItems['saveFileAsAction'].setEnabled(at_least_one_flag)
00556         self._fileMenuItems['reloadFileAction'].setEnabled(at_least_one_flag)
00557         self._fileMenuItems['closeFileAction'].setEnabled(at_least_one_flag)
00558         
00559         self._fileMenuItems['saveAllFilesAction'].setEnabled(at_least_two_flag)
00560         self._fileMenuItems['closeAllAction'].setEnabled(at_least_two_flag)
00561         
00562         try:
00563             if at_least_one_flag:
00564                 if not self.currentTabController().isEditable():
00565                     self._fileMenuItems['saveFileAction'].setEnabled(False)
00566                     self._fileMenuItems['saveFileAsAction'].setEnabled(False)
00567                 if not self.currentTabController().isModified():
00568                     self._fileMenuItems['saveFileAction'].setEnabled(False)
00569                 
00570             # Copy / Cut / Paste
00571             copy_paste_enabled_flag = at_least_one_flag and self.currentTabController().isCopyPasteEnabled()
00572             self._editMenuItems['cutAction'].setEnabled(copy_paste_enabled_flag)
00573             self._editMenuItems['copyAction'].setEnabled(copy_paste_enabled_flag)
00574             self._editMenuItems['pasteAction'].setEnabled(copy_paste_enabled_flag)
00575             
00576             self._editMenuItems['selectAllAction'].setVisible(self.currentTabController().allowSelectAll())
00577             
00578             self._editMenuItems['findAction'].setEnabled(at_least_one_flag and self.currentTabController().isFindEnabled())
00579             
00580             # Undo / Redo
00581             undo_supported_flag = at_least_one_flag and self.currentTabController().supportsUndo()
00582             self._editMenuItems["undoAction"].setEnabled(undo_supported_flag)
00583             self._editMenuItems["undoAction"].setVisible(undo_supported_flag)
00584             self._editMenuItems["redoAction"].setEnabled(undo_supported_flag)
00585             self._editMenuItems["redoAction"].setVisible(undo_supported_flag)
00586             self.showPluginToolBar(self._undoToolBar, undo_supported_flag)
00587             
00588             if undo_supported_flag:
00589                 undo_events = self.currentTabController().undoEvents()
00590                 num_undo_events = min(len(undo_events), self.MAX_VISIBLE_UNDO_EVENTS)
00591                 self._editMenuItems["undoAction"].setEnabled(num_undo_events > 0)
00592                 if num_undo_events > 1:
00593                     self._editMenuItems["undoAction"].setMenu(self._undoActionsMenu)
00594                 else:
00595                     self._editMenuItems["undoAction"].setMenu(None)
00596                 for i in range(0, num_undo_events):
00597                     undo_event = undo_events[num_undo_events - i - 1]   # iterate backwards
00598                     self._undoMenuActions[i].setText(undo_event.LABEL)
00599                     self._undoMenuActions[i].setToolTip(undo_event.description())
00600                     self._undoMenuActions[i].setStatusTip(undo_event.description())
00601                     self._undoMenuActions[i].setData(QVariant(i+1))
00602                     self._undoMenuActions[i].setVisible(True)
00603                 for i in range(num_undo_events, self.MAX_VISIBLE_UNDO_EVENTS):
00604                     self._undoMenuActions[i].setVisible(False)
00605                 
00606                 redo_events = self.currentTabController().redoEvents()
00607                 num_redo_events = min(len(redo_events), self.MAX_VISIBLE_UNDO_EVENTS)
00608                 self._editMenuItems["redoAction"].setEnabled(num_redo_events > 0)
00609                 if num_redo_events > 1:
00610                     self._editMenuItems["redoAction"].setMenu(self._redoActionsMenu)
00611                 else:
00612                     self._editMenuItems["redoAction"].setMenu(None)
00613                 for i in range(0, num_redo_events):
00614                     redo_event = redo_events[num_redo_events - i - 1]   # iterate backwards
00615                     self._redoMenuActions[i].setText(redo_event.LABEL)
00616                     self._redoMenuActions[i].setToolTip(redo_event.description())
00617                     self._redoMenuActions[i].setStatusTip(redo_event.description())
00618                     self._redoMenuActions[i].setData(QVariant(i+1))
00619                     self._redoMenuActions[i].setVisible(True)
00620                 for i in range(num_redo_events, self.MAX_VISIBLE_UNDO_EVENTS):
00621                     self._redoMenuActions[i].setVisible(False)
00622             
00623         except NoCurrentTabControllerException:
00624             pass
00625             
00626     def _openDocumentation(self):
00627         """ Opens Vispa Offline Documentation
00628         """
00629         webbrowser.open(os.path.join(docDirectory,"index.html"), 2, True)
00630         
00631     def _openWebsite(self):
00632         """ Open new browser tab and opens Vispa Project Website.
00633         """
00634         webbrowser.open(websiteUrl, 2, True)
00635         
00636     def createPluginMenu(self, name):
00637         """ Creates menu in main window's menu bar before help menu and adds it to _pluginMenus list.
00638         """
00639         menu = QMenu(name)
00640         self._window.menuBar().insertMenu(self._window.helpMenu().menuAction(), menu)
00641         self._pluginMenus.append(menu)
00642         return menu
00643     
00644     def showPluginMenu(self, menuObject, show=True):
00645         """ Shows given menu if it is in _pluginMenus list.
00646         """
00647         #logging.debug(self.__class__.__name__ +": showPluginMenu()")
00648         if menuObject in self._pluginMenus:
00649             # show all actions and activate their shortcuts
00650             if show:
00651                 for action in menuObject.actions():
00652                     if hasattr(action,"_wasVisible") and action._wasVisible!=None:
00653                         action.setVisible(action._wasVisible)
00654                         action._wasVisible=None
00655                     else:
00656                         action.setVisible(True)     # has to be after actions() loop to prevent permanant invisibility on Mac OS X
00657             menuObject.menuAction().setVisible(show)
00658         
00659     def hidePluginMenu(self, menuObject):
00660         """ Hides given menu object if it it in _pluginMenus list.
00661         """
00662         self.showPluginMenu(menuObject, False)
00663             
00664     def hidePluginMenus(self):
00665         """ Hides all menus in _pluginMenus list.
00666         """
00667         for menuObject in self._pluginMenus:
00668             # hide all actions and deactivate their shortcuts
00669             menuObject.menuAction().setVisible(False)
00670             for action in menuObject.actions():
00671                 if not hasattr(action,"_wasVisible") or action._wasVisible==None:
00672                     action._wasVisible=action.isVisible()
00673                 action.setVisible(False)    # setVisible() here hides plugin menu forever on Mac OS X (10.5.7), Qt 4.5.
00674     
00675     def createPluginToolBar(self, name):
00676         """ Creates tool bar in main window and adds it to _pluginToolBars list.
00677         """
00678         toolBar = self._window.addToolBar(name)
00679         self._pluginToolBars.append(toolBar)
00680         return toolBar
00681 
00682     def showPluginToolBar(self, toolBarObject, show=True):
00683         """ Shows given toolbar if it is in _pluginToolBars list.
00684         """
00685         if toolBarObject in self._pluginToolBars:
00686             toolBarObject.setVisible(show)
00687             
00688     def hidePluginMenu(self, toolBarObject):
00689         """ Hides given toolbar object if it it in _pluginToolBars list.
00690         """
00691         self.showPluginToolBar(toolBarObject, False)
00692         #if toolBarObject in self._pluginToolBars:
00693         #    toolBarObject.menuAction().setVisible(False)
00694             
00695     def hidePluginToolBars(self):
00696         """ Hides all toolbars in _toolBarMenus list.
00697         """
00698         for toolBar in self._pluginToolBars:
00699             toolBar.hide()
00700             
00701     def createZoomToolBar(self):
00702         """ Creates tool bar with three buttons "user", "100 %" and "all".
00703         
00704         See TabController's documentation of zoomUser(), zoomHundred() and zoomAll() to find out more on the different zoom levels.
00705         """
00706         self._zoomToolBar = self.createPluginToolBar('Zoom ToolBar')
00707         self._zoomToolBar.addAction(self.createAction('Revert Zoom', self.zoomUserEvent, image='zoomuser'))
00708         self._zoomToolBar.addAction(self.createAction('Zoom to 100 %', self.zoomHundredEvent, image='zoom100'))
00709         self._zoomToolBar.addAction(self.createAction('Zoom to all', self.zoomAllEvent, image='zoomall'))
00710     
00711     def showZoomToolBar(self):
00712         """ Makes zoom tool bar visible.
00713         
00714         Should be called from TabController's selected() function, if the controller wants to use the tool bar.
00715         """
00716         self.showPluginToolBar(self._zoomToolBar)
00717     
00718     def hideZoomToolBar(self):
00719         """ Makes zoom tool bar invisible.
00720         """
00721         self._zoomToolBar.hide()
00722         
00723     def createUndoToolBar(self):
00724         """ Creates tool bar with buttons to invoke undo and redo events.
00725         
00726         Needs to be called after _fillEditMenu() as actions are defined there.
00727         """
00728         self._undoToolBar = self.createPluginToolBar("Undo ToolBar")
00729         self._undoToolBar.addAction(self._editMenuItems["undoAction"])
00730         self._undoToolBar.addAction(self._editMenuItems["redoAction"])
00731         
00732     def showUndoToolBar(self):
00733         """ Makes undo tool bar visible.
00734         """
00735         self.showPluginToolBar(self._undoToolBar)
00736         
00737     def hideUndoToolBar(self):
00738         """ Hides undo tool bar.
00739         """
00740         self._undoToolBar.hide()
00741     
00742     def clearRecentFiles(self):
00743         """ Empties list of recent files and updates main menu.
00744         """ 
00745         self._recentFiles = []
00746         self._saveIni()
00747         self.updateMenu()
00748         
00749     def clearMissingRecentFiles(self):
00750         """ Removes entries from recent files menu if file does no longer exist.
00751         """
00752         newList = []
00753         for file in self._recentFiles:
00754             if os.path.exists(file):
00755                 newList.append(file)
00756         self._recentFiles = newList
00757         self._saveIni()
00758         self.updateMenu()
00759 
00760     def addRecentFile(self, filename):
00761         """ Adds given filename to list of recent files.
00762         """
00763         logging.debug('Application: addRecentFile() - ' + filename)
00764         if isinstance(filename, QString):
00765             filename = str(filename)    # Make sure filename is a python string not a QString
00766         leftCount = self.MAX_RECENT_FILES - 1
00767         if filename in self._recentFiles:
00768             del self._recentFiles[self._recentFiles.index(filename)]
00769         self._recentFiles = [filename] + self._recentFiles[:leftCount]
00770         self._saveIni()
00771        
00772     def recentFiles(self):
00773         """ Returns list of recently opened files.
00774         """
00775         return self._recentFiles    
00776     
00777     def getLastOpenLocation(self):
00778         """ Returns directory name of first entry of recent files list.
00779         """
00780         # if current working dir is vispa directory use recentfile or home
00781         if os.path.abspath(os.getcwd()) in [os.path.abspath(baseDirectory),os.path.abspath(os.path.join(baseDirectory,"bin"))] or platform.system() == "Darwin":
00782             if len(self._recentFiles) > 0:
00783                 return os.path.dirname(self._recentFiles[0])
00784             elif platform.system() == "Darwin":
00785                 # Mac OS X
00786                 return homeDirectory + "/Documents"
00787             else:
00788                 return homeDirectory
00789         # if user navigated to another directory use this
00790         else:
00791             return os.getcwd()
00792     
00793     def recentFilesFromPlugin(self,plugin):
00794         files=[]
00795         filetypes = plugin.filetypes()
00796         extension=None
00797         if len(filetypes) > 0:
00798             extension=filetypes[0].extension().lower()
00799         for file in self._recentFiles:
00800             if os.path.splitext(os.path.basename(file))[1][1:].lower()==extension:
00801                 files+=[file]
00802         return files
00803     
00804     def updateStartupScreen(self):
00805         screen=self.mainWindow().startupScreen()
00806         screen.analysisDesignerRecentFilesList().clear()
00807         screen.analysisDesignerRecentFilesList().addItem("...")
00808         screen.analysisDesignerRecentFilesList().setCurrentRow(0)
00809         plugin=self.plugin("AnalysisDesignerPlugin")
00810         if plugin:
00811             files = self.recentFilesFromPlugin(plugin)
00812             for file in files:
00813                 screen.analysisDesignerRecentFilesList().addItem(os.path.basename(file))
00814         
00815         screen.pxlEditorRecentFilesList().clear()
00816         screen.pxlEditorRecentFilesList().addItem("...")
00817         screen.pxlEditorRecentFilesList().setCurrentRow(0)
00818         plugin=self.plugin("PxlPlugin")
00819         if plugin:
00820             files = self.recentFilesFromPlugin(plugin)
00821             for file in files:
00822                 screen.pxlEditorRecentFilesList().addItem(os.path.basename(file))
00823         
00824     def exit(self):
00825         self._window.close()
00826 
00827     def shutdownPlugins(self):
00828         logging.debug('Application: shutting down plugins' )
00829         for plugin in self._plugins:
00830           plugin.shutdown()
00831 
00832 
00833     def _collectFileExtensions(self):
00834         """ Loop over all plugins and remember their file extensions.
00835         """
00836         self._knownExtensionsDictionary = {}
00837         self._knownFiltersList = []
00838         self._knownFiltersList.append('All files (*.*)')
00839         for plugin in self.plugins():
00840             for ft in plugin.filetypes():
00841                 self._knownExtensionsDictionary[ft.extension()] = plugin
00842                 self._knownFiltersList.append(ft.fileDialogFilter())
00843         if len(self._knownFiltersList) > 0:
00844             allKnownFilter = 'All known files (*.' + " *.".join(self._knownExtensionsDictionary.keys()) + ')'
00845             self._knownFiltersList.insert(1, allKnownFilter)
00846             logging.debug('Application: _collectFileExtensions() - ' + allKnownFilter)
00847         else:
00848             logging.debug('Application: _collectFileExtensions()')
00849         
00850         
00851     def openFileDialog(self, defaultFileFilter=None):
00852         """Displays a common open dialog for all known file types.
00853         """
00854         logging.debug('Application: openFileDialog()')
00855         
00856         if not defaultFileFilter:
00857             if len(self._knownFiltersList) > 1:
00858                 # Set defaultFileFilter to all known files
00859                 defaultFileFilter = self._knownFiltersList[1]
00860             else:
00861                 # Set dfaultFileFilter to any file type
00862                 defaultFileFilter = self._knownFiltersList[0]
00863         
00864         # Dialog
00865         filename = QFileDialog.getOpenFileName(
00866                                                self._window,
00867                                                'Select a file',
00868                                                self.getLastOpenLocation(),
00869                                                ";;".join(self._knownFiltersList),
00870                                                defaultFileFilter)
00871         if not filename.isEmpty():
00872             self.openFile(filename)
00873 
00874     def openFile(self, filename):
00875         """ Decides which plugin should handle opening of the given file name.
00876         """
00877         logging.debug('Application: openFile()')
00878         statusMessage = self.startWorking("Opening file " + filename)
00879         if isinstance(filename, QString):
00880             filename = str(filename)  # convert QString to Python String
00881         
00882         # Check whether file is already opened
00883         for controller in self.tabControllers():
00884             if filename == controller.filename():
00885                 self.setCurrentTabController(controller)
00886                 self.stopWorking(statusMessage, "already open")
00887                 return
00888         
00889         baseName = os.path.basename(filename)
00890         ext = os.path.splitext(baseName)[1].lower().strip(".")
00891         errormsg = None
00892 
00893         if self._knownExtensionsDictionary == {}:
00894             self._collectFileExtensions()
00895         
00896         foundCorrectPlugin = False
00897         if os.path.exists(filename):
00898             if ext in self._knownExtensionsDictionary:
00899                 foundCorrectPlugin = True
00900                 try:
00901                     if self._knownExtensionsDictionary[ext].openFile(filename):
00902                         self.addRecentFile(filename)
00903                     else:
00904                         logging.error(self.__class__.__name__ + ": openFile() - Error while opening '" + str(filename) + "'.")
00905                         self.errorMessage("Failed to open file.")
00906                 except Exception:
00907                     logging.error(self.__class__.__name__ + ": openFile() - Error while opening '" + str(filename) + "' : " + exception_traceback())
00908                     self.errorMessage("Exception while opening file. See log for details.")
00909 
00910             if not foundCorrectPlugin:
00911                 errormsg = 'Unknown file type (.' + ext + '). Aborting.'
00912         else:
00913             errormsg = 'File does not exist: ' + filename
00914 
00915         self.updateMenu()
00916         
00917         # Error messages
00918         if not errormsg:
00919             self.stopWorking(statusMessage)
00920         else:
00921             logging.error(errormsg)
00922             self.stopWorking(statusMessage, "failed")
00923             self.warningMessage(errormsg)
00924     
00925     def reloadFile(self):
00926         """ Tells current tab controller to refresh.
00927         """
00928         logging.debug('Application: reloadFile()')
00929         try:
00930             if self.currentTabController().filename() and self.currentTabController().allowClose():
00931                 self.currentTabController().refresh()
00932                 self.currentTabController().setModified(False)
00933         except NoCurrentTabControllerException:
00934             pass
00935         # call tabChanged instead of updateMenu to be qt 4.3 compatible
00936         self.tabChanged()
00937     
00938     def closeFile(self):
00939         """ Tells current tab controller to close.
00940         """
00941         logging.debug('Application: closeCurrentFile()')
00942         try:
00943             self.currentTabController().close()
00944         except NoCurrentTabControllerException:
00945             pass
00946         # call tabChanged instead of updateMenu to be qt 4.3 compatible
00947         self.tabChanged()
00948         
00949     def closeAllFiles(self):
00950         """ Closes all open tabs unless user aborts closing.
00951         """
00952         logging.debug('Application: closeAllFiles()')
00953         # to prevent unneeded updates set flag
00954         self._closeAllFlag = True
00955         while len(self.tabControllers())>0:
00956             controller=self.tabControllers()[0]
00957             if controller.close() == False:
00958                 break
00959         self._closeAllFlag = False
00960 
00961         # call tabChanged instead of updateMenu to be qt 4.3 compatible
00962         self.tabChanged()
00963         
00964     def saveFile(self):
00965         """ Tells current tab controller to save its file.
00966         """
00967         logging.debug('Application: saveFile()')
00968         try:
00969             self.currentTabController().save()
00970         except NoCurrentTabControllerException:
00971             logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
00972         
00973     def saveFileAsDialog(self):
00974         """This functions asks the user for a file name. 
00975         """
00976         logging.debug('Application: saveFileAsDialog()')
00977         try:
00978             currentTabController = self.currentTabController()
00979         except NoCurrentTabControllerException:
00980             logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
00981             return
00982 
00983         if currentTabController.filename():
00984             startDirectory = currentTabController.filename()
00985         else:
00986             startDirectory = self.getLastOpenLocation()
00987                 
00988         filetypesList = []
00989         for filetype in currentTabController.supportedFileTypes():
00990             filetypesList.append(Filetype(filetype[0], filetype[1]).fileDialogFilter())
00991         filetypesList.append('Any (*.*)')
00992 
00993         selectedFilter = QString("")
00994         filename = str(QFileDialog.getSaveFileName(
00995                                             self._window,
00996                                             'Select a file',
00997                                             startDirectory,
00998                                             ";;".join(filetypesList), selectedFilter))
00999         if filename != "":
01000             # add extension if necessary
01001             if os.path.splitext(filename)[1].strip(".") == "" and str(selectedFilter) != 'Any (*.*)':
01002                 ext = currentTabController.supportedFileTypes()[filetypesList.index(str(selectedFilter))][0]
01003                 filename = os.path.splitext(filename)[0] + "." + ext
01004             return currentTabController.save(filename)
01005         return False
01006         
01007     def saveAllFiles(self):
01008         """ Tells tab controllers of all tabs to save.
01009         """
01010         logging.debug('Application: saveAllFiles()')
01011         
01012         for controller in self.tabControllers():
01013             if controller.filename() or controller == self.currentTabController():
01014                 controller.save()
01015 
01016     def cutEvent(self):
01017         """ Called when cut action is triggered (e.g. from menu entry) and forwards it to current tab controller.
01018         """
01019         try:
01020             self.currentTabController().cut()
01021         except NoCurrentTabControllerException:
01022             logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
01023         
01024     def copyEvent(self):
01025         """ Called when copy action is triggered (e.g. from menu entry) and forwards it to current tab controller.
01026         """
01027         try:
01028             self.currentTabController().copy()
01029         except NoCurrentTabControllerException:
01030             logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
01031         
01032     def pasteEvent(self):
01033         """ Called when paste action is triggered (e.g. from menu entry) and forwards it to current tab controller.
01034         """
01035         try:
01036             self.currentTabController().paste()
01037         except NoCurrentTabControllerException:
01038             logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
01039             
01040     def selectAllEvent(self):
01041         """ Called when selectAll action is triggered (e.g. from menu entry) and forwards it to current tab controller.
01042         """
01043         try:
01044             self.currentTabController().selectAll()
01045         except NoCurrentTabControllerException:
01046             logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
01047             
01048     def findEvent(self):
01049         """ Called when find action is triggered (e.g. from menu entry) and forwards it to current tab controller.
01050         """
01051         try:
01052             self.currentTabController().find()
01053         except NoCurrentTabControllerException:
01054             logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
01055        
01056     def zoomUserEvent(self):
01057         """ Handles button pressed event from zoom tool bar and forwards it to current tab controller.
01058         """
01059         try:
01060             self.currentTabController().zoomUser()
01061         except NoCurrentTabControllerException:
01062             logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
01063         
01064     def zoomHundredEvent(self):
01065         """ Handles button pressed event from zoom tool bar and forwards it to current tab controller.
01066         """
01067         try:
01068             self.currentTabController().zoomHundred()
01069         except NoCurrentTabControllerException:
01070             logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
01071     
01072     def zoomAllEvent(self):
01073         """ Handles button pressed event from zoom tool bar and forwards it to current tab controller.
01074         """
01075         try:
01076             self.currentTabController().zoomAll()
01077         except NoCurrentTabControllerException:
01078             logging.warning(self.__class__.__name__ + ": " + self.TAB_PREMATURELY_CLOSED_WARNING)
01079          
01080     def undoEvent(self):
01081         """ Handles undo action for buttons in undo tool bar and edit menu.
01082         """
01083         try:
01084             num = 1
01085             sender = self.sender()
01086             if sender:
01087                 num = sender.data().toInt()
01088                 if len(num) > 1:
01089                     # strange: toInt returns tuple like (1, True), QT 4.6.0, Mac OS X 10.6.4, 2010-06-28
01090                     num = num[0]
01091             self.currentTabController().undo(num)
01092         except NoCurrentTabControllerException:
01093             logging.warning(self.__class__.__name__ + ": "+ self.TAB_PREMATURELY_CLOSED_WARNING) 
01094          
01095     def redoEvent(self):
01096         """ Handles redo action for buttons in undo tool bar and edit menu.
01097         """
01098         try:
01099             num = 1
01100             sender = self.sender()
01101             if sender:
01102                 num = sender.data().toInt()
01103                 if len(num) > 1:
01104                     # strange: toInt returns tuple like (1, True), QT 4.6.0, Mac OS X 10.6.4, 2010-06-28
01105                     num = num[0]
01106             self.currentTabController().redo(num)
01107         except NoCurrentTabControllerException:
01108             logging.warning(self.__class__.__name__ + ": "+ self.TAB_PREMATURELY_CLOSED_WARNING) 
01109          
01110     def aboutBoxSlot(self):
01111         """ Displays about box. 
01112         """
01113         logging.debug('Application: aboutBoxSlot()')
01114         about = AboutDialog(self)
01115         about.onScreen()
01116         
01117     def openLogFileSlot(self):
01118         if self._logFile:
01119             self.doubleClickOnFile(self._logFile)
01120         else:
01121             logging.warning("%s: openLogFileSlot(): _logFile not set. Aborting..." % self.__class__.__name__)
01122 
01123     def openRecentFileSlot(self):
01124         """ Slot for opening recent file.
01125         
01126         Called from recent file menu action. Filename is set as data object (QVariant) of action.
01127         """
01128         filename = self.sender().data().toString()
01129         logging.debug('Application: openRecentFileSlot() - ' + filename)
01130         self.openFile(filename)
01131 
01132     def tabChanged(self, tab=None):
01133         """ when a different tab is activated update menu
01134         """
01135         logging.debug('Application: tabChanged()')
01136         # only update once when closing all files at once
01137         if not self._closeAllFlag:
01138             self.hidePluginMenus()
01139             self.hidePluginToolBars()
01140             self.updateWindowTitle()
01141             self.updateMenu()
01142             try:
01143                 self.currentTabController().activated()
01144                 self.currentTabController().checkModificationTimestamp()
01145             except NoCurrentTabControllerException:
01146                 pass
01147             
01148         self.mainWindow().setStartupScreenVisible(self.mainWindow().tabWidget().count() == 0)
01149     
01150     def updateMenuAndWindowTitle(self):
01151         """ Update menu and window title.
01152         """
01153         self.updateMenu()
01154         self.updateWindowTitle()
01155 
01156     def windowTitle(self):
01157         return str(self._window.windowTitle()).split("-")[0].strip()
01158     
01159     def updateWindowTitle(self):
01160         """ update window caption
01161         """
01162         #logging.debug('Application: updateWindowTitle()')
01163         name = self.windowTitle()
01164         
01165         try:
01166             filename = self.currentTabController().filename()
01167         except NoCurrentTabControllerException:
01168             filename = None
01169             
01170         if filename:
01171             dirName = os.path.dirname(sys.argv[0])
01172             if os.path.abspath(dirName) in filename:
01173                 filename = filename[len(os.path.abspath(dirName)) + 1:]
01174             name = name + " - " + filename
01175         self._window.setWindowTitle(name)
01176 
01177     def ini(self):
01178         if not self._ini:
01179             self._ini = ConfigParser.ConfigParser()
01180             self._ini.read(self._iniFileName)
01181         return self._ini 
01182 
01183     def writeIni(self):
01184         try:
01185             configfile = open(self._iniFileName, "w")
01186             self._ini.write(configfile)
01187             configfile.close()
01188         except IOError:
01189             pass
01190         self._ini = None
01191         
01192     def _loadIni(self):
01193         """ Save the list of recent files.
01194         """
01195         logging.debug('Application: _loadIni()')
01196         ini = self.ini()
01197         self._recentFiles = []
01198         if ini.has_section("history"):
01199             for i in range(0, self.MAX_RECENT_FILES):
01200                 if ini.has_option("history", str(i)):
01201                     self._recentFiles+=[ini.get("history", str(i))]
01202                
01203     def _saveIni(self):
01204         """ Load the list of recent files.
01205         """
01206         logging.debug('Application: _saveIni()')
01207         ini = self.ini()
01208         if ini.has_section("history"):
01209             ini.remove_section("history")
01210         ini.add_section("history")
01211         for i in range(len(self._recentFiles)):
01212             ini.set("history", str(i), self._recentFiles[i])
01213                 
01214         self.writeIni()
01215 
01216     def errorMessage(self, message):
01217         """ Displays error message.
01218         """
01219         QMessageBox.critical(self.mainWindow(), 'Error', message)
01220         
01221     def warningMessage(self, message):
01222         """ Displays warning message.
01223         """
01224         QMessageBox.warning(self.mainWindow(), 'Warning', message)
01225         
01226     def infoMessage(self, message):
01227         """ Displays info message.
01228         """
01229         QMessageBox.about(self.mainWindow(), 'Info', message)
01230         
01231     def showMessageBox(self, text, informativeText="", standardButtons=QMessageBox.Ok | QMessageBox.Cancel | QMessageBox.Ignore, defaultButton=QMessageBox.Ok, extraButtons=None):
01232         """ Shows a standardized message box and returns the pressed button.
01233         
01234         See documentation on Qt's QMessageBox for a list of possible standard buttons.
01235         """
01236         
01237         msgBox = QMessageBox(self.mainWindow())
01238         msgBox.setParent(self.mainWindow(), Qt.Sheet)     # Qt.Sheet: Indicates that the widget is a Macintosh sheet.
01239         msgBox.setText(text)
01240         msgBox.setInformativeText(informativeText)
01241         msgBox.setStandardButtons(standardButtons)
01242         if extraButtons!=None:
01243             for button,role in extraButtons:
01244                 msgBox.addButton(button,role)
01245         msgBox.setDefaultButton(defaultButton)
01246         return msgBox.exec_()
01247         
01248     def doubleClickOnFile(self, filename):
01249         """ Opens file given as argument if possible in Vispa.
01250         
01251         If Vispa cannot handle the file type the file will be opened in it's default application.
01252         """
01253         logging.debug(self.__class__.__name__ + ": doubleClickOnFile() - " + str(filename))
01254         
01255         if filename == "":
01256             return
01257         
01258         baseName = os.path.basename(filename)
01259         ext = os.path.splitext(baseName)[1].lower().strip(".")
01260         if self._knownExtensionsDictionary == {}:
01261             self._collectFileExtensions()
01262         if os.path.exists(filename):
01263             if ext in self._knownExtensionsDictionary:
01264                 return self.openFile(filename)
01265         
01266         # open file in default application
01267         try:
01268           if 'Windows' in platform.system():
01269               os.startfile(filename)
01270           elif 'Darwin' in platform.system():
01271             if os.access(filename, os.X_OK):
01272               logging.warning("It seems that executing the python program is the default action on this system, which is processed when double clicking a file. Please change that to open the file witrh your favourite editor, to use this feature.")
01273             else:
01274               subprocess.call(("open", filename))
01275           elif 'Linux' in platform.system():
01276           # Linux comes with many Desktop Enviroments
01277             if os.access(filename, os.X_OK):
01278               logging.warning("It seems that executing the python program is the default action on this system, which is processed when double clicking a file. Please change that to open the file witrh your favourite editor, to use this feature.")
01279             else:
01280               try:
01281                   #Freedesktop Standard
01282                   subprocess.call(("xdg-open", filename))
01283               except:
01284                 try:
01285                    subprocess.call(("gnome-open", filename))
01286                 except:
01287                    logging.error(self.__class__.__name__ + ": doubleClickOnFile() - Platform '" + platform.platform() + "'. Cannot open file. I Don't know how!")
01288         except:
01289           logging.error(self.__class__.__name__ + ": doubleClickOnFile() - Platform '" + platform.platform() + "'. Error while opening file: " + str(filename))
01290 
01291     def createStatusBar(self):
01292         self._workingMessages = {}
01293         
01294         self._progressWidget = RotatingIcon(":/resources/vispabutton.png")
01295         self._window.statusBar().addPermanentWidget(self._progressWidget)
01296 
01297     def startWorking(self, message=""):
01298         if len(self._workingMessages.keys()) == 0:
01299             self._progressWidget.start()
01300         self._window.statusBar().showMessage(message + "...")
01301         self._messageId+=1
01302         self._workingMessages[self._messageId] = message
01303         self._progressWidget.setToolTip(message)
01304         return self._messageId
01305 
01306     def stopWorking(self, id, end="done"):
01307         if not id in self._workingMessages.keys():
01308             logging.error(self.__class__.__name__ +": stopWorking() - Unknown id %s. Aborting..." % str(id))
01309             return
01310         if len(self._workingMessages.keys()) > 1:
01311             self._window.statusBar().showMessage(self._workingMessages[self._workingMessages.keys()[0]] + "...")
01312             self._progressWidget.setToolTip(self._workingMessages[self._workingMessages.keys()[0]])
01313         else:
01314             self._progressWidget.stop()
01315             self._progressWidget.setToolTip("")
01316             self._window.statusBar().showMessage(self._workingMessages[id] + "... " + end + ".")
01317         del self._workingMessages[id]
01318             
01319     def tabCloseRequest(self, i):
01320         self.mainWindow().tabWidget().setCurrentIndex(i)
01321         self.closeFile()
01322 
01323     def showStatusMessage(self, message, timeout=0):
01324         self._window.statusBar().showMessage(message, timeout)
01325 
01326     def cancel(self):
01327         """ Cancel operations in current tab.
01328         """
01329         logging.debug(__name__ + ": cancel")
01330         try:
01331             self.currentTabController().cancel()
01332         except NoCurrentTabControllerException:
01333             pass