]> git.saurik.com Git - wxWidgets.git/blobdiff - wxPython/samples/ide/activegrid/tool/STCTextEditor.py
Docview and IDE patch from Morag Hua with fix for bug #1217890
[wxWidgets.git] / wxPython / samples / ide / activegrid / tool / STCTextEditor.py
index fca235ada21da433b091ec46749a1bdee1f667f2..549c708c23c250636920f5e51ebadd408336f2dd 100644 (file)
@@ -47,25 +47,16 @@ TEXT_STATUS_BAR_ID = wx.NewId()
 class TextDocument(wx.lib.docview.Document):
 
 
-    def OnSaveDocument(self, filename):
+    def SaveObject(self, fileObject):
         view = self.GetFirstView()
-        docFile = file(self._documentFile, "w")
-        docFile.write(view.GetValue())
-        docFile.close()
-        self.Modify(False)
-        self.SetDocumentSaved(True)
+        fileObject.write(view.GetValue())
         return True
+        
 
-
-    def OnOpenDocument(self, filename):
+    def LoadObject(self, fileObject):
         view = self.GetFirstView()
-        docFile = file(self._documentFile, 'r')
-        data = docFile.read()
+        data = fileObject.read()
         view.SetValue(data)
-        self.SetFilename(filename, True)
-        self.Modify(False)
-        self.UpdateAllViews()
-        self._savedYet = True
         return True
 
 
@@ -88,12 +79,6 @@ class TextDocument(wx.lib.docview.Document):
         # Don't create a command processor, it has its own
         pass
 
-# Use this to override MultiClient.Select to prevent yellow background.
-def MultiClientSelectBGNotYellow(a):
-        a.GetParent().multiView.UnSelect()
-        a.selected = True
-        #a.SetBackgroundColour(wx.Colour(255,255,0)) # Yellow
-        a.Refresh()
 
 class TextView(wx.lib.docview.View):
     MARKER_NUM = 0
@@ -108,39 +93,32 @@ class TextView(wx.lib.docview.View):
         self._textEditor = None
         self._markerCount = 0
         self._commandProcessor = None
-        self._multiSash = None
+        self._dynSash = None
 
 
     def GetCtrlClass(self):
+        """ Used in split window to instantiate new instances """
         return TextCtrl
-
+        
 
     def GetCtrl(self):
-        # look for active one first
-        self._textEditor = self._GetActiveCtrl(self._multiSash)
-        if self._textEditor == None:  # it is possible none are active
-            # look for any existing one
-            self._textEditor = self._FindCtrl(self._multiSash)
         return self._textEditor
-        
 
-##    def GetCtrls(self, parent = None):
-##        """ Walk through the MultiSash windows and find all Ctrls """
-##        controls = []
-##        if isinstance(parent, self.GetCtrlClass()):
-##            return [parent]
-##        if hasattr(parent, "GetChildren"):
-##            for child in parent.GetChildren():
-##                controls = controls + self.GetCtrls(child)
-##        return controls
+
+    def SetCtrl(self, ctrl):
+        self._textEditor = ctrl
+                
+
+    def OnCreatePrintout(self):
+        """ for Print Preview and Print """
+        return TextPrintout(self, self.GetDocument().GetPrintableName())
 
             
     def OnCreate(self, doc, flags):
         frame = wx.GetApp().CreateDocumentFrame(self, doc, flags, style = wx.DEFAULT_FRAME_STYLE | wx.NO_FULL_REPAINT_ON_RESIZE)
-        wx.lib.multisash.MultiClient.Select = MultiClientSelectBGNotYellow
-        self._multiSash = wx.lib.multisash.MultiSash(frame, -1)
-        self._multiSash.SetDefaultChildClass(self.GetCtrlClass()) # wxBug:  MultiSash instantiates the first TextCtrl with this call
-        self._textEditor = self.GetCtrl()  # wxBug: grab the TextCtrl from the MultiSash datastructure
+        self._dynSash = wx.gizmos.DynamicSashWindow(frame, -1, style=wx.CLIP_CHILDREN)
+        self._dynSash._view = self
+        self._textEditor = self.GetCtrlClass()(self._dynSash, -1, style=wx.NO_BORDER)
         self._CreateSizer(frame)
         self.Activate()
         frame.Show(True)
