+    def SyncBPLine(self, event):
+        if self.currentItem != -1:
+            list = self._bpListCtrl
+            fileName = list.GetItem(self.currentItem, 2).GetText()
+            lineNumber = list.GetItem(self.currentItem, 1).GetText()
+            self._ui.SynchCurrentLine( fileName, int(lineNumber) , noArrow=True)
+
+    def ClearBreakPoint(self, event):
+        if self.currentItem >= 0:
+            list = self._bpListCtrl
+            fileName = list.GetItem(self.currentItem, 2).GetText()
+            lineNumber = list.GetItem(self.currentItem, 1).GetText()
+            wx.GetApp().GetService(DebuggerService).OnToggleBreakpoint(None, line=int(lineNumber) -1, fileName=fileName )
+
+    def ListItemSelected(self, event):
+        self.currentItem = event.m_itemIndex
+
+    def ListItemDeselected(self, event):
+        self.currentItem = -1
+
+class Watch:
+    CODE_ALL_FRAMES = 1
+    CODE_THIS_BLOCK = 2
+    CODE_THIS_LINE = 4
+    CODE_RUN_ONCE = 8
+
+    def __init__(self, name, command, show_code=CODE_ALL_FRAMES):
+        self._name = name
+        self._command = command
+        self._show_code = show_code
+
+class WatchDialog(wx.Dialog):
+    WATCH_ALL_FRAMES = "Watch in all frames"
+    WATCH_THIS_FRAME = "Watch in this frame only"
+    WATCH_ONCE = "Watch once and delete"
+    def __init__(self, parent, title, chain):
+        wx.Dialog.__init__(self, parent, -1, title, style=wx.DEFAULT_DIALOG_STYLE)
+        self._chain = chain
+        self.label_2 = wx.StaticText(self, -1, "Watch Name:")
+        self._watchNameTextCtrl = wx.TextCtrl(self, -1, "")
+        self.label_3 = wx.StaticText(self, -1, "eval(", style=wx.ALIGN_RIGHT)
+        self._watchValueTextCtrl = wx.TextCtrl(self, -1, "")
+        self.label_4 = wx.StaticText(self, -1, ",frame.f_globals, frame.f_locals)")
+        self.radio_box_1 = wx.RadioBox(self, -1, "Watch Information", choices=[WatchDialog.WATCH_ALL_FRAMES, WatchDialog.WATCH_THIS_FRAME, WatchDialog.WATCH_ONCE], majorDimension=0, style=wx.RA_SPECIFY_ROWS)
+
+        self._okButton = wx.Button(self, wx.ID_OK, "OK")
+        self._okButton.SetDefault()
+        self._okButton.SetHelpText(_("The OK button completes the dialog"))
+        def OnOkClick(event):
+            if self._watchNameTextCtrl.GetValue() == "":
+                wx.MessageBox(_("You must enter a name for the watch."), _("Add Watch"))
+                return
+            if self._watchValueTextCtrl.GetValue() == "":
+                wx.MessageBox(_("You must enter some code to run for the watch."), _("Add Watch"))
+                return
+            self.EndModal(wx.ID_OK)
+        self.Bind(wx.EVT_BUTTON, OnOkClick, self._okButton)
+
+        self._cancelButton = wx.Button(self, wx.ID_CANCEL, _("Cancel"))
+        self._cancelButton.SetHelpText(_("The Cancel button cancels the dialog."))
+
+        self.__set_properties()
+        self.__do_layout()
+
+    def GetSettings(self):
+        return self._watchNameTextCtrl.GetValue(), self._watchValueTextCtrl.GetValue(), self.GetSendFrame(), self.GetRunOnce()
+
+    def GetSendFrame(self):
+        return (WatchDialog.WATCH_ALL_FRAMES != self.radio_box_1.GetStringSelection())
+
+    def GetRunOnce(self):
+        return (WatchDialog.WATCH_ONCE == self.radio_box_1.GetStringSelection())
+
+    def __set_properties(self):
+        self.SetTitle("Add a Watch")
+        #self.SetSize((400, 250))
+        self.radio_box_1.SetSelection(0)
+
+    def __do_layout(self):
+        sizer_1 = wx.BoxSizer(wx.VERTICAL)
+        grid_sizer_4 = wx.FlexGridSizer(1, 3, 5, 5)
+        grid_sizer_2 = wx.FlexGridSizer(1, 2, 5, 5)
+        grid_sizer_2.Add(self.label_2, 0, wx.ALIGN_CENTER_VERTICAL|wx.FIXED_MINSIZE, 0)
+        grid_sizer_2.Add(self._watchNameTextCtrl, 0, wx.EXPAND, 0)
+        grid_sizer_2.AddGrowableCol(1)
+        sizer_1.Add(grid_sizer_2, 1, wx.EXPAND, 0)
+        grid_sizer_4.Add(self.label_3, 0, wx.ALIGN_CENTER_VERTICAL|wx.FIXED_MINSIZE, 0)
+        grid_sizer_4.Add(self._watchValueTextCtrl, 0, wx.EXPAND, 0)
+        grid_sizer_4.AddGrowableCol(1)
+        grid_sizer_4.Add(self.label_4, 0, wx.ALIGN_CENTER_VERTICAL|wx.FIXED_MINSIZE, 0)
+        sizer_1.Add(grid_sizer_4, 0, wx.EXPAND, 0)
+        sizer_1.Add(self.radio_box_1, 0, wx.EXPAND, 0)
+
+        box = wx.BoxSizer(wx.HORIZONTAL)
+        box.Add(self._okButton, 0, wx.ALIGN_RIGHT|wx.ALL, 5)
+        box.Add(self._cancelButton, 0, wx.ALIGN_RIGHT|wx.ALL, 5)
+        sizer_1.Add(box, 1, wx.EXPAND, 0)
+        self.SetSizer(sizer_1)
+        self.Layout()
+
+class BaseFramesUI(wx.SplitterWindow):
+    def __init__(self, parent, id, ui):
+        wx.SplitterWindow.__init__(self, parent, id, style = wx.SP_3D)
+        self._ui = ui
+        self._p1 = p1 = wx.ScrolledWindow(self, -1)
+
+        sizer = wx.BoxSizer(wx.HORIZONTAL)
+        framesLabel = wx.StaticText(self, -1, "Stack Frame:")
+        sizer.Add(framesLabel, 0, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_LEFT|wx.LEFT, border=2)
+
+        self._framesChoiceCtrl = wx.Choice(p1, -1, choices=["                                           "])
+        sizer.Add(self._framesChoiceCtrl, 1, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 1)
+        self._framesChoiceCtrl.Bind(wx.EVT_LIST_ITEM_RIGHT_CLICK, self.OnListRightClick)
+        self.Bind(wx.EVT_CHOICE, self.ListItemSelected, self._framesChoiceCtrl)
+
+        sizer2 = wx.BoxSizer(wx.VERTICAL)
+        p1.SetSizer(sizer2)
+        self._treeCtrl = wx.gizmos.TreeListCtrl(p1, -1, style=wx.TR_DEFAULT_STYLE| wx.TR_FULL_ROW_HIGHLIGHT)
+        self._treeCtrl.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.OnRightClick)
+        sizer2.Add(sizer, 0, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 1)
+        sizer2.Add(self._treeCtrl,1, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 1)
+        tree = self._treeCtrl
+        tree.AddColumn("Thing")
+        tree.AddColumn("Value")
+        tree.SetMainColumn(0) # the one with the tree in it...
+        tree.SetColumnWidth(0, 175)
+        tree.SetColumnWidth(1, 355)
+        self._root = tree.AddRoot("Frame")
+        tree.SetPyData(self._root, "root")
+        tree.SetItemText(self._root, "", 1)
+        tree.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.IntrospectCallback)
+        self._p2 = p2 = wx.Window(self, -1)
+        sizer3 = wx.BoxSizer(wx.HORIZONTAL)
+        p2.SetSizer(sizer3)
+        p2.Bind(wx.EVT_SIZE, self.OnSize)
+        self._notebook = wx.Notebook(p2, -1, size=(20,20))
+        self._notebook.Hide()
+        sizer3.Add(self._notebook, 1, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 1)
+        self.consoleTab = self.MakeConsoleTab(self._notebook, wx.NewId())
+        self.inspectConsoleTab = self.MakeInspectConsoleTab(self._notebook, wx.NewId())
+        self.breakPointsTab = self.MakeBreakPointsTab(self._notebook, wx.NewId())
+        self._notebook.AddPage(self.consoleTab, "Output")
+        self._notebook.AddPage(self.inspectConsoleTab, "Interact")
+        self._notebook.AddPage(self.breakPointsTab, "Break Points")
+        self.SetMinimumPaneSize(20)
+        self.SplitVertically(p1, p2, 550)
+        self.currentItem = None
+        self._notebook.Show(True)
+
+    def PopulateBPList(self):
+        self.breakPointsTab.PopulateBPList()
+
+    def OnSize(self, event):
+        self._notebook.SetSize(self._p2.GetSize())
+
+    def OnDoubleClick(self, event):
+        # Looking for a stack trace line.
+        lineText, pos = self._textCtrl.GetCurLine()
+        fileBegin = lineText.find("File \"")
+        fileEnd = lineText.find("\", line ")
+        lineEnd = lineText.find(", in ")
+        if lineText == "\n" or  fileBegin == -1 or fileEnd == -1 or lineEnd == -1:
+            # Check the line before the one that was clicked on
+            lineNumber = self._textCtrl.GetCurrentLine()
+            if(lineNumber == 0):
+                return
+            lineText = self._textCtrl.GetLine(lineNumber - 1)
+            fileBegin = lineText.find("File \"")
+            fileEnd = lineText.find("\", line ")
+            lineEnd = lineText.find(", in ")
+            if lineText == "\n" or  fileBegin == -1 or fileEnd == -1 or lineEnd == -1:
+                return
+
+        filename = lineText[fileBegin + 6:fileEnd]
+        lineNum = int(lineText[fileEnd + 8:lineEnd])
+
+        foundView = None
+        openDocs = wx.GetApp().GetDocumentManager().GetDocuments()
+        for openDoc in openDocs:
+            if openDoc.GetFilename() == filename:
+                foundView = openDoc.GetFirstView()
+                break
+
+        if not foundView:
+            doc = wx.GetApp().GetDocumentManager().CreateDocument(filename, wx.lib.docview.DOC_SILENT)
+            foundView = doc.GetFirstView()
+
+        if foundView:
+            foundView.GetFrame().SetFocus()
+            foundView.Activate()
+            foundView.GotoLine(lineNum)
+            startPos = foundView.PositionFromLine(lineNum)
+            lineText = foundView.GetCtrl().GetLine(lineNum - 1)
+            foundView.SetSelection(startPos, startPos + len(lineText.rstrip("\n")))
+            import OutlineService
+            wx.GetApp().GetService(OutlineService.OutlineService).LoadOutline(foundView, position=startPos)
+
+    def MakeConsoleTab(self, parent, id):
+        panel = wx.Panel(parent, id)
+        sizer = wx.BoxSizer(wx.HORIZONTAL)
+        self._textCtrl = STCTextEditor.TextCtrl(panel, wx.NewId())
+        sizer.Add(self._textCtrl, 1, wx.ALIGN_LEFT|wx.ALL|wx.EXPAND, 2)
+        self._textCtrl.SetViewLineNumbers(False)
+        self._textCtrl.SetReadOnly(True)
+        if wx.Platform == '__WXMSW__':
+            font = "Courier New"
+        else:
+            font = "Courier"
+        self._textCtrl.SetFont(wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = font))
+        self._textCtrl.SetFontColor(wx.BLACK)
+        self._textCtrl.StyleClearAll()
+        wx.stc.EVT_STC_DOUBLECLICK(self._textCtrl, self._textCtrl.GetId(), self.OnDoubleClick)
+
+        panel.SetSizer(sizer)
+        #sizer.Fit(panel)
+
+        return panel
+    def ExecuteCommand(self, command):
+        assert False, "ExecuteCommand not overridden"
+
+    def MakeInspectConsoleTab(self, parent, id):
+        def handleCommand():
+            cmdStr = self._cmdInput.GetValue()
+            if cmdStr:
+                self._cmdList.append(cmdStr)
+                self._cmdIndex = len(self._cmdList)
+            self._cmdInput.Clear()
+            self._cmdOutput.SetDefaultStyle(style=self._cmdOutputTextStyle)
+            self._cmdOutput.AppendText(">>> " + cmdStr + "\n")
+            self._cmdOutput.SetDefaultStyle(style=self._defaultOutputTextStyle)
+            self.ExecuteCommand(cmdStr)
+            return
+
+        def OnCmdButtonPressed(event):
+            handleCommand()
+            return
+
+        def OnKeyPressed(event):
+            key = event.KeyCode()
+            if key == wx.WXK_RETURN:
+                handleCommand()
+            elif key == wx.WXK_UP:
+                if len(self._cmdList) < 1 or self._cmdIndex < 1:
+                    return
+
+                self._cmdInput.Clear()
+                self._cmdInput.AppendText(self._cmdList[self._cmdIndex - 1])
+                self._cmdIndex = self._cmdIndex - 1
+            elif key == wx.WXK_DOWN:
+                if len(self._cmdList) < 1 or self._cmdIndex >= len(self._cmdList):
+                    return
+
+                self._cmdInput.Clear()
+                self._cmdInput.AppendText(self._cmdList[self._cmdIndex - 1])
+                self._cmdIndex = self._cmdIndex + 1
+            else:
+                event.Skip()
+            return
+
+        def OnClrButtonPressed(event):
+            self._cmdOutput.Clear()
+
+        panel           = wx.Panel(parent, id)
+
+        cmdLabel        = wx.StaticText(panel, -1, "Cmd: ")
+        self._cmdInput  = wx.TextCtrl(panel)
+        cmdButton       = wx.Button(panel, label="Execute")
+        clrButton       = wx.Button(panel, label="Clear")
+        self._cmdOutput = wx.TextCtrl(panel, style=wx.TE_MULTILINE | wx.HSCROLL | wx.TE_READONLY | wx.TE_RICH2)
+
+        hbox            = wx.BoxSizer()
+        hbox.Add(cmdLabel, proportion=0, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL)
+        hbox.Add(self._cmdInput, proportion=1, flag=wx.EXPAND)
+        hbox.Add(cmdButton, proportion=0, flag=wx.RIGHT)
+        hbox.Add(clrButton, proportion=0, flag=wx.RIGHT)
+
+        vbox            = wx.BoxSizer(wx.VERTICAL)
+        vbox.Add(hbox, proportion=0, flag=wx.EXPAND | wx.ALL, border=2)
+        vbox.Add(self._cmdOutput, proportion=1, flag=wx.EXPAND | wx.LEFT, border=2)
+
+        panel.SetSizer(vbox)
+        cmdButton.Bind(wx.EVT_BUTTON, OnCmdButtonPressed)
+        clrButton.Bind(wx.EVT_BUTTON, OnClrButtonPressed)
+        wx.EVT_KEY_DOWN(self._cmdInput, OnKeyPressed)
+
+        fixedFont = wx.Font(self._cmdInput.GetFont().GetPointSize(), family=wx.TELETYPE, style=wx.NORMAL, weight=wx.NORMAL)
+        self._defaultOutputTextStyle = wx.TextAttr("BLACK", wx.NullColour, fixedFont)
+        self._cmdOutputTextStyle = wx.TextAttr("RED", wx.NullColour, fixedFont)
+        self._cmdOutput.SetDefaultStyle(style=self._defaultOutputTextStyle)
+        self._cmdList  = []
+        self._cmdIndex = 0
+
+        panel.Show()
+        return panel
+
+    def MakeBreakPointsTab(self, parent, id):
+        panel = BreakpointsUI(parent, id, self._ui)
+        return panel
+
+    def OnRightClick(self, event):
+        assert False, "OnRightClick not overridden"
+
+    def ClearWhileRunning(self):
+        list = self._framesChoiceCtrl
+        list.Clear()
+        list.Enable(False)
+        tree = self._treeCtrl
+        root = self._root
+        tree.DeleteChildren(root)
+        self._cmdInput.Enable(False)
+        self._cmdOutput.Enable(False)
+
+    def OnListRightClick(self, event):
+        if not hasattr(self, "syncFrameID"):
+            self.syncFrameID = wx.NewId()
+            self.Bind(wx.EVT_MENU, self.OnSyncFrame, id=self.syncFrameID)
+        menu = wx.Menu()
+        item = wx.MenuItem(menu, self.syncFrameID, "Goto Source Line")
+        menu.AppendItem(item)
+        self.PopupMenu(menu, event.GetPosition())
+        menu.Destroy()
+
+    def OnSyncFrame(self, event):
+        assert False, "OnSyncFrame not overridden"
+
+    def LoadFramesList(self, framesXML):
+        assert False, "LoadFramesList not overridden"
+
+    def ListItemSelected(self, event):
+        assert False, "ListItemSelected not overridden"
+
+    def PopulateTreeFromFrameMessage(self, message):
+        assert False, "PopulateTreeFromFrameMessage not overridden"
+
+    def IntrospectCallback(self, event):
+        assert False, "IntrospectCallback not overridden"
+
+    def AppendText(self, text):
+        self._textCtrl.SetReadOnly(False)
+        self._textCtrl.AddText(text)
+        self._textCtrl.ScrollToLine(self._textCtrl.GetLineCount())
+        self._textCtrl.SetReadOnly(True)
+
+    def AppendErrorText(self, text):
+        self._textCtrl.SetReadOnly(False)
+        self._textCtrl.SetFontColor(wx.RED)
+        self._textCtrl.StyleClearAll()
+        self._textCtrl.AddText(text)
+        self._textCtrl.ScrollToLine(self._textCtrl.GetLineCount())
+        self._textCtrl.SetFontColor(wx.BLACK)
+        self._textCtrl.StyleClearAll()
+        self._textCtrl.SetReadOnly(True)
+
+    def ClearOutput(self, event):
+        self._textCtrl.SetReadOnly(False)
+        self._textCtrl.ClearAll()
+        self._textCtrl.SetReadOnly(True)
+
+    def SwitchToOutputTab(self):
+        self._notebook.SetSelection(0)
+
+class PHPFramesUI(BaseFramesUI):
+    def __init__(self, parent, id, ui):
+        BaseFramesUI.__init__(self, parent, id, ui)
+
+    def LoadFramesList(self, stackList):
+        wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
+
+        self._cmdInput.Enable(True)
+        self._cmdOutput.Enable(True)
+        index       = 0
+        self._stack = stackList
+        list        = self._framesChoiceCtrl
+        list.Clear()
+
+        if len(stackList) > 0:
+            self._displayVariableTreeRootNode()
+
+            for stackFrame in stackList:
+                message = stackFrame.getDisplayStr(stackList)
+                list.Append(message)
+
+            self.currentItem = index
+            list.SetSelection(index)
+            list.Enable(True)
+            self.OnSyncFrame(None)
+            self._p1.FitInside()
+
+        wx.GetApp().GetTopWindow().SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT))
+
+    def PopulateTreeFromStackFrame(self, frameNode):
+        vars         = frameNode.getVariables()
+        tree         = self._treeCtrl
+        rootTreeNode = self._root
+
+        #
+        # Build a new sub variable tree from the root node.
+        #
+        tree.DeleteChildren(rootTreeNode)
+        for var in vars:
+            aTreeNode = self.AppendSubTreeFromAVariable(tree, var, rootTreeNode)
+
+        #
+        # No need to expand the node here, as the IntrospectCallback() has
+        # already called it.
+        #
+        self._p2.FitInside()
+
+    def AppendSubTreeFromAVariable(self, tree, var, parentTreeNode, previousOne = None):
+        varName     = var.getName()
+        varValueStr = var.getValueString()
+
+        #
+        # If previously we already have this item in the tree, replace it.
+        # Otherwise, insert a new one.
+        #
+        if previousOne:
+            newNode = tree.InsertItem(parentTreeNode, previousOne, varName)
+        else:
+            newNode = tree.AppendItem(parentTreeNode, varName)
+
+        #
+        # Associate this variable object with this newNode.
+        #
+        tree.SetPyData(newNode, var)
+
+        #
+        # Set this variable's value string (for displaying).
+        #
+        if varValueStr and len(varValueStr) > 0:
+            tree.SetItemText(newNode, varValueStr, 1)
+
+        #
+        # If this variable has child variables, recursively build the sub
+        # variable tree.
+        #
+        if var.hasChildren():
+            childrenVarList = var.getChildrenVariables()
+            for childVar in childrenVarList:
+                self.AppendSubTreeFromAVariable(tree, childVar, newNode)
+
+            #
+            # If its child variables are sortable, sort it.
+            #
+            if var.childrenIsSortable():
+                tree.SortChildren(newNode)
+
+        return newNode
+
+    def IntrospectCallback(self, event):
+        tree = self._treeCtrl
+        item = event.GetItem()
+
+        #
+        # Only when the introspection happens to root, we get the whole
+        # variable tree.  For all the individual nodes, we have populated
+        # the subtree already, so don't need to do anything.
+        #
+        if tree.GetPyData(item) == "root" and self._stack and self._stack[self.currentItem]:
+            stackFrame = self._stack[self.currentItem]
+            self.PopulateTreeFromStackFrame(stackFrame)
+
+        event.Skip()
+
+    def OnSyncFrame(self, event):
+        stackFrame = self._stack[self.currentItem]
+        fileName   = stackFrame.getFileName()
+        lineNo     = stackFrame.getLineNo()
+        if _VERBOSE:
+            print "OnSyncFrame(): about to sync: fileName: %s, lineNo: %d" % (fileName, lineNo)
+        self._ui.SynchCurrentLine(fileName, lineNo)
+        if _VERBOSE:
+            print "OnSyncFrame(): sync done"