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

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

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

import os

from qt import *

from ViewManager import ViewManager
from QScintilla.EditorIcons import IconFileModified
import QScintilla.Editor
from Icons.Icons import IconEmpty

class TabWidget(QTabWidget):
    """
    Class implementing a custimized TabWidget.
    """
    def __init__(self, parent):
        """
        Constructor
        
        Arguments
        
            parent -- parent widget (QWidget)
        """
        QTabWidget.__init__(self, parent)
        
        self.editors = []
        self.curIndex = 0
        
        self.connect(self, SIGNAL("currentChanged(QWidget *)"), self.handleCurrentChanged)
        
    def handleCurrentChanged(self):
        """
        Private slot called by the currentChanged signal.
        """
        self.curIndex = self.currentPageIndex()
        
    def addTab(self, editor, title):
        """
        Overwritten method to add a new tab.
        
        Arguments
        
            editor -- the editor object to be added (QScintilla.Editor.Editor)
            
            title -- title for the new tab (string, QString or QTab)
        """
        QTabWidget.addTab(self, editor, title)
        
        if not editor in self.editors:
            self.editors.append(editor)
            self.connect(editor, PYSIGNAL('captionChanged'),
                self.handleCaptionChange)
                
    def showPage(self, editor):
        """
        Overridden method to show a tab.
        
        Arguments
        
            editor -- the editor object to be shown (QScintilla.Editor.Editor)
        """
        QTabWidget.showPage(self, editor)
        self.curIndex = self.indexOf(editor)
        
    def nextTab(self):
        """
        Public slot used to show the next tab.
        """
        if self.count():
            self.curIndex += 1
            if self.curIndex == self.count():
                self.curIndex = 0
                
            QTabWidget.showPage(self, self.page(self.curIndex))

    def prevTab(self):
        """
        Public slot used to show the previous tab.
        """
        if self.count():
            self.curIndex -= 1
            if self.curIndex == -1:
                self.curIndex = self.count() - 1
                
            QTabWidget.showPage(self, self.page(self.curIndex))

    def handleCaptionChange(self, cap, editor):
        """
        Private method to handle Caption change signals from the editor. 
        
        Updates the listview text to reflect the new caption information.
        
        Arguments
        
            cap -- Caption for the editor
            
            editor -- Editor to update the caption for
        """
        fn = editor.getFileName()
        if fn:
            txt = os.path.basename(fn)
            if editor.isReadOnly():
                txt = '%s (ro)' % txt
            self.changeTab(editor, txt)
        
    def removePage(self, object):
        """
        Overwritten method to remove a page.
        
        Arguments
        
            object -- object to be removed (QObject)
        """
        QTabWidget.removePage(self, object)
        
        if isinstance(object, QScintilla.Editor.Editor):
            self.disconnect(object, PYSIGNAL('captionChanged'),
                self.handleCaptionChange)
            self.editors.remove(object)
        
    def hasEditor(self, editor):
        """
        Public method to check for an editor.
        
        Arguments
        
            editor -- editor object to check for
            
        Returns
        
            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.
        
        Returns
        
            flag indicating editors are managed
        """
        return len(self.editors) and 1 or 0
        
