]> git.saurik.com Git - wxWidgets.git/blobdiff - wxPython/samples/ide/activegrid/tool/STCTextEditor.py
removed code inside USE_SIZABLE_CALENDAR, we should allow making the main calendar...
[wxWidgets.git] / wxPython / samples / ide / activegrid / tool / STCTextEditor.py
index 8d27a19d24ace33557902ea8182d9449b5934683..f3ab9508e2b75441874e99db2bb061aa87326682 100644 (file)
@@ -6,7 +6,7 @@
 #
 # Created:      8/10/03
 # CVS-ID:       $Id$
-# Copyright:    (c) 2003-2005 ActiveGrid, Inc.
+# Copyright:    (c) 2003-2006 ActiveGrid, Inc.
 # License:      wxWindows License
 #----------------------------------------------------------------------------
 
@@ -47,55 +47,58 @@ TEXT_STATUS_BAR_ID = wx.NewId()
 class TextDocument(wx.lib.docview.Document):
 
 
-    def OnSaveDocument(self, filename):
+    def __init__(self):
+        wx.lib.docview.Document .__init__(self)
+        self._inModify = False
+
+
+    def SaveObject(self, fileObject):
         view = self.GetFirstView()
-        docFile = file(self._documentFile, "w")
-        docFile.write(view.GetValue())
-        docFile.close()
-        self.Modify(False)
-        self.SetDocumentModificationDate()
-        self.SetDocumentSaved(True)
+        fileObject.write(view.GetValue())
+        view.SetModifyFalse()
         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.SetDocumentModificationDate()
-        self.UpdateAllViews()
-        self._savedYet = True
+        view.SetModifyFalse()
         return True
 
 
     def IsModified(self):
         view = self.GetFirstView()
         if view:
-            return wx.lib.docview.Document.IsModified(self) or view.IsModified()
-        else:
-            return wx.lib.docview.Document.IsModified(self)
+            return view.IsModified()
+        return False
 
 
-    def Modify(self, mod):
+    def Modify(self, modify):
+        if self._inModify:
+            return
+        self._inModify = True
+        
         view = self.GetFirstView()
-        wx.lib.docview.Document.Modify(self, mod)
-        if not mod and view:
+        if not modify and view:
             view.SetModifyFalse()
 
+        wx.lib.docview.Document.Modify(self, modify)  # this must called be after the SetModifyFalse call above.
 
+        self._inModify = False
+        
+    
     def OnCreateCommandProcessor(self):
         # 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()
+
+# 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
@@ -110,39 +113,50 @@ 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)
+        if wx.Platform == "__WXMAC__":
+            # look for active one first         
+            self._textEditor = self._GetActiveCtrl(self._dynSash)       
+            if self._textEditor == None:  # it is possible none are active      
+                # look for any existing one     
+                self._textEditor = self._FindCtrl(self._dynSash)
         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
+        # wxBug: DynamicSashWindow doesn't work on Mac, so revert to
+        # multisash implementation
+        if wx.Platform == "__WXMAC__":
+            wx.lib.multisash.MultiClient.Select = MultiClientSelectBGNotYellow
+            self._dynSash = wx.lib.multisash.MultiSash(frame, -1)
+            self._dynSash.SetDefaultChildClass(self.GetCtrlClass()) # wxBug:  MultiSash instantiates the first TextCtrl with this call
+            
+            self._textEditor = self.GetCtrl()  # wxBug: grab the TextCtrl from the MultiSash datastructure
+        else:
+            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)
+        wx.EVT_LEFT_DOWN(self._textEditor, self.OnLeftClick)
+        self._textEditor.Bind(wx.stc.EVT_STC_MODIFIED, self.OnModify)
+        
         self._CreateSizer(frame)
         self.Activate()
         frame.Show(True)
@@ -150,42 +164,29 @@ 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 OnModify(self, event):
+        self.GetDocument().Modify(self._textEditor.GetModify())
+        
 
     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)
