Plugins/ViewManagerPlugins/Listspace/Listspace.py

changeset 0
de9c2efb9d02
child 6
52e8c820d0dd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/ViewManagerPlugins/Listspace/Listspace.py	Mon Dec 28 16:03:33 2009 +0000
@@ -0,0 +1,651 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2002 - 2009 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the listspace viewmanager class.
+"""
+
+import os
+
+from PyQt4.QtCore import *
+from PyQt4.QtGui import *
+
+from E4Gui.E4Application import e4App
+
+from ViewManager.ViewManager import ViewManager
+
+import QScintilla.Editor
+
+import UI.PixmapCache
+
+class StackedWidget(QStackedWidget):
+    """
+    Class implementing a custimized StackedWidget.
+    """
+    def __init__(self, parent):
+        """
+        Constructor
+        
+        @param parent parent widget (QWidget)
+        """
+        QStackedWidget.__init__(self, parent)
+        self.setAttribute(Qt.WA_DeleteOnClose, True)
+        
+        self.editors = []
+        
+    def addWidget(self, editor):
+        """
+        Overwritten method to add a new widget.
+        
+        @param editor the editor object to be added (QScintilla.Editor.Editor)
+        """
+        QStackedWidget.addWidget(self, editor)
+        if not editor in self.editors:
+            self.editors.append(editor)
+        
+    def removeWidget(self, widget):
+        """
+        Overwritten method to remove a widget.
+        
+        @param widget widget to be removed (QWidget)
+        """
+        QStackedWidget.removeWidget(self, widget)
+        if isinstance(widget, QScintilla.Editor.Editor):
+            self.editors.remove(widget)
+        
+    def setCurrentWidget(self, widget):
+        """
+        Overwritten method to set the current widget.
+        
+        @param widget widget to be made current (QWidget)
+        """
+        if isinstance(widget, QScintilla.Editor.Editor):
+            self.editors.remove(widget)
+            self.editors.insert(0, widget)
+        QStackedWidget.setCurrentWidget(self, widget)
+        
+    def setCurrentIndex(self, index):
+        """
+        Overwritten method to set the current widget by it's index.
+        
+        @param index index of widget to be made current (integer)
+        """
+        widget = self.widget(index)
+        if widget is not None:
+            self.setCurrentWidget(widget)
+        
+    def nextTab(self):
+        """
+        Public slot used to show the next tab.
+        """
+        ind = self.currentIndex() + 1
+        if ind == self.count():
+            ind = 0
+            
+        self.setCurrentIndex(ind)
+        self.currentWidget().setFocus()
+
+    def prevTab(self):
+        """
+        Public slot used to show the previous tab.
+        """
+        ind = self.currentIndex() - 1
+        if ind == -1:
+            ind = self.count() - 1
+            
+        self.setCurrentIndex(ind)
+        self.currentWidget().setFocus()
+
+    def hasEditor(self, editor):
+        """
+        Public method to check for an editor.
+        
+        @param editor editor object to check for
+        @return flag indicating, whether the editor to be checked belongs
+            to the list of editors managed by this stacked widget.
+        """
+        return editor in self.editors
+        
+    def firstEditor(self):
+        """
+        Public method to retrieve the first editor in the list of managed editors.
+        
+        @return first editor in list (QScintilla.Editor.Editor)
+        """
+        return len(self.editors) and self.editors[0] or None
+
+class Listspace(QSplitter, ViewManager):
+    """
+    Class implementing the listspace viewmanager class.
+    
+    @signal changeCaption(string) emitted if a change of the caption is necessary
+    @signal editorChanged(string) emitted when the current editor has changed
+    """
+    def __init__(self, parent):
+        """
+        Constructor
+        
+        @param parent parent widget (QWidget)
+        @param ui reference to the main user interface
+        @param dbs reference to the debug server object
+        """
+        self.stacks = []
+        
+        QSplitter.__init__(self, parent)
+        ViewManager.__init__(self)
+        
+        self.viewlist = QListWidget(self)
+        policy = self.viewlist.sizePolicy()
+        policy.setHorizontalPolicy(QSizePolicy.Ignored)
+        self.viewlist.setSizePolicy(policy)
+        self.addWidget(self.viewlist)
+        self.viewlist.setContextMenuPolicy(Qt.CustomContextMenu)
+        self.connect(self.viewlist, SIGNAL("itemActivated(QListWidgetItem*)"),
+                     self.__showSelectedView)
+        self.connect(self.viewlist, SIGNAL("itemClicked(QListWidgetItem*)"),
+                     self.__showSelectedView)
+        self.connect(self.viewlist, SIGNAL("customContextMenuRequested(const QPoint &)"),
+                     self.__showMenu)
+        
+        self.stackArea = QSplitter(self)
+        self.addWidget(self.stackArea)
+        self.stackArea.setOrientation(Qt.Vertical)
+        stack = StackedWidget(self.stackArea)
+        self.stackArea.addWidget(stack)
+        self.stacks.append(stack)
+        self.currentStack = stack
+        self.connect(stack, SIGNAL('currentChanged(int)'),
+            self.__currentChanged)
+        stack.installEventFilter(self)
+        self.setSizes([int(self.width() * 0.2), int(self.width() * 0.8)]) # 20% for viewlist
+        self.__inRemoveView = False
+        
+        self.__initMenu()
+        self.contextMenuEditor = None
+        
+    def __initMenu(self):
+        """
+        Private method to initialize the viewlist context menu.
+        """
+        self.__menu = QMenu(self)
+        self.__menu.addAction(UI.PixmapCache.getIcon("close.png"),
+            self.trUtf8('Close'), self.__contextMenuClose)
+        self.__menu.addAction(self.trUtf8('Close All'), self.__contextMenuCloseAll)
+        self.__menu.addSeparator()
+        self.saveMenuAct = \
+            self.__menu.addAction(UI.PixmapCache.getIcon("fileSave.png"),
+            self.trUtf8('Save'), self.__contextMenuSave)
+        self.__menu.addAction(UI.PixmapCache.getIcon("fileSaveAs.png"),
+            self.trUtf8('Save As...'), self.__contextMenuSaveAs)
+        self.__menu.addAction(UI.PixmapCache.getIcon("fileSaveAll.png"),
+            self.trUtf8('Save All'), self.__contextMenuSaveAll)
+        self.projectMenuAct = \
+            self.__menu.addAction(UI.PixmapCache.getIcon("fileSaveProject.png"),
+                self.trUtf8('Save to Project'), self.__contextMenuSaveToProject)
+        self.__menu.addSeparator()
+        self.__menu.addAction(UI.PixmapCache.getIcon("print.png"),
+            self.trUtf8('Print'), self.__contextMenuPrintFile)
+        
+    def __showMenu(self, point):
+        """
+        Private slot to handle the customContextMenuRequested signal of the viewlist.
+        """
+        if self.editors:
+            itm = self.viewlist.itemAt(point)
+            if itm is not None:
+                row = self.viewlist.row(itm)
+                self.contextMenuEditor = self.editors[row]
+                if self.contextMenuEditor:
+                    self.saveMenuAct.setEnabled(self.contextMenuEditor.isModified())
+                    self.projectMenuAct.setEnabled(e4App().getObject("Project").isOpen())
+                    self.__menu.popup(self.viewlist.mapToGlobal(point))
+        
+    def canCascade(self):
+        """
+        Public method to signal if cascading of managed windows is available.
+        
+        @return flag indicating cascading of windows is available
+        """
+        return False
+        
+    def canTile(self):
+        """
+        Public method to signal if tiling of managed windows is available.
+        
+        @return flag indicating tiling of windows is available
+        """
+        return False
+    
+    def canSplit(self):
+        """
+        public method to signal if splitting of the view is available.
+        
+        @return flag indicating splitting of the view is available.
+        """
+        return True
+        
+    def tile(self):
+        """
+        Public method to tile the managed windows.
+        """
+        pass
+        
+    def cascade(self):
+        """
+        Public method to cascade the managed windows.
+        """
+        pass
+        
+    def _removeAllViews(self):
+        """
+        Protected method to remove all views (i.e. windows)
+        """
+        self.viewlist.clear()
+        for win in self.editors:
+            for stack in self.stacks:
+                if stack.hasEditor(win):
+                    stack.removeWidget(win)
+                    break
+            win.closeIt()
+        
+    def _removeView(self, win):
+        """
+        Protected method to remove a view (i.e. window)
+        
+        @param win editor window to be removed
+        """
+        self.__inRemoveView = True
+        ind = self.editors.index(win)
+        itm = self.viewlist.takeItem(ind)
+        if itm:
+            del itm
+        for stack in self.stacks:
+            if stack.hasEditor(win):
+                stack.removeWidget(win)
+                break
+        win.closeIt()
+        self.__inRemoveView = False
+        if ind > 0:
+            ind -= 1
+        else:
+            if len(self.editors) > 1:
+                ind = 1
+            else:
+                return
+        stack.setCurrentWidget(stack.firstEditor())
+        self._showView(self.editors[ind])
+        
+        aw = self.activeWindow()
+        fn = aw and aw.getFileName() or None
+        if fn:
+            self.emit(SIGNAL('changeCaption'), fn)
+            self.emit(SIGNAL('editorChanged'), fn)
+        else:
+            self.emit(SIGNAL('changeCaption'), "")
+        
+    def _addView(self, win, fn = None, noName = ""):
+        """
+        Protected method to add a view (i.e. window)
+        
+        @param win editor window to be added
+        @param fn filename of this editor (string)
+        @param noName name to be used for an unnamed editor (string)
+        """
+        if fn is None:
+            if not noName:
+                self.untitledCount += 1
+                noName = self.trUtf8("Untitled {0}").format(self.untitledCount)
+            self.viewlist.addItem(noName)
+            win.setNoName(noName)
+        else:
+            txt = os.path.basename(fn)
+            if not QFileInfo(fn).isWritable():
+                txt = self.trUtf8("{0} (ro)").format(txt)
+            itm = QListWidgetItem(txt)
+            itm.setToolTip(fn)
+            self.viewlist.addItem(itm)
+        self.currentStack.addWidget(win)
+        self.currentStack.setCurrentWidget(win)
+        self.connect(win, SIGNAL('captionChanged'),
+            self.__captionChange)
+        
+        index = self.editors.index(win)
+        self.viewlist.setCurrentRow(index)
+        win.setFocus()
+        if fn:
+            self.emit(SIGNAL('changeCaption'), fn)
+            self.emit(SIGNAL('editorChanged'), fn)
+        else:
+            self.emit(SIGNAL('changeCaption'), "")
+        
+    def __captionChange(self, cap, editor):
+        """
+        Private method to handle caption change signals from the editor. 
+        
+        Updates the listwidget text to reflect the new caption information.
+        
+        @param cap Caption for the editor (string)
+        @param editor Editor to update the caption for
+        """
+        fn = editor.getFileName()
+        if fn:
+            self.setEditorName(editor, fn)
+        
+    def _showView(self, win, fn = None):
+        """
+        Protected method to show a view (i.e. window)
+        
+        @param win editor window to be shown
+        @param fn filename of this editor (string)
+        """
+        for stack in self.stacks:
+            if stack.hasEditor(win):
+                stack.setCurrentWidget(win)
+                self.currentStack = stack
+                break
+        index = self.editors.index(win)
+        self.viewlist.setCurrentRow(index)
+        win.setFocus()
+        fn = win.getFileName()
+        if fn:
+            self.emit(SIGNAL('changeCaption'), fn)
+            self.emit(SIGNAL('editorChanged'), fn)
+        else:
+            self.emit(SIGNAL('changeCaption'), "")
+        
+    def __showSelectedView(self, itm):
+        """
+        Private slot called to show a view selected in the list by a mouse click.
+        
+        @param itm item clicked on (QListWidgetItem)
+        """
+        if itm:
+            row = self.viewlist.row(itm)
+            self._showView(self.editors[row])
+            self._checkActions(self.editors[row])
+        
+    def activeWindow(self):
+        """
+        Public method to return the active (i.e. current) window.
+        
+        @return reference to the active editor
+        """
+        return self.currentStack.currentWidget()
+        
+    def showWindowMenu(self, windowMenu):
+        """
+        Public method to set up the viewmanager part of the Window menu.
+        
+        @param windowMenu reference to the window menu
+        """
+        pass
+        
+    def _initWindowActions(self):
+        """
+        Protected method to define the user interface actions for window handling.
+        """
+        pass
+        
+    def setEditorName(self, editor, newName):
+        """
+        Change the displayed name of the editor.
+        
+        @param editor editor window to be changed
+        @param newName new name to be shown (string)
+        """
+        currentRow = self.viewlist.currentRow()
+        index = self.editors.index(editor)
+        txt = os.path.basename(newName)
+        if not QFileInfo(newName).isWritable():
+            txt = self.trUtf8("{0} (ro)").format(txt)
+        itm = self.viewlist.item(index)
+        itm.setText(txt)
+        itm.setToolTip(newName)
+        self.viewlist.setCurrentRow(currentRow)
+        self.emit(SIGNAL('changeCaption'), unicode(newName))
+        
+    def _modificationStatusChanged(self, m, editor):
+        """
+        Protected slot to handle the modificationStatusChanged signal.
+        
+        @param m flag indicating the modification status (boolean)
+        @param editor editor window changed
+        """
+        currentRow = self.viewlist.currentRow()
+        index = self.editors.index(editor)
+        if m:
+            self.viewlist.item(index).setIcon(UI.PixmapCache.getIcon("fileModified.png"))
+        elif editor.hasSyntaxErrors():
+            self.viewlist.item(index).setIcon(UI.PixmapCache.getIcon("syntaxError.png"))
+        else:
+            self.viewlist.item(index).setIcon(UI.PixmapCache.getIcon("empty.png"))
+        self.viewlist.setCurrentRow(currentRow)
+        self._checkActions(editor)
+        
+    def _syntaxErrorToggled(self, editor):
+        """
+        Protected slot to handle the syntaxerrorToggled signal.
+        
+        @param editor editor that sent the signal
+        """
+        currentRow = self.viewlist.currentRow()
+        index = self.editors.index(editor)
+        if editor.hasSyntaxErrors():
+            self.viewlist.item(index).setIcon(UI.PixmapCache.getIcon("syntaxError.png"))
+        else:
+            self.viewlist.item(index).setIcon(UI.PixmapCache.getIcon("empty.png"))
+        self.viewlist.setCurrentRow(currentRow)
+        
+        ViewManager._syntaxErrorToggled(self, editor)
+        
+    def addSplit(self):
+        """
+        Public method used to split the current view.
+        """
+        stack = StackedWidget(self.stackArea)
+        stack.show()
+        self.stackArea.addWidget(stack)
+        self.stacks.append(stack)
+        self.currentStack = stack
+        self.connect(stack, SIGNAL('currentChanged(int)'),
+            self.__currentChanged)
+        stack.installEventFilter(self)
+        if self.stackArea.orientation() == Qt.Horizontal:
+            size = self.stackArea.width()
+        else:
+            size = self.stackArea.height()
+        self.stackArea.setSizes([int(size/len(self.stacks))] * len(self.stacks))
+        self.splitRemoveAct.setEnabled(True)
+        self.nextSplitAct.setEnabled(True)
+        self.prevSplitAct.setEnabled(True)
+        
+    def removeSplit(self):
+        """
+        Public method used to remove the current split view.
+        
+        @return flag indicating successfull removal
+        """
+        if len(self.stacks) > 1:
+            stack = self.currentStack
+            res = True
+            savedEditors = stack.editors[:]
+            for editor in savedEditors:
+                res &= self.closeEditor(editor)
+            if res:
+                try:
+                    i = self.stacks.index(stack)
+                except ValueError:
+                    return True
+                if i == len(self.stacks) - 1:
+                    i -= 1
+                self.stacks.remove(stack)
+                stack.close()
+                self.currentStack = self.stacks[i]
+                if len(self.stacks) == 1:
+                    self.splitRemoveAct.setEnabled(False)
+                    self.nextSplitAct.setEnabled(False)
+                    self.prevSplitAct.setEnabled(False)
+                return True
+        
+        return False
+        
+    def setSplitOrientation(self, orientation):
+        """
+        Public method used to set the orientation of the split view.
+        
+        @param orientation orientation of the split
+                (Qt.Horizontal or Qt.Vertical)
+        """
+        self.stackArea.setOrientation(orientation)
+        
+    def nextSplit(self):
+        """
+        Public slot used to move to the next split.
+        """
+        aw = self.activeWindow()
+        _hasFocus = aw and aw.hasFocus()
+        ind = self.stacks.index(self.currentStack) + 1
+        if ind == len(self.stacks):
+            ind = 0
+        
+        self.currentStack = self.stacks[ind]
+        if _hasFocus:
+            aw = self.activeWindow()
+            if aw:
+                aw.setFocus()
+        
+        index = self.editors.index(self.currentStack.currentWidget())
+        self.viewlist.setCurrentRow(index)
+        
+    def prevSplit(self):
+        """
+        Public slot used to move to the previous split.
+        """
+        aw = self.activeWindow()
+        _hasFocus = aw and aw.hasFocus()
+        ind = self.stacks.index(self.currentStack) - 1
+        if ind == -1:
+            ind = len(self.stacks) - 1
+        
+        self.currentStack = self.stacks[ind]
+        if _hasFocus:
+            aw = self.activeWindow()
+            if aw:
+                aw.setFocus()
+        index = self.editors.index(self.currentStack.currentWidget())
+        self.viewlist.setCurrentRow(index)
+        
+    def __contextMenuClose(self):
+        """
+        Private method to close the selected tab.
+        """
+        if self.contextMenuEditor:
+            self.closeEditorWindow(self.contextMenuEditor)
+        
+    def __contextMenuCloseAll(self):
+        """
+        Private method to close all tabs.
+        """
+        savedEditors = self.editors[:]
+        for editor in savedEditors:
+            self.closeEditorWindow(editor)
+        
+    def __contextMenuSave(self):
+        """
+        Private method to save the selected tab.
+        """
+        if self.contextMenuEditor:
+            self.saveEditorEd(self.contextMenuEditor)
+        
+    def __contextMenuSaveAs(self):
+        """
+        Private method to save the selected tab to a new file.
+        """
+        if self.contextMenuEditor:
+            self.saveAsEditorEd(self.contextMenuEditor)
+        
+    def __contextMenuSaveAll(self):
+        """
+        Private method to save all tabs.
+        """
+        self.saveEditorsList(self.editors)
+        
+    def __contextMenuSaveToProject(self):
+        """
+        Private method to save the selected tab to the current project.
+        """
+        if self.contextMenuEditor:
+            self.saveEditorToProjectEd(self.contextMenuEditor)
+        
+    def __contextMenuPrintFile(self):
+        """
+        Private method to print the selected tab.
+        """
+        if self.contextMenuEditor:
+            self.printEditor(self.contextMenuEditor)
+        
+    def __currentChanged(self, index):
+        """
+        Private slot to handle the currentChanged signal.
+        
+        @param index index of the current editor
+        """
+        if index == -1 or not self.editors:
+            return
+        
+        editor = self.activeWindow()
+        if editor is None:
+            return
+        
+        self._checkActions(editor)
+        editor.setFocus()
+        fn = editor.getFileName()
+        if fn:
+            self.emit(SIGNAL('changeCaption'), fn)
+            if not self.__inRemoveView:
+                self.emit(SIGNAL('editorChanged'), fn)
+        else:
+            self.emit(SIGNAL('changeCaption'), "")
+        
+        cindex = self.editors.index(editor)
+        self.viewlist.setCurrentRow(cindex)
+        
+    def eventFilter(self, watched, event):
+        """
+        Method called to filter the event queue.
+        
+        @param watched the QObject being watched
+        @param event the event that occurred
+        @return flag indicating, if we handled the event
+        """
+        if event.type() == QEvent.MouseButtonPress and \
+           not event.button() == Qt.RightButton:
+            if isinstance(watched, QStackedWidget):
+                switched = watched is not self.currentStack
+                self.currentStack = watched
+            elif isinstance(watched, QScintilla.Editor.Editor):
+                for stack in self.stacks:
+                    if stack.hasEditor(watched):
+                        switched = stack is not self.currentStack
+                        self.currentStack = stack
+                        break
+            currentWidget = self.currentStack.currentWidget()
+            if currentWidget:
+                index = self.editors.index(currentWidget)
+                self.viewlist.setCurrentRow(index)
+            
+            aw = self.activeWindow()
+            if aw is not None:
+                self._checkActions(aw)
+                aw.setFocus()
+                fn = aw.getFileName()
+                if fn:
+                    self.emit(SIGNAL('changeCaption'), fn)
+                    if switched:
+                        self.emit(SIGNAL('editorChanged'), fn)
+                else:
+                    self.emit(SIGNAL('changeCaption'), "")
+        
+        return False

eric ide

mercurial