Example #1
0
def restore_splitter_sizes(window: QDialog, splitter: QSplitter):
    global cache
    if cache:
        symbol = window.__class__.__name__  + '_' + splitter.objectName()
        sizes = cache.get_value(symbol, None, list)
        if not isinstance(sizes, list) or len(sizes) != 2:
            sizes = [100, 100]
            if splitter.parent():
                if splitter.orientation() == Qt.Vertical:
                    sizes[0], sizes[1] = round(splitter.parent().height() / 2), round(splitter.parent().height() / 2)
                else:
                    sizes[0], sizes[1] = round(splitter.parent().width() / 2), round(splitter.parent().width() / 2)
        splitter.setSizes(sizes)
Example #2
0
    def decorate(splitter: QSplitter, index: int = 1):

        gripLength = 35
        gripWidth = 1  # may need to be 1 or 2 depending on theme
        gripSpacing = 0
        grips = 3

        splitter.setOpaqueResize(False)
        splitter.setChildrenCollapsible(False)
        splitter.setHandleWidth(7)
        handle = splitter.handle(index)
        orientation = splitter.orientation()
        layout = QHBoxLayout(handle)
        layout.setSpacing(gripSpacing)
        layout.setContentsMargins(0, 0, 0, 0)

        if orientation == Qt.Horizontal:
            for i in range(grips):
                line = QFrame(handle)
                line.setMinimumSize(gripWidth, gripLength)
                line.setMaximumSize(gripWidth, gripLength)
                line.setLineWidth(gripWidth)
                line.setFrameShape(line.StyledPanel)
                line.setStyleSheet("border: 1px solid lightgray;")
                layout.addWidget(line)
        else:
            # center the vertical grip by adding spacers before and after
            layout.addStretch()
            vBox = QVBoxLayout()

            for i in range(grips):
                line = QFrame(handle)
                line.setMinimumSize(gripLength, gripWidth)
                line.setMaximumSize(gripLength, gripWidth)
                line.setFrameShape(line.StyledPanel)
                line.setStyleSheet("border: 1px solid lightgray;")
                vBox.addWidget(line)
            layout.addLayout(vBox)
            layout.addStretch()