@@ -148,33 +126,9 @@ class TextView(wx.lib.docview.View):
         return True
 
 
-    def _GetActiveCtrl(self, parent):
-        """ Walk through the MultiSash windows and find the active Control """
-        if isinstance(parent, wx.lib.multisash.MultiClient) and parent.selected:
-            return parent.child
-        if hasattr(parent, "GetChildren"):
-            for child in parent.GetChildren():
-                found = self._GetActiveCtrl(child)
-                if found:
-                    return found
-        return None
-
-
-    def _FindCtrl(self, parent):
-        """ Walk through the MultiSash windows and find the first TextCtrl """
-        if isinstance(parent, self.GetCtrlClass()):
-            return parent
-        if hasattr(parent, "GetChildren"):
-            for child in parent.GetChildren():
-                found = self._FindCtrl(child)
-                if found:
-                    return found
-        return None
-
-
     def _CreateSizer(self, frame):
         sizer = wx.BoxSizer(wx.HORIZONTAL)
-        sizer.Add(self._multiSash, 1, wx.EXPAND)
+        sizer.Add(self._dynSash, 1, wx.EXPAND)
         frame.SetSizer(sizer)
         frame.SetAutoLayout(True)
 
@@ -192,10 +146,15 @@ class TextView(wx.lib.docview.View):
         if activate and self.GetCtrl():
             # In MDI mode just calling set focus doesn't work and in SDI mode using CallAfter causes an endless loop
             if self.GetDocumentManager().GetFlags() & wx.lib.docview.DOC_SDI:
-                self.GetCtrl().SetFocus()
+                self.SetFocus()
             else:
-                wx.CallAfter(self.GetCtrl().SetFocus)
-                   
+                wx.CallAfter(self.SetFocus)
+
+
+    def SetFocus(self):
+        if self.GetCtrl():
+            self.GetCtrl().SetFocus()
+            
                                 
     def OnClose(self, deleteWindow = True):
         if not wx.lib.docview.View.OnClose(self, deleteWindow):
@@ -291,54 +250,53 @@ class TextView(wx.lib.docview.View):
         if not self.GetCtrl():
             return False
 
-        hasSelection = self.GetCtrl().GetSelectionStart() != self.GetCtrl().GetSelectionEnd()
-        hasText = self.GetCtrl().GetTextLength() > 0
-        notOnLastChar = self.GetCtrl().GetSelectionStart() != self.GetCtrl().GetTextLength()
-
         id = event.GetId()
         if id == wx.ID_UNDO:
-             event.Enable(self.GetCtrl().CanUndo())
-             event.SetText(_("Undo") + '\t' + _('Ctrl+Z'))
-             return True
+            event.Enable(self.GetCtrl().CanUndo())
+            event.SetText(_("&Undo\tCtrl+Z"))  # replace menu string
+            return True
         elif id == wx.ID_REDO:
             event.Enable(self.GetCtrl().CanRedo())
-            event.SetText(_("Redo") + '\t' + _('Ctrl+Y'))
-            return True
-        elif id == wx.ID_CUT:
-            event.Enable(hasSelection)
+            event.SetText(_("&Redo\tCtrl+Y"))  # replace menu string
             return True
-        elif id == wx.ID_COPY:
+        elif (id == wx.ID_CUT
+        or id == wx.ID_COPY
+        or id == wx.ID_CLEAR):
+            hasSelection = self.GetCtrl().GetSelectionStart() != self.GetCtrl().GetSelectionEnd()
             event.Enable(hasSelection)
             return True
         elif id == wx.ID_PASTE:
             event.Enable(self.GetCtrl().CanPaste())
             return True
-        elif id == wx.ID_CLEAR:
-            event.Enable(hasSelection)
-            return True
         elif id == wx.ID_SELECTALL:
+            hasText = self.GetCtrl().GetTextLength() > 0
             event.Enable(hasText)
             return True
         elif id == TEXT_ID:
             event.Enable(True)
             return True
         elif id == VIEW_WHITESPACE_ID:
