Plugins/ViewManagerPlugins/MdiArea/MdiArea.py

Sun, 05 Jun 2011 18:25:36 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Sun, 05 Jun 2011 18:25:36 +0200
changeset 1112
8a7d1b9d18db
parent 945
8cd4d08fa9f6
child 1356
16e55845cff0
permissions
-rw-r--r--

Improved code quality by getting rid of star imports. That way pyflakes can do its job. A few bugs fixed found by flakes.

# -*- coding: utf-8 -*-

# Copyright (c) 2008 - 2011 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing the mdi area viewmanager class.
"""

from PyQt4.QtCore import QSignalMapper, pyqtSignal, QEvent, QSize, Qt
from PyQt4.QtGui import QWidget, QMdiArea, QMenu

from ViewManager.ViewManager import ViewManager

import QScintilla.Editor
from QScintilla.Editor import Editor

import UI.PixmapCache

from E5Gui.E5Action import E5Action, addActions

import Utilities


class MdiArea(QMdiArea, ViewManager):
    """
    Class implementing the mdi area 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 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 editorSaved(str) 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.
    """
    changeCaption = pyqtSignal(str)
    editorChanged = pyqtSignal(str)
    
    lastEditorClosed = pyqtSignal()
    editorOpened = pyqtSignal(str)
    editorOpenedEd = pyqtSignal(Editor)
    editorClosed = pyqtSignal(str)
    editorClosedEd = pyqtSignal(Editor)
    editorSaved = pyqtSignal(str)
    checkActions = pyqtSignal(Editor)
    cursorChanged = pyqtSignal(Editor)
    breakpointToggled = pyqtSignal(Editor)
    bookmarkToggled = pyqtSignal(Editor)
    syntaxerrorToggled = pyqtSignal(Editor)
    
    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
        """
        QMdiArea.__init__(self, parent)
        ViewManager.__init__(self)
        self.lastFN = ''
        self.__removingView = False
        
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        
        self.__windowMapper = QSignalMapper(self)
        
        self.__windowMapper.mapped[QWidget].connect(self.__mapped)
        self.subWindowActivated.connect(self.__subWindowActivated)
        
    def __mapped(self, subWindow):
        """
        Private slot to handle the activation of a sub window.
        
        @param subWindow sub window to be activated (QMdiSubWindow)
        """
        self.setActiveSubWindow(subWindow)
    
    def canCascade(self):
        """
        Public method to signal if cascading of managed windows is available.
        
        @return flag indicating cascading of windows is available
        """
        return True
        
    def canTile(self):
        """
        Public method to signal if tiling of managed windows is available.
        
        @return flag indicating tiling of windows is available
        """
        return True
    
    def canSplit(self):
        """
        public method to signal if splitting of the view is available.
        
        @return flag indicating splitting of the view is available.
        """
        return False
        
    def tile(self):
        """
        Public method to tile the managed windows.
        """
        self.tileSubWindows()
        
    def cascade(self):
        """
        Public method to cascade the managed windows.
        """
        self.cascadeSubWindows()
        
    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.__removingView = True
        self.lastFN = ''
        win.removeEventFilter(self)
        self.closeActiveSubWindow()
        win.closeIt()
        self.__removingView = False
        
    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
        @param noName name to be used for an unnamed editor (string)
        """
        self.addSubWindow(win)
        if fn is None:
            if not noName:
                self.untitledCount += 1
                noName = self.trUtf8("Untitled {0}").format(self.untitledCount)
            win.setWindowTitle(noName)
            win.setNoName(noName)
        else:
            if self.lastFN != fn:
                self.lastFN = fn
        win.show()
        if win.hasSyntaxErrors():
            self.__setSubWindowIcon(win, UI.PixmapCache.getIcon("syntaxError.png"))
        elif win.hasFlakesWarnings():
            self.__setSubWindowIcon(win, UI.PixmapCache.getIcon("warning.png"))
        else:
            self.__setSubWindowIcon(win, UI.PixmapCache.getIcon("empty.png"))
        
        # Make the editor window a little bit smaller to make the whole
        # window with all decorations visible. This is not the most elegant
        # solution but more of a workaround for another QWorkspace strangeness.
        # 25 points are subtracted to give space for the scrollbars
        pw = win.parentWidget()
        sz = QSize(self.width() - 25, self.height() - 25)
        pw.resize(sz)
        
        win.setFocus()
        win.installEventFilter(self)
        
    def _showView(self, win, fn=None):
        """
        Private method to show a view (i.e. window)
        
        @param win editor window to be shown
        @param fn filename of this editor (string)
        """
        if fn is not None and self.lastFN != fn:
            self.lastFN = fn
        win.show()
        win.setFocus()
        
    def activeWindow(self):
        """
        Private method to return the active (i.e. current) window.
        
        @return reference to the active editor
        """
        subWindow = self.activeSubWindow()
        if subWindow is None:
            return None
        else:
            return subWindow.widget()
        
    def showWindowMenu(self, windowMenu):
        """
        Public method to set up the viewmanager part of the Window menu.
        
        @param windowMenu reference to the window menu
        """
        self.windowsMenu = QMenu(self.trUtf8('&Windows'))
        
        menu = self.windowsMenu
        idx = 1
        for subWindow in self.subWindowList():
            sv = subWindow.widget()
            if idx == 10:
                menu.addSeparator()
                menu = menu.addMenu(self.trUtf8("&More"))
            fn = sv.fileName
            if fn:
                txt = Utilities.compactPath(fn, self.ui.maxMenuFilePathLen)
            else:
                txt = sv.windowTitle()
            accel = ""
            if idx < 10:
                accel = "&{0:d}. ".format(idx)
            elif idx < 36:
                accel = "&{0}. ".format(chr(idx - 9 + ord("@")))
            act = menu.addAction("{0}{1}".format(accel, txt))
            act.triggered[()].connect(self.__windowMapper.map)
            self.__windowMapper.setMapping(act, subWindow)
            idx += 1
        
        addActions(windowMenu,
                   [None, self.nextChildAct, self.prevChildAct,
                    self.tileAct, self.cascadeAct,
                    self.restoreAllAct, self.iconizeAllAct,
                    None])
        for act in [self.restoreAllAct, self.iconizeAllAct]:
            act.setEnabled(len(self.editors) != 0)
        for act in [self.nextChildAct, self.prevChildAct, self.tileAct,
                    self.cascadeAct]:
            act.setEnabled(len(self.editors) > 1)
        
        act = windowMenu.addMenu(self.windowsMenu)
        if len(self.editors) == 0:
            act.setEnabled(False)
        
    def _initWindowActions(self):
        """
        Protected method to define the user interface actions for window handling.
        """
        self.tileAct = E5Action(self.trUtf8('Tile'),
            self.trUtf8('&Tile'), 0, 0, self, 'vm_window_tile')
        self.tileAct.setStatusTip(self.trUtf8('Tile the windows'))
        self.tileAct.setWhatsThis(self.trUtf8(
            """<b>Tile the windows</b>"""
            """<p>Rearrange and resize the windows so that they are tiled.</p>"""
        ))
        self.tileAct.triggered[()].connect(self.tile)
        self.windowActions.append(self.tileAct)
        
        self.cascadeAct = E5Action(self.trUtf8('Cascade'),
            self.trUtf8('&Cascade'), 0, 0, self, 'vm_window_cascade')
        self.cascadeAct.setStatusTip(self.trUtf8('Cascade the windows'))
        self.cascadeAct.setWhatsThis(self.trUtf8(
            """<b>Cascade the windows</b>"""
            """<p>Rearrange and resize the windows so that they are cascaded.</p>"""
        ))
        self.cascadeAct.triggered[()].connect(self.cascade)
        self.windowActions.append(self.cascadeAct)
        
        self.nextChildAct = E5Action(self.trUtf8('Next'),
            self.trUtf8('&Next'), 0, 0, self, 'vm_window_next')
        self.nextChildAct.setStatusTip(self.trUtf8('Activate next window'))
        self.nextChildAct.setWhatsThis(self.trUtf8(
            """<b>Next</b>"""
            """<p>Activate the next window of the list of open windows.</p>"""
        ))
        self.nextChildAct.triggered[()].connect(self.activateNextSubWindow)
        self.windowActions.append(self.nextChildAct)
        
        self.prevChildAct = E5Action(self.trUtf8('Previous'),
            self.trUtf8('&Previous'), 0, 0, self, 'vm_window_previous')
        self.prevChildAct.setStatusTip(self.trUtf8('Activate previous window'))
        self.prevChildAct.setWhatsThis(self.trUtf8(
            """<b>Previous</b>"""
            """<p>Activate the previous window of the list of open windows.</p>"""
        ))
        self.prevChildAct.triggered[()].connect(self.activatePreviousSubWindow)
        self.windowActions.append(self.prevChildAct)
        
        self.restoreAllAct = E5Action(self.trUtf8('Restore All'),
            self.trUtf8('&Restore All'), 0, 0, self, 'vm_window_restore_all')
        self.restoreAllAct.setStatusTip(self.trUtf8('Restore all windows'))
        self.restoreAllAct.setWhatsThis(self.trUtf8(
            """<b>Restore All</b>"""
            """<p>Restores all windows to their original size.</p>"""
        ))
        self.restoreAllAct.triggered[()].connect(self.__restoreAllWindows)
        self.windowActions.append(self.restoreAllAct)
        
        self.iconizeAllAct = E5Action(self.trUtf8('Iconize All'),
            self.trUtf8('&Iconize All'), 0, 0, self, 'vm_window_iconize_all')
        self.iconizeAllAct.setStatusTip(self.trUtf8('Iconize all windows'))
        self.iconizeAllAct.setWhatsThis(self.trUtf8(
            """<b>Iconize All</b>"""
            """<p>Iconizes all windows.</p>"""
        ))
        self.iconizeAllAct.triggered[()].connect(self.__iconizeAllWindows)
        self.windowActions.append(self.iconizeAllAct)
        
    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)
        """
        pass
        
    def __setSubWindowIcon(self, widget, icon):
        """
        Private method to set the icon of a subwindow given it's internal widget.
        
        @param widget reference to the internal widget (QWidget)
        @param icon reference to the icon (QIcon)
        """
        for subWindow in self.subWindowList():
            if subWindow.widget() == widget:
                subWindow.setWindowIcon(icon)
                return
        
    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
        """
        if m:
            self.__setSubWindowIcon(editor, UI.PixmapCache.getIcon("fileModified.png"))
        elif editor.hasSyntaxErrors():
            self.__setSubWindowIcon(editor, UI.PixmapCache.getIcon("syntaxError.png"))
        elif editor.hasFlakesWarnings():
            self.__setSubWindowIcon(editor, UI.PixmapCache.getIcon("warning.png"))
        else:
            self.__setSubWindowIcon(editor, UI.PixmapCache.getIcon("empty.png"))
        self._checkActions(editor)
        
    def _syntaxErrorToggled(self, editor):
        """
        Protected slot to handle the syntaxerrorToggled signal.
        
        @param editor editor that sent the signal
        """
        if editor.hasSyntaxErrors():
            self.__setSubWindowIcon(editor, UI.PixmapCache.getIcon("syntaxError.png"))
        elif editor.hasFlakesWarnings():
            self.__setSubWindowIcon(editor, UI.PixmapCache.getIcon("warning.png"))
        else:
            self.__setSubWindowIcon(editor, UI.PixmapCache.getIcon("empty.png"))
        
        ViewManager._syntaxErrorToggled(self, editor)
        
    def __subWindowActivated(self, subWindow):
        """
        Private slot to handle the windowActivated signal.
        
        @param subWindow the activated subwindow (QMdiSubWindow)
        """
        if subWindow is not None:
            editor = subWindow.widget()
            self._checkActions(editor)
            if editor is not None:
                fn = editor.getFileName()
                self.editorChanged.emit(fn)
        
    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, whether the event was handled (boolean)
        """
        if event.type() == QEvent.Close and \
           not self.__removingView and \
           isinstance(watched, QScintilla.Editor.Editor):
            watched.close()
            return True
        
        return QMdiArea.eventFilter(self, watched, event)
        
    def __restoreAllWindows(self):
        """
        Private slot to restore all windows.
        """
        for win in self.subWindowList():
            win.showNormal()
        
    def __iconizeAllWindows(self):
        """
        Private slot to iconize all windows.
        """
        for win in self.subWindowList():
            win.showMinimized()

eric ide

mercurial