Example #3
0
class Listspace(QSplitter, ViewManager):
    """
    Class implementing the listspace viewmanager class.
    
    @signal changeCaption(str) emitted if a change of the caption is necessary
    @signal editorChanged(str) emitted when the current editor has changed
    @signal editorChangedEd(Editor) emitted when the current editor has changed
    @signal lastEditorClosed() emitted after the last editor window was closed
    @signal editorOpened(str) emitted after an editor window was opened
    @signal editorOpenedEd(Editor) emitted after an editor window was opened
    @signal editorClosed(str) emitted just before an editor window gets closed
    @signal editorClosedEd(Editor) emitted just before an editor window gets
        closed
    @signal editorRenamed(str) emitted after an editor was renamed
    @signal editorRenamedEd(Editor) emitted after an editor was renamed
    @signal editorSaved(str) emitted after an editor window was saved
    @signal editorSavedEd(Editor) emitted after an editor window was saved
    @signal checkActions(Editor) emitted when some actions should be checked
        for their status
    @signal cursorChanged(Editor) emitted after the cursor position of the
        active window has changed
    @signal breakpointToggled(Editor) emitted when a breakpoint is toggled.
    @signal bookmarkToggled(Editor) emitted when a bookmark is toggled.
    @signal syntaxerrorToggled(Editor) emitted when a syntax error is toggled.
    @signal previewStateChanged(bool) emitted to signal a change in the
        preview state
    @signal editorLanguageChanged(Editor) emitted to signal a change of an
        editors language
    @signal editorTextChanged(Editor) emitted to signal a change of an
        editor's text
    @signal editorLineChanged(str,int) emitted to signal a change of an
        editor's current line (line is given one based)
    """
    changeCaption = pyqtSignal(str)
    editorChanged = pyqtSignal(str)
    editorChangedEd = pyqtSignal(Editor)
    lastEditorClosed = pyqtSignal()
    editorOpened = pyqtSignal(str)
    editorOpenedEd = pyqtSignal(Editor)
    editorClosed = pyqtSignal(str)
    editorClosedEd = pyqtSignal(Editor)
    editorRenamed = pyqtSignal(str)
    editorRenamedEd = pyqtSignal(Editor)
    editorSaved = pyqtSignal(str)
    editorSavedEd = pyqtSignal(Editor)
    checkActions = pyqtSignal(Editor)
    cursorChanged = pyqtSignal(Editor)
    breakpointToggled = pyqtSignal(Editor)
    bookmarkToggled = pyqtSignal(Editor)
    syntaxerrorToggled = pyqtSignal(Editor)
    previewStateChanged = pyqtSignal(bool)
    editorLanguageChanged = pyqtSignal(Editor)
    editorTextChanged = pyqtSignal(Editor)
    editorLineChanged = pyqtSignal(str, int)

    def __init__(self, parent):
        """
        Constructor
        
        @param parent parent widget (QWidget)
        """
        self.stacks = []

        QSplitter.__init__(self, parent)
        ViewManager.__init__(self)
        self.setChildrenCollapsible(False)

        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.viewlist.currentRowChanged.connect(self.__showSelectedView)
        self.viewlist.customContextMenuRequested.connect(self.__showMenu)

        self.stackArea = QSplitter(self)
        self.stackArea.setChildrenCollapsible(False)
        self.addWidget(self.stackArea)
        self.stackArea.setOrientation(Qt.Vertical)
        stack = StackedWidget(self.stackArea)
        self.stackArea.addWidget(stack)
        self.stacks.append(stack)
        self.currentStack = stack
        stack.currentChanged.connect(self.__currentChanged)
        stack.installEventFilter(self)
        self.setSizes([int(self.width() * 0.2), int(self.width() * 0.8)])
        # 20% for viewlist, 80% for the editors
        self.__inRemoveView = False

        self.__initMenu()
        self.contextMenuEditor = None
        self.contextMenuIndex = -1

    def __initMenu(self):
        """
        Private method to initialize the viewlist context menu.
        """
        self.__menu = QMenu(self)
        self.__menu.addAction(UI.PixmapCache.getIcon("tabClose.png"),
                              self.tr('Close'), self.__contextMenuClose)
        self.closeOthersMenuAct = self.__menu.addAction(
            UI.PixmapCache.getIcon("tabCloseOther.png"),
            self.tr("Close Others"), self.__contextMenuCloseOthers)
        self.__menu.addAction(self.tr('Close All'), self.__contextMenuCloseAll)
        self.__menu.addSeparator()
        self.saveMenuAct = self.__menu.addAction(
            UI.PixmapCache.getIcon("fileSave.png"), self.tr('Save'),
            self.__contextMenuSave)
        self.__menu.addAction(UI.PixmapCache.getIcon("fileSaveAs.png"),
                              self.tr('Save As...'), self.__contextMenuSaveAs)
        self.__menu.addAction(UI.PixmapCache.getIcon("fileSaveAll.png"),
                              self.tr('Save All'), self.__contextMenuSaveAll)
        self.__menu.addSeparator()
        self.openRejectionsMenuAct = self.__menu.addAction(
            self.tr("Open 'rejection' file"), self.__contextMenuOpenRejections)
        self.__menu.addSeparator()
        self.__menu.addAction(UI.PixmapCache.getIcon("print.png"),
                              self.tr('Print'), self.__contextMenuPrintFile)
        self.__menu.addSeparator()
        self.copyPathAct = self.__menu.addAction(
            self.tr("Copy Path to Clipboard"),
            self.__contextMenuCopyPathToClipboard)

    def __showMenu(self, point):
        """
        Private slot to handle the customContextMenuRequested signal of
        the viewlist.
        
        @param point position to open the menu at (QPoint)
        """
        if self.editors:
            itm = self.viewlist.itemAt(point)
            if itm is not None:
                row = self.viewlist.row(itm)
                self.contextMenuEditor = self.editors[row]
                self.contextMenuIndex = row
                if self.contextMenuEditor:
                    self.saveMenuAct.setEnabled(
                        self.contextMenuEditor.isModified())
                    fileName = self.contextMenuEditor.getFileName()
                    self.copyPathAct.setEnabled(bool(fileName))
                    if fileName:
                        rej = "{0}.rej".format(fileName)
                        self.openRejectionsMenuAct.setEnabled(
                            os.path.exists(rej))
                    else:
                        self.openRejectionsMenuAct.setEnabled(False)

                    self.closeOthersMenuAct.setEnabled(
                        self.viewlist.count() > 1)

                    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].parent())

        aw = self.activeWindow()
        fn = aw and aw.getFileName() or None
        if fn:
            self.changeCaption.emit(fn)
            self.editorChanged.emit(fn)
            self.editorLineChanged.emit(fn, aw.getCursorPosition()[0] + 1)
        else:
            self.changeCaption.emit("")
        self.editorChangedEd.emit(aw)

    def _addView(self, win, fn=None, noName="", next=False):
        """
        Protected method to add a view (i.e. window).
        
        @param win editor assembly to be added
        @param fn filename of this editor (string)
        @param noName name to be used for an unnamed editor (string)
        @param next flag indicating to add the view next to the current
            view (bool)
        """
        editor = win.getEditor()
        if fn is None:
            if not noName:
                self.untitledCount += 1
                noName = self.tr("Untitled {0}").format(self.untitledCount)
            self.viewlist.addItem(noName)
            editor.setNoName(noName)
        else:
            txt = os.path.basename(fn)
            if not QFileInfo(fn).isWritable():
                txt = self.tr("{0} (ro)").format(txt)
            itm = QListWidgetItem(txt)
            itm.setToolTip(fn)
            self.viewlist.addItem(itm)
        self.currentStack.addWidget(win)
        self.currentStack.setCurrentWidget(win)
        editor.captionChanged.connect(self.__captionChange)
        editor.cursorLineChanged.connect(self.__cursorLineChanged)

        index = self.editors.index(editor)
        self.viewlist.setCurrentRow(index)
        editor.setFocus()
        if fn:
            self.changeCaption.emit(fn)
            self.editorChanged.emit(fn)
            self.editorLineChanged.emit(fn, editor.getCursorPosition()[0] + 1)
        else:
            self.changeCaption.emit("")
        self.editorChangedEd.emit(editor)

    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 __cursorLineChanged(self, lineno):
        """
        Private slot to handle a change of the current editor's cursor line.
        
        @param lineno line number of the current editor's cursor (zero based)
        """
        editor = self.sender()
        if editor:
            fn = editor.getFileName()
            if fn:
                self.editorLineChanged.emit(fn, lineno + 1)

    def _showView(self, win, fn=None):
        """
        Protected method to show a view (i.e. window).
        
        @param win editor assembly to be shown
        @param fn filename of this editor (string)
        """
        editor = win.getEditor()
        for stack in self.stacks:
            if stack.hasEditor(editor):
                stack.setCurrentWidget(win)
                self.currentStack = stack
                break
        index = self.editors.index(editor)
        self.viewlist.setCurrentRow(index)
        editor.setFocus()
        fn = editor.getFileName()
        if fn:
            self.changeCaption.emit(fn)
            self.editorChanged.emit(fn)
            self.editorLineChanged.emit(fn, editor.getCursorPosition()[0] + 1)
        else:
            self.changeCaption.emit("")
        self.editorChangedEd.emit(editor)

    def __showSelectedView(self, row):
        """
        Private slot called to show a view selected in the list.
        
        @param row row number of the item clicked on (integer)
        """
        if row != -1:
            self._showView(self.editors[row].parent())
            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):
        """
        Public method to change the displayed name of the editor.
        
        @param editor editor window to be changed
        @param newName new name to be shown (string)
        """
        if newName:
            currentRow = self.viewlist.currentRow()
            index = self.editors.index(editor)
            txt = os.path.basename(newName)
            if not QFileInfo(newName).isWritable():
                txt = self.tr("{0} (ro)").format(txt)
            itm = self.viewlist.item(index)
            itm.setText(txt)
            itm.setToolTip(newName)
            self.viewlist.setCurrentRow(currentRow)
            self.changeCaption.emit(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)
        keys = []
        if m:
            keys.append("fileModified.png")
        if editor.hasSyntaxErrors():
            keys.append("syntaxError22.png")
        elif editor.hasWarnings():
            keys.append("warning22.png")
        if not keys:
            keys.append("empty.png")
        self.viewlist.item(index).setIcon(UI.PixmapCache.getCombinedIcon(keys))
        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)
        keys = []
        if editor.isModified():
            keys.append("fileModified.png")
        if editor.hasSyntaxErrors():
            keys.append("syntaxError22.png")
        elif editor.hasWarnings():
            keys.append("warning22.png")
        if not keys:
            keys.append("empty.png")
        self.viewlist.item(index).setIcon(UI.PixmapCache.getCombinedIcon(keys))
        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
        stack.currentChanged.connect(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 getSplitOrientation(self):
        """
        Public method to get the orientation of the split view.
        
        @return orientation of the split (Qt.Horizontal or Qt.Vertical)
        """
        return self.stackArea.orientation()

    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 editor.
        """
        if self.contextMenuEditor:
            self.closeEditorWindow(self.contextMenuEditor)

    def __contextMenuCloseOthers(self):
        """
        Private method to close the other editors.
        """
        index = self.contextMenuIndex
        for i in list(range(self.viewlist.count() - 1, index, -1)) + \
                list(range(index - 1, -1, -1)):
            editor = self.editors[i]
            self.closeEditorWindow(editor)

    def __contextMenuCloseAll(self):
        """
        Private method to close all editors.
        """
        savedEditors = self.editors[:]
        for editor in savedEditors:
            self.closeEditorWindow(editor)

    def __contextMenuSave(self):
        """
        Private method to save the selected editor.
        """
        if self.contextMenuEditor:
            self.saveEditorEd(self.contextMenuEditor)

    def __contextMenuSaveAs(self):
        """
        Private method to save the selected editor to a new file.
        """
        if self.contextMenuEditor:
            self.saveAsEditorEd(self.contextMenuEditor)

    def __contextMenuSaveAll(self):
        """
        Private method to save all editors.
        """
        self.saveEditorsList(self.editors)

    def __contextMenuOpenRejections(self):
        """
        Private slot to open a rejections file associated with the selected
        editor.
        """
        if self.contextMenuEditor:
            fileName = self.contextMenuEditor.getFileName()
            if fileName:
                rej = "{0}.rej".format(fileName)
                if os.path.exists(rej):
                    self.openSourceFile(rej)

    def __contextMenuPrintFile(self):
        """
        Private method to print the selected editor.
        """
        if self.contextMenuEditor:
            self.printEditor(self.contextMenuEditor)

    def __contextMenuCopyPathToClipboard(self):
        """
        Private method to copy the file name of the selected editor to the
        clipboard.
        """
        if self.contextMenuEditor:
            fn = self.contextMenuEditor.getFileName()
            if fn:
                cb = QApplication.clipboard()
                cb.setText(fn)

    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.changeCaption.emit(fn)
            if not self.__inRemoveView:
                self.editorChanged.emit(fn)
                self.editorLineChanged.emit(fn,
                                            editor.getCursorPosition()[0] + 1)
        else:
            self.changeCaption.emit("")
        self.editorChangedEd.emit(editor)

        cindex = self.editors.index(editor)
        self.viewlist.setCurrentRow(cindex)

    def eventFilter(self, watched, event):
        """
        Public 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:
            switched = True
            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.changeCaption.emit(fn)
                    if switched:
                        self.editorChanged.emit(fn)
                        self.editorLineChanged.emit(
                            fn,
                            aw.getCursorPosition()[0] + 1)
                else:
                    self.changeCaption.emit("")
                self.editorChangedEd.emit(aw)

        return False
Example #4
0
class Tabview(ViewManager):
    """
    Class implementing a tabbed viewmanager class embedded in a splitter.
    
    @signal changeCaption(str) emitted if a change of the caption is necessary
    @signal editorChanged(str) emitted when the current editor has changed
    @signal editorChangedEd(Editor) emitted when the current editor has changed
    @signal lastEditorClosed() emitted after the last editor window was closed
    @signal editorOpened(str) emitted after an editor window was opened
    @signal editorOpenedEd(Editor) emitted after an editor window was opened
    @signal editorClosed(str) emitted just before an editor window gets closed
    @signal editorClosedEd(Editor) emitted just before an editor window gets
        closed
    @signal editorRenamed(str) emitted after an editor was renamed
    @signal editorRenamedEd(Editor) emitted after an editor was renamed
    @signal editorSaved(str) emitted after an editor window was saved
    @signal editorSavedEd(Editor) emitted after an editor window was saved
    @signal checkActions(Editor) emitted when some actions should be checked
        for their status
    @signal cursorChanged(Editor) emitted after the cursor position of the
        active window has changed
    @signal breakpointToggled(Editor) emitted when a breakpoint is toggled.
    @signal bookmarkToggled(Editor) emitted when a bookmark is toggled.
    @signal syntaxerrorToggled(Editor) emitted when a syntax error is toggled.
    @signal previewStateChanged(bool) emitted to signal a change in the
        preview state
    @signal editorLanguageChanged(Editor) emitted to signal a change of an
        editors language
    @signal editorTextChanged(Editor) emitted to signal a change of an
        editor's text
    @signal editorLineChanged(str,int) emitted to signal a change of an
        editor's current line (line is given one based)
    """
    changeCaption = pyqtSignal(str)
    editorChanged = pyqtSignal(str)
    editorChangedEd = pyqtSignal(Editor)
    lastEditorClosed = pyqtSignal()
    editorOpened = pyqtSignal(str)
    editorOpenedEd = pyqtSignal(Editor)
    editorClosed = pyqtSignal(str)
    editorClosedEd = pyqtSignal(Editor)
    editorRenamed = pyqtSignal(str)
    editorRenamedEd = pyqtSignal(Editor)
    editorSaved = pyqtSignal(str)
    editorSavedEd = pyqtSignal(Editor)
    checkActions = pyqtSignal(Editor)
    cursorChanged = pyqtSignal(Editor)
    breakpointToggled = pyqtSignal(Editor)
    bookmarkToggled = pyqtSignal(Editor)
    syntaxerrorToggled = pyqtSignal(Editor)
    previewStateChanged = pyqtSignal(bool)
    editorLanguageChanged = pyqtSignal(Editor)
    editorTextChanged = pyqtSignal(Editor)
    editorLineChanged = pyqtSignal(str, int)

    def __init__(self, parent):
        """
        Constructor
        
        @param parent parent widget (QWidget)
        """
        self.tabWidgets = []

        self.__splitter = QSplitter(parent)
        ViewManager.__init__(self)
        self.__splitter.setChildrenCollapsible(False)

        tw = TabWidget(self)
        self.__splitter.addWidget(tw)
        self.tabWidgets.append(tw)
        self.currentTabWidget = tw
        self.currentTabWidget.showIndicator(True)
        tw.currentChanged.connect(self.__currentChanged)
        tw.installEventFilter(self)
        tw.tabBar().installEventFilter(self)
        self.__splitter.setOrientation(Qt.Vertical)
        self.__inRemoveView = False

        self.maxFileNameChars = Preferences.getUI(
            "TabViewManagerFilenameLength")
        self.filenameOnly = Preferences.getUI("TabViewManagerFilenameOnly")

    def mainWidget(self):
        """
        Public method to return a reference to the main Widget of a
        specific view manager subclass.
        
        @return reference to the main widget
        @rtype QWidget
        """
        return self.__splitter

    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).
        """
        for win in self.editors:
            self._removeView(win)

    def _removeView(self, win):
        """
        Protected method to remove a view (i.e. window).
        
        @param win editor window to be removed
        """
        self.__inRemoveView = True
        for tw in self.tabWidgets:
            if tw.hasEditor(win):
                tw.removeWidget(win)
                break
        win.closeIt()
        self.__inRemoveView = False

        # if this was the last editor in this view, switch to the next, that
        # still has open editors
        for i in list(range(self.tabWidgets.index(tw), -1, -1)) + \
            list(range(self.tabWidgets.index(tw) + 1,
                 len(self.tabWidgets))):
            if self.tabWidgets[i].hasEditors():
                self.currentTabWidget.showIndicator(False)
                self.currentTabWidget = self.tabWidgets[i]
                self.currentTabWidget.showIndicator(True)
                self.activeWindow().setFocus()
                break

        aw = self.activeWindow()
        fn = aw and aw.getFileName() or None
        if fn:
            self.changeCaption.emit(fn)
            self.editorChanged.emit(fn)
            self.editorLineChanged.emit(fn, aw.getCursorPosition()[0] + 1)
        else:
            self.changeCaption.emit("")
        self.editorChangedEd.emit(aw)

    def _addView(self, win, fn=None, noName="", next=False):
        """
        Protected method to add a view (i.e. window).
        
        @param win editor assembly to be added
        @param fn filename of this editor (string)
        @param noName name to be used for an unnamed editor (string)
        @param next flag indicating to add the view next to the current
            view (bool)
        """
        editor = win.getEditor()
        if fn is None:
            if not noName:
                self.untitledCount += 1
                noName = self.tr("Untitled {0}").format(self.untitledCount)
            if next:
                index = self.currentTabWidget.currentIndex() + 1
                self.currentTabWidget.insertWidget(index, win, noName)
            else:
                self.currentTabWidget.addTab(win, noName)
            editor.setNoName(noName)
        else:
            if self.filenameOnly:
                txt = os.path.basename(fn)
            else:
                txt = e5App().getObject("Project").getRelativePath(fn)
            if len(txt) > self.maxFileNameChars:
                txt = "...{0}".format(txt[-self.maxFileNameChars:])
            if not QFileInfo(fn).isWritable():
                txt = self.tr("{0} (ro)").format(txt)
            if next:
                index = self.currentTabWidget.currentIndex() + 1
                self.currentTabWidget.insertWidget(index, win, txt)
            else:
                self.currentTabWidget.addTab(win, txt)
            index = self.currentTabWidget.indexOf(win)
            self.currentTabWidget.setTabToolTip(index, fn)
        self.currentTabWidget.setCurrentWidget(win)
        win.show()
        editor.setFocus()
        if fn:
            self.changeCaption.emit(fn)
            self.editorChanged.emit(fn)
            self.editorLineChanged.emit(fn, editor.getCursorPosition()[0] + 1)
        else:
            self.changeCaption.emit("")
        self.editorChangedEd.emit(editor)

    def insertView(self, win, tabWidget, index, fn=None, noName=""):
        """
        Public method to add a view (i.e. window).
        
        @param win editor assembly to be inserted
        @param tabWidget reference to the tab widget to insert the editor into
            (TabWidget)
        @param index index position to insert at (integer)
        @param fn filename of this editor (string)
        @param noName name to be used for an unnamed editor (string)
        """
        editor = win.getEditor()
        if fn is None:
            if not noName:
                self.untitledCount += 1
                noName = self.tr("Untitled {0}").format(self.untitledCount)
            tabWidget.insertWidget(index, win, noName)
            editor.setNoName(noName)
        else:
            if self.filenameOnly:
                txt = os.path.basename(fn)
            else:
                txt = e5App().getObject("Project").getRelativePath(fn)
            if len(txt) > self.maxFileNameChars:
                txt = "...{0}".format(txt[-self.maxFileNameChars:])
            if not QFileInfo(fn).isWritable():
                txt = self.tr("{0} (ro)").format(txt)
            nindex = tabWidget.insertWidget(index, win, txt)
            tabWidget.setTabToolTip(nindex, fn)
        tabWidget.setCurrentWidget(win)
        win.show()
        editor.setFocus()
        if fn:
            self.changeCaption.emit(fn)
            self.editorChanged.emit(fn)
            self.editorLineChanged.emit(fn, editor.getCursorPosition()[0] + 1)
        else:
            self.changeCaption.emit("")
        self.editorChangedEd.emit(editor)

        self._modificationStatusChanged(editor.isModified(), editor)
        self._checkActions(editor)

    def _showView(self, win, fn=None):
        """
        Protected method to show a view (i.e. window).
        
        @param win editor assembly to be shown
        @param fn filename of this editor (string)
        """
        win.show()
        editor = win.getEditor()
        for tw in self.tabWidgets:
            if tw.hasEditor(editor):
                tw.setCurrentWidget(win)
                self.currentTabWidget.showIndicator(False)
                self.currentTabWidget = tw
                self.currentTabWidget.showIndicator(True)
                break
        editor.setFocus()

    def activeWindow(self):
        """
        Public method to return the active (i.e. current) window.
        
        @return reference to the active editor
        """
        cw = self.currentTabWidget.currentWidget()
        if cw:
            return cw.getEditor()
        else:
            return None

    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):
        """
        Public method to change the displayed name of the editor.
        
        @param editor editor window to be changed
        @param newName new name to be shown (string)
        """
        if newName:
            if self.filenameOnly:
                tabName = os.path.basename(newName)
            else:
                tabName = e5App().getObject("Project").getRelativePath(newName)
            if len(tabName) > self.maxFileNameChars:
                tabName = "...{0}".format(tabName[-self.maxFileNameChars:])
            index = self.currentTabWidget.indexOf(editor)
            self.currentTabWidget.setTabText(index, tabName)
            self.currentTabWidget.setTabToolTip(index, newName)
            self.changeCaption.emit(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
        """
        for tw in self.tabWidgets:
            if tw.hasEditor(editor):
                break
        index = tw.indexOf(editor)
        keys = []
        if m:
            keys.append("fileModified.png")
        if editor.hasSyntaxErrors():
            keys.append("syntaxError22.png")
        elif editor.hasWarnings():
            keys.append("warning22.png")
        if not keys:
            keys.append("empty.png")
        tw.setTabIcon(index, UI.PixmapCache.getCombinedIcon(keys))
        self._checkActions(editor)

    def _syntaxErrorToggled(self, editor):
        """
        Protected slot to handle the syntaxerrorToggled signal.
        
        @param editor editor that sent the signal
        """
        for tw in self.tabWidgets:
            if tw.hasEditor(editor):
                break
        index = tw.indexOf(editor)
        keys = []
        if editor.isModified():
            keys.append("fileModified.png")
        if editor.hasSyntaxErrors():
            keys.append("syntaxError22.png")
        elif editor.hasWarnings():
            keys.append("warning22.png")
        if not keys:
            keys.append("empty.png")
        tw.setTabIcon(index, UI.PixmapCache.getCombinedIcon(keys))

        ViewManager._syntaxErrorToggled(self, editor)

    def addSplit(self):
        """
        Public method used to split the current view.
        """
        tw = TabWidget(self)
        tw.show()
        self.__splitter.addWidget(tw)
        self.tabWidgets.append(tw)
        self.currentTabWidget.showIndicator(False)
        self.currentTabWidget = self.tabWidgets[-1]
        self.currentTabWidget.showIndicator(True)
        tw.currentChanged.connect(self.__currentChanged)
        tw.installEventFilter(self)
        tw.tabBar().installEventFilter(self)
        if self.__splitter.orientation() == Qt.Horizontal:
            size = self.width()
        else:
            size = self.height()
        self.__splitter.setSizes([int(size / len(self.tabWidgets))] *
                                 len(self.tabWidgets))
        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.tabWidgets) > 1:
            tw = self.currentTabWidget
            res = True
            savedEditors = tw.editors[:]
            for editor in savedEditors:
                res &= self.closeEditor(editor)
            if res:
                try:
                    i = self.tabWidgets.index(tw)
                except ValueError:
                    return True
                if i == len(self.tabWidgets) - 1:
                    i -= 1
                self.tabWidgets.remove(tw)
                tw.close()
                self.currentTabWidget = self.tabWidgets[i]
                for tw in self.tabWidgets:
                    tw.showIndicator(tw == self.currentTabWidget)
                if self.currentTabWidget is not None:
                    assembly = self.currentTabWidget.currentWidget()
                    if assembly is not None:
                        editor = assembly.getEditor()
                        if editor is not None:
                            editor.setFocus(Qt.OtherFocusReason)
                if len(self.tabWidgets) == 1:
                    self.splitRemoveAct.setEnabled(False)
                    self.nextSplitAct.setEnabled(False)
                    self.prevSplitAct.setEnabled(False)
                return True

        return False

    def getSplitOrientation(self):
        """
        Public method to get the orientation of the split view.
        
        @return orientation of the split (Qt.Horizontal or Qt.Vertical)
        """
        return self.__splitter.orientation()

    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.__splitter.setOrientation(orientation)

    def nextSplit(self):
        """
        Public slot used to move to the next split.
        """
        aw = self.activeWindow()
        _hasFocus = aw and aw.hasFocus()
        ind = self.tabWidgets.index(self.currentTabWidget) + 1
        if ind == len(self.tabWidgets):
            ind = 0

        self.currentTabWidget.showIndicator(False)
        self.currentTabWidget = self.tabWidgets[ind]
        self.currentTabWidget.showIndicator(True)
        if _hasFocus:
            aw = self.activeWindow()
            if aw:
                aw.setFocus()

    def prevSplit(self):
        """
        Public slot used to move to the previous split.
        """
        aw = self.activeWindow()
        _hasFocus = aw and aw.hasFocus()
        ind = self.tabWidgets.index(self.currentTabWidget) - 1
        if ind == -1:
            ind = len(self.tabWidgets) - 1

        self.currentTabWidget.showIndicator(False)
        self.currentTabWidget = self.tabWidgets[ind]
        self.currentTabWidget.showIndicator(True)
        if _hasFocus:
            aw = self.activeWindow()
            if aw:
                aw.setFocus()

    def __currentChanged(self, index):
        """
        Private slot to handle the currentChanged signal.
        
        @param index index of the current tab (integer)
        """
        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.changeCaption.emit(fn)
            if not self.__inRemoveView:
                self.editorChanged.emit(fn)
                self.editorLineChanged.emit(fn,
                                            editor.getCursorPosition()[0] + 1)
        else:
            self.changeCaption.emit("")
        self.editorChangedEd.emit(editor)

    def eventFilter(self, watched, event):
        """
        Public method called to filter the event queue.
        
        @param watched the QObject being watched (QObject)
        @param event the event that occurred (QEvent)
        @return always False
        """
        if event.type() == QEvent.MouseButtonPress and \
           not event.button() == Qt.RightButton:
            switched = True
            self.currentTabWidget.showIndicator(False)
            if isinstance(watched, E5TabWidget):
                switched = watched is not self.currentTabWidget
                self.currentTabWidget = watched
            elif isinstance(watched, QTabBar):
                switched = watched.parent() is not self.currentTabWidget
                self.currentTabWidget = watched.parent()
                if switched:
                    index = self.currentTabWidget.selectTab(event.pos())
                    switched = self.currentTabWidget.widget(index) is \
                        self.activeWindow()
            elif isinstance(watched, QScintilla.Editor.Editor):
                for tw in self.tabWidgets:
                    if tw.hasEditor(watched):
                        switched = tw is not self.currentTabWidget
                        self.currentTabWidget = tw
                        break
            self.currentTabWidget.showIndicator(True)

            aw = self.activeWindow()
            if aw is not None:
                self._checkActions(aw)
                aw.setFocus()
                fn = aw.getFileName()
                if fn:
                    self.changeCaption.emit(fn)
                    if switched:
                        self.editorChanged.emit(fn)
                        self.editorLineChanged.emit(
                            fn,
                            aw.getCursorPosition()[0] + 1)
                else:
                    self.changeCaption.emit("")
                self.editorChangedEd.emit(aw)

        return False

    def preferencesChanged(self):
        """
        Public slot to handle the preferencesChanged signal.
        """
        ViewManager.preferencesChanged(self)

        self.maxFileNameChars = Preferences.getUI(
            "TabViewManagerFilenameLength")
        self.filenameOnly = Preferences.getUI("TabViewManagerFilenameOnly")

        for tabWidget in self.tabWidgets:
            for index in range(tabWidget.count()):
                editor = tabWidget.widget(index)
                if isinstance(editor, QScintilla.Editor.Editor):
                    fn = editor.getFileName()
                    if fn:
                        if self.filenameOnly:
                            txt = os.path.basename(fn)
                        else:
                            txt = e5App().getObject("Project")\
                                .getRelativePath(fn)
                        if len(txt) > self.maxFileNameChars:
                            txt = "...{0}".format(txt[-self.maxFileNameChars:])
                        if not QFileInfo(fn).isWritable():
                            txt = self.tr("{0} (ro)").format(txt)
                        tabWidget.setTabText(index, txt)

    def getTabWidgetById(self, id_):
        """
        Public method to get a reference to a tab widget knowing its ID.
        
        @param id_ id of the tab widget (long)
        @return reference to the tab widget (TabWidget)
        """
        for tw in self.tabWidgets:
            if id(tw) == id_:
                return tw
        return None