+
+
+    def OnLeftClick(self, event):
+        self.Activate()
+        event.Skip()
 
 
     def OnUpdate(self, sender = None, hint = None):
+        if wx.lib.docview.View.OnUpdate(self, sender, hint):
+            return
+
         if hint == "ViewStuff":
             self.GetCtrl().SetViewDefaults()
         elif hint == "Font":
-            font, color = self.GetFontAndColorFromConfig()
+            font, color = self.GetCtrl().GetFontAndColorFromConfig()
             self.GetCtrl().SetFont(font)
             self.GetCtrl().SetFontColor(color)
 
@@ -194,10 +195,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):
@@ -238,7 +244,7 @@ class TextView(wx.lib.docview.View):
             self.GetCtrl().SetViewEOL(not self.GetCtrl().GetViewEOL())
             return True
         elif id == VIEW_INDENTATION_GUIDES_ID:
-            self.GetCtrl().SetViewIndentationGuides(not self.GetCtrl().GetViewIndentationGuides())
+            self.GetCtrl().SetIndentationGuides(not self.GetCtrl().GetIndentationGuides())
             return True
         elif id == VIEW_RIGHT_EDGE_ID:
             self.GetCtrl().SetViewRightEdge(not self.GetCtrl().GetViewRightEdge())
@@ -295,12 +301,12 @@ class TextView(wx.lib.docview.View):
 
         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'))
+            event.SetText(_("&Redo\tCtrl+Y"))  # replace menu string
             return True
         elif (id == wx.ID_CUT
         or id == wx.ID_COPY
@@ -395,6 +401,29 @@ class TextView(wx.lib.docview.View):
     def _GetParentFrame(self):
         return wx.GetTopLevelParent(self.GetFrame())
 
+    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     
 
     #----------------------------------------------------------------------------
     # Methods for TextDocument to call
@@ -444,6 +473,7 @@ class TextView(wx.lib.docview.View):
         data.SetInitialFont(self.GetCtrl().GetFont())
         data.SetColour(self.GetCtrl().GetFontColor())
         fontDialog = wx.FontDialog(self.GetFrame(), data)
+        fontDialog.CenterOnParent()
         if fontDialog.ShowModal() == wx.ID_OK:
             data = fontDialog.GetFontData()
             self.GetCtrl().SetFont(data.GetChosenFont())
@@ -563,9 +593,11 @@ class TextView(wx.lib.docview.View):
     def EnsureVisible(self, line):
         self.GetCtrl().EnsureVisible(line-1)  # line numbering for editor is 0 based, we are 1 based.
 
+
     def EnsureVisibleEnforcePolicy(self, line):
         self.GetCtrl().EnsureVisibleEnforcePolicy(line-1)  # line numbering for editor is 0 based, we are 1 based.
 
+
     def LineFromPosition(self, pos):
         return self.GetCtrl().LineFromPosition(pos)+1  # line numbering for editor is 0 based, we are 1 based.
 
@@ -581,6 +613,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)
@@ -596,6 +629,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()
@@ -655,7 +689,13 @@ class TextView(wx.lib.docview.View):
         else:
             return False
 
-
+    def GetMarkerLines(self, mask=MARKER_MASK):
+        retval = []
+        for lineNum in range(self.GetCtrl().GetLineCount()):
+            if self.GetCtrl().MarkerGet(lineNum) & mask:
+                retval.append(lineNum)
+        return retval
+        
     def GetMarkerCount(self):
         return self._markerCount
 
@@ -797,11 +837,12 @@ class TextStatusBar(wx.StatusBar):
 class TextOptionsPanel(wx.Panel):
 
 
-    def __init__(self, parent, id, configPrefix = "Text", label = "Text", hasWordWrap = True, hasTabs = False, addPage=True):
+    def __init__(self, parent, id, configPrefix = "Text", label = "Text", hasWordWrap = True, hasTabs = False, addPage=True, hasFolding=False):
         wx.Panel.__init__(self, parent, id)
         self._configPrefix = configPrefix
         self._hasWordWrap = hasWordWrap
         self._hasTabs = hasTabs
