# A general purpose MFC CCtrlView view that uses Scintilla. import control import IDLEenvironment # IDLE emulation. from pywin.mfc import docview from pywin.mfc import dialog import scintillacon import win32con import win32ui import afxres import string import array import sys import types import __main__ # for attribute lookup import bindings import keycodes import struct import re import os PRINTDLGORD = 1538 IDC_PRINT_MAG_EDIT = 1010 EM_FORMATRANGE = win32con.WM_USER+57 wordbreaks = "._" + string.ascii_uppercase + string.ascii_lowercase + string.digits patImport=re.compile('import (?P.*)') _event_commands = [ # File menu "win32ui.ID_FILE_LOCATE", "win32ui.ID_FILE_CHECK", "afxres.ID_FILE_CLOSE", "afxres.ID_FILE_NEW", "afxres.ID_FILE_OPEN", "afxres.ID_FILE_SAVE", "afxres.ID_FILE_SAVE_AS", "win32ui.ID_FILE_SAVE_ALL", # Edit menu "afxres.ID_EDIT_UNDO", "afxres.ID_EDIT_REDO", "afxres.ID_EDIT_CUT", "afxres.ID_EDIT_COPY", "afxres.ID_EDIT_PASTE", "afxres.ID_EDIT_SELECT_ALL", "afxres.ID_EDIT_FIND", "afxres.ID_EDIT_REPEAT", "afxres.ID_EDIT_REPLACE", # View menu "win32ui.ID_VIEW_WHITESPACE", "win32ui.ID_VIEW_FIXED_FONT", "win32ui.ID_VIEW_BROWSE", "win32ui.ID_VIEW_INTERACTIVE", # Window menu "afxres.ID_WINDOW_ARRANGE", "afxres.ID_WINDOW_CASCADE", "afxres.ID_WINDOW_NEW", "afxres.ID_WINDOW_SPLIT", "afxres.ID_WINDOW_TILE_HORZ", "afxres.ID_WINDOW_TILE_VERT", # Others "afxres.ID_APP_EXIT", "afxres.ID_APP_ABOUT", ] _extra_event_commands = [ ("EditDelete", afxres.ID_EDIT_CLEAR), ("LocateModule", win32ui.ID_FILE_LOCATE), ("GotoLine", win32ui.ID_EDIT_GOTO_LINE), ("DbgBreakpointToggle", win32ui.IDC_DBG_ADD), ("DbgGo", win32ui.IDC_DBG_GO), ("DbgStepOver", win32ui.IDC_DBG_STEPOVER), ("DbgStep", win32ui.IDC_DBG_STEP), ("DbgStepOut", win32ui.IDC_DBG_STEPOUT), ("DbgBreakpointClearAll", win32ui.IDC_DBG_CLEAR), ("DbgClose", win32ui.IDC_DBG_CLOSE), ] event_commands = [] def _CreateEvents(): for name in _event_commands: val = eval(name) name_parts = name.split("_")[1:] name_parts = [p.capitalize() for p in name_parts] event = ''.join(name_parts) event_commands.append((event, val)) for name, id in _extra_event_commands: event_commands.append((name, id)) _CreateEvents() del _event_commands; del _extra_event_commands command_reflectors = [ (win32ui.ID_EDIT_UNDO, win32con.WM_UNDO), (win32ui.ID_EDIT_REDO, scintillacon.SCI_REDO), (win32ui.ID_EDIT_CUT, win32con.WM_CUT), (win32ui.ID_EDIT_COPY, win32con.WM_COPY), (win32ui.ID_EDIT_PASTE, win32con.WM_PASTE), (win32ui.ID_EDIT_CLEAR, win32con.WM_CLEAR), (win32ui.ID_EDIT_SELECT_ALL, scintillacon.SCI_SELECTALL), ] def DoBraceMatch(control): curPos = control.SCIGetCurrentPos() charBefore = ' ' if curPos: charBefore = control.SCIGetCharAt(curPos-1) charAt = control.SCIGetCharAt(curPos) braceAtPos = braceOpposite = -1 if charBefore in "[](){}": braceAtPos = curPos-1 if braceAtPos==-1: if charAt in "[](){}": braceAtPos = curPos if braceAtPos != -1: braceOpposite = control.SCIBraceMatch(braceAtPos, 0) if braceAtPos != -1 and braceOpposite==-1: control.SCIBraceBadHighlight(braceAtPos) else: # either clear them both or set them both. control.SCIBraceHighlight(braceAtPos, braceOpposite) def _get_class_attributes(ob): # Recurse into base classes looking for attributes items = [] try: items = items + dir(ob) for i in ob.__bases__: for item in _get_class_attributes(i): if item not in items: items.append(item) except AttributeError: pass return items # Supposed to look like an MFC CEditView, but # also supports IDLE extensions and other source code generic features. class CScintillaView(docview.CtrlView, control.CScintillaColorEditInterface): def __init__(self, doc): docview.CtrlView.__init__(self, doc, "Scintilla", win32con.WS_CHILD | win32con.WS_VSCROLL | win32con.WS_HSCROLL | win32con.WS_CLIPCHILDREN | win32con.WS_VISIBLE) self._tabWidth = 8 # Mirror of what we send to Scintilla - never change this directly self.bAutoCompleteAttributes = 1 self.bShowCallTips = 1 self.bMatchBraces = 0 # Editor option will default this to true later! self.bindings = bindings.BindingsManager(self) self.idle = IDLEenvironment.IDLEEditorWindow(self) self.idle.IDLEExtension("AutoExpand") # SendScintilla is called so frequently it is worth optimizing. self.SendScintilla = self._obj_.SendMessage def OnDestroy(self, msg): self.SendScintilla = None return docview.CtrlView.OnDestroy(self, msg) def _MakeColorizer(self): ext = os.path.splitext(self.GetDocument().GetPathName())[1] import formatter return formatter.BuiltinPythonSourceFormatter(self, ext) # def SendScintilla(self, msg, w=0, l=0): # return self._obj_.SendMessage(msg, w, l) def SCISetTabWidth(self, width): # I need to remember the tab-width for the AutoIndent extension. This may go. self._tabWidth = width control.CScintillaEditInterface.SCISetTabWidth(self, width) def GetTabWidth(self): return self._tabWidth def HookHandlers(self): # Create events for all the menu names. for name, val in event_commands: # handler = lambda id, code, tosend=val, parent=parent: parent.OnCommand(tosend, 0) and 0 self.bindings.bind(name, None, cid=val) # Hook commands that do nothing other than send Scintilla messages. for command, reflection in command_reflectors: handler = lambda id, code, ss=self.SendScintilla, tosend=reflection: ss(tosend) and 0 self.HookCommand(handler, command) self.HookCommand(self.OnCmdViewWS, win32ui.ID_VIEW_WHITESPACE) self.HookCommandUpdate(self.OnUpdateViewWS, win32ui.ID_VIEW_WHITESPACE) self.HookCommand(self.OnCmdViewIndentationGuides, win32ui.ID_VIEW_INDENTATIONGUIDES) self.HookCommandUpdate(self.OnUpdateViewIndentationGuides, win32ui.ID_VIEW_INDENTATIONGUIDES) self.HookCommand(self.OnCmdViewRightEdge, win32ui.ID_VIEW_RIGHT_EDGE) self.HookCommandUpdate(self.OnUpdateViewRightEdge, win32ui.ID_VIEW_RIGHT_EDGE) self.HookCommand(self.OnCmdViewEOL, win32ui.ID_VIEW_EOL) self.HookCommandUpdate(self.OnUpdateViewEOL, win32ui.ID_VIEW_EOL) self.HookCommand(self.OnCmdViewFixedFont, win32ui.ID_VIEW_FIXED_FONT) self.HookCommandUpdate(self.OnUpdateViewFixedFont, win32ui.ID_VIEW_FIXED_FONT) self.HookCommand(self.OnCmdFileLocate, win32ui.ID_FILE_LOCATE) self.HookCommand(self.OnCmdEditFind, win32ui.ID_EDIT_FIND) self.HookCommand(self.OnCmdEditRepeat, win32ui.ID_EDIT_REPEAT) self.HookCommand(self.OnCmdEditReplace, win32ui.ID_EDIT_REPLACE) self.HookCommand(self.OnCmdGotoLine, win32ui.ID_EDIT_GOTO_LINE) self.HookCommand(self.OnFilePrint, afxres.ID_FILE_PRINT) self.HookCommand(self.OnFilePrint, afxres.ID_FILE_PRINT_DIRECT) self.HookCommand(self.OnFilePrintPreview, win32ui.ID_FILE_PRINT_PREVIEW) # Key bindings. self.HookMessage(self.OnKeyDown, win32con.WM_KEYDOWN) self.HookMessage(self.OnKeyDown, win32con.WM_SYSKEYDOWN) # Hook wheeley mouse events # self.HookMessage(self.OnMouseWheel, win32con.WM_MOUSEWHEEL) self.HookFormatter() def OnInitialUpdate(self): doc = self.GetDocument() # Enable Unicode self.SendScintilla(scintillacon.SCI_SETCODEPAGE, scintillacon.SC_CP_UTF8, 0) self.SendScintilla(scintillacon.SCI_SETKEYSUNICODE, 1, 0) # Create margins self.SendScintilla(scintillacon.SCI_SETMARGINTYPEN, 1, scintillacon.SC_MARGIN_SYMBOL); self.SendScintilla(scintillacon.SCI_SETMARGINMASKN, 1, 0xF); self.SendScintilla(scintillacon.SCI_SETMARGINTYPEN, 2, scintillacon.SC_MARGIN_SYMBOL); self.SendScintilla(scintillacon.SCI_SETMARGINMASKN, 2, scintillacon.SC_MASK_FOLDERS); self.SendScintilla(scintillacon.SCI_SETMARGINSENSITIVEN, 2, 1); self.GetDocument().HookViewNotifications(self) # is there an MFC way to grab this? self.HookHandlers() # Load the configuration information. self.OnWinIniChange(None) self.SetSel() self.GetDocument().FinalizeViewCreation(self) # is there an MFC way to grab this? def _GetSubConfigNames(self): return None # By default we use only sections without sub-sections. def OnWinIniChange(self, section = None): self.bindings.prepare_configure() try: self.DoConfigChange() finally: self.bindings.complete_configure() def DoConfigChange(self): # Bit of a hack I dont kow what to do about - these should be "editor options" from pywin.framework.editor import GetEditorOption self.bAutoCompleteAttributes = GetEditorOption("Autocomplete Attributes", 1) self.bShowCallTips = GetEditorOption("Show Call Tips", 1) # Update the key map and extension data. configManager.configure(self, self._GetSubConfigNames()) if configManager.last_error: win32ui.MessageBox(configManager.last_error, "Configuration Error") self.bMatchBraces = GetEditorOption("Match Braces", 1) self.ApplyFormattingStyles(1) def OnDestroy(self, msg): self.bindings.close() self.bindings = None self.idle.close() self.idle = None control.CScintillaColorEditInterface.close(self) return docview.CtrlView.OnDestroy(self, msg) def OnMouseWheel(self, msg): zDelta = msg[2] >> 16 vpos = self.GetScrollPos(win32con.SB_VERT) vpos = vpos - zDelta/40 # 3 lines per notch self.SetScrollPos(win32con.SB_VERT, vpos) self.SendScintilla(win32con.WM_VSCROLL, (vpos<<16) | win32con.SB_THUMBPOSITION, 0) def OnBraceMatch(self, std, extra): if not self.bMatchBraces: return DoBraceMatch(self) def OnNeedShown(self, std, extra): notify = self.SCIUnpackNotifyMessage(extra) # OnNeedShown is called before an edit operation when # text is folded (as it is possible the text insertion will happen # in a folded region.) As this happens _before_ the insert, # we ignore the length (if we are at EOF, pos + length may # actually be beyond the end of buffer) self.EnsureCharsVisible(notify.position) def EnsureCharsVisible(self, start, end = None): if end is None: end = start lineStart = self.LineFromChar(min(start, end)) lineEnd = self.LineFromChar(max(start, end)) while lineStart <= lineEnd: self.SCIEnsureVisible(lineStart) lineStart = lineStart + 1 # Helper to add an event to a menu. def AppendMenu(self, menu, text="", event=None, flags = None, checked=0): if event is None: assert flags is not None, "No event or custom flags!" cmdid = 0 else: cmdid = self.bindings.get_command_id(event) if cmdid is None: # No event of that name - no point displaying it. print 'View.AppendMenu(): Unknown event "%s" specified for menu text "%s" - ignored' % (event, text) return keyname = configManager.get_key_binding( event, self._GetSubConfigNames() ) if keyname is not None: text = text + "\t" + keyname if flags is None: flags = win32con.MF_STRING|win32con.MF_ENABLED if checked: flags = flags | win32con.MF_CHECKED menu.AppendMenu(flags, cmdid, text) def OnKeyDown(self, msg): return self.bindings.fire_key_event( msg ) def GotoEndOfFileEvent(self, event): self.SetSel(-1) def KeyDotEvent(self, event): ## Don't trigger autocomplete if any text is selected s,e = self.GetSel() if s!=e: return 1 self.SCIAddText(".") if self.bAutoCompleteAttributes: self._AutoComplete() # View Whitespace/EOL/Indentation UI. def OnCmdViewWS(self, cmd, code): # Handle the menu command viewWS = self.SCIGetViewWS() self.SCISetViewWS(not viewWS) def OnUpdateViewWS(self, cmdui): # Update the tick on the UI. cmdui.SetCheck(self.SCIGetViewWS()) cmdui.Enable() def OnCmdViewIndentationGuides(self, cmd, code): # Handle the menu command viewIG = self.SCIGetIndentationGuides() self.SCISetIndentationGuides(not viewIG) def OnUpdateViewIndentationGuides(self, cmdui): # Update the tick on the UI. cmdui.SetCheck(self.SCIGetIndentationGuides()) cmdui.Enable() def OnCmdViewRightEdge(self, cmd, code): # Handle the menu command if self.SCIGetEdgeMode() == scintillacon.EDGE_NONE: mode = scintillacon.EDGE_BACKGROUND else: mode = scintillacon.EDGE_NONE self.SCISetEdgeMode(mode) def OnUpdateViewRightEdge(self, cmdui): # Update the tick on the UI. cmdui.SetCheck(self.SCIGetEdgeMode() != scintillacon.EDGE_NONE) cmdui.Enable() def OnCmdViewEOL(self, cmd, code): # Handle the menu command viewEOL = self.SCIGetViewEOL() self.SCISetViewEOL(not viewEOL) def OnUpdateViewEOL(self, cmdui): # Update the tick on the UI. cmdui.SetCheck(self.SCIGetViewEOL()) cmdui.Enable() def OnCmdViewFixedFont(self, cmd, code): # Handle the menu command self._GetColorizer().bUseFixed = not self._GetColorizer().bUseFixed self.ApplyFormattingStyles(0) # Ensure the selection is visible! self.ScrollCaret() def OnUpdateViewFixedFont(self, cmdui): # Update the tick on the UI. c = self._GetColorizer() if c is not None: cmdui.SetCheck(c.bUseFixed) cmdui.Enable(c is not None) def OnCmdEditFind(self, cmd, code): import find find.ShowFindDialog() def OnCmdEditRepeat(self, cmd, code): import find find.FindNext() def OnCmdEditReplace(self, cmd, code): import find find.ShowReplaceDialog() def OnCmdFileLocate(self, cmd, id): line = self.GetLine().strip() import pywin.framework.scriptutils m = patImport.match(line) if m: # Module name on this line - locate that! modName = m.group('name') fileName = pywin.framework.scriptutils.LocatePythonFile(modName) if fileName is None: win32ui.SetStatusText("Can't locate module %s" % modName) return 1 # Let the default get it. else: win32ui.GetApp().OpenDocumentFile(fileName) else: # Just to a "normal" locate - let the default handler get it. return 1 return 0 def OnCmdGotoLine(self, cmd, id): try: lineNo = int(raw_input("Enter Line Number"))-1 except (ValueError, KeyboardInterrupt): return 0 self.SCIEnsureVisible(lineNo) self.SCIGotoLine(lineNo) return 0 def SaveTextFile(self, filename, encoding=None): doc = self.GetDocument() doc._SaveTextToFile(self, filename, encoding=encoding) doc.SetModifiedFlag(0) return 1 def _AutoComplete(self): def list2dict(l): ret={} for i in l: ret[i] = None return ret self.SCIAutoCCancel() # Cancel old auto-complete lists. # First try and get an object without evaluating calls ob = self._GetObjectAtPos(bAllowCalls = 0) # If that failed, try and process call or indexing to get the object. if ob is None: ob = self._GetObjectAtPos(bAllowCalls = 1) items_dict = {} if ob is not None: try: # Catch unexpected errors when fetching attribute names from the object # extra attributes of win32ui objects if hasattr(ob, "_obj_"): try: items_dict.update(list2dict(dir(ob._obj_))) except AttributeError: pass # object has no __dict__ # normal attributes try: items_dict.update(list2dict(dir(ob))) except AttributeError: pass # object has no __dict__ if hasattr(ob, "__class__"): items_dict.update(list2dict(_get_class_attributes(ob.__class__))) # The object may be a COM object with typelib support - lets see if we can get its props. # (contributed by Stefan Migowsky) try: # Get the automation attributes items_dict.update(ob.__class__._prop_map_get_) # See if there is an write only property # could be optimized items_dict.update(ob.__class__._prop_map_put_) # append to the already evaluated list except AttributeError: pass # The object might be a pure COM dynamic dispatch with typelib support - lets see if we can get its props. if hasattr(ob, "_oleobj_"): try: for iTI in xrange(0,ob._oleobj_.GetTypeInfoCount()): typeInfo = ob._oleobj_.GetTypeInfo(iTI) self._UpdateWithITypeInfo (items_dict, typeInfo) except: pass except: win32ui.SetStatusText("Error attempting to get object attributes - %s" % (repr(sys.exc_info()[0]),)) # ensure all keys are strings. items = [str(k) for k in items_dict.iterkeys()] # All names that start with "_" go! items = [k for k in items if not k.startswith('_')] if not items: # Heuristics a-la AutoExpand # The idea is to find other usages of the current binding # and assume, that it refers to the same object (or at least, # to an object of the same type) # Contributed by Vadim Chugunov [vadimch@yahoo.com] left, right = self._GetWordSplit() if left=="": # Ignore standalone dots return None # We limit our search to the current class, if that # information is available minline, maxline, curclass = self._GetClassInfoFromBrowser() endpos = self.LineIndex(maxline) text = self.GetTextRange(self.LineIndex(minline),endpos) try: l = re.findall(r"\b"+left+"\.\w+",text) except re.error: # parens etc may make an invalid RE, but this code wouldnt # benefit even if the RE did work :-) l = [] prefix = len(left)+1 unique = {} for li in l: unique[li[prefix:]] = 1 # Assuming traditional usage of self... if curclass and left=="self": self._UpdateWithClassMethods(unique,curclass) items = [word for word in unique.iterkeys() if word[:2]!='__' or word[-2:]!='__'] # Ignore the word currently to the right of the dot - probably a red-herring. try: items.remove(right[1:]) except ValueError: pass if items: items.sort() self.SCIAutoCSetAutoHide(0) self.SCIAutoCShow(items) def _UpdateWithITypeInfo (self, items_dict, typeInfo): import pythoncom typeInfos = [typeInfo] # suppress IDispatch and IUnknown methods inspectedIIDs = {pythoncom.IID_IDispatch:None} while len(typeInfos)>0: typeInfo = typeInfos.pop() typeAttr = typeInfo.GetTypeAttr() if typeAttr.iid not in inspectedIIDs: inspectedIIDs[typeAttr.iid] = None for iFun in xrange(0,typeAttr.cFuncs): funDesc = typeInfo.GetFuncDesc(iFun) funName = typeInfo.GetNames(funDesc.memid)[0] if funName not in items_dict: items_dict[funName] = None # Inspect the type info of all implemented types # E.g. IShellDispatch5 implements IShellDispatch4 which implements IShellDispatch3 ... for iImplType in xrange(0,typeAttr.cImplTypes): iRefType = typeInfo.GetRefTypeOfImplType(iImplType) refTypeInfo = typeInfo.GetRefTypeInfo(iRefType) typeInfos.append(refTypeInfo) # TODO: This is kinda slow. Probably need some kind of cache # here that is flushed upon file save # Or maybe we don't need the superclass methods at all ? def _UpdateWithClassMethods(self,dict,classinfo): if not hasattr(classinfo,"methods"): # No 'methods' - probably not what we think it is. return dict.update(classinfo.methods) for super in classinfo.super: if hasattr(super,"methods"): self._UpdateWithClassMethods(dict,super) # Find which class definition caret is currently in and return # indexes of the the first and the last lines of that class definition # Data is obtained from module browser (if enabled) def _GetClassInfoFromBrowser(self,pos=-1): minline = 0 maxline = self.GetLineCount()-1 doc = self.GetParentFrame().GetActiveDocument() browser = None try: if doc is not None: browser = doc.GetAllViews()[1] except IndexError: pass if browser is None: return (minline,maxline,None) # Current window has no browser if not browser.list: return (minline,maxline,None) # Not initialized path = self.GetDocument().GetPathName() if not path: return (minline,maxline,None) # No current path import pywin.framework.scriptutils curmodule, path = pywin.framework.scriptutils.GetPackageModuleName(path) try: clbrdata = browser.list.root.clbrdata except AttributeError: return (minline,maxline,None) # No class data for this module. curline = self.LineFromChar(pos) curclass = None # Find out which class we are in for item in clbrdata.itervalues(): if item.module==curmodule: item_lineno = item.lineno - 1 # Scintilla counts lines from 0, whereas pyclbr - from 1 if minline < item_lineno <= curline: minline = item_lineno curclass = item if curline < item_lineno < maxline: maxline = item_lineno return (minline,maxline,curclass) def _GetObjectAtPos(self, pos = -1, bAllowCalls = 0): left, right = self._GetWordSplit(pos, bAllowCalls) if left: # It is an attribute lookup # How is this for a hack! namespace = sys.modules.copy() namespace.update(__main__.__dict__) # Get the debugger's context. try: from pywin.framework import interact if interact.edit is not None and interact.edit.currentView is not None: globs, locs = interact.edit.currentView.GetContext()[:2] if globs: namespace.update(globs) if locs: namespace.update(locs) except ImportError: pass try: return eval(left, namespace) except: pass return None def _GetWordSplit(self, pos = -1, bAllowCalls = 0): if pos==-1: pos = self.GetSel()[0]-1 # Character before current one limit = self.GetTextLength() before = [] after = [] index = pos-1 wordbreaks_use = wordbreaks if bAllowCalls: wordbreaks_use = wordbreaks_use + "()[]" while index>=0: char = self.SCIGetCharAt(index) if char not in wordbreaks_use: break before.insert(0, char) index = index-1 index = pos while index<=limit: char = self.SCIGetCharAt(index) if char not in wordbreaks_use: break after.append(char) index=index+1 return ''.join(before), ''.join(after) def OnPrepareDC (self, dc, pInfo): # print "OnPrepareDC for page", pInfo.GetCurPage(), "of", pInfo.GetFromPage(), "to", pInfo.GetToPage(), ", starts=", self.starts if dc.IsPrinting(): # Check if we are beyond the end. # (only do this when actually printing, else messes up print preview!) if not pInfo.GetPreview() and self.starts is not None: prevPage = pInfo.GetCurPage() - 1 if prevPage > 0 and self.starts[prevPage] >= self.GetTextLength(): # All finished. pInfo.SetContinuePrinting(0) return dc.SetMapMode(win32con.MM_TEXT); def OnPreparePrinting(self, pInfo): flags = win32ui.PD_USEDEVMODECOPIES | \ win32ui.PD_ALLPAGES | \ win32ui.PD_NOSELECTION # Dont support printing just a selection. # NOTE: Custom print dialogs are stopping the user's values from coming back :-( # self.prtDlg = PrintDialog(pInfo, PRINTDLGORD, flags) # pInfo.SetPrintDialog(self.prtDlg) pInfo.SetMinPage(1) # max page remains undefined for now. pInfo.SetFromPage(1) pInfo.SetToPage(1) ret = self.DoPreparePrinting(pInfo) return ret def OnBeginPrinting(self, dc, pInfo): self.starts = None return self._obj_.OnBeginPrinting(dc, pInfo) def CalculatePageRanges(self, dc, pInfo): # Calculate page ranges and max page self.starts = {0:0} metrics = dc.GetTextMetrics() left, top, right, bottom = pInfo.GetDraw() # Leave space at the top for the header. rc = (left, top + int((9*metrics['tmHeight'])/2), right, bottom) pageStart = 0 maxPage = 0 textLen = self.GetTextLength() while pageStart < textLen: pageStart = self.FormatRange(dc, pageStart, textLen, rc, 0) maxPage = maxPage + 1 self.starts[maxPage] = pageStart # And a sentinal for one page past the end self.starts[maxPage+1] = textLen # When actually printing, maxPage doesnt have any effect at this late state. # but is needed to make the Print Preview work correctly. pInfo.SetMaxPage(maxPage) def OnFilePrintPreview(self, *arg): self._obj_.OnFilePrintPreview() def OnFilePrint(self, *arg): self._obj_.OnFilePrint() def FormatRange(self, dc, pageStart, lengthDoc, rc, draw): """ typedef struct _formatrange { HDC hdc; HDC hdcTarget; RECT rc; RECT rcPage; CHARRANGE chrg;} FORMATRANGE; """ fmt='PPIIIIIIIIll' hdcRender = dc.GetHandleOutput() hdcFormat = dc.GetHandleAttrib() fr = struct.pack(fmt, hdcRender, hdcFormat, rc[0], rc[1], rc[2], rc[3], rc[0], rc[1], rc[2], rc[3], pageStart, lengthDoc) nextPageStart = self.SendScintilla(EM_FORMATRANGE, draw, fr) return nextPageStart def OnPrint(self, dc, pInfo): metrics = dc.GetTextMetrics() # print "dev", w, h, l, metrics['tmAscent'], metrics['tmDescent'] if self.starts is None: self.CalculatePageRanges(dc, pInfo) pageNum = pInfo.GetCurPage() - 1 # Setup the header of the page - docname on left, pagenum on right. doc = self.GetDocument() cxChar = metrics['tmAveCharWidth'] cyChar = metrics['tmHeight'] left, top, right, bottom = pInfo.GetDraw() dc.TextOut(0, 2*cyChar, doc.GetTitle()) pagenum_str = win32ui.LoadString(afxres.AFX_IDS_PRINTPAGENUM) % (pageNum+1,) dc.SetTextAlign(win32con.TA_RIGHT) dc.TextOut(right, 2*cyChar, pagenum_str) dc.SetTextAlign(win32con.TA_LEFT) top = top + int((7*cyChar)/2) dc.MoveTo(left, top) dc.LineTo(right, top) top = top + cyChar rc = (left, top, right, bottom) nextPageStart = self.FormatRange(dc, self.starts[pageNum], self.starts[pageNum+1], rc, 1) def LoadConfiguration(): global configManager # Bit of a hack I dont kow what to do about? from config import ConfigManager configName = rc = win32ui.GetProfileVal("Editor", "Keyboard Config", "default") configManager = ConfigManager(configName) if configManager.last_error: bTryDefault = 0 msg = "Error loading configuration '%s'\n\n%s" % (configName, configManager.last_error) if configName != "default": msg = msg + "\n\nThe default configuration will be loaded." bTryDefault = 1 win32ui.MessageBox(msg) if bTryDefault: configManager = ConfigManager("default") if configManager.last_error: win32ui.MessageBox("Error loading configuration 'default'\n\n%s" % (configManager.last_error)) configManager = None LoadConfiguration()