class _s_CentralWidget(QWidget):

###############################################################################
# CentralWidget SIGNALS
###############################################################################

    """
    splitterCentralRotated()
    """
    splitterCentralRotated = pyqtSignal()

###############################################################################

    def __init__(self, parent=None):
        super(_s_CentralWidget, self).__init__(parent)
        self.parent = parent
        #This variables are used to save the splitter sizes before hide
        self._splitterMainSizes = None
        self._splitterAreaSizes = None
        self.lateralPanel = None

        hbox = QHBoxLayout(self)
        hbox.setContentsMargins(0, 0, 0, 0)
        hbox.setSpacing(0)
        #Create Splitters to divide the UI in: MainPanel, Explorer, Misc
        self._splitterArea = QSplitter(Qt.Horizontal)
        self._splitterMain = QSplitter(Qt.Vertical)

        #Create scrollbar for follow mode
        self.scrollBar = QScrollBar(Qt.Vertical, self)
        self.scrollBar.setFixedWidth(20)
        self.scrollBar.setToolTip('Follow Mode: Scroll the Editors together')
        self.scrollBar.hide()
        self.scrollBar.valueChanged[int].connect(self.move_follow_scrolls)

        #Add to Main Layout
        hbox.addWidget(self.scrollBar)
        hbox.addWidget(self._splitterArea)

    def insert_central_container(self, container):
        self.mainContainer = container
        self._splitterMain.insertWidget(0, container)

    def insert_lateral_container(self, container):
        self.lateralPanel = LateralPanel(container)
        self._splitterArea.insertWidget(0, self.lateralPanel)

    def insert_bottom_container(self, container):
        self.misc = container
        self._splitterMain.insertWidget(1, container)

    def showEvent(self, event):
        #Show Event
        QWidget.showEvent(self, event)
        #Avoid recalculate the panel sizes if they are already loaded
        if self._splitterArea.count() == 2:
            return
        #Rearrange widgets on Window
        self._splitterArea.insertWidget(0, self._splitterMain)
        if not event.spontaneous():
            self.change_misc_visibility()
        if bin(settings.UI_LAYOUT)[-1] == '1':
            self.splitter_central_rotate()
        if bin(settings.UI_LAYOUT >> 1)[-1] == '1':
            self.splitter_misc_rotate()
        if bin(settings.UI_LAYOUT >> 2)[-1] == '1':
            self.splitter_central_orientation()
        qsettings = QSettings(resources.SETTINGS_PATH, QSettings.IniFormat)
        #Lists of sizes as list of QVariant- heightList = [QVariant, QVariant]
        heightList = list(qsettings.value("window/central/mainSize",
            [(self.height() / 3) * 2, self.height() / 3]))
        widthList = list(qsettings.value("window/central/areaSize",
            [(self.width() / 6) * 5, self.width() / 6]))
        self._splitterMainSizes = [int(heightList[0]), int(heightList[1])]
        self._splitterAreaSizes = [int(widthList[0]), int(widthList[1])]
        #Set the sizes to splitters
        #self._splitterMain.setSizes(self._splitterMainSizes)
        self._splitterMain.setSizes(self._splitterMainSizes)
        self._splitterArea.setSizes(self._splitterAreaSizes)
        self.misc.setVisible(
            qsettings.value("window/show_misc", False, type=bool))

    def change_misc_visibility(self, on_start=False):
        if self.misc.isVisible():
            self._splitterMainSizes = self._splitterMain.sizes()
            self.misc.hide()
            widget = self.mainContainer.get_actual_widget()
            if widget:
                widget.setFocus()
        else:
            self.misc.show()
            self.misc.gain_focus()

    def change_main_visibility(self):
        if self.mainContainer.isVisible():
            self.mainContainer.hide()
        else:
            self.mainContainer.show()

    def change_explorer_visibility(self, force_hide=False):
        if self.lateralPanel.isVisible() or force_hide:
            self._splitterAreaSizes = self._splitterArea.sizes()
            self.lateralPanel.hide()
        else:
            self.lateralPanel.show()

    def splitter_central_rotate(self):
        w1, w2 = self._splitterArea.widget(0), self._splitterArea.widget(1)
        self._splitterArea.insertWidget(0, w2)
        self._splitterArea.insertWidget(1, w1)
        self.splitterCentralRotated.emit()

    def splitter_central_orientation(self):
        if self._splitterArea.orientation() == Qt.Horizontal:
            self._splitterArea.setOrientation(Qt.Vertical)
        else:
            self._splitterArea.setOrientation(Qt.Horizontal)

    def splitter_misc_rotate(self):
        w1, w2 = self._splitterMain.widget(0), self._splitterMain.widget(1)
        self._splitterMain.insertWidget(0, w2)
        self._splitterMain.insertWidget(1, w1)

    def splitter_misc_orientation(self):
        if self._splitterMain.orientation() == Qt.Horizontal:
            self._splitterMain.setOrientation(Qt.Vertical)
        else:
            self._splitterMain.setOrientation(Qt.Horizontal)

    def get_area_sizes(self):
        if self.lateralPanel.isVisible():
            self._splitterAreaSizes = self._splitterArea.sizes()
        return self._splitterAreaSizes

    def get_main_sizes(self):
        if self.misc.isVisible():
            self._splitterMainSizes = self._splitterMain.sizes()
        return self._splitterMainSizes

    def enable_follow_mode_scrollbar(self, val):
        if val:
            editorWidget = self.mainContainer.get_actual_editor()
            maxScroll = editorWidget.verticalScrollBar().maximum()
            position = editorWidget.verticalScrollBar().value()
            self.scrollBar.setMaximum(maxScroll)
            self.scrollBar.setValue(position)
        self.scrollBar.setVisible(val)

    def move_follow_scrolls(self, val):
        widget = self.mainContainer._tabMain.currentWidget()
        diff = widget._sidebarWidget.highest_line - val
        s1 = self.mainContainer._tabMain.currentWidget().verticalScrollBar()
        s2 = self.mainContainer._tabSecondary.\
            currentWidget().verticalScrollBar()
        s1.setValue(val)
        s2.setValue(val + diff)