+        self._hasFolding = hasFolding
         SPACE = 10
         HALF_SPACE   = 5
         config = wx.ConfigBase_Get()
@@ -838,6 +879,9 @@ class TextOptionsPanel(wx.Panel):
         self._viewRightEdgeCheckBox.SetValue(config.ReadInt(self._configPrefix + "EditorViewRightEdge", False))
         self._viewLineNumbersCheckBox = wx.CheckBox(self, -1, _("Show line numbers"))
         self._viewLineNumbersCheckBox.SetValue(config.ReadInt(self._configPrefix + "EditorViewLineNumbers", True))
+        if self._hasFolding:
+            self._viewFoldingCheckBox = wx.CheckBox(self, -1, _("Show folding"))
+            self._viewFoldingCheckBox.SetValue(config.ReadInt(self._configPrefix + "EditorViewFolding", True))
         if self._hasTabs:
             self._hasTabsCheckBox = wx.CheckBox(self, -1, _("Use spaces instead of tabs"))
             self._hasTabsCheckBox.SetValue(not wx.ConfigBase_Get().ReadInt(self._configPrefix + "EditorUseTabs", False))
@@ -848,9 +892,9 @@ class TextOptionsPanel(wx.Panel):
         textPanelSizer = wx.BoxSizer(wx.VERTICAL)
         textFontSizer = wx.BoxSizer(wx.HORIZONTAL)
         textFontSizer.Add(fontLabel, 0, wx.ALIGN_LEFT | wx.RIGHT | wx.TOP, HALF_SPACE)
-        textFontSizer.Add(self._sampleTextCtrl, 0, wx.ALIGN_LEFT | wx.EXPAND | wx.RIGHT, HALF_SPACE)
+        textFontSizer.Add(self._sampleTextCtrl, 1, wx.ALIGN_LEFT | wx.EXPAND | wx.RIGHT, HALF_SPACE)
         textFontSizer.Add(chooseFontButton, 0, wx.ALIGN_RIGHT | wx.LEFT, HALF_SPACE)
-        textPanelSizer.Add(textFontSizer, 0, wx.ALL, HALF_SPACE)
+        textPanelSizer.Add(textFontSizer, 0, wx.ALL|wx.EXPAND, HALF_SPACE)
         if self._hasWordWrap:
             textPanelSizer.Add(self._wordWrapCheckBox, 0, wx.ALL, HALF_SPACE)
         textPanelSizer.Add(self._viewWhitespaceCheckBox, 0, wx.ALL, HALF_SPACE)
@@ -858,13 +902,15 @@ class TextOptionsPanel(wx.Panel):
         textPanelSizer.Add(self._viewIndentationGuideCheckBox, 0, wx.ALL, HALF_SPACE)
         textPanelSizer.Add(self._viewRightEdgeCheckBox, 0, wx.ALL, HALF_SPACE)
         textPanelSizer.Add(self._viewLineNumbersCheckBox, 0, wx.ALL, HALF_SPACE)
+        if self._hasFolding:
+            textPanelSizer.Add(self._viewFoldingCheckBox, 0, wx.ALL, HALF_SPACE)
         if self._hasTabs:
             textPanelSizer.Add(self._hasTabsCheckBox, 0, wx.ALL, HALF_SPACE)
             textIndentWidthSizer = wx.BoxSizer(wx.HORIZONTAL)
             textIndentWidthSizer.Add(indentWidthLabel, 0, wx.ALIGN_LEFT | wx.RIGHT | wx.TOP, HALF_SPACE)
             textIndentWidthSizer.Add(self._indentWidthChoice, 0, wx.ALIGN_LEFT | wx.EXPAND, HALF_SPACE)
             textPanelSizer.Add(textIndentWidthSizer, 0, wx.ALL, HALF_SPACE)