+            hasText = self.GetCtrl().GetTextLength() > 0
             event.Enable(hasText)
             event.Check(self.GetCtrl().GetViewWhiteSpace())
             return True
         elif id == VIEW_EOL_ID:
+            hasText = self.GetCtrl().GetTextLength() > 0
             event.Enable(hasText)
             event.Check(self.GetCtrl().GetViewEOL())
             return True
         elif id == VIEW_INDENTATION_GUIDES_ID:
+            hasText = self.GetCtrl().GetTextLength() > 0
             event.Enable(hasText)
             event.Check(self.GetCtrl().GetIndentationGuides())
             return True
         elif id == VIEW_RIGHT_EDGE_ID:
+            hasText = self.GetCtrl().GetTextLength() > 0
             event.Enable(hasText)
             event.Check(self.GetCtrl().GetViewRightEdge())
             return True
         elif id == VIEW_LINE_NUMBERS_ID:
+            hasText = self.GetCtrl().GetTextLength() > 0
             event.Enable(hasText)
             event.Check(self.GetCtrl().GetViewLineNumbers())
             return True
@@ -362,19 +320,23 @@ class TextView(wx.lib.docview.View):
             event.Check(self.GetCtrl().CanWordWrap() and self.GetCtrl().GetWordWrap())
             return True
         elif id == FindService.FindService.FIND_ID:
+            hasText = self.GetCtrl().GetTextLength() > 0
             event.Enable(hasText)
             return True
         elif id == FindService.FindService.FIND_PREVIOUS_ID:
+            hasText = self.GetCtrl().GetTextLength() > 0
             event.Enable(hasText and
                          self._FindServiceHasString() and
                          self.GetCtrl().GetSelection()[0] > 0)
             return True
         elif id == FindService.FindService.FIND_NEXT_ID:
+            hasText = self.GetCtrl().GetTextLength() > 0
             event.Enable(hasText and
                          self._FindServiceHasString() and
                          self.GetCtrl().GetSelection()[0] < self.GetCtrl().GetLength())
             return True
         elif id == FindService.FindService.REPLACE_ID:
+            hasText = self.GetCtrl().GetTextLength() > 0
             event.Enable(hasText)
             return True
         elif id == FindService.FindService.GOTO_LINE_ID:
@@ -576,6 +538,7 @@ class TextView(wx.lib.docview.View):
     def GetLine(self, lineNum):
         return self.GetCtrl().GetLine(lineNum-1)  # line numbering for editor is 0 based, we are 1 based.
 
+
     def MarkerDefine(self):
         """ This must be called after the texteditor is instantiated """
         self.GetCtrl().MarkerDefine(TextView.MARKER_NUM, wx.stc.STC_MARK_CIRCLE, wx.BLACK, wx.BLUE)
@@ -591,6 +554,7 @@ class TextView(wx.lib.docview.View):
             self.GetCtrl().MarkerAdd(lineNum, marker_index)
             self._markerCount += 1
 
+
     def MarkerAdd(self, lineNum = -1, marker_index=MARKER_NUM, mask=MARKER_MASK):
         if lineNum == -1:
             lineNum = self.GetCtrl().GetCurrentLine()
@@ -743,40 +707,18 @@ class TextService(wx.lib.pydocview.DocService):
 
     def ProcessUpdateUIEvent(self, event):
         id = event.GetId()
