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

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

"""
Module implementing the listspace viewmanager class.
"""

import os

from qt import *

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

class WidgetStack(QWidgetStack):
    """
    Class implementing a custimized WidgetStack.
    """
    def __init__(self, parent):
        """
        Constructor
        
        Arguments
        
            parent -- parent widget (QWidget)
        """
        QWidgetStack.__init__(self, parent)
        
        self.editors = []
        
    def addWidget(self, editor, id = -1):
        """
        Overwritten method to add a new widget.
        
        Arguments
        
            editor -- the editor object to be added (QScintilla.Editor.Editor)
            
            id -- id of the widget (integer)
        """
        QWidgetStack.addWidget(self, editor, id)
        if not editor in self.editors:
            self.editors.append(editor)
        
    def removeWidget(self, object):
        """
        Overwritten method to remove a widget.
        
        Arguments
        
            object -- object to be removed (QObject)
        """
        QWidgetStack.removeWidget(self, object)
        if isinstance(object, QScintilla.Editor.Editor):
            self.editors.remove(object)
            
    def raiseWidget(self, widgetOrId):
        """
        Overwritten method to raise a widget.
        
        Arguments
        
            widgetOrId -- widget to be raised or its id (QWidget or integer)
        """
        if isinstance(widgetOrId, QScintilla.Editor.Editor):
            self.editors.remove(widgetOrId)
            self.editors.insert(0, widgetOrId)
        QWidgetStack.raiseWidget(self, widgetOrId)
        
    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 firstEditor(self):
        """
        Public method to retrieve the first editor in the list of managed editors.
        
        Returns
        
            first editor in list (QScintilla.Editor.Editor)
        """
        return len(self.editors) and self.editors[0] or None
        
class Listspace(QSplitter, ViewManager):
    """
    Class implementing the listspace viewmanager class.
    
    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.stacks = []
        
        QSplitter.__init__(self,parent)
        ViewManager.__init__(self, ui, dbs)
        self.viewlist = QListBox(self)
        self.setResizeMode(self.viewlist, QSplitter.KeepSize)
        self.stackArea = QSplitter(self)
        self.stackArea.setOrientation(QSplitter.Vertical)
        stack = WidgetStack(self.stackArea)
        self.stacks.append(stack)
        self.currentStack = stack
        stack.installEventFilter(self)
        self.connect(self.viewlist, SIGNAL("highlighted(int)"),
                     self.__showViewByIndex)
        
    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 i in range(self.viewlist.count() - 1, -1, -1):
            self.viewlist.removeItem(i)
        for win in self.editors:
            for stack in self.stacks:
                if stack.hasEditor(win):
                    stack.removeWidget(win)
                    break
            self.disconnect(win, PYSIGNAL('captionChanged'),
                self.handleCaptionChange)
            win.closeIt()
            
    def removeView(self, win):
        """
        Private method to remove a view (i.e. window)
        
        Arguments
        
            win -- editor window to be removed
        """
        ind = self.editors.index(win)
        self.disconnect(win, PYSIGNAL('captionChanged'),
            self.handleCaptionChange)
        self.viewlist.removeItem(ind)
        for stack in self.stacks:
            if stack.hasEditor(win):
                break
        stack.removeWidget(win)
        win.closeIt()
        if ind > 0:
            ind -= 1
        else:
            if len(self.editors) > 1:
                ind = 1
            else:
                return
        stack.raiseWidget(stack.firstEditor())
        self.showView(self.editors[ind])
    
    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
        """
        if fn is None:
            self.untitledCount += 1
            self.viewlist.insertItem(
                self.trUtf8("Untitled %1").arg(self.untitledCount), 
                len(self.editors) - 1)
        else:
            txt = os.path.basename(fn)
            if not QFileInfo(fn).isWritable():
                txt = '%s (ro)' % txt
            self.viewlist.insertItem(txt, len(self.editors) - 1)
        self.currentStack.addWidget(win, len(self.editors) - 1)
        self.currentStack.raiseWidget(win)
        self.connect(win, PYSIGNAL('captionChanged'),
            self.handleCaptionChange)

        self.viewlist.setSelected(len(self.editors) - 1, 1)
        win.setFocus()
    
    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:
            self.setEditorName(editor, fn)

    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
        """
        for stack in self.stacks:
            if stack.hasEditor(win):
                stack.raiseWidget(win)
                self.currentStack = stack
                break
        self.viewlist.setSelected(self.editors.index(win), 1)
        win.setFocus()
        
    def __showViewByIndex(self, index):
        """
        Internal slot called to show a view (i.e. window) selected in the list.
        
        Arguments
        
            index -- index of the selected entry (int)
        """
        self.showView(self.editors[index])
        self.checkActions(self.editors[index])

    def activeWindow(self):
        """
        Private method to return the active (i.e. current) window.
        
        Returns
        
            reference to the active editor
        """
        return self.currentStack.visibleWidget()
        
    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)
        """
        idx = self.editors.index(editor)
        txt = os.path.basename(newName)
        if not QFileInfo(newName).isWritable():
            txt = '%s (ro)' % txt
        self.viewlist.changeItem(txt, idx)

    def handleModificationStatusChanged(self, m, editor):
        """
        Private slot to handle the modificationStatusChanged signal.
        
        Arguments
        
            m -- flag indicating the modification status (boolean)
            
            editor -- editor window changed
        """
        idx = self.editors.index(editor)
        if m:
            self.viewlist.changeItem(IconFileModified, self.viewlist.text(idx), idx)
        else:
            self.viewlist.changeItem(self.viewlist.text(idx), idx)
        self.checkActions(editor)
        
    def addSplit(self):
        """
        Public method used to split the current view.
        """
        stack = WidgetStack(self.stackArea)
        stack.show()
        self.stacks.append(stack)
        self.currentStack = stack
        stack.installEventFilter(self)
        self.stackArea.setSizes([int(100/len(self.stacks))] * len(self.stacks))
        self.splitRemoveAct.setEnabled(1)
        
    def removeSplit(self):
        """
        Public method used to remove the current split view.
        """
        if len(self.stacks) > 1:
            stack = self.currentStack
            res = 1
            savedEditors = stack.editors[:]
            for editor in savedEditors:
                res &= self.closeEditor(editor)
            if res:
                i = self.stacks.index(stack)
                if i == len(self.stacks)-1:
                    i -= 1
                self.stacks.remove(stack)
                stack.close(1)
                self.currentStack = self.stacks[i]
                if len(self.stacks) == 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.stackArea.setOrientation(orientation)
        
    def eventFilter(self, watched, event):
        if event.type() == QEvent.MouseButtonPress:
            if isinstance(watched, QWidgetStack):
                self.currentStack = watched
            elif isinstance(watched, QScintilla.Editor.Editor):
                for stack in self.stacks:
                    if stack.hasEditor(watched):
                        self.currentStack = stack
                        break
            id = self.currentStack.id(self.currentStack.visibleWidget())
            self.viewlist.setSelected(id, 1)
            
            aw = self.activeWindow()
            if aw is not None:
                self.checkActions(aw)
                aw.setFocus()
            
        return 0