-        textPanelBorderSizer.Add(textPanelSizer, 0, wx.ALL, SPACE)
+        textPanelBorderSizer.Add(textPanelSizer, 0, wx.ALL|wx.EXPAND, SPACE)
 ##        styleButton = wx.Button(self, -1, _("Choose Style..."))
 ##        wx.EVT_BUTTON(self, styleButton.GetId(), self.OnChooseStyle)
 ##        textPanelBorderSizer.Add(styleButton, 0, wx.ALL, SPACE)
@@ -897,6 +943,7 @@ class TextOptionsPanel(wx.Panel):
 ##                                #'HTML', 'html',
 ##                                #'XML', 'xml',
 ##                                config)
+##        dlg.CenterOnParent()
 ##        try:
 ##            dlg.ShowModal()
 ##        finally:
@@ -909,6 +956,7 @@ class TextOptionsPanel(wx.Panel):
         data.SetInitialFont(self._textFont)
         data.SetColour(self._textColor)
         fontDialog = wx.FontDialog(self, data)
+        fontDialog.CenterOnParent()
         if fontDialog.ShowModal() == wx.ID_OK:
             data = fontDialog.GetFontData()
             self._textFont = data.GetChosenFont()
@@ -929,6 +977,9 @@ class TextOptionsPanel(wx.Panel):
         config.WriteInt(self._configPrefix + "EditorViewRightEdge", self._viewRightEdgeCheckBox.GetValue())
         doViewStuffUpdate = doViewStuffUpdate or config.ReadInt(self._configPrefix + "EditorViewLineNumbers", True) != self._viewLineNumbersCheckBox.GetValue()
         config.WriteInt(self._configPrefix + "EditorViewLineNumbers", self._viewLineNumbersCheckBox.GetValue())
+        if self._hasFolding:
+            doViewStuffUpdate = doViewStuffUpdate or config.ReadInt(self._configPrefix + "EditorViewFolding", True) != self._viewFoldingCheckBox.GetValue()
+            config.WriteInt(self._configPrefix + "EditorViewFolding", self._viewFoldingCheckBox.GetValue())
         if self._hasWordWrap:
             doViewStuffUpdate = doViewStuffUpdate or config.ReadInt(self._configPrefix + "EditorWordWrap", False) != self._wordWrapCheckBox.GetValue()
             config.WriteInt(self._configPrefix + "EditorWordWrap", self._wordWrapCheckBox.GetValue())
@@ -950,14 +1001,22 @@ class TextOptionsPanel(wx.Panel):
                         document.UpdateAllViews(hint = "ViewStuff")
                     if doFontUpdate:
                         document.UpdateAllViews(hint = "Font")
+               
+         
+    def GetIcon(self):
+        return getTextIcon()
 
 
 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
@@ -970,6 +1029,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)
@@ -993,28 +1054,56 @@ 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:
+        # 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):
+        # wxBug: On Mac, the STC control may fire a focus/kill focus event
+        # on shutdown even if the control is in an invalid state. So check
+        # before handling the event.
+        if self.IsBeingDeleted():
+            return
+            
+        self.SetSelBackground(1, "BLUE")
+        self.SetSelForeground(1, "WHITE")
+        if hasattr(self, "_dynSash"):
+            self._dynSash._view.SetCtrl(self)
+        event.Skip()
+
+
+    def OnKillFocus(self, event):
+        # wxBug: On Mac, the STC control may fire a focus/kill focus event
+        # on shutdown even if the control is in an invalid state. So check
+        # before handling the event.
+        if self.IsBeingDeleted():
+            return
+        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):
+
+    def SetViewDefaults(self, configPrefix="Text", hasWordWrap=True, hasTabs=False, hasFolding=False):
         config = wx.ConfigBase_Get()
         self.SetViewWhiteSpace(config.ReadInt(configPrefix + "EditorViewWhitespace", False))
         self.SetViewEOL(config.ReadInt(configPrefix + "EditorViewEOL", False))
         self.SetIndentationGuides(config.ReadInt(configPrefix + "EditorViewIndentationGuides", False))
         self.SetViewRightEdge(config.ReadInt(configPrefix + "EditorViewRightEdge", False))
         self.SetViewLineNumbers(config.ReadInt(configPrefix + "EditorViewLineNumbers", True))