class Tabview(QSplitter, ViewManager):
    """
    Class implementing a tabbed viewmanager class embedded in a splitter.
    
    Signals
    
        lastEditorClosed -- emitted after the last editor window was closed
        
        editorOpened -- emitted after an editor window was opened
        
        editorSaved -- emitted after an editor window was saved
    """
    def __init__(self,parent, ui, dbs):
        """
        Constructor
        
        Arguments
        
            parent -- parent widget (QWidget)
            
            ui -- reference to the main user interface
            
            dbs -- reference to the debug server object
        """
        self.tabWidgets = []
        
        QSplitter.__init__(self,parent)
        ViewManager.__init__(self, ui, dbs)
        tw = TabWidget(self)
        self.tabWidgets.append(tw)
        self.currentTabWidget = tw
        self.connect(tw, SIGNAL('currentChanged(QWidget*)'),
            self.handleCurrentChanged)
        tw.installEventFilter(self)
        tw.tabBar().installEventFilter(self)
        self.setOrientation(QSplitter.Vertical)
        
    def initViewActions(self):
        """
        Protected method defining the user interface actions for the view commands.
        """
        ViewManager.initViewActions(self)
        
        self.nextTabAct = QAction(self.trUtf8('Show next tab'), 
                      self.trUtf8('Show next tab'), 
                      QKeySequence(self.trUtf8('Ctrl+Alt+Tab')), self)
        self.connect(self.nextTabAct, SIGNAL('activated()'), self.nextTab)
        self.viewActions.append(self.nextTabAct)
        
        self.prevTabAct = QAction(self.trUtf8('Show previous tab'), 
                      self.trUtf8('Show previous tab'), 
                      QKeySequence(self.trUtf8('Shift+Ctrl+Alt+Tab')), self)
        self.connect(self.prevTabAct, SIGNAL('activated()'), self.prevTab)
        self.viewActions.append(self.prevTabAct)
        
    def nextTab(self):
        """
        Private slot used to show the next tab of the current tabwidget.
        """
        self.currentTabWidget.nextTab()
        
    def prevTab(self):
        """
        Private slot used to show the previous tab of the current tabwidget.
        """
        self.currentTabWidget.prevTab()
        
    def canCascade(self):
        """
        Public method to signal if cascading of managed windows is available.
        
        Returns
        
            flag indicating cascading of windows is available
        """
        return 0
        
    def canTile(self):
        """
        Public method to signal if tiling of managed windows is available.
        
        Returns
        
            flag indicating tiling of windows is available
        """
        return 0
        
    def canSplit(self):
        """
        public method to signal if splitting of the view is available.
        
        Returns
        
            flag indicating splitting of the view is available.
        """
        return 1
        
    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):
        """
        Private method to remove all views (i.e. windows)
        """
        for win in self.editors:
            self.removeView(win)
            
    def removeView(self, win):
        """
        Private method to remove a view (i.e. window)
        
        Arguments
        
            win -- editor window to be removed
        """
        for tw in self.tabWidgets:
            if tw.hasEditor(win):
                tw.removePage(win)
                break
        win.closeIt()
        
        # 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 = self.tabWidgets[i]
                self.activeWindow().setFocus()
                break
    
    def addView(self, win, fn=None):
        """
        Private method to add a view (i.e. window)
        
        Arguments
        
            win -- editor window to be added
            
            fn -- filename of this editor
        """
        win.show()
        if fn is None:
            self.untitledCount += 1
            self.currentTabWidget.addTab(win, self.trUtf8("Untitled %1").arg(self.untitledCount))
        else:
            txt = os.path.basename(fn)
            if not QFileInfo(fn).isWritable():
                txt = '%s (ro)' % txt
            self.currentTabWidget.addTab(win, txt)
            self.currentTabWidget.setTabToolTip(win, os.path.dirname(fn))
        self.currentTabWidget.showPage(win)
        win.setFocus()
    
    def showView(self, win, fn=None):
        """
        Private method to show a view (i.e. window)
        
        Arguments
        
            win -- editor window to be shown
            
            fn -- filename of this editor
        """
        win.show()
        for tw in self.tabWidgets:
            if tw.hasEditor(win):
                tw.showPage(win)
                self.currentTabWidget = tw
                break
        win.setFocus()
    
    def activeWindow(self):
        """
        Private method to return the active (i.e. current) window.
        
        Returns
        
            reference to the active editor
        """
        return self.currentTabWidget.currentPage()
        
    def handleShowWindowMenu(self, windowMenu):
        """
        Private method to set up the viewmanager part of the Window menu.
        
        Arguments
        
            windowMenu -- reference to the window menu
        """
        pass
        
    def initWindowActions(self):
        """
        Define the user interface actions for window handling.
        """
        pass
        
    def setEditorName(self, editor, newName):
        """
        Change the displayed name of the editor.
        
        Arguments
        
            editor -- editor window to be changed
            
            newName -- new name to be shown (string or QString)
        """
        self.currentTabWidget.changeTab(editor, os.path.basename(str(newName)))
        self.currentTabWidget.setTabToolTip(editor, os.path.dirname(str(newName)))

    def handleModificationStatusChanged(self, m, editor):
        """
        Private slot to handle the modificationStatusChanged signal.
        
        Arguments
        
            m -- flag indicating the modification status (boolean)
            
            editor -- editor window changed
        """
        for tw in self.tabWidgets:
            if tw.hasEditor(editor):
                break
        if m:
            tw.setTabIconSet(editor, QIconSet(IconFileModified))
        else:
            tw.setTabIconSet(editor, QIconSet(IconEmpty))
        self.checkActions(editor)
        
    def addSplit(self):
        """
        Public method used to split the current view.
        """
        tw = TabWidget(self)
        tw.show()
        self.tabWidgets.append(tw)
        self.currentTabWidget = self.tabWidgets[-1]
        self.connect(tw, SIGNAL('currentChanged(QWidget*)'),
            self.handleCurrentChanged)
        tw.installEventFilter(self)
        tw.tabBar().installEventFilter(self)
        self.setSizes([int(100/len(self.tabWidgets))] * len(self.tabWidgets))
        self.splitRemoveAct.setEnabled(1)
        
    def removeSplit(self):
        """
        Public method used to remove the current split view.
        """
        if len(self.tabWidgets) > 1:
            tw = self.currentTabWidget
            res = 1
            savedEditors = tw.editors[:]
            for editor in savedEditors:
                res &= self.closeEditor(editor)
            if res:
                i = self.tabWidgets.index(tw)
                if i == len(self.tabWidgets)-1:
                    i -= 1
                self.tabWidgets.remove(tw)
                tw.close(1)
                self.currentTabWidget = self.tabWidgets[i]
                if len(self.tabWidgets) == 1:
                    self.splitRemoveAct.setEnabled(0)
                return 1
                
        return 0
        
    def setSplitOrientation(self, orientation):
        """
        Public method used to set the orientation of the split view.
        
        Arguments
        
            orientation -- orientation of the split
                (QSplitter.Horizontal or QSplitter.Vertical)
        """
        self.setOrientation(orientation)
        
    def handleCurrentChanged(self, editor):
        """
        Private slot to handle the currentChanged signal.
        
        Arguments
        
            editor -- selected editor window
        """
        self.checkActions(editor)
        editor.setFocus()
        
    def eventFilter(self, watched, event):
        if event.type() == QEvent.MouseButtonPress:
            if isinstance(watched, QTabWidget):
                self.currentTabWidget = watched
            elif isinstance(watched, QTabBar):
                self.currentTabWidget = watched.parent()
            elif isinstance(watched, QScintilla.Editor.Editor):
                for tw in self.tabWidgets:
                    if tw.hasEditor(watched):
                        self.currentTabWidget = tw
                        break
                        
            aw = self.activeWindow()
            if aw is not None:
                self.checkActions(aw)
                aw.setFocus()
            
        return 0
