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

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

"""
Module implementing the Breakpoint viewer widget.
"""

from qt import *

from EditBreakpointDialog import EditBreakpointDialog
from KdeQt import KQMessageBox

class BreakPointViewer(QListView):
    """
    Class implementing the Breakpoint viewer widget.
    
    Breakpoints will be shown with all their details. They can be modified through
    the context menu of this widget.
    """
    def __init__(self, parent = None):
        """
        Constructor
        
        @param parent the parent (QWidget)
        """
        QListView.__init__(self,parent)
        
        self.dbg = None
        
        self.addColumn(self.trUtf8("Filename"))
        
        col = self.addColumn(self.trUtf8("Line"))
        self.setColumnAlignment(col, Qt.AlignRight)
        
        self.addColumn(self.trUtf8('Condition'))
        
        col = self.addColumn(self.trUtf8('Temporary'))
        self.setColumnAlignment(col, Qt.AlignHCenter)
        
        col = self.addColumn(self.trUtf8('Enabled'))
        self.setColumnAlignment(col, Qt.AlignHCenter)
        
        col = self.addColumn(self.trUtf8('Ignore Count'))
        self.setColumnAlignment(col, Qt.AlignRight)
        
        self.idCol = self.addColumn('#')
        self.setColumnWidth(self.idCol, 0)
        self.header().setResizeEnabled(0, self.idCol)
        
        self.setSorting(0)
        self.setSelectionMode(QListView.Extended)
        self.setAllColumnsShowFocus(1)
        
        self.setCaption(self.trUtf8("Breakpoints"))
        
        self.connect(self,SIGNAL('contextMenuRequested(QListViewItem *, const QPoint &, int)'),
                     self.handleContextMenu)
        self.connect(self,SIGNAL('doubleClicked(QListViewItem *, const QPoint &, int)'),
                     self.handleDoubleClicked)
        
        self.createPopupMenus()
        
        self.condHistory = QStringList()
        self.fnHistory = QStringList()
        self.fnHistory.append('')
        self.noRefresh = 0
        
    def createPopupMenus(self):
        """
        Private method to generate the popup menus.
        """
        self.menu = QPopupMenu()
        self.menu.insertItem(self.trUtf8("Add"), self.addBreak)
        self.menu.insertItem(self.trUtf8("Edit..."), self.editBreak)
        self.menu.insertSeparator()
        self.menu.insertItem(self.trUtf8("Enable"), self.enableBreak)
        self.menu.insertItem(self.trUtf8("Enable all"), self.enableAllBreaks)
        self.menu.insertSeparator()
        self.menu.insertItem(self.trUtf8("Disable"), self.disableBreak)
        self.menu.insertItem(self.trUtf8("Disable all"), self.disableAllBreaks)
        self.menu.insertSeparator()
        self.menu.insertItem(self.trUtf8("Delete"), self.deleteBreak)
        self.menu.insertItem(self.trUtf8("Delete all"), self.deleteAllBreaks)
        self.menu.insertSeparator()
        self.menu.insertItem(self.trUtf8("Goto"), self.showSource)
        self.menu.insertSeparator()
        self.menu.insertItem(self.trUtf8("Refresh"), self.refresh)

        self.backMenuIds = {}
        self.backMenu = QPopupMenu()
        self.backMenu.insertItem(self.trUtf8("Add"), self.addBreak)
        self.backMenuIds["EnableAll"] = \
            self.backMenu.insertItem(self.trUtf8("Enable all"), self.enableAllBreaks)
        self.backMenuIds["DisableAll"] = \
            self.backMenu.insertItem(self.trUtf8("Disable all"), self.disableAllBreaks)
        self.backMenuIds["DeleteAll"] = \
            self.backMenu.insertItem(self.trUtf8("Delete all"), self.deleteAllBreaks)
        self.backMenu.insertSeparator()
        self.backMenu.insertItem(self.trUtf8("Refresh"), self.refresh)
        self.connect(self.backMenu, SIGNAL('aboutToShow()'), self.handleShowBackMenu)

        self.multiMenu = QPopupMenu()
        self.multiMenu.insertItem(self.trUtf8("Add"), self.addBreak)
        self.multiMenu.insertSeparator()
        self.multiMenu.insertItem(self.trUtf8("Enable selected"), self.enableSelectedBreaks)
        self.multiMenu.insertItem(self.trUtf8("Enable all"), self.enableAllBreaks)
        self.multiMenu.insertSeparator()
        self.multiMenu.insertItem(self.trUtf8("Disable selected"), self.disableSelectedBreaks)
        self.multiMenu.insertItem(self.trUtf8("Disable all"), self.disableAllBreaks)
        self.multiMenu.insertSeparator()
        self.multiMenu.insertItem(self.trUtf8("Delete selected"), self.deleteSelectedBreaks)
        self.multiMenu.insertItem(self.trUtf8("Delete all"), self.deleteAllBreaks)
        self.multiMenu.insertSeparator()
        self.multiMenu.insertItem(self.trUtf8("Refresh"), self.refresh)
    
    def handleContextMenu(self,itm,coord,col):
        """
        Private slot to show the context menu of the listview.
        
        @param itm the selected listview item (QListViewItem)
        @param coord the position of the mouse pointer (QPoint)
        @param col the column of the mouse pointer (int)
        """
        cnt = self.getSelectedItemsCount()
        if cnt > 1:
            self.multiMenu.popup(coord)
        elif cnt == 1:
            self.menu.popup(coord)
        else:
            self.backMenu.popup(coord)
    
    def showEvent(self, evt):
        """
        Protected slot called when this widget is being shown.
        
        @param evt the event (QShowEvent)
        """
        self.refresh()
        
    def setDebugger(self, dbg):
        """
        Public method to set a reference to the Debug UI.
        
        @param dbg reference to the DebugUI objectTrees
        """
        self.dbg = dbg
    
    def findItems(self, filename):
        """
        Private method to find all items matching the given filename.
        
        @param filename name of the file to be matched (string or QString)
        @return list of items found (list of QListViewItem)
        """
        items = []
        itm = self.firstChild()
        while itm is not None:
            if filename is None:
                filename = QString.null
            if QString.compare(itm.text(0), filename) == 0:
                items.append(itm)
            itm = itm.itemBelow()
        return items
    
    def findBreakpoint(self, fn, lineno):
        """
        Private method to find a specific breakpoint in the list.
        
        @param fn filename of the breakpoint (QString)
        @param lineno line number of the breakpoint (integer)
        """
        itm = self.firstChild()
        while itm is not None:
            if fn is None:
                fn = QString.null
            if QString.compare(itm.text(0), fn) == 0 and \
               QString.compare(itm.text(1), " %5s" % str(lineno)) == 0:
                break
            itm = itm.itemBelow()
        return itm
    
    def getRefreshedItem(self, itm):
        """
        Private method to get an item with refreshed values.
        
        @param itm item to be refreshed (QListViewItem)
        @return refreshed item (QListViewItem)
        """
        id = itm.text(self.idCol)
        self.refresh()
        return self.findItem(id, self.idCol)
    
    def refresh(self):
        """
        Public slot to refresh the breakpoints display.
        """
        if self.noRefresh or self.dbg is None:
            return
        
        id = 0
        self.clear()
        bpDict = self.dbg.getAllBreakpoints()
        for fname, bpList in bpDict.items():
            for (lineno, cond, temp, enabled, count) in bpList:
                if lineno > 0:
                    if cond is None:
                        cond = ""
                    QListViewItem(self, 
                        fname, 
                        " %5s" % str(lineno), 
                        cond,
                        temp and self.trUtf8("Yes") or self.trUtf8("No"),
                        enabled and self.trUtf8("Yes") or self.trUtf8("No"),
                        " %5s" % str(count),
                        "%d" % id,
                    )
                    id += 1
    
    def handleDoubleClicked(self,itm,coord,col):
        """
        Private slot to handle the double clicked signal.
        
        @param itm the selected listview item (QListViewItem)
        @param coord the position of the mouse pointer (QPoint)
        @param col the column of the mouse pointer (int)
        """
        if itm is not None:
            self.editBreakpoint(itm)

    def addBreak(self):
        """
        Private slot to handle the add breakpoint context menu entry.
        """
        dlg = EditBreakpointDialog((self.fnHistory[0], None), None,
            self.condHistory, self, modal = 1, addMode = 1,
            filenameHistory = self.fnHistory)
        if dlg.exec_loop() == QDialog.Accepted:
            fn, line, cond, temp, enabled, count = dlg.getAddData()
            if fn is not None:
                self.fnHistory.remove(fn)
                self.fnHistory.prepend(fn)
            
            if not cond.isEmpty():
                self.condHistory.remove(cond)
                self.condHistory.prepend(cond)
            
            self.dbg.newBreakpointWithProperties(fn, line, (cond, temp, enabled, count))
    
    def editBreak(self):
        """
        Private slot to handle the edit breakpoint context menu entry.
        """
        itm = self.currentItem()
        if itm is not None:
            self.editBreakpoint(itm)
    
    def editBreakpoint(self, itm):
        """
        Private slot to edit a breakpoint.
        
        @param itm breakpoint to be edited (QListViewItem)
        """
        itm = self.getRefreshedItem(itm)
        fn = unicode(itm.text(0))
        line = int(str(itm.text(1)))
        cond = itm.text(2)
        temp = itm.text(3).compare(self.trUtf8("Yes")) == 0 and 1 or 0
        enabled = itm.text(4).compare(self.trUtf8("Yes")) == 0 and 1 or 0
        count = int(str(itm.text(5)))
            
        dlg = EditBreakpointDialog((fn, line), (cond, temp, enabled, count),
            self.condHistory, self, modal = 1)
        if dlg.exec_loop() == QDialog.Accepted:
            cond, temp, enabled, count = dlg.getData()
            if not cond.isEmpty():
                self.condHistory.remove(cond)
                self.condHistory.prepend(cond)
                            
            self.dbg.setBreakpointProperties(fn, line, (cond, temp, enabled, count))

    def setEnabledStatus(self, itm, enabled):
        """
        Private method to set the enabled status of a breakpoint.
        
        @param itm reference to the breakpoint item (QListViewItem)
        @param enabled flag indicating the enabled status to be set (boolean)
        """
        fn = unicode(itm.text(0))
        line = int(str(itm.text(1)))
        cond = itm.text(2)
        temp = itm.text(3).compare(self.trUtf8("Yes")) == 0 and 1 or 0
        count = int(str(itm.text(5)))
        self.dbg.setBreakpointProperties(fn, line, (cond, temp, enabled, count))
        
    def enableBreak(self):
        """
        Private slot to handle the enable breakpoint context menu entry.
        """
        itm = self.currentItem()
        itm = self.getRefreshedItem(itm)
        self.setEnabledStatus(itm, 1)

    def enableAllBreaks(self):
        """
        Private slot to handle the enable all breakpoints context menu entry.
        """
        self.refresh()
        self.noRefresh = 1
        itm = self.firstChild()
        while itm:
            self.setEnabledStatus(itm, 1)
            itm = itm.itemBelow()
        self.noRefresh = 0
        self.refresh()

    def enableSelectedBreaks(self):
        """
        Private slot to handle the enable selected breakpoints context menu entry.
        """
        items = self.getRefreshedSelectedItems()
        self.noRefresh = 1
        for itm in items:
            self.setEnabledStatus(itm, 1)
        self.noRefresh = 0
        self.refresh()

    def disableBreak(self):
        """
        Private slot to handle the disable breakpoint context menu entry.
        """
        itm = self.currentItem()
        itm = self.getRefreshedItem(itm)
        self.setEnabledStatus(itm, 0)

    def disableAllBreaks(self):
        """
        Private slot to handle the disable all breakpoints context menu entry.
        """
        self.refresh()
        self.noRefresh = 1
        itm = self.firstChild()
        while itm:
            self.setEnabledStatus(itm, 0)
            itm = itm.itemBelow()
        self.noRefresh = 0
        self.refresh()

    def disableSelectedBreaks(self):
        """
        Private slot to handle the disable selected breakpoints context menu entry.
        """
        items = self.getRefreshedSelectedItems()
        self.noRefresh = 1
        for itm in items:
            self.setEnabledStatus(itm, 0)
        self.noRefresh = 0
        self.refresh()

    def deleteBreakpoint(self, itm):
        """
        Private method to delete a breakpoint.
        
        @param itm reference to the breakpoint item (QListViewItem)
        """
        fn = unicode(itm.text(0))
        line = int(str(itm.text(1)))
        self.dbg.toggleBreakpoint(fn, line)
        
    def deleteBreak(self):
        """
        Private slot to handle the delete breakpoint context menu entry.
        """
        itm = self.currentItem()
        itm = self.getRefreshedItem(itm)
        self.deleteBreakpoint(itm)
        
    def deleteAllBreaks(self):
        """
        Private slot to handle the delete all breakpoints context menu entry.
        """
        self.refresh()
        self.noRefresh = 1
        itm = self.firstChild()
        while itm:
            self.deleteBreakpoint(itm)
            itm = itm.itemBelow()
        self.noRefresh = 0
        self.refresh()

    def deleteSelectedBreaks(self):
        """
        Private slot to handle the delete selected breakpoints context menu entry.
        """
        items = self.getRefreshedSelectedItems()
        self.noRefresh = 1
        for itm in items:
            self.deleteBreakpoint(itm)
        self.noRefresh = 0
        self.refresh()

    def showSource(self):
        """
        Private slot to handle the goto context menu entry.
        """
        itm = self.currentItem()
        itm = self.getRefreshedItem(itm)
        self.dbg.handleGotoBreakpoint(itm.text(0), int(str(itm.text(1))))
    
    def clearBreakpoint(self, fn, lineno):
        """
        Public slot to handle the clientClearBreak signal.
        
        @param fn filename of the breakpoint (QString)
        @param lineno line number of the breakpoint (integer)
        """
        itm = self.findBreakpoint(fn, lineno)
        if itm:
            self.takeItem(itm)
            del itm
    
    def highlightBreakpoint(self, fn, lineno):
        """
        Public slot to handle the clientLine signal.
        
        @param fn filename of the breakpoint (QString)
        @param lineno line number of the breakpoint (integer)
        """
        itm = self.findBreakpoint(fn, lineno)
        if itm:
            self.clearSelection()
            self.setSelected(itm, 1)
    
    def handleResetUI(self):
        """
        Public slot to reset the breakpoint viewer.
        """
        self.clearSelection()
    
    def editorClosed(self, fn):
        """
        Public slot to handle the editorClosed signal.
        
        @param fn filename of the editor to be closed (string or QString)
        """
        for itm in self.findItems(fn):
            self.takeItem(itm)
            del itm
    
    def handleShowBackMenu(self):
        """
        Private slot to handle the aboutToShow signal of the background menu.
        """
        if self.childCount() == 0:
            self.backMenu.setItemEnabled(self.backMenuIds["EnableAll"], 0)
            self.backMenu.setItemEnabled(self.backMenuIds["DisableAll"], 0)
            self.backMenu.setItemEnabled(self.backMenuIds["DeleteAll"], 0)
        else:
            self.backMenu.setItemEnabled(self.backMenuIds["EnableAll"], 1)
            self.backMenu.setItemEnabled(self.backMenuIds["DisableAll"], 1)
            self.backMenu.setItemEnabled(self.backMenuIds["DeleteAll"], 1)
    
    def getRefreshedSelectedItems(self):
        """
        Public method to get the selected items after a refresh of the display.
        
        @return list of refreshed selected items (list of QListViewItem)
        """
        selectedItems = []
        selectedItemIds = []
        
        # step 1: get Ids of selected items
        itm = self.firstChild()
        while itm:
            if itm.isSelected():
                selectedItemIds.append(itm.text(self.idCol))
            itm = itm.itemBelow()
        
        # step 2: refresh to get up to date breakpoint values
        self.refresh()
        
        # step 3: get the list of items by their id
        for id in selectedItemIds:
            selectedItems.append(self.findItem(id, self.idCol))
        
        return selectedItems
    
    def getSelectedItemsCount(self):
        """
        Public method to get the count of items selcted.
        
        @return count of items selected (integer)
        """
        count = 0
        itm = self.firstChild()
        while itm:
            if itm.isSelected():
                count += 1
            itm = itm.itemBelow()
        return count