+        if hasFolding:
+            self.SetViewFolding(config.ReadInt(configPrefix + "EditorViewFolding", True))
         if hasWordWrap:
             self.SetWordWrap(config.ReadInt(configPrefix + "EditorWordWrap", False))
         if hasTabs:  # These methods do not exist in STCTextEditor and are meant for subclasses
@@ -1026,7 +1115,6 @@ class TextCtrl(wx.stc.StyledTextCtrl):
             self.SetIndent(4)
             self.SetTabWidth(4)
 
-
         
     def GetDefaultFont(self):
         """ Subclasses should override this """
@@ -1059,6 +1147,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)
@@ -1183,6 +1272,17 @@ class TextCtrl(wx.stc.StyledTextCtrl):
             self.SetMarginWidth(1, 0)
 
 
+    def GetViewFolding(self):
+        return self.GetMarginWidth(2) > 0
+
+
+    def SetViewFolding(self, viewFolding = True):
+        if viewFolding:
+            self.SetMarginWidth(2, 12)
+        else:
+            self.SetMarginWidth(2, 0)
+
+
     def CanWordWrap(self):
         return True
 
@@ -1197,6 +1297,160 @@ 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
@@ -1207,13 +1461,21 @@ import cStringIO
 
 def getTextData():
     return \
-'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
+"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
-\x00\x00`IDAT8\x8d\xed\x931\x0e\xc00\x08\x03m\x92\xff\xff8q\xa7JU!$\x12\x1d\
-\xeb\t\t8n\x81\xb4\x86J\xfa]h\x0ee\x83\xb4\xc6\x14\x00\x00R\xcc \t\xcd\xa1\
-\x08\xd2\xa3\xe1\x08*\t$\x1d\xc4\x012\x0b\x00\xce\xe4\xc8\xe0\t}\xf7\x8f\rV\
-\xd9\x1a\xec\xe0\xbf\xc1\xd7\x06\xd9\xf5UX\xfdF+m\x03\xb8\x00\xe4\xc74B"x\
-\xf1\xf4\x00\x00\x00\x00IEND\xaeB`\x82' 
+\x00\x015IDAT8\x8d\xad\x90\xb1N\xc2P\x14\x86\xbf\x02/\xe0\xec#\x18g\xc3\xe6T\
+\x13':1\x18H\x98\x14\x12\x17G\x177\x17\x9c4a\xc5\xc0d0\xc2\xccdLx\x02^@+\t\
+\xc1\x90\xf6r\xdb\xc6\x94\xe5:\\\xdbP)\xc5DOr\x92\x9b{\xff\xfb\xfd\xff9\xc6h\
+l+\xbek.\x02\x00\xec\x99\x03\x80\xeb\xf8\\\x9d\x1d\x1bd\xd5hl\xab\xd7O\x15\
+\xf7x\xa1\xfb\xeeq\xa4^>\x94\xba\xb8yRF.\xcf\xa6.D\xa0Nw\x18C\xad\xb2\x19\
+\x9f\x0f\xca\x165\xd1V\xed\xebZj\x92\xc2\\\x04\xec\x02\xd5\x8a\x89\xb7\xd4\
+\x97n\xa8\xe3?\x0f\x86\x08\x19dNP\x00\xf0\x96\xd0\x7f\xd0\t\x84\x0c(U-\x0eK&\
+\xd3P\x8bz\xcdV6 \x8a\xed\x86\x99f\xe9\x00{\xe6\xb0\x13\xc2\xa0\xd3\xd7\t\
+\x84\x9f\x10\xec\x9dTp\x1d\xb1=A\xa9j\x01\xc4\xb1\x01&\xfe\x9a~\x1d\xe0:Zu\
+\x7f\xdb\x05@J/!(\xd6\x1bL\xde\xec\xcd\x00!\x03\xa6!\x1c\x9dVR\x9d\xdf\xe5\
+\x96\x04\xd1au\xd3\xab3\xef\x9f_f\x03\xa2\xa5\x15\xeb\x8d\xc4\xc36\xe7\x18 \
+\xa5G\xaf\xd9J\xb8f\xcd\xfc\xb3\x0c#\x97\xff\xb58\xadr\x7f\xfa\xfd\x1f\x80/\
+\x04\x1f\x8fW\x0e^\xc3\x12\x00\x00\x00\x00IEND\xaeB`\x82" 
 
 
 def getTextBitmap():
