# Command Handlers for the debugger.

# Not in the debugger package, as I always want these interfaces to be
# available, even if the debugger has not yet been (or can not be)
# imported
import win32ui, win32con
import scriptutils
import warnings
from pywin.scintilla.control import CScintillaEditInterface

IdToBarNames = {
	win32ui.IDC_DBG_STACK : ("Stack",0),
	win32ui.IDC_DBG_BREAKPOINTS : ("Breakpoints",0),
	win32ui.IDC_DBG_WATCH : ("Watch",1),
	}

class DebuggerCommandHandler:
	def HookCommands(self):
		commands = ( (self.OnStep, None, win32ui.IDC_DBG_STEP),
		                  (self.OnStepOut, self.OnUpdateOnlyBreak, win32ui.IDC_DBG_STEPOUT),
		                  (self.OnStepOver, None, win32ui.IDC_DBG_STEPOVER),
		                  (self.OnGo, None, win32ui.IDC_DBG_GO),
		                  (self.OnClose, self.OnUpdateClose, win32ui.IDC_DBG_CLOSE),
		                  (self.OnAdd, self.OnUpdateAddBreakpoints, win32ui.IDC_DBG_ADD),
		                  (self.OnClearAll, self.OnUpdateClearAllBreakpoints, win32ui.IDC_DBG_CLEAR),
#		                  (self.OnDebuggerToolbar, self.OnUpdateDebuggerToolbar, win32ui.ID_DEBUGGER_TOOLBAR),
		                )

		frame = win32ui.GetMainFrame()

		for methHandler, methUpdate, id in commands:
			frame.HookCommand(methHandler, id);
			if not methUpdate is None:
				frame.HookCommandUpdate(methUpdate, id)

		for id in IdToBarNames.keys():
			frame.HookCommand( self.OnDebuggerBar, id)
			frame.HookCommandUpdate(self.OnUpdateDebuggerBar, id)

	def OnDebuggerToolbar(self, id, code):
		if code==0:
			return not win32ui.GetMainFrame().OnBarCheck(id)

	def OnUpdateDebuggerToolbar(self, cmdui):
		win32ui.GetMainFrame().OnUpdateControlBarMenu(cmdui)
		cmdui.Enable(1)

	def _GetDebugger(self):
		try:
			import pywin.debugger
			return pywin.debugger.currentDebugger
		except ImportError:
			return None

	def _DoOrStart(self, doMethod, startFlag):
		d=self._GetDebugger()
		if d is not None and d.IsDebugging():
			method = getattr(d, doMethod)
			method()
		else:
			scriptutils.RunScript(defName=None, defArgs=None, bShowDialog = 0, debuggingType=startFlag)

	def OnStep(self, msg, code):
		self._DoOrStart("do_set_step", scriptutils.RS_DEBUGGER_STEP)

	def OnStepOver(self, msg, code):
		self._DoOrStart("do_set_next", scriptutils.RS_DEBUGGER_STEP)

	def OnStepOut(self, msg, code):
		d=self._GetDebugger()
		if d is not None and d.IsDebugging():
			d.do_set_return()

	def OnGo(self, msg, code):
		self._DoOrStart("do_set_continue", scriptutils.RS_DEBUGGER_GO)

	def OnClose(self, msg, code):
		d=self._GetDebugger()
		if d is not None:
			if d.IsDebugging():
				d.set_quit()
			else:
				d.close()

	def OnUpdateClose(self, cmdui):
		d=self._GetDebugger()
		if d is not None and d.inited:
			cmdui.Enable(1)
		else:
			cmdui.Enable(0)

	def OnAdd(self, msg, code):
		doc, view = scriptutils.GetActiveEditorDocument()
		if doc is None:
			## Don't do a messagebox, as this could be triggered from the app's
			## idle loop whenever the debug toolbar is visible, giving a never-ending
			## series of dialogs.  This can happen when the OnUpdate handler
			## for the toolbar button IDC_DBG_ADD fails, since MFC falls back to
			## sending a normal command if the UI update command fails.
			## win32ui.MessageBox('There is no active window - no breakpoint can be added')
			warnings.warn('There is no active window - no breakpoint can be added')
			return None
		pathName = doc.GetPathName()
		lineNo = view.LineFromChar(view.GetSel()[0])+1
		# If I have a debugger, then tell it, otherwise just add a marker
		d=self._GetDebugger()
		if d is None:
			import pywin.framework.editor.color.coloreditor
			doc.MarkerToggle(lineNo, pywin.framework.editor.color.coloreditor.MARKER_BREAKPOINT)
		else:
			if d.get_break(pathName, lineNo):
				win32ui.SetStatusText('Clearing breakpoint',1)
				rc = d.clear_break(pathName, lineNo)
			else:
				win32ui.SetStatusText('Setting breakpoint',1)
				rc = d.set_break(pathName, lineNo)
			if rc:
				win32ui.MessageBox(rc)
			d.GUIRespondDebuggerData()

	def OnClearAll(self, msg, code):
		win32ui.SetStatusText('Clearing all breakpoints')
		d=self._GetDebugger()
		if d is None:
			import pywin.framework.editor
			import pywin.framework.editor.color.coloreditor
			for doc in pywin.framework.editor.editorTemplate.GetDocumentList():
				doc.MarkerDeleteAll(pywin.framework.editor.color.coloreditor.MARKER_BREAKPOINT)
		else:
			d.clear_all_breaks()
			d.UpdateAllLineStates()
			d.GUIRespondDebuggerData()

	def OnUpdateOnlyBreak(self, cmdui):
		d=self._GetDebugger()
		ok = d is not None and d.IsBreak()
		cmdui.Enable(ok)

	def OnUpdateAddBreakpoints(self, cmdui):
		doc, view = scriptutils.GetActiveEditorDocument()
		if doc is None or not isinstance(view, CScintillaEditInterface):
			enabled = 0
		else:
			enabled = 1
			lineNo = view.LineFromChar(view.GetSel()[0])+1
			import pywin.framework.editor.color.coloreditor
			cmdui.SetCheck(doc.MarkerAtLine(lineNo, pywin.framework.editor.color.coloreditor.MARKER_BREAKPOINT) != 0)
		cmdui.Enable(enabled)

	def OnUpdateClearAllBreakpoints(self, cmdui):
		d=self._GetDebugger()
		cmdui.Enable(d is None or len(d.breaks)!=0)

	def OnUpdateDebuggerBar(self, cmdui):
		name, always = IdToBarNames.get(cmdui.m_nID)
		enabled = always
		d=self._GetDebugger()
		if d is not None and d.IsDebugging() and name is not None:
			enabled = 1
			bar = d.GetDebuggerBar(name)
			cmdui.SetCheck(bar.IsWindowVisible())
		cmdui.Enable(enabled)

	def OnDebuggerBar(self, id, code):
		name = IdToBarNames.get(id)[0]
		d=self._GetDebugger()
		if d is not None and name is not None:
			bar = d.GetDebuggerBar(name)
			newState = not bar.IsWindowVisible()
			win32ui.GetMainFrame().ShowControlBar(bar, newState, 1)
