--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/ViewManagerPlugins/Tabview/Tabview.py Mon Dec 28 16:03:33 2009 +0000 @@ -0,0 +1,1098 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2002 - 2009 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a tabbed viewmanager class. +""" + +import os + +from PyQt4.QtCore import * +from PyQt4.QtGui import * + +from E4Gui.E4Application import e4App + +from ViewManager.ViewManager import ViewManager + +import QScintilla.Editor + +import UI.PixmapCache + +from E4Gui.E4TabWidget import E4TabWidget, E4WheelTabBar +from E4Gui.E4Led import E4Led + +import Preferences + +from eric4config import getConfig + +class TabBar(E4WheelTabBar): + """ + Class implementing a customized tab bar supporting drag & drop. + + @signal tabMoveRequested(int, int) emitted to signal a tab move request giving + the old and new index position + @signal tabRelocateRequested(long, int, int) emitted to signal a tab relocation + request giving the id of the old tab widget, the index in the old tab widget + and the new index position + @signal tabCopyRequested(long, int, int) emitted to signal a clone request + giving the id of the source tab widget, the index in the source tab widget + and the new index position + @signal tabCopyRequested(int, int) emitted to signal a clone request giving + the old and new index position + """ + def __init__(self, parent = None): + """ + Constructor + + @param parent reference to the parent widget (QWidget) + """ + E4WheelTabBar.__init__(self, parent) + self.setAcceptDrops(True) + + self.__dragStartPos = QPoint() + + def mousePressEvent(self, event): + """ + Protected method to handle mouse press events. + + @param event reference to the mouse press event (QMouseEvent) + """ + if event.button() == Qt.LeftButton: + self.__dragStartPos = QPoint(event.pos()) + E4WheelTabBar.mousePressEvent(self, event) + + def mouseMoveEvent(self, event): + """ + Protected method to handle mouse move events. + + @param event reference to the mouse move event (QMouseEvent) + """ + if event.buttons() == Qt.MouseButtons(Qt.LeftButton) and \ + (event.pos() - self.__dragStartPos).manhattanLength() > \ + QApplication.startDragDistance(): + drag = QDrag(self) + mimeData = QMimeData() + index = self.tabAt(event.pos()) + mimeData.setText(self.tabText(index)) + mimeData.setData("action", "tab-reordering") + mimeData.setData("tabbar-id", QByteArray.number(id(self))) + mimeData.setData("source-index", + QByteArray.number(self.tabAt(self.__dragStartPos))) + mimeData.setData("tabwidget-id", QByteArray.number(id(self.parentWidget()))) + drag.setMimeData(mimeData) + if event.modifiers() == Qt.KeyboardModifiers(Qt.ShiftModifier): + drag.exec_(Qt.DropActions(Qt.CopyAction)) + elif event.modifiers() == Qt.KeyboardModifiers(Qt.NoModifier): + drag.exec_(Qt.DropActions(Qt.MoveAction)) + E4WheelTabBar.mouseMoveEvent(self, event) + + def dragEnterEvent(self, event): + """ + Protected method to handle drag enter events. + + @param event reference to the drag enter event (QDragEnterEvent) + """ + mimeData = event.mimeData() + formats = mimeData.formats() + if formats.contains("action") and \ + mimeData.data("action") == "tab-reordering" and \ + formats.contains("tabbar-id") and \ + formats.contains("source-index") and \ + formats.contains("tabwidget-id"): + event.acceptProposedAction() + E4WheelTabBar.dragEnterEvent(self, event) + + def dropEvent(self, event): + """ + Protected method to handle drop events. + + @param event reference to the drop event (QDropEvent) + """ + mimeData = event.mimeData() + oldID = mimeData.data("tabbar-id").toLong()[0] + fromIndex = mimeData.data("source-index").toInt()[0] + toIndex = self.tabAt(event.pos()) + if oldID != id(self): + parentID = mimeData.data("tabwidget-id").toLong()[0] + if event.proposedAction() == Qt.MoveAction: + self.emit(SIGNAL("tabRelocateRequested(long, int, int)"), + parentID, fromIndex, toIndex) + event.acceptProposedAction() + elif event.proposedAction() == Qt.CopyAction: + self.emit(SIGNAL("tabCopyRequested(long, int, int)"), + parentID, fromIndex, toIndex) + event.acceptProposedAction() + else: + if fromIndex != toIndex: + if event.proposedAction() == Qt.MoveAction: + self.emit(SIGNAL("tabMoveRequested(int, int)"), fromIndex, toIndex) + event.acceptProposedAction() + elif event.proposedAction() == Qt.CopyAction: + self.emit(SIGNAL("tabCopyRequested(int, int)"), fromIndex, toIndex) + event.acceptProposedAction() + E4WheelTabBar.dropEvent(self, event) + +class TabWidget(E4TabWidget): + """ + Class implementing a custimized tab widget. + """ + def __init__(self, vm): + """ + Constructor + + @param vm view manager widget (Tabview) + """ + E4TabWidget.__init__(self) + self.setAttribute(Qt.WA_DeleteOnClose, True) + + self.__tabBar = TabBar(self) + self.setTabBar(self.__tabBar) + + self.connect(self.__tabBar, SIGNAL("tabMoveRequested(int, int)"), + self.moveTab) + self.connect(self.__tabBar, SIGNAL("tabRelocateRequested(long, int, int)"), + self.relocateTab) + self.connect(self.__tabBar, SIGNAL("tabCopyRequested(long, int, int)"), + self.copyTabOther) + self.connect(self.__tabBar, SIGNAL("tabCopyRequested(int, int)"), + self.copyTab) + + self.vm = vm + self.editors = [] + + self.indicator = E4Led(self) + self.setCornerWidget(self.indicator, Qt.TopLeftCorner) + + self.rightCornerWidget = QWidget(self) + self.rightCornerWidgetLayout = QHBoxLayout(self.rightCornerWidget) + self.rightCornerWidgetLayout.setMargin(0) + self.rightCornerWidgetLayout.setSpacing(0) + + self.__navigationMenu = QMenu(self) + self.connect(self.__navigationMenu, SIGNAL("aboutToShow()"), + self.__showNavigationMenu) + self.connect(self.__navigationMenu, SIGNAL("triggered(QAction*)"), + self.__navigationMenuTriggered) + + self.navigationButton = QToolButton(self) + self.navigationButton.setIcon(UI.PixmapCache.getIcon("1downarrow.png")) + self.navigationButton.setToolTip(self.trUtf8("Show a navigation menu")) + self.navigationButton.setPopupMode(QToolButton.InstantPopup) + self.navigationButton.setMenu(self.__navigationMenu) + self.navigationButton.setEnabled(False) + self.rightCornerWidgetLayout.addWidget(self.navigationButton) + + if Preferences.getUI("SingleCloseButton") or \ + not hasattr(self, 'setTabsClosable'): + self.closeButton = QToolButton(self) + self.closeButton.setIcon(UI.PixmapCache.getIcon("close.png")) + self.closeButton.setToolTip(self.trUtf8("Close the current editor")) + self.closeButton.setEnabled(False) + self.connect(self.closeButton, SIGNAL("clicked(bool)"), + self.__closeButtonClicked) + self.rightCornerWidgetLayout.addWidget(self.closeButton) + else: + self.connect(self, SIGNAL("tabCloseRequested(int)"), + self.__closeRequested) + self.closeButton = None + + self.setCornerWidget(self.rightCornerWidget, Qt.TopRightCorner) + + self.__initMenu() + self.contextMenuEditor = None + self.contextMenuIndex = -1 + + self.setTabContextMenuPolicy(Qt.CustomContextMenu) + self.connect(self, SIGNAL('customTabContextMenuRequested(const QPoint &, int)'), + self.__showContextMenu) + + ericPic = QPixmap(os.path.join(getConfig('ericPixDir'), 'eric_small.png')) + self.emptyLabel = QLabel() + self.emptyLabel.setPixmap(ericPic) + self.emptyLabel.setAlignment(Qt.AlignVCenter | Qt.AlignHCenter) + E4TabWidget.addTab(self, self.emptyLabel, UI.PixmapCache.getIcon("empty.png"), "") + + def __initMenu(self): + """ + Private method to initialize the tab context menu. + """ + self.__menu = QMenu(self) + self.leftMenuAct = \ + self.__menu.addAction(UI.PixmapCache.getIcon("1leftarrow.png"), + self.trUtf8('Move Left'), self.__contextMenuMoveLeft) + self.rightMenuAct = \ + self.__menu.addAction(UI.PixmapCache.getIcon("1rightarrow.png"), + self.trUtf8('Move Right'), self.__contextMenuMoveRight) + self.firstMenuAct = \ + self.__menu.addAction(UI.PixmapCache.getIcon("2leftarrow.png"), + self.trUtf8('Move First'), self.__contextMenuMoveFirst) + self.lastMenuAct = \ + self.__menu.addAction(UI.PixmapCache.getIcon("2rightarrow.png"), + self.trUtf8('Move Last'), self.__contextMenuMoveLast) + self.__menu.addSeparator() + self.__menu.addAction(UI.PixmapCache.getIcon("close.png"), + self.trUtf8('Close'), self.__contextMenuClose) + self.closeOthersMenuAct = self.__menu.addAction(self.trUtf8("Close Others"), + self.__contextMenuCloseOthers) + self.__menu.addAction(self.trUtf8('Close All'), self.__contextMenuCloseAll) + self.__menu.addSeparator() + self.saveMenuAct = \ + self.__menu.addAction(UI.PixmapCache.getIcon("fileSave.png"), + self.trUtf8('Save'), self.__contextMenuSave) + self.__menu.addAction(UI.PixmapCache.getIcon("fileSaveAs.png"), + self.trUtf8('Save As...'), self.__contextMenuSaveAs) + self.__menu.addAction(UI.PixmapCache.getIcon("fileSaveAll.png"), + self.trUtf8('Save All'), self.__contextMenuSaveAll) + self.projectMenuAct = \ + self.__menu.addAction(UI.PixmapCache.getIcon("fileSaveProject.png"), + self.trUtf8('Save to Project'), self.__contextMenuSaveToProject) + self.__menu.addSeparator() + self.__menu.addAction(UI.PixmapCache.getIcon("print.png"), + self.trUtf8('Print'), self.__contextMenuPrintFile) + + def __showContextMenu(self, coord, index): + """ + Private slot to show the tab context menu. + + @param coord the position of the mouse pointer (QPoint) + @param index index of the tab the menu is requested for (integer) + """ + if self.editors: + self.contextMenuEditor = self.widget(index) + if self.contextMenuEditor: + self.saveMenuAct.setEnabled(self.contextMenuEditor.isModified()) + self.projectMenuAct.setEnabled(e4App().getObject("Project").isOpen()) + + self.contextMenuIndex = index + self.leftMenuAct.setEnabled(index > 0) + self.rightMenuAct.setEnabled(index < self.count() - 1) + self.firstMenuAct.setEnabled(index > 0) + self.lastMenuAct.setEnabled(index < self.count() - 1) + + self.closeOthersMenuAct.setEnabled(self.count() > 1) + + coord = self.mapToGlobal(coord) + self.__menu.popup(coord) + + def __showNavigationMenu(self): + """ + Private slot to show the navigation button menu. + """ + self.__navigationMenu.clear() + for index in range(self.count()): + act = self.__navigationMenu.addAction(self.tabIcon(index), + self.tabText(index)) + act.setData(QVariant(index)) + + def __navigationMenuTriggered(self, act): + """ + Private slot called to handle the navigation button menu selection. + + @param act reference to the selected action (QAction) + """ + index, ok = act.data().toInt() + if ok: + self.setCurrentIndex(index) + + def showIndicator(self, on): + """ + Public slot to set the indicator on or off. + + @param on flag indicating the dtate of the indicator (boolean) + """ + if on: + self.indicator.setColor(QColor("green")) + else: + self.indicator.setColor(QColor("red")) + + def addTab(self, editor, title): + """ + Overwritten method to add a new tab. + + @param editor the editor object to be added (QScintilla.Editor.Editor) + @param title title for the new tab (string) + """ + E4TabWidget.addTab(self, editor, UI.PixmapCache.getIcon("empty.png"), title) + if self.closeButton: + self.closeButton.setEnabled(True) + else: + self.setTabsClosable(True) + self.navigationButton.setEnabled(True) + + if not editor in self.editors: + self.editors.append(editor) + self.connect(editor, SIGNAL('captionChanged'), + self.__captionChange) + + emptyIndex = self.indexOf(self.emptyLabel) + if emptyIndex > -1: + self.removeTab(emptyIndex) + + def insertWidget(self, index, editor, title): + """ + Overwritten method to insert a new tab. + + @param index index position for the new tab (integer) + @param editor the editor object to be added (QScintilla.Editor.Editor) + @param title title for the new tab (string) + @return index of the inserted tab (integer) + """ + newIndex = E4TabWidget.insertTab(self, index, editor, + UI.PixmapCache.getIcon("empty.png"), + title) + if self.closeButton: + self.closeButton.setEnabled(True) + else: + self.setTabsClosable(True) + self.navigationButton.setEnabled(True) + + if not editor in self.editors: + self.editors.append(editor) + self.connect(editor, SIGNAL('captionChanged'), + self.__captionChange) + + emptyIndex = self.indexOf(self.emptyLabel) + if emptyIndex > -1: + self.removeTab(emptyIndex) + + return newIndex + + def __captionChange(self, cap, editor): + """ + Private method to handle Caption change signals from the editor. + + Updates the tab text and tooltip text to reflect the new caption information. + + @param cap Caption for the editor + @param editor Editor to update the caption for + """ + fn = editor.getFileName() + if fn: + if Preferences.getUI("TabViewManagerFilenameOnly"): + txt = os.path.basename(fn) + else: + txt = fn + ppath = e4App().getObject("Project").getProjectPath() + if ppath: + txt = txt.replace(ppath + os.sep, "") + + maxFileNameChars = Preferences.getUI("TabViewManagerFilenameLength") + if len(txt) > maxFileNameChars: + txt = "...%s" % txt[-maxFileNameChars:] + if editor.isReadOnly(): + txt = self.trUtf8("{0} (ro)").format(txt) + + index = self.indexOf(editor) + if index > -1: + self.setTabText(index, txt) + self.setTabToolTip(index, fn) + + def removeWidget(self, object): + """ + Public method to remove a widget. + + @param object object to be removed (QWidget) + """ + index = self.indexOf(object) + if index > -1: + self.removeTab(index) + + if isinstance(object, QScintilla.Editor.Editor): + self.disconnect(object, SIGNAL('captionChanged'), + self.__captionChange) + self.editors.remove(object) + + if not self.editors: + E4TabWidget.addTab(self, self.emptyLabel, + UI.PixmapCache.getIcon("empty.png"), "") + self.emptyLabel.show() + if self.closeButton: + self.closeButton.setEnabled(False) + else: + self.setTabsClosable(False) + self.navigationButton.setEnabled(False) + + def relocateTab(self, sourceId, sourceIndex, targetIndex): + """ + Public method to relocate an editor from another TabWidget. + + @param sourceId id of the TabWidget to get the editor from (long) + @param sourceIndex index of the tab in the old tab widget (integer) + @param targetIndex index position to place it to (integer) + """ + tw = self.vm.getTabWidgetById(sourceId) + if tw is not None: + # step 1: get data of the tab of the source + toolTip = tw.tabToolTip(sourceIndex) + text = tw.tabText(sourceIndex) + icon = tw.tabIcon(sourceIndex) + whatsThis = tw.tabWhatsThis(sourceIndex) + editor = tw.widget(sourceIndex) + + # step 2: relocate the tab + tw.removeWidget(editor) + self.insertWidget(targetIndex, editor, text) + + # step 3: set the tab data again + self.setTabIcon(targetIndex, icon) + self.setTabToolTip(targetIndex, toolTip) + self.setTabWhatsThis(targetIndex, whatsThis) + + # step 4: set current widget + self.setCurrentIndex(targetIndex) + + def copyTabOther(self, sourceId, sourceIndex, targetIndex): + """ + Public method to copy an editor from another TabWidget. + + @param sourceId id of the TabWidget to get the editor from (long) + @param sourceIndex index of the tab in the old tab widget (integer) + @param targetIndex index position to place it to (integer) + """ + tw = self.vm.getTabWidgetById(sourceId) + if tw is not None: + editor = tw.widget(sourceIndex) + newEditor = self.vm.cloneEditor(editor, editor.getFileType(), + editor.getFileName()) + self.vm.insertView(newEditor, self, targetIndex, + editor.getFileName(), editor.getNoName()) + + def copyTab(self, sourceIndex, targetIndex): + """ + Public method to copy an editor. + + @param sourceIndex index of the tab (integer) + @param targetIndex index position to place it to (integer) + """ + editor = self.widget(sourceIndex) + newEditor = self.vm.cloneEditor(editor, editor.getFileType(), + editor.getFileName()) + self.vm.insertView(newEditor, self, targetIndex, + editor.getFileName(), editor.getNoName()) + + def currentWidget(self): + """ + Overridden method to return a reference to the current page. + + @return reference to the current page (QWidget) + """ + if not self.editors: + return None + else: + return E4TabWidget.currentWidget(self) + + def hasEditor(self, editor): + """ + Public method to check for an editor. + + @param editor editor object to check for + @return flag indicating, whether the editor to be checked belongs + to the list of editors managed by this tab widget. + """ + return editor in self.editors + + def hasEditors(self): + """ + Public method to test, if any editor is managed. + + @return flag indicating editors are managed + """ + return len(self.editors) > 0 + + def __contextMenuClose(self): + """ + Private method to close the selected tab. + """ + if self.contextMenuEditor: + self.vm.closeEditorWindow(self.contextMenuEditor) + + def __contextMenuCloseOthers(self): + """ + Private method to close the other tabs. + """ + index = self.contextMenuIndex + for i in range(self.count() - 1, index, -1) + range(index - 1, -1, -1): + editor = self.widget(i) + self.vm.closeEditorWindow(editor) + + def __contextMenuCloseAll(self): + """ + Private method to close all tabs. + """ + savedEditors = self.editors[:] + for editor in savedEditors: + self.vm.closeEditorWindow(editor) + + def __contextMenuSave(self): + """ + Private method to save the selected tab. + """ + if self.contextMenuEditor: + self.vm.saveEditorEd(self.contextMenuEditor) + + def __contextMenuSaveAs(self): + """ + Private method to save the selected tab to a new file. + """ + if self.contextMenuEditor: + self.vm.saveAsEditorEd(self.contextMenuEditor) + + def __contextMenuSaveAll(self): + """ + Private method to save all tabs. + """ + self.vm.saveEditorsList(self.editors) + + def __contextMenuSaveToProject(self): + """ + Private method to save the selected tab to the current project. + """ + if self.contextMenuEditor: + self.vm.saveEditorToProjectEd(self.contextMenuEditor) + + def __contextMenuPrintFile(self): + """ + Private method to print the selected tab. + """ + if self.contextMenuEditor: + self.vm.printEditor(self.contextMenuEditor) + + def __contextMenuMoveLeft(self): + """ + Private method to move a tab one position to the left. + """ + self.moveTab(self.contextMenuIndex, self.contextMenuIndex - 1) + + def __contextMenuMoveRight(self): + """ + Private method to move a tab one position to the right. + """ + self.moveTab(self.contextMenuIndex, self.contextMenuIndex + 1) + + def __contextMenuMoveFirst(self): + """ + Private method to move a tab to the first position. + """ + self.moveTab(self.contextMenuIndex, 0) + + def __contextMenuMoveLast(self): + """ + Private method to move a tab to the last position. + """ + self.moveTab(self.contextMenuIndex, self.count() - 1) + + def __closeButtonClicked(self): + """ + Private method to handle the press of the close button. + """ + self.vm.closeEditorWindow(self.currentWidget()) + + def __closeRequested(self, index): + """ + Private method to handle the press of the individual tab close button. + + @param index index of the tab (integer) + """ + if index >= 0: + self.vm.closeEditorWindow(self.widget(index)) + + def mouseDoubleClickEvent(self, event): + """ + Protected method handling double click events. + + @param event reference to the event object (QMouseEvent) + """ + self.vm.newEditor() + +class Tabview(QSplitter, ViewManager): + """ + Class implementing a tabbed viewmanager class embedded in a splitter. + + @signal changeCaption(string) emitted if a change of the caption is necessary + @signal editorChanged(string) emitted when the current editor has changed + """ + def __init__(self, parent): + """ + Constructor + + @param parent parent widget (QWidget) + @param ui reference to the main user interface + @param dbs reference to the debug server object + """ + self.tabWidgets = [] + + QSplitter.__init__(self, parent) + ViewManager.__init__(self) + tw = TabWidget(self) + self.addWidget(tw) + self.tabWidgets.append(tw) + self.currentTabWidget = tw + self.currentTabWidget.showIndicator(True) + self.connect(tw, SIGNAL('currentChanged(int)'), + self.__currentChanged) + tw.installEventFilter(self) + tw.tabBar().installEventFilter(self) + self.setOrientation(Qt.Vertical) + self.__inRemoveView = False + + self.maxFileNameChars = Preferences.getUI("TabViewManagerFilenameLength") + self.filenameOnly = Preferences.getUI("TabViewManagerFilenameOnly") + + 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 range(self.tabWidgets.index(tw), -1, -1) + \ + 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.emit(SIGNAL('changeCaption'), fn) + self.emit(SIGNAL('editorChanged'), fn) + else: + self.emit(SIGNAL('changeCaption'), "") + + def _addView(self, win, fn = None, noName = ""): + """ + Protected method to add a view (i.e. window) + + @param win editor window to be added + @param fn filename of this editor (string) + @param noName name to be used for an unnamed editor (string) + """ + if fn is None: + if not noName: + self.untitledCount += 1 + noName = self.trUtf8("Untitled {0}").format(self.untitledCount) + self.currentTabWidget.addTab(win, noName) + win.setNoName(noName) + else: + if self.filenameOnly: + txt = os.path.basename(fn) + else: + txt = fn + ppath = e4App().getObject("Project").getProjectPath() + if ppath: + txt = txt.replace(ppath + os.sep, "") + if len(txt) > self.maxFileNameChars: + txt = "...%s" % txt[-self.maxFileNameChars:] + if not QFileInfo(fn).isWritable(): + txt = self.trUtf8("{0} (ro)").format(txt) + self.currentTabWidget.addTab(win, txt) + index = self.currentTabWidget.indexOf(win) + self.currentTabWidget.setTabToolTip(index, fn) + self.currentTabWidget.setCurrentWidget(win) + win.show() + win.setFocus() + if fn: + self.emit(SIGNAL('changeCaption'), fn) + self.emit(SIGNAL('editorChanged'), fn) + else: + self.emit(SIGNAL('changeCaption'), "") + + def insertView(self, win, tabWidget, index, fn = None, noName = ""): + """ + Protected method to add a view (i.e. window) + + @param win editor window to be added + @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) + """ + if fn is None: + if not noName: + self.untitledCount += 1 + noName = self.trUtf8("Untitled {0}").format(self.untitledCount) + tabWidget.insertWidget(index, win, noName) + win.setNoName(noName) + else: + if self.filenameOnly: + txt = os.path.basename(fn) + else: + txt = fn + ppath = e4App().getObject("Project").getProjectPath() + if ppath: + txt = txt.replace(ppath + os.sep, "") + if len(txt) > self.maxFileNameChars: + txt = "...%s" % txt[-self.maxFileNameChars:] + if not QFileInfo(fn).isWritable(): + txt = self.trUtf8("{0} (ro)").format(txt) + nindex = tabWidget.insertWidget(index, win, txt) + tabWidget.setTabToolTip(nindex, fn) + tabWidget.setCurrentWidget(win) + win.show() + win.setFocus() + if fn: + self.emit(SIGNAL('changeCaption'), fn) + self.emit(SIGNAL('editorChanged'), fn) + else: + self.emit(SIGNAL('changeCaption'), "") + + self._modificationStatusChanged(win.isModified(), win) + self._checkActions(win) + + def _showView(self, win, fn = None): + """ + Protected method to show a view (i.e. window) + + @param win editor window to be shown + @param fn filename of this editor (string) + """ + win.show() + for tw in self.tabWidgets: + if tw.hasEditor(win): + tw.setCurrentWidget(win) + self.currentTabWidget.showIndicator(False) + self.currentTabWidget = tw + self.currentTabWidget.showIndicator(True) + break + win.setFocus() + + def activeWindow(self): + """ + Public method to return the active (i.e. current) window. + + @return reference to the active editor + """ + return self.currentTabWidget.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 self.filenameOnly: + tabName = os.path.basename(newName) + else: + tabName = newName + ppath = e4App().getObject("Project").getProjectPath() + if ppath: + tabName = tabName.replace(ppath + os.sep, "") + if len(tabName) > self.maxFileNameChars: + tabName = "...%s" % tabName[-self.maxFileNameChars:] + index = self.currentTabWidget.indexOf(editor) + self.currentTabWidget.setTabText(index, tabName) + self.currentTabWidget.setTabToolTip(index, newName) + self.emit(SIGNAL('changeCaption'), 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) + if m: + tw.setTabIcon(index, UI.PixmapCache.getIcon("fileModified.png")) + elif editor.hasSyntaxErrors(): + tw.setTabIcon(index, UI.PixmapCache.getIcon("syntaxError.png")) + else: + tw.setTabIcon(index, 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 + """ + for tw in self.tabWidgets: + if tw.hasEditor(editor): + break + index = tw.indexOf(editor) + if editor.hasSyntaxErrors(): + tw.setTabIcon(index, UI.PixmapCache.getIcon("syntaxError.png")) + else: + tw.setTabIcon(index, UI.PixmapCache.getIcon("empty.png")) + + ViewManager._syntaxErrorToggled(self, editor) + + def addSplit(self): + """ + Public method used to split the current view. + """ + tw = TabWidget(self) + tw.show() + self.addWidget(tw) + self.tabWidgets.append(tw) + self.currentTabWidget.showIndicator(False) + self.currentTabWidget = self.tabWidgets[-1] + self.currentTabWidget.showIndicator(True) + self.connect(tw, SIGNAL('currentChanged(int)'), + self.__currentChanged) + tw.installEventFilter(self) + tw.tabBar().installEventFilter(self) + if self.orientation() == Qt.Horizontal: + size = self.width() + else: + size = self.height() + self.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] + self.currentTabWidget.showIndicator(True) + if len(self.tabWidgets) == 1: + self.splitRemoveAct.setEnabled(False) + self.nextSplitAct.setEnabled(False) + self.prevSplitAct.setEnabled(False) + return True + + return False + + def setSplitOrientation(self, orientation): + """ + Public method used to set the orientation of the split view. + + @param orientation orientation of the split + (Qt.Horizontal or Qt.Vertical) + """ + self.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.emit(SIGNAL('changeCaption'), fn) + if not self.__inRemoveView: + self.emit(SIGNAL('editorChanged'), fn) + else: + self.emit(SIGNAL('changeCaption'), "") + + 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: + self.currentTabWidget.showIndicator(False) + if isinstance(watched, E4TabWidget): + 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.emit(SIGNAL('changeCaption'), fn) + if switched: + self.emit(SIGNAL('editorChanged'), fn) + else: + self.emit(SIGNAL('changeCaption'), "") + + 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 = fn + ppath = e4App().getObject("Project").getProjectPath() + if ppath: + txt = txt.replace(ppath + os.sep, "") + if len(txt) > self.maxFileNameChars: + txt = "...%s" % txt[-self.maxFileNameChars:] + if not QFileInfo(fn).isWritable(): + txt = self.trUtf8("{0} (ro)").format(txt) + tabWidget.setTabText(index, txt) + + def getTabWidgetById(self, id_): + """ + Public method to get a reference to a tab widget knowing it's 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