@@ -1235,12 +1497,20 @@ def getZoomInData():
     return \
 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
-\x00\x00wIDAT8\x8d\xa5\x93Q\x12\x80 \x08D\xb5\xe9X\xee\xe9\xb7{\xd5Gc\xa9\
-\xacX\xca\x1f\xa0\x8fE0\x92<\xc3\x82\xed*\x08\xa0\xf2I~\x07\x000\x17T,\xdb\
-\xd6;\x08\xa4\x00\xa4GA\xab\xca\x00\xbc*\x1eD\xb4\x90\xa4O\x1e\xe3\x16f\xcc(\
-\xc8\x95F\x95\x8d\x02\xef\xa1n\xa0\xce\xc5v\x91zc\xacU\xbey\x03\xf0.\xa8\xb8\
-\x04\x8c\xac\x04MM\xa1lA\xfe\x85?\x90\xe5=X\x06\\\xebCA\xb3Q\xf34\x14\x00\
-\x00\x00\x00IEND\xaeB`\x82' 
+\x00\x01TIDAT8\x8d\x8d\x93\xbbJ\x03A\x14\x86\xbf\xd9,\xc6\xd8E%`)VF[{\xc1v\
+\xf1\x82\x8f\xb0\xb94\xda\xa5\x13\x11\x8b`\xa9h\x10F\xe3#H.\xa6\x15\xccKhg\
+\x10\xc1B\x8bTF\x90\xc0X\x8c3\xbb\xd9\xcdF\x7f\x18\xf6\xec\x9cs\xbe\xfd\xe70\
++\x84\x93"\xacb\xc1W\xe1\xf7\xeb\xfa\x8d`\x82\xdcXcI\x8e\x02AM\x02\t\xe1\xa4\
+(\x16|uz)y\x19\xc0\xc9\xdd;\x99\xee!\x00\xd9\xbd\x00\xd6\xaf\x95\xc7B\xac\
+\x03\xd3\x1c\xd6\xc2t\x10\xf7\x13\x8e\xe0\x14\x0b\xbe\xa2$m\xf3\xca\xea\xacM\
+\xe6\xd2\xc1\xcaWdl>#\x0e\x8c\xed\xe7n\x90|\xa8\x96m\xbc~ y\x04Z\xcd\x86\xda\
+\xda\xde\xb1Gq\x00\xb2S\t\xfeB\x9aK\xa8\xb1\x0e\xf2\x15I.\xad\x0bo\x8f\xf4\
+\x97\xab\xe7z\x88\x1f\xdf\xf0\xfa9\x1e\xe0x\x9eG\xbf\x16X\xcd\xb8Ar\xc6\xd5\
+\x0b4\xd4\xf3\xbcd\x07F_\xc3 \x1e\x0c\xa3Y\x08\x9f\x1f~\xefA\xab\xd9P\x9dN\
+\x07\x80\xddcI\xc6\x85\xf9\xb4.8\xabhwK\xbd+6\x16\xf5\xdeZ=%F\x00\xa0\xa7\
+\x0b`@F\xc6\xf6\xd3\xc5&@\x0c"\xa2\xff\x82\x01\x85-\xb7\x9a\re\x00QH\x0c0N\
+\x06\x1a\x85\xbcym}\x0f\xfe\x92\x19\xdc\xf2~\xdb\xee\xdd\xf7\xf4\xf3_\x0e\
+\xa2N\xc2\xfa\x01MYp\xbc\xe4a\x0f\xa9\x00\x00\x00\x00IEND\xaeB`\x82' 
 
 def getZoomInBitmap():
     return BitmapFromImage(getZoomInImage())
@@ -1254,11 +1524,20 @@ def getZoomOutData():
     return \
 '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
 \x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
-\x00\x00qIDAT8\x8d\xa5\x92Q\x0e\xc0 \x08C-z\xff\x13O\xd9\xd7\x16"\x05\x8d\
-\xf6O\xa2\x8f"\x05\xa4\x96\x1b5V\xd4\xd1\xd5\x9e!\x15\xdb\x00\x1d]\xe7\x07\
-\xac\xf6Iv.B*fW\x0e\x90u\xc9 d\x84\x87v\x82\xb4\xf5\x08\'r\x0e\xa2N\x91~\x07\
-\xd9G\x95\xe2W\xeb\x00\x19\xc4\xd6\\FX\x12\xa3 \xb1:\x05\xacdAG[\xb0y9r`u\
-\x9d\x83k\xc0\x0b#3@0A\x0c"\x93\x00\x00\x00\x00IEND\xaeB`\x82' 
+\x00\x01RIDAT8\x8d\x8d\x93\xbbJ\x03A\x14\x86\xbf\xd9\x04\x93\x90J\x0cj#Dl\
+\xf4\x01\xec\x05\xdb\xc5\x0b>B\x92]\x1b+\xed,D\xb0\xb4\x08\x9afc|\x04\xc9\
+\x85\xb4>\x84\x95`\x93\x80`\x15\xd8*\x98\x84\xc0X\xcc\xce\xde7\xf8\xc30\x97=\
+\xf3\xcd\x7f\xce\xcc\na\xe4\x08\xabQ\xaf\xc9\xf0\xfc\xa5\xf3*X\xa1|b\xa3\xe5\
+D\x81 W\x81\x840r4\xea5\xf9\xf0\xe40Y@\xf3+\xf8\xb8\xbe\x16\x8c\xdd\x96\x9d\
+\n1\xf4\xc0\xdf\xdc\xb6\x01\xa8\xca\x19[\x05\xfc\x96%aY\x96\x0c\xdb\xae\xca\
+\x99\xea7\x8b\x91@w.\xf9x\xbcL\xb8\xf0k\xa0O\x1e{\xd31Q\x1d\xdd\xaaC\xfa\xbd\
+\xae<=;\xf7!F<\xd7,md\xc4\xf8\x0e\xf6\xaf\x1d\xb6\x8b*p\xa7\x0c\x95\xd0\x86\
+\xc9\x02\xbe\xa7\xe9\x00\xc34M\xdc\x96MA\xa8[,y\xc8r>h\x00ow6\xa6if;\x98K\
+\x95\xd6\xef\x12(\xc0t\x99~b8\x7f\xf0\xdeA\xbf\xd7\x95\xc3\xe1\x10\x80\x8b{\
+\x87R\x1e*\xde\xd55oTq\xf7Fm\x8ew\xd5\xdaa\'\'"\x00P\xd5\x05\xd0 -m\xfb\xf3\
+\xf9\x04 \x01\x11\xf1\x7fA\x83\xc2\x96\xfb\xbd\xae\xd4\x808$\x01H\x93\x86\
+\xc6!?\xe6 x\xca\xab\xa4\x0bwp5\xf0\xd7\xdeG\xaa\xff\x97\x83\xb8\x93\xb0\xfe\
+\x00\xc3\xa8ov\xfd\xe4\x9c\xa2\x00\x00\x00\x00IEND\xaeB`\x82' 
  
 
 def getZoomOutBitmap():