-        if id == TEXT_ID:
-            event.Enable(False)
-            return True
-        elif id == VIEW_WHITESPACE_ID:
-            event.Enable(False)
-            return True
-        elif id == VIEW_EOL_ID:
-            event.Enable(False)
-            return True
-        elif id == VIEW_INDENTATION_GUIDES_ID:
-            event.Enable(False)
-            return True
-        elif id == VIEW_RIGHT_EDGE_ID:
-            event.Enable(False)
-            return True
-        elif id == VIEW_LINE_NUMBERS_ID:
-            event.Enable(False)
-            return True
-        elif id == ZOOM_ID:
-            event.Enable(False)
-            return True
-        elif id == ZOOM_NORMAL_ID:
-            event.Enable(False)
-            return True
-        elif id == ZOOM_IN_ID:
-            event.Enable(False)
-            return True
-        elif id == ZOOM_OUT_ID:
-            event.Enable(False)
-            return True
-        elif id == CHOOSE_FONT_ID:
-            event.Enable(False)
-            return True
-        elif id == WORD_WRAP_ID:
+        if (id == TEXT_ID
+        or id == VIEW_WHITESPACE_ID
+        or id == VIEW_EOL_ID
+        or id == VIEW_INDENTATION_GUIDES_ID
+        or id == VIEW_RIGHT_EDGE_ID
+        or id == VIEW_LINE_NUMBERS_ID
+        or id == ZOOM_ID
+        or id == ZOOM_NORMAL_ID
+        or id == ZOOM_IN_ID
+        or id == ZOOM_OUT_ID
+        or id == CHOOSE_FONT_ID
+        or id == WORD_WRAP_ID):
             event.Enable(False)
             return True
         else:
@@ -971,16 +913,19 @@ class TextOptionsPanel(wx.Panel):
 
 class TextCtrl(wx.stc.StyledTextCtrl):
 
-    def __init__(self, parent, ID = -1, style = wx.NO_FULL_REPAINT_ON_RESIZE):
-        if ID == -1:
-            ID = wx.NewId()
-        wx.stc.StyledTextCtrl.__init__(self, parent, ID, style = style)
+    def __init__(self, parent, id=-1, style=wx.NO_FULL_REPAINT_ON_RESIZE):
+        wx.stc.StyledTextCtrl.__init__(self, parent, id, style=style)
+
+        if isinstance(parent, wx.gizmos.DynamicSashWindow):
+            self._dynSash = parent
+            self.SetupDSScrollBars()
+            self.Bind(wx.gizmos.EVT_DYNAMIC_SASH_SPLIT, self.OnDSSplit)
+            self.Bind(wx.gizmos.EVT_DYNAMIC_SASH_UNIFY, self.OnDSUnify)
 
         self._font = None
         self._fontColor = None
         
-        self.SetVisiblePolicy(wx.stc.STC_VISIBLE_STRICT,0)
-        self.SetYCaretPolicy(0, 0)
+        self.SetVisiblePolicy(wx.stc.STC_VISIBLE_STRICT,1)
         
         self.CmdKeyClear(wx.stc.STC_KEY_ADD, wx.stc.STC_SCMOD_CTRL)
         self.CmdKeyClear(wx.stc.STC_KEY_SUBTRACT, wx.stc.STC_SCMOD_CTRL)
@@ -988,6 +933,8 @@ class TextCtrl(wx.stc.StyledTextCtrl):
         self.CmdKeyAssign(wx.stc.STC_KEY_NEXT, wx.stc.STC_SCMOD_CTRL, wx.stc.STC_CMD_ZOOMOUT)
         self.Bind(wx.stc.EVT_STC_ZOOM, self.OnUpdateLineNumberMarginWidth)  # auto update line num width on zoom
         wx.EVT_KEY_DOWN(self, self.OnKeyPressed)
+        wx.EVT_KILL_FOCUS(self, self.OnKillFocus)
+        wx.EVT_SET_FOCUS(self, self.OnFocus)
         self.SetMargins(0,0)
 
         self.SetUseTabs(0)
@@ -1011,21 +958,23 @@ class TextCtrl(wx.stc.StyledTextCtrl):
         self.SetFontColor(color)
         self.MarkerDefineDefault()
 
-        # for multisash initialization
-        if isinstance(parent, wx.lib.multisash.MultiClient):
-            while parent.GetParent():
-                parent = parent.GetParent()
-                if hasattr(parent, "GetView"):
-                    break
-            if hasattr(parent, "GetView"):
-                textEditor = parent.GetView()._textEditor
-                if textEditor:
-                    doc = textEditor.GetDocPointer()
-                    if doc:
-                        self.SetDocPointer(doc)
+
+    def OnFocus(self, event):
+        self.SetSelBackground(1, "BLUE")
+        self.SetSelForeground(1, "WHITE")
+        if hasattr(self, "_dynSash"):
+            self._dynSash._view.SetCtrl(self)
+        event.Skip()
 
 
+    def OnKillFocus(self, event):
+        self.SetSelBackground(0, "BLUE")
+        self.SetSelForeground(0, "WHITE")
+        self.SetSelBackground(1, "#C0C0C0")
+        # Don't set foreground color, use syntax highlighted default colors.
+        event.Skip()
         
