| 1 | """EditWindow class.""" |
| 2 | |
| 3 | __author__ = "Patrick K. O'Brien <pobrien@orbtech.com>" |
| 4 | __cvsid__ = "$Id$" |
| 5 | __revision__ = "$Revision$"[11:-2] |
| 6 | |
| 7 | import wx |
| 8 | from wx import stc |
| 9 | |
| 10 | import keyword |
| 11 | import os |
| 12 | import sys |
| 13 | import time |
| 14 | |
| 15 | import dispatcher |
| 16 | from version import VERSION |
| 17 | |
| 18 | |
| 19 | if 'wxMSW' in wx.PlatformInfo: |
| 20 | FACES = { 'times' : 'Times New Roman', |
| 21 | 'mono' : 'Courier New', |
| 22 | 'helv' : 'Arial', |
| 23 | 'lucida' : 'Lucida Console', |
| 24 | 'other' : 'Comic Sans MS', |
| 25 | 'size' : 10, |
| 26 | 'lnsize' : 8, |
| 27 | 'backcol' : '#FFFFFF', |
| 28 | 'calltipbg' : '#FFFFB8', |
| 29 | 'calltipfg' : '#404040', |
| 30 | } |
| 31 | |
| 32 | elif 'wxGTK' in wx.PlatformInfo and 'gtk2' in wx.PlatformInfo: |
| 33 | FACES = { 'times' : 'Serif', |
| 34 | 'mono' : 'Monospace', |
| 35 | 'helv' : 'Sans', |
| 36 | 'other' : 'new century schoolbook', |
| 37 | 'size' : 10, |
| 38 | 'lnsize' : 9, |
| 39 | 'backcol' : '#FFFFFF', |
| 40 | 'calltipbg' : '#FFFFB8', |
| 41 | 'calltipfg' : '#404040', |
| 42 | } |
| 43 | |
| 44 | elif 'wxMac' in wx.PlatformInfo: |
| 45 | FACES = { 'times' : 'Lucida Grande', |
| 46 | 'mono' : 'Courier New', |
| 47 | 'helv' : 'Geneva', |
| 48 | 'other' : 'new century schoolbook', |
| 49 | 'size' : 13, |
| 50 | 'lnsize' : 10, |
| 51 | 'backcol' : '#FFFFFF', |
| 52 | 'calltipbg' : '#FFFFB8', |
| 53 | 'calltipfg' : '#404040', |
| 54 | } |
| 55 | |
| 56 | else: # GTK1, etc. |
| 57 | FACES = { 'times' : 'Times', |
| 58 | 'mono' : 'Courier', |
| 59 | 'helv' : 'Helvetica', |
| 60 | 'other' : 'new century schoolbook', |
| 61 | 'size' : 12, |
| 62 | 'lnsize' : 10, |
| 63 | 'backcol' : '#FFFFFF', |
| 64 | 'calltipbg' : '#FFFFB8', |
| 65 | 'calltipfg' : '#404040', |
| 66 | } |
| 67 | |
| 68 | |
| 69 | class EditWindow(stc.StyledTextCtrl): |
| 70 | """EditWindow based on StyledTextCtrl.""" |
| 71 | |
| 72 | revision = __revision__ |
| 73 | |
| 74 | def __init__(self, parent, id=-1, pos=wx.DefaultPosition, |
| 75 | size=wx.DefaultSize, style=wx.CLIP_CHILDREN | wx.SUNKEN_BORDER): |
| 76 | """Create EditWindow instance.""" |
| 77 | stc.StyledTextCtrl.__init__(self, parent, id, pos, size, style) |
| 78 | self.__config() |
| 79 | stc.EVT_STC_UPDATEUI(self, id, self.OnUpdateUI) |
| 80 | dispatcher.connect(receiver=self._fontsizer, signal='FontIncrease') |
| 81 | dispatcher.connect(receiver=self._fontsizer, signal='FontDecrease') |
| 82 | dispatcher.connect(receiver=self._fontsizer, signal='FontDefault') |
| 83 | |
| 84 | def _fontsizer(self, signal): |
| 85 | """Receiver for Font* signals.""" |
| 86 | size = self.GetZoom() |
| 87 | if signal == 'FontIncrease': |
| 88 | size += 1 |
| 89 | elif signal == 'FontDecrease': |
| 90 | size -= 1 |
| 91 | elif signal == 'FontDefault': |
| 92 | size = 0 |
| 93 | self.SetZoom(size) |
| 94 | |
| 95 | def __config(self): |
| 96 | """Configure shell based on user preferences.""" |
| 97 | self.SetMarginType(1, stc.STC_MARGIN_NUMBER) |
| 98 | self.SetMarginWidth(1, 40) |
| 99 | |
| 100 | self.SetLexer(stc.STC_LEX_PYTHON) |
| 101 | self.SetKeyWords(0, ' '.join(keyword.kwlist)) |
| 102 | |
| 103 | self.setStyles(FACES) |
| 104 | self.SetViewWhiteSpace(False) |
| 105 | self.SetTabWidth(4) |
| 106 | self.SetUseTabs(False) |
| 107 | # Do we want to automatically pop up command completion options? |
| 108 | self.autoComplete = True |
| 109 | self.autoCompleteIncludeMagic = True |
| 110 | self.autoCompleteIncludeSingle = True |
| 111 | self.autoCompleteIncludeDouble = True |
| 112 | self.autoCompleteCaseInsensitive = True |
| 113 | self.AutoCompSetIgnoreCase(self.autoCompleteCaseInsensitive) |
| 114 | self.autoCompleteAutoHide = False |
| 115 | self.AutoCompSetAutoHide(self.autoCompleteAutoHide) |
| 116 | self.AutoCompStops(' .,;:([)]}\'"\\<>%^&+-=*/|`') |
| 117 | # Do we want to automatically pop up command argument help? |
| 118 | self.autoCallTip = True |
| 119 | self.CallTipSetBackground(FACES['calltipbg']) |
| 120 | self.CallTipSetForeground(FACES['calltipfg']) |
| 121 | self.SetWrapMode(False) |
| 122 | try: |
| 123 | self.SetEndAtLastLine(False) |
| 124 | except AttributeError: |
| 125 | pass |
| 126 | |
| 127 | def setStyles(self, faces): |
| 128 | """Configure font size, typeface and color for lexer.""" |
| 129 | |
| 130 | # Default style |
| 131 | self.StyleSetSpec(stc.STC_STYLE_DEFAULT, |
| 132 | "face:%(mono)s,size:%(size)d,back:%(backcol)s" % \ |
| 133 | faces) |
| 134 | |
| 135 | self.StyleClearAll() |
| 136 | |
| 137 | # Built in styles |
| 138 | self.StyleSetSpec(stc.STC_STYLE_LINENUMBER, |
| 139 | "back:#C0C0C0,face:%(mono)s,size:%(lnsize)d" % faces) |
| 140 | self.StyleSetSpec(stc.STC_STYLE_CONTROLCHAR, |
| 141 | "face:%(mono)s" % faces) |
| 142 | self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, |
| 143 | "fore:#0000FF,back:#FFFF88") |
| 144 | self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, |
| 145 | "fore:#FF0000,back:#FFFF88") |
| 146 | |
| 147 | # Python styles |
| 148 | self.StyleSetSpec(stc.STC_P_DEFAULT, |
| 149 | "face:%(mono)s" % faces) |
| 150 | self.StyleSetSpec(stc.STC_P_COMMENTLINE, |
| 151 | "fore:#007F00,face:%(mono)s" % faces) |
| 152 | self.StyleSetSpec(stc.STC_P_NUMBER, |
| 153 | "") |
| 154 | self.StyleSetSpec(stc.STC_P_STRING, |
| 155 | "fore:#7F007F,face:%(mono)s" % faces) |
| 156 | self.StyleSetSpec(stc.STC_P_CHARACTER, |
| 157 | "fore:#7F007F,face:%(mono)s" % faces) |
| 158 | self.StyleSetSpec(stc.STC_P_WORD, |
| 159 | "fore:#00007F,bold") |
| 160 | self.StyleSetSpec(stc.STC_P_TRIPLE, |
| 161 | "fore:#7F0000") |
| 162 | self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, |
| 163 | "fore:#000033,back:#FFFFE8") |
| 164 | self.StyleSetSpec(stc.STC_P_CLASSNAME, |
| 165 | "fore:#0000FF,bold") |
| 166 | self.StyleSetSpec(stc.STC_P_DEFNAME, |
| 167 | "fore:#007F7F,bold") |
| 168 | self.StyleSetSpec(stc.STC_P_OPERATOR, |
| 169 | "") |
| 170 | self.StyleSetSpec(stc.STC_P_IDENTIFIER, |
| 171 | "") |
| 172 | self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, |
| 173 | "fore:#7F7F7F") |
| 174 | self.StyleSetSpec(stc.STC_P_STRINGEOL, |
| 175 | "fore:#000000,face:%(mono)s,back:#E0C0E0,eolfilled" % faces) |
| 176 | |
| 177 | def OnUpdateUI(self, event): |
| 178 | """Check for matching braces.""" |
| 179 | # If the auto-complete window is up let it do its thing. |
| 180 | if self.AutoCompActive() or self.CallTipActive(): |
| 181 | return |
| 182 | braceAtCaret = -1 |
| 183 | braceOpposite = -1 |
| 184 | charBefore = None |
| 185 | caretPos = self.GetCurrentPos() |
| 186 | if caretPos > 0: |
| 187 | charBefore = self.GetCharAt(caretPos - 1) |
| 188 | styleBefore = self.GetStyleAt(caretPos - 1) |
| 189 | |
| 190 | # Check before. |
| 191 | if charBefore and chr(charBefore) in '[]{}()' \ |
| 192 | and styleBefore == stc.STC_P_OPERATOR: |
| 193 | braceAtCaret = caretPos - 1 |
| 194 | |
| 195 | # Check after. |
| 196 | if braceAtCaret < 0: |
| 197 | charAfter = self.GetCharAt(caretPos) |
| 198 | styleAfter = self.GetStyleAt(caretPos) |
| 199 | if charAfter and chr(charAfter) in '[]{}()' \ |
| 200 | and styleAfter == stc.STC_P_OPERATOR: |
| 201 | braceAtCaret = caretPos |
| 202 | |
| 203 | if braceAtCaret >= 0: |
| 204 | braceOpposite = self.BraceMatch(braceAtCaret) |
| 205 | |
| 206 | if braceAtCaret != -1 and braceOpposite == -1: |
| 207 | self.BraceBadLight(braceAtCaret) |
| 208 | else: |
| 209 | self.BraceHighlight(braceAtCaret, braceOpposite) |
| 210 | |
| 211 | def CanCopy(self): |
| 212 | """Return True if text is selected and can be copied.""" |
| 213 | return self.GetSelectionStart() != self.GetSelectionEnd() |
| 214 | |
| 215 | def CanCut(self): |
| 216 | """Return True if text is selected and can be cut.""" |
| 217 | return self.CanCopy() and self.CanEdit() |
| 218 | |
| 219 | def CanEdit(self): |
| 220 | """Return True if editing should succeed.""" |
| 221 | return not self.GetReadOnly() |
| 222 | |
| 223 | def CanPaste(self): |
| 224 | """Return True if pasting should succeed.""" |
| 225 | return stc.StyledTextCtrl.CanPaste(self) and self.CanEdit() |