Example #6
0
class Listspace(QSplitter, ViewManager):
    """
    Class implementing the listspace viewmanager class.
    
    @signal changeCaption(str) emitted if a change of the caption is necessary
    @signal editorChanged(str) emitted when the current editor has changed
    @signal editorChangedEd(Editor) emitted when the current editor has changed
    @signal lastEditorClosed() emitted after the last editor window was closed
    @signal editorOpened(str) emitted after an editor window was opened
    @signal editorOpenedEd(Editor) emitted after an editor window was opened
    @signal editorClosed(str) emitted just before an editor window gets closed
    @signal editorClosedEd(Editor) emitted just before an editor window gets
        closed
    @signal editorRenamed(str) emitted after an editor was renamed
    @signal editorRenamedEd(Editor) emitted after an editor was renamed
    @signal editorSaved(str) emitted after an editor window was saved
    @signal editorSavedEd(Editor) emitted after an editor window was saved
    @signal checkActions(Editor) emitted when some actions should be checked
        for their status
    @signal cursorChanged(Editor) emitted after the cursor position of the
        active window has changed
    @signal breakpointToggled(Editor) emitted when a breakpoint is toggled.
    @signal bookmarkToggled(Editor) emitted when a bookmark is toggled.
    @signal syntaxerrorToggled(Editor) emitted when a syntax error is toggled.
    @signal previewStateChanged(bool) emitted to signal a change in the
        preview state
    @signal editorLanguageChanged(Editor) emitted to signal a change of an
        editors language
    @signal editorTextChanged(Editor) emitted to signal a change of an
        editor's text
    @signal editorLineChanged(str,int) emitted to signal a change of an
        editor's current line (line is given one based)
    """
    changeCaption = pyqtSignal(str)
    editorChanged = pyqtSignal(str)
    editorChangedEd = pyqtSignal(Editor)
    lastEditorClosed = pyqtSignal()
    editorOpened = pyqtSignal(str)
    editorOpenedEd = pyqtSignal(Editor)
    editorClosed = pyqtSignal(str)
    editorClosedEd = pyqtSignal(Editor)
    editorRenamed = pyqtSignal(str)
    editorRenamedEd = pyqtSignal(Editor)
    editorSaved = pyqtSignal(str)
    editorSavedEd = pyqtSignal(Editor)
    checkActions = pyqtSignal(Editor)
    cursorChanged = pyqtSignal(Editor)
    breakpointToggled = pyqtSignal(Editor)
    bookmarkToggled = pyqtSignal(Editor)
    syntaxerrorToggled = pyqtSignal(Editor)
    previewStateChanged = pyqtSignal(bool)
    editorLanguageChanged = pyqtSignal(Editor)
    editorTextChanged = pyqtSignal(Editor)
    editorLineChanged = pyqtSignal(str, int)
    
    def __init__(self, parent):
        """
        Constructor
        
        @param parent parent widget (QWidget)
        """
        self.stacks = []
        
        QSplitter.__init__(self, parent)
        ViewManager.__init__(self)
        self.setChildrenCollapsible(False)
        
        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.viewlist.currentRowChanged.connect(self.__showSelectedView)
        self.viewlist.customContextMenuRequested.connect(self.__showMenu)
        
        self.stackArea = QSplitter(self)
        self.stackArea.setChildrenCollapsible(False)
        self.addWidget(self.stackArea)
        self.stackArea.setOrientation(Qt.Vertical)
        stack = StackedWidget(self.stackArea)
        self.stackArea.addWidget(stack)
        self.stacks.append(stack)
        self.currentStack = stack
        stack.currentChanged.connect(self.__currentChanged)
        stack.installEventFilter(self)
        self.setSizes([int(self.width() * 0.2), int(self.width() * 0.8)])
        # 20% for viewlist, 80% for the editors
        self.__inRemoveView = False
        
        self.__initMenu()
        self.contextMenuEditor = None
        self.contextMenuIndex = -1
        
    def __initMenu(self):
        """
        Private method to initialize the viewlist context menu.
        """
        self.__menu = QMenu(self)
        self.__menu.addAction(
            UI.PixmapCache.getIcon("tabClose.png"),
            self.tr('Close'), self.__contextMenuClose)
        self.closeOthersMenuAct = self.__menu.addAction(
            UI.PixmapCache.getIcon("tabCloseOther.png"),
            self.tr("Close Others"),
            self.__contextMenuCloseOthers)
        self.__menu.addAction(
            self.tr('Close All'), self.__contextMenuCloseAll)
        self.__menu.addSeparator()
        self.saveMenuAct = self.__menu.addAction(
            UI.PixmapCache.getIcon("fileSave.png"),
            self.tr('Save'), self.__contextMenuSave)
        self.__menu.addAction(
            UI.PixmapCache.getIcon("fileSaveAs.png"),
            self.tr('Save As...'), self.__contextMenuSaveAs)
        self.__menu.addAction(
            UI.PixmapCache.getIcon("fileSaveAll.png"),
            self.tr('Save All'), self.__contextMenuSaveAll)
        self.__menu.addSeparator()
        self.openRejectionsMenuAct = self.__menu.addAction(
            self.tr("Open 'rejection' file"),
            self.__contextMenuOpenRejections)
        self.__menu.addSeparator()
        self.__menu.addAction(
            UI.PixmapCache.getIcon("print.png"),
            self.tr('Print'), self.__contextMenuPrintFile)
        self.__menu.addSeparator()
        self.copyPathAct = self.__menu.addAction(
            self.tr("Copy Path to Clipboard"),
            self.__contextMenuCopyPathToClipboard)
        
    def __showMenu(self, point):
        """
        Private slot to handle the customContextMenuRequested signal of
        the viewlist.
        
        @param point position to open the menu at (QPoint)
        """
        if self.editors:
            itm = self.viewlist.itemAt(point)
            if itm is not None:
                row = self.viewlist.row(itm)
                self.contextMenuEditor = self.editors[row]
                self.contextMenuIndex = row
                if self.contextMenuEditor:
                    self.saveMenuAct.setEnabled(
                        self.contextMenuEditor.isModified())
                    fileName = self.contextMenuEditor.getFileName()
                    self.copyPathAct.setEnabled(bool(fileName))
                    if fileName:
                        rej = "{0}.rej".format(fileName)
                        self.openRejectionsMenuAct.setEnabled(
                            os.path.exists(rej))
                    else:
                        self.openRejectionsMenuAct.setEnabled(False)
                    
                    self.closeOthersMenuAct.setEnabled(
                        self.viewlist.count() > 1)
                    
                    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].parent())
        
        aw = self.activeWindow()
        fn = aw and aw.getFileName() or None
        if fn:
            self.changeCaption.emit(fn)
            self.editorChanged.emit(fn)
            self.editorLineChanged.emit(fn, aw.getCursorPosition()[0] + 1)
        else:
            self.changeCaption.emit("")
        self.editorChangedEd.emit(aw)
        
    def _addView(self, win, fn=None, noName="", next=False):
        """
        Protected method to add a view (i.e. window).
        
        @param win editor assembly to be added
        @param fn filename of this editor (string)
        @param noName name to be used for an unnamed editor (string)
        @param next flag indicating to add the view next to the current
            view (bool)
        """
        editor = win.getEditor()
        if fn is None:
            if not noName:
                self.untitledCount += 1
                noName = self.tr("Untitled {0}").format(self.untitledCount)
            self.viewlist.addItem(noName)
            editor.setNoName(noName)
        else:
            txt = os.path.basename(fn)
            if not QFileInfo(fn).isWritable():
                txt = self.tr("{0} (ro)").format(txt)
            itm = QListWidgetItem(txt)
            itm.setToolTip(fn)
            self.viewlist.addItem(itm)
        self.currentStack.addWidget(win)
        self.currentStack.setCurrentWidget(win)
        editor.captionChanged.connect(self.__captionChange)
        editor.cursorLineChanged.connect(self.__cursorLineChanged)
        
        index = self.editors.index(editor)
        self.viewlist.setCurrentRow(index)
        editor.setFocus()
        if fn:
            self.changeCaption.emit(fn)
            self.editorChanged.emit(fn)
            self.editorLineChanged.emit(fn, editor.getCursorPosition()[0] + 1)
        else:
            self.changeCaption.emit("")
        self.editorChangedEd.emit(editor)
        
    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 __cursorLineChanged(self, lineno):
        """
        Private slot to handle a change of the current editor's cursor line.
        
        @param lineno line number of the current editor's cursor (zero based)
        """
        editor = self.sender()
        if editor:
            fn = editor.getFileName()
            if fn:
                self.editorLineChanged.emit(fn, lineno + 1)
        
    def _showView(self, win, fn=None):
        """
        Protected method to show a view (i.e. window).
        
        @param win editor assembly to be shown
        @param fn filename of this editor (string)
        """
        editor = win.getEditor()
        for stack in self.stacks:
            if stack.hasEditor(editor):
                stack.setCurrentWidget(win)
                self.currentStack = stack
                break
        index = self.editors.index(editor)
        self.viewlist.setCurrentRow(index)
        editor.setFocus()
        fn = editor.getFileName()
        if fn:
            self.changeCaption.emit(fn)
            self.editorChanged.emit(fn)
            self.editorLineChanged.emit(fn, editor.getCursorPosition()[0] + 1)
        else:
            self.changeCaption.emit("")
        self.editorChangedEd.emit(editor)
        
    def __showSelectedView(self, row):
        """
        Private slot called to show a view selected in the list.
        
        @param row row number of the item clicked on (integer)
        """
        if row != -1:
            self._showView(self.editors[row].parent())
            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):
        """
        Public method to change the displayed name of the editor.
        
        @param editor editor window to be changed
        @param newName new name to be shown (string)
        """
        if newName:
            currentRow = self.viewlist.currentRow()
            index = self.editors.index(editor)
            txt = os.path.basename(newName)
            if not QFileInfo(newName).isWritable():
                txt = self.tr("{0} (ro)").format(txt)
            itm = self.viewlist.item(index)
            itm.setText(txt)
            itm.setToolTip(newName)
            self.viewlist.setCurrentRow(currentRow)
            self.changeCaption.emit(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)
        keys = []
        if m:
            keys.append("fileModified.png")
        if editor.hasSyntaxErrors():
            keys.append("syntaxError22.png")
        elif editor.hasWarnings():
            keys.append("warning22.png")
        if not keys:
            keys.append("empty.png")
        self.viewlist.item(index).setIcon(
            UI.PixmapCache.getCombinedIcon(keys))
        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)
        keys = []
        if editor.isModified():
            keys.append("fileModified.png")
        if editor.hasSyntaxErrors():
            keys.append("syntaxError22.png")
        elif editor.hasWarnings():
            keys.append("warning22.png")
        if not keys:
            keys.append("empty.png")
        self.viewlist.item(index).setIcon(
            UI.PixmapCache.getCombinedIcon(keys))
        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
        stack.currentChanged.connect(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 getSplitOrientation(self):
        """
        Public method to get the orientation of the split view.
        
        @return orientation of the split (Qt.Horizontal or Qt.Vertical)
        """
        return self.stackArea.orientation()
        
    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 editor.
        """
        if self.contextMenuEditor:
            self.closeEditorWindow(self.contextMenuEditor)
        
    def __contextMenuCloseOthers(self):
        """
        Private method to close the other editors.
        """
        index = self.contextMenuIndex
        for i in list(range(self.viewlist.count() - 1, index, -1)) + \
                list(range(index - 1, -1, -1)):
            editor = self.editors[i]
            self.closeEditorWindow(editor)
        
    def __contextMenuCloseAll(self):
        """
        Private method to close all editors.
        """
        savedEditors = self.editors[:]
        for editor in savedEditors:
            self.closeEditorWindow(editor)
        
    def __contextMenuSave(self):
        """
        Private method to save the selected editor.
        """
        if self.contextMenuEditor:
            self.saveEditorEd(self.contextMenuEditor)
        
    def __contextMenuSaveAs(self):
        """
        Private method to save the selected editor to a new file.
        """
        if self.contextMenuEditor:
            self.saveAsEditorEd(self.contextMenuEditor)
        
    def __contextMenuSaveAll(self):
        """
        Private method to save all editors.
        """
        self.saveEditorsList(self.editors)
        
    def __contextMenuOpenRejections(self):
        """
        Private slot to open a rejections file associated with the selected
        editor.
        """
        if self.contextMenuEditor:
            fileName = self.contextMenuEditor.getFileName()
            if fileName:
                rej = "{0}.rej".format(fileName)
                if os.path.exists(rej):
                    self.openSourceFile(rej)
        
    def __contextMenuPrintFile(self):
        """
        Private method to print the selected editor.
        """
        if self.contextMenuEditor:
            self.printEditor(self.contextMenuEditor)
        
    def __contextMenuCopyPathToClipboard(self):
        """
        Private method to copy the file name of the selected editor to the
        clipboard.
        """
        if self.contextMenuEditor:
            fn = self.contextMenuEditor.getFileName()
            if fn:
                cb = QApplication.clipboard()
                cb.setText(fn)
        
    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.changeCaption.emit(fn)
            if not self.__inRemoveView:
                self.editorChanged.emit(fn)
                self.editorLineChanged.emit(
                    fn, editor.getCursorPosition()[0] + 1)
        else:
            self.changeCaption.emit("")
        self.editorChangedEd.emit(editor)
        
        cindex = self.editors.index(editor)
        self.viewlist.setCurrentRow(cindex)
        
    def eventFilter(self, watched, event):
        """
        Public 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:
            switched = True
            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.changeCaption.emit(fn)
                    if switched:
                        self.editorChanged.emit(fn)
                        self.editorLineChanged.emit(
                            fn, aw.getCursorPosition()[0] + 1)
                else:
                    self.changeCaption.emit("")
                self.editorChangedEd.emit(aw)
        
        return False