+
     def SetViewDefaults(self, configPrefix = "Text", hasWordWrap = True, hasTabs = False):
         config = wx.ConfigBase_Get()
         self.SetViewWhiteSpace(config.ReadInt(configPrefix + "EditorViewWhitespace", False))
@@ -1044,7 +993,6 @@ class TextCtrl(wx.stc.StyledTextCtrl):
             self.SetIndent(4)
             self.SetTabWidth(4)
 
-
         
     def GetDefaultFont(self):
         """ Subclasses should override this """
@@ -1077,6 +1025,7 @@ class TextCtrl(wx.stc.StyledTextCtrl):
     def GetFont(self):
         return self._font
         
+
     def SetFont(self, font):
         self._font = font
         self.StyleSetFont(wx.stc.STC_STYLE_DEFAULT, self._font)
@@ -1215,12 +1164,165 @@ class TextCtrl(wx.stc.StyledTextCtrl):
         else:
             self.SetWrapMode(wx.stc.STC_WRAP_NONE)
 
+
+    #----------------------------------------------------------------------------
+    # DynamicSashWindow methods
+    #----------------------------------------------------------------------------
+
+    def SetupDSScrollBars(self):
+        # hook the scrollbars provided by the wxDynamicSashWindow
+        # to this view
+        v_bar = self._dynSash.GetVScrollBar(self)
+        h_bar = self._dynSash.GetHScrollBar(self)
+        v_bar.Bind(wx.EVT_SCROLL, self.OnDSSBScroll)
+        h_bar.Bind(wx.EVT_SCROLL, self.OnDSSBScroll)
+        v_bar.Bind(wx.EVT_SET_FOCUS, self.OnDSSBFocus)
+        h_bar.Bind(wx.EVT_SET_FOCUS, self.OnDSSBFocus)
+
+        # And set the wxStyledText to use these scrollbars instead
+        # of its built-in ones.
+        self.SetVScrollBar(v_bar)
+        self.SetHScrollBar(h_bar)
+
+
+    def OnDSSplit(self, evt):
+        newCtrl = self._dynSash._view.GetCtrlClass()(self._dynSash, -1, style=wx.NO_BORDER)
+        newCtrl.SetDocPointer(self.GetDocPointer())     # use the same document
+        self.SetupDSScrollBars()
+        if self == self._dynSash._view.GetCtrl():  # originally had focus
+            wx.CallAfter(self.SetFocus)  # do this to set colors correctly.  wxBug:  for some reason, if we don't do a CallAfter, it immediately calls OnKillFocus right after our SetFocus.
+
+
+    def OnDSUnify(self, evt):
+        self.SetupDSScrollBars()
+        self.SetFocus()  # do this to set colors correctly
+
+
+    def OnDSSBScroll(self, evt):
+        # redirect the scroll events from the _dynSash's scrollbars to the STC
+        self.GetEventHandler().ProcessEvent(evt)
+
+
+    def OnDSSBFocus(self, evt):
+        # when the scrollbar gets the focus move it back to the STC
+        self.SetFocus()
+
+
+    def DSProcessEvent(self, event):
+        # wxHack: Needed for customized right mouse click menu items.        
+        if hasattr(self, "_dynSash"):
+            if event.GetId() == wx.ID_SELECTALL:
+                # force focus so that select all occurs in the window user right clicked on.
+                self.SetFocus()
+
+            return self._dynSash._view.ProcessEvent(event)
+        return False
+
+
+    def DSProcessUpdateUIEvent(self, event):
+        # wxHack: Needed for customized right mouse click menu items.        
+        if hasattr(self, "_dynSash"):
+            id = event.GetId()
+            if (id == wx.ID_SELECTALL  # allow select all even in non-active window, then force focus to it, see above ProcessEvent
+            or id == wx.ID_UNDO
+            or id == wx.ID_REDO):
+                pass  # allow these actions even in non-active window
+            else:  # disallow events in non-active windows.  Cut/Copy/Paste/Delete is too confusing user experience.
+                if self._dynSash._view.GetCtrl() != self:
+                     event.Enable(False)
+                     return True
+
+            return self._dynSash._view.ProcessUpdateUIEvent(event)
+        return False
+
+
+class TextPrintout(wx.lib.docview.DocPrintout):
+    """ for Print Preview and Print """
+    
+
+    def OnPreparePrinting(self):
+        """ initialization """
+        dc = self.GetDC()
+
+        ppiScreenX, ppiScreenY = self.GetPPIScreen()
+        ppiPrinterX, ppiPrinterY = self.GetPPIPrinter()
+        scaleX = float(ppiPrinterX)/ppiScreenX
+        scaleY = float(ppiPrinterY)/ppiScreenY
+
+        pageWidth, pageHeight = self.GetPageSizePixels()
+        self._scaleFactorX = scaleX/pageWidth
+        self._scaleFactorY = scaleY/pageHeight
+
+        w, h = dc.GetSize()
+        overallScaleX = self._scaleFactorX * w
+        overallScaleY = self._scaleFactorY * h
+        
+        txtCtrl = self._printoutView.GetCtrl()
+        font, color = txtCtrl.GetFontAndColorFromConfig()
+
+        self._margin = 40
+        self._fontHeight = font.GetPointSize() + 1
+        self._pageLines = int((h/overallScaleY - (2 * self._margin))/self._fontHeight)
+        self._maxLines = txtCtrl.GetLineCount()
+        self._numPages, remainder = divmod(self._maxLines, self._pageLines)
+        if remainder != 0:
+            self._numPages += 1
+
+        spaces = 1
+        lineNum = self._maxLines
+        while lineNum >= 10:
+            lineNum = lineNum/10
+            spaces += 1
+        self._printFormat = "%%0%sd: %%s" % spaces
+
+
+    def OnPrintPage(self, page):
+        """ Prints the given page of the view """
+        dc = self.GetDC()
+        
+        txtCtrl = self._printoutView.GetCtrl()
+        font, color = txtCtrl.GetFontAndColorFromConfig()
+        dc.SetFont(font)
+        
+        w, h = dc.GetSize()
+        dc.SetUserScale(self._scaleFactorX * w, self._scaleFactorY * h)
+        
+        dc.BeginDrawing()
+        
+        dc.DrawText("%s - page %s" % (self.GetTitle(), page), self._margin, self._margin/2)
+
+        startY = self._margin
+        startLine = (page - 1) * self._pageLines
+        endLine = min((startLine + self._pageLines), self._maxLines)
+        for i in range(startLine, endLine):
+            text = txtCtrl.GetLine(i).rstrip()
+            startY += self._fontHeight
+            if txtCtrl.GetViewLineNumbers():
+                dc.DrawText(self._printFormat % (i+1, text), self._margin, startY)
+            else:
+                dc.DrawText(text, self._margin, startY)
+                
+        dc.EndDrawing()
+
+        return True
+
+
+    def HasPage(self, pageNum):
+        return pageNum <= self._numPages
+
+
+    def GetPageInfo(self):
+        minPage = 1
+        maxPage = self._numPages
+        selPageFrom = 1
+        selPageTo = self._numPages
+        return (minPage, maxPage, selPageFrom, selPageTo)
+
         
 #----------------------------------------------------------------------------
 # Icon Bitmaps - generated by encode_bitmaps.py
 #----------------------------------------------------------------------------
 from wx import ImageFromStream, BitmapFromImage
-from wx import EmptyIcon
 import cStringIO
 
 
@@ -1243,9 +1345,7 @@ def getTextImage():
     return ImageFromStream(stream)
 
 def getTextIcon():
-    icon = EmptyIcon()
-    icon.CopyFromBitmap(getTextBitmap())
-    return icon
+    return wx.IconFromBitmap(getTextBitmap())
 
 
 #----------------------------------------------------------------------------