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

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

"""
Module implementing a tabbed viewmanager class.
"""

import os

from PyQt4.QtCore import *
from PyQt4.QtGui import *

from KdeQt.KQApplication 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 Globals import isMacPlatform

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", str(id(self)))
            mimeData.setData("source-index", 
                             QByteArray.number(self.tabAt(self.__dragStartPos)))
            mimeData.setData("tabwidget-id", str(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 = long(mimeData.data("tabbar-id"))
        fromIndex = mimeData.data("source-index").toInt()[0]
        toIndex = self.tabAt(event.pos())
        if oldID != id(self):
            parentID = long(mimeData.data("tabwidget-id"))
            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.setUsesScrollButtons(True)
        self.setElideMode(Qt.ElideNone)
        if isMacPlatform():
            self.setDocumentMode(True)
        
        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.__menu.addSeparator()
        self.__menu.addAction(UI.PixmapCache.getIcon("print.png"),
            self.trUtf8('Print'), self.__contextMenuPrintFile)
        self.__menu.addSeparator()
        self.copyPathAct = self.__menu.addAction(self.trUtf8("Copy Path to Clipboard"), 
            self.__contextMenuCopyPathToClipboard)
        
    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.copyPathAct.setEnabled(bool(self.contextMenuEditor.getFileName()))
            
            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 or QString)
        """
        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 or QString)
        @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 listview 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 = e4App().getObject("Project").getRelativePath(fn)
            
            maxFileNameChars = Preferences.getUI("TabViewManagerFilenameLength")
            if len(txt) > maxFileNameChars:
                txt = "...%s" % txt[-maxFileNameChars:]
            if editor.isReadOnly():
                txt = self.trUtf8("%1 (ro)").arg(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 __contextMenuPrintFile(self):
        """
        Private method to print the selected tab.
        """
        if self.contextMenuEditor:
            self.vm.printEditor(self.contextMenuEditor)
        
    def __contextMenuCopyPathToClipboard(self):
        """
        Private method to copy the file name of the editor to the clipboard.
        """
        if self.contextMenuEditor:
            fn = self.contextMenuEditor.getFileName()
            if fn:
                cb = QApplication.clipboard()
                cb.setText(fn)
        
    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'), unicode(fn))
            self.emit(SIGNAL('editorChanged'), unicode(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
        @param noName name to be used for an unnamed editor (string or QString)
        """
        if fn is None:
            if not unicode(noName):
                self.untitledCount += 1
                noName = self.trUtf8("Untitled %1").arg(self.untitledCount)
            self.currentTabWidget.addTab(win, noName)
            win.setNoName(noName)
        else:
            if self.filenameOnly:
                txt = os.path.basename(fn)
            else:
                txt = e4App().getObject("Project").getRelativePath(fn)
            if len(txt) > self.maxFileNameChars:
                txt = "...%s" % txt[-self.maxFileNameChars:]
            if not QFileInfo(fn).isWritable():
                txt = self.trUtf8("%1 (ro)").arg(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'), unicode(fn))
            self.emit(SIGNAL('editorChanged'), unicode(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
        @param noName name to be used for an unnamed editor (string or QString)
        """
        if fn is None:
            if not unicode(noName):
                self.untitledCount += 1
                noName = self.trUtf8("Untitled %1").arg(self.untitledCount)
            tabWidget.insertWidget(index, win, noName)
            win.setNoName(noName)
        else:
            if self.filenameOnly:
                txt = os.path.basename(fn)
            else:
                txt = e4App().getObject("Project").getRelativePath(fn)
            if len(txt) > self.maxFileNameChars:
                txt = "...%s" % txt[-self.maxFileNameChars:]
            if not QFileInfo(fn).isWritable():
                txt = self.trUtf8("%1 (ro)").arg(txt)
            nindex = tabWidget.insertWidget(index, win, txt)
            tabWidget.setTabToolTip(nindex, fn)
        tabWidget.setCurrentWidget(win)
        win.show()
        win.setFocus()
        if fn:
            self.emit(SIGNAL('changeCaption'), unicode(fn))
            self.emit(SIGNAL('editorChanged'), unicode(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
        """
        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 or QString)
        """
        if self.filenameOnly:
            tabName = os.path.basename(unicode(newName))
        else:
            tabName = e4App().getObject("Project").getRelativePath(newName)
        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'), unicode(newName))

    def _modificationStatusChanged(self, m, editor):
        """
        Protected slot to handle the modificationStatusChanged signal.
        
        @param m flag indicating the modification status (boolean)
        @param editor editor window changed
        """
        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
        """
        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'), unicode(fn))
            if not self.__inRemoveView:
                self.emit(SIGNAL('editorChanged'), unicode(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
        @param event the event that occurred
        @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'), unicode(fn))
                    if switched:
                        self.emit(SIGNAL('editorChanged'), unicode(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 = e4App().getObject("Project").getRelativePath(fn)
                        if len(txt) > self.maxFileNameChars:
                            txt = "...%s" % txt[-self.maxFileNameChars:]
                        if not QFileInfo(fn).isWritable():
                            txt = self.trUtf8("%1 (ro)").arg(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
