| Home | Trees | Indices | Help |
|
|---|
|
|
1 # -*- coding: utf-8 -*-
2 __doc__ = """GNUmed StyledTextCtrl subclass for SOAP editing.
3
4 based on: 11/21/2003 - Jeff Grimmett (grimmtooth@softhome.net)"""
5 #================================================================
6 __author__ = "K. Hilbert <Karsten.Hilbert@gmx.net>"
7 __license__ = "GPL v2 or later (details at http://www.gnu.org)"
8
9 import logging
10 import sys
11
12
13 import wx
14 import wx.stc
15
16
17 if __name__ == '__main__':
18 sys.path.insert(0, '../../')
19 from Gnumed.business import gmSoapDefs
20 from Gnumed.wxpython import gmKeywordExpansionWidgets
21 from Gnumed.wxpython.gmTextCtrl import cUnicodeInsertion_TextCtrlMixin
22
23
24 _log = logging.getLogger('gm.stc')
25
26 #================================================================
28
30 if not isinstance(self, wx.stc.StyledTextCtrl):
31 raise TypeError('[%s]: can only be applied to wx.stc.StyledTextCtrl, not [%s]' % (cWxTextCtrlCompatibility_StcMixin, self.__class__.__name__))
32
33 #--------------------------------------------------
34 # wx.TextCtrl compatibility
35 #--------------------------------------------------
37 _log.debug('%s.GetValue() - %s', cWxTextCtrlCompatibility_StcMixin, self.__class__.__name__)
38 return self.GetText()
39
40 #--------------------------------------------------
42 _log.debug('%s.SetValue() - %s', cWxTextCtrlCompatibility_StcMixin, self.__class__.__name__)
43 return self.SetText(value)
44
45 #--------------------------------------------------
48
49 #--------------------------------------------------
52
53 LastPosition = property(GetLastPosition, lambda x:x)
54
55 #--------------------------------------------------
58
59 #--------------------------------------------------
62
63 #--------------------------------------------------
66
69
70 InsertionPoint = property(GetInsertionPoint, SetInsertionPoint)
71
72 #--------------------------------------------------
74 #self.ScrollToLine(self.LineFromPosition(position))
75 self.CurrentPos = position
76 self.EnsureCaretVisible()
77
78 #--------------------------------------------------
81
82 #--------------------------------------------------
84 try:
85 #return wx.stc.StyledTextCtrl.PositionToXY(position) # does not work
86 #return wx.TextAreaBase.PositionToXY(position) # does not work
87 return super(wx.TextAreaBase, self).PositionToXY(position)
88 except AttributeError:
89 # reimplement for wxPython 2.8,
90 # this is moot now, hwoever, since 2.8 returned an (x, y) tuple
91 return (True, self.GetColumn(position), self.LineFromPosition(position))
92
93 #--------------------------------------------------
95 self.SetSelection(start, end)
96 self.ReplaceSelection(replacement)
97 wx.CallAfter(self.SetSelection, 0, 0)
98
99 #----------------------------------------------------------------------
100 -class cSoapSTC(cUnicodeInsertion_TextCtrlMixin, gmKeywordExpansionWidgets.cKeywordExpansion_TextCtrlMixin, cWxTextCtrlCompatibility_StcMixin, wx.stc.StyledTextCtrl):
101
102 _MARKER_ADM = 0
103 _MARKER_S = 1
104 _MARKER_O = 2
105 _MARKER_A = 3
106 _MARKER_P = 4
107 _MARKER_U = 5
108 _MARKER_LINE_BG_LIGHT_GREY = 31
109
110 _DEFINED_MARKERS_MASK = (
111 _MARKER_ADM
112 |
113 _MARKER_S
114 |
115 _MARKER_O
116 |
117 _MARKER_A
118 |
119 _MARKER_P
120 |
121 _MARKER_U
122 |
123 _MARKER_LINE_BG_LIGHT_GREY
124 )
125
126 _DEFINED_MARKER_NUMS = [
127 _MARKER_ADM,
128 _MARKER_S,
129 _MARKER_O,
130 _MARKER_A,
131 _MARKER_P,
132 _MARKER_U,
133 _MARKER_LINE_BG_LIGHT_GREY
134 ]
135 _SOAP_MARKER_NUMS = [
136 _MARKER_ADM,
137 _MARKER_S,
138 _MARKER_O,
139 _MARKER_A,
140 _MARKER_P,
141 _MARKER_U
142 ]
143 _SOAP2MARKER = {
144 None: _MARKER_ADM,
145 ' ': _MARKER_ADM,
146 '.': _MARKER_ADM,
147 's': _MARKER_S,
148 'o': _MARKER_O,
149 'a': _MARKER_A,
150 'p': _MARKER_P,
151 'u': _MARKER_U,
152 'S': _MARKER_S,
153 'O': _MARKER_O,
154 'A': _MARKER_A,
155 'P': _MARKER_P,
156 'U': _MARKER_U
157 }
158 _MARKER2SOAP = {
159 _MARKER_ADM: None,
160 _MARKER_S: 's',
161 _MARKER_O: 'o',
162 _MARKER_A: 'a',
163 _MARKER_P: 'p',
164 _MARKER_U: 'u'
165 }
166 _SOAPMARKER2BACKGROUND = {
167 _MARKER_ADM: False,
168 _MARKER_S: True,
169 _MARKER_O: False,
170 _MARKER_A: True,
171 _MARKER_P: False,
172 _MARKER_U: True
173 }
174
176
177 # normalize wxGlade output
178 if args[2] == '':
179 l_args = list(args)
180 l_args[2] = wx.DefaultPosition
181 args = tuple(l_args)
182 wx.stc.StyledTextCtrl.__init__(self, *args, **kwargs)
183 cWxTextCtrlCompatibility_StcMixin.__init__(self)
184 gmKeywordExpansionWidgets.cKeywordExpansion_TextCtrlMixin.__init__(self)
185 cUnicodeInsertion_TextCtrlMixin.__init__(self)
186
187 # wrapping and overflow
188 self.SetWrapMode(wx.stc.STC_WRAP_NONE)
189 # said to be problematic:
190 self.SetEdgeColumn(80)
191 self.SetEdgeColour('grey')
192 #self.SetEdgeMode(wx.stc.STC_EDGE_LINE)
193 self.SetEdgeMode(wx.stc.STC_EDGE_BACKGROUND)
194
195 # EOL style
196 self.SetEOLMode(wx.stc.STC_EOL_LF)
197 #self.SetViewEOL(1) # visual debugging
198 self.SetViewEOL(0)
199
200 # whitespace handling
201 #self.SetViewWhiteSpace(wx.stc.STC_WS_VISIBLEAFTERINDENT) # visual debugging
202 self.SetViewWhiteSpace(wx.stc.STC_WS_INVISIBLE)
203 #self.SetWhitespaceBackground(1, a_color) # 1 = override lexer
204 #self.SetWhitespaceForeground(1, a_color) # 1 = override lexer
205
206 # caret handling
207 #self.SetCaretLineBackground('light goldenrod yellow')
208 self.SetCaretLineBackground('khaki')
209 self.SetCaretLineVisible(1)
210
211 # margins
212 # left margin: 0 pixel widths
213 self.SetMarginLeft(0)
214 # margin 0: SOAP markers
215 self.SetMarginType(0, wx.stc.STC_MARGIN_SYMBOL)
216 self.SetMarginWidth(0, 16)
217 self.SetMarginMask(0, cSoapSTC._DEFINED_MARKERS_MASK)
218 # margin 1 and 2: additional 2-letter markers (not yet supported)
219 self.SetMarginType(1, wx.stc.STC_MARGIN_SYMBOL)
220 self.SetMarginMask(1, 0)
221 self.SetMarginWidth(1, 0)
222 self.SetMarginType(2, wx.stc.STC_MARGIN_SYMBOL)
223 self.SetMarginMask(2, 0)
224 self.SetMarginWidth(2, 0)
225
226 # markers
227 # can only use ASCII so far, so must make sure translations are ASCII:
228 self.MarkerDefine(cSoapSTC._MARKER_ADM, wx.stc.STC_MARK_CHARACTER + ord('.'), 'blue', 'white')
229 self.MarkerDefine(cSoapSTC._MARKER_S, wx.stc.STC_MARK_CHARACTER + ord(gmSoapDefs.soap_cat2l10n['s']), 'blue', 'grey96')
230 self.MarkerDefine(cSoapSTC._MARKER_O, wx.stc.STC_MARK_CHARACTER + ord(gmSoapDefs.soap_cat2l10n['o']), 'blue', 'white')
231 self.MarkerDefine(cSoapSTC._MARKER_A, wx.stc.STC_MARK_CHARACTER + ord(gmSoapDefs.soap_cat2l10n['a']), 'blue', 'grey96')
232 self.MarkerDefine(cSoapSTC._MARKER_P, wx.stc.STC_MARK_CHARACTER + ord(gmSoapDefs.soap_cat2l10n['p']), 'blue', 'white')
233 self.MarkerDefine(cSoapSTC._MARKER_U, wx.stc.STC_MARK_CHARACTER + ord(gmSoapDefs.soap_cat2l10n['u']), 'blue', 'grey96')
234 self.MarkerDefine(cSoapSTC._MARKER_LINE_BG_LIGHT_GREY, wx.stc.STC_MARK_BACKGROUND, 'grey96', 'grey96')
235
236 # unset hotkeys we want to re-define
237 #self.CmdKeyClear('t', wx.stc.STC_SCMOD_CTRL) # does not seem to work
238 self.__changing_SOAP_cat = False
239 self.__markers_of_prev_line = None
240 self.__ensure_has_all_soap_types = False
241
242 # we do our own popup menu
243 self.UsePopUp(0)
244 self.__build_context_menu()
245
246 # always keep one line of each of .SOAP around
247 self.SetText_from_SOAP()
248
249 self.__register_events()
250
251 # text expansion mixin
252 self.enable_keyword_expansions()
253
254 #-------------------------------------------------------
255 # SOAP-enhanced text setting
256 #-------------------------------------------------------
258 _log.debug('%s.SetText()', self.__class__.__name__)
259 wx.stc.StyledTextCtrl.SetText(self, *args, **kwargs)
260
262 _log.debug('%s.AddText()', self.__class__.__name__)
263 wx.stc.StyledTextCtrl.AddText(self, *args, **kwargs)
264
266 _log.debug('%s.AddStyledText()', self.__class__.__name__)
267 wx.stc.StyledTextCtrl.AddStyledText(self, *args, **kwargs)
268
270 _log.debug('%s.InsertText()', self.__class__.__name__)
271 wx.stc.StyledTextCtrl.InsertText(self, *args, **kwargs)
272
273 #-------------------------------------------------------
275 sel_start, sel_end = self.GetSelection()
276 start_line = self.LineFromPosition(sel_start)
277 end_line = start_line + text.count('\n')
278 start_line_soap_cat = self.MarkerGet(start_line)
279 #_log.debug(u'replacing @ pos %s-%s with %s lines (line %s to line %s)', sel_start, sel_end, text.count(u'\n'), start_line, end_line)
280 wx.stc.StyledTextCtrl.ReplaceSelection(self, text)
281 if start_line != end_line:
282 for target_line in range(start_line, end_line):
283 self.MarkerDelete(target_line, -1)
284 self.__set_markers_of_line(target_line, start_line_soap_cat)
285
286 #-------------------------------------------------------
288 _log.debug('%s.ReplaceTarget()', self.__class__.__name__)
289 wx.stc.StyledTextCtrl.ReplaceTarget(self, *args, **kwargs)
290
292 _log.debug('%s.ReplaceTargetRE()', self.__class__.__name__)
293 wx.stc.StyledTextCtrl.ReplaceTargetRE(self, *args, **kwargs)
294
295 #-------------------------------------------------------
296 # external API
297 #-------------------------------------------------------
299 # defaults
300 if soap is None:
301 #soap = {None: [u'']} # 'soap' will be added below by normalization
302 soap = {}
303 if sort_order is None:
304 sort_order = ['s', 'o', 'a', 'p', None, 'u']
305
306 # normalize input
307 for cat in 'soap':
308 try:
309 soap[cat]
310 except KeyError:
311 soap[cat] = ['']
312 try:
313 soap['u']
314 except KeyError:
315 soap['u'] = []
316 try:
317 soap[None]
318 except KeyError:
319 soap[None] = []
320 if '.' in soap:
321 soap[None].extend(soap['.'])
322 del soap['.']
323 if ' ' in soap:
324 soap[None].extend(soap[' '])
325 del soap[' ']
326
327 # normalize sort order
328 for cat in 'soapu':
329 if cat not in sort_order:
330 sort_order.append(cat)
331 if None not in sort_order:
332 sort_order.append(None)
333
334 # sort and flatten
335 soap_lines = []
336 line_categories = []
337 for cat in sort_order:
338 lines = soap[cat]
339 if len(lines) == 0:
340 continue
341 for line in lines:
342 soap_lines.append(line.strip())
343 line_categories.append(cat)
344
345 _log.debug('%s.SetText_from_SOAP(): 1 controlled use of .SetText() follows', self.__class__.__name__)
346 self.SetText('\n'.join(soap_lines))
347
348 for idx in range(len(line_categories)):
349 #self.set_soap_cat_of_line(idx, line_categories[idx], unconditionally = True)
350 self.set_soap_cat_of_line(idx, line_categories[idx])
351
352 #-------------------------------------------------------
354 lines = self.GetText().split('\n')
355 soap = {}
356 for line_idx in range(len(lines)):
357 cat = self.get_soap_cat_of_line(line_idx)
358 try:
359 soap[cat]
360 except KeyError:
361 soap[cat] = []
362 soap[cat].append(lines[line_idx])
363 return soap
364
365 soap = property(GetText_as_SOAP, lambda x:x)
366
367 #--------------------------------------------------------
369 soap = self.GetText_as_SOAP()
370 for cat in soap:
371 if ''.join([ l.strip() for l in soap[cat] ]) != '':
372 return False
373 return True
374
375 empty = property(_get_empty, lambda x:x)
376
377 #-------------------------------------------------------
380
381 #-------------------------------------------------------
383 caret_pos = self.CurrentPos
384 self.GotoPos(self.Length)
385 self.AddText('\n')
386 #self.set_soap_cat_of_line(self.LineCount, soap_cat, True)
387 self.set_soap_cat_of_line(self.LineCount, soap_cat)
388
389 #-------------------------------------------------------
390 # generic helpers
391 #-------------------------------------------------------
393 line_text = self.GetLine(line)
394 line_start = self.PositionFromLine(line)
395 line_end = self.GetLineEndPosition(line)
396 self.SetTargetStart(line_start)
397 self.SetTargetEnd(line_end)
398 self.ReplaceTarget(line_text.rstrip())
399
400 #-------------------------------------------------------
403
404 #-------------------------------------------------------
406 return self.ClientToScreen(self.caret_coords_in_stc())
407
408 #-------------------------------------------------------
409 # internal helpers
410 #-------------------------------------------------------
480
481 #-------------------------------------------------------
491
492 #-------------------------------------------------------
494 if wx.TheClipboard.IsOpened():
495 _log.debug('clipboard already open')
496 return ''
497 if not wx.TheClipboard.Open():
498 _log.debug('cannot open clipboard')
499 return ''
500 data_obj = wx.TextDataObject()
501 got_it = wx.TheClipboard.GetData(data_obj)
502 if not got_it:
503 return ''
504 return data_obj.Text
505
506 #-------------------------------------------------------
507 # context menu handlers
508 #-------------------------------------------------------
510 self.attempt_expansion(show_list_if_needed = True)
511
512 #-------------------------------------------------------
514 self.mixin_insert_unicode_character()
515
516 #-------------------------------------------------------
522
523 #-------------------------------------------------------
525 txt = self.GetText().strip()
526 if txt == '':
527 return
528 txt = self.__get_clipboard_text() + '\n' + txt
529 self.CopyText(len(txt), txt)
530
531 #-------------------------------------------------------
534
535 #-------------------------------------------------------
537 region = self.GetTextRange(self.SelectionStart, self.SelectionEnd)
538 if region.strip() == '':
539 return
540 txt = self.__get_clipboard_text() + '\n' + region
541 self.CopyText(len(txt), txt)
542
543 #-------------------------------------------------------
545 txt = self.GetLine(self.CurrentLine).strip()
546 if txt == '':
547 return
548 self.CopyText(len(txt), txt)
549
550 #-------------------------------------------------------
552 txt = self.GetLine(self.CurrentLine).strip()
553 if txt == '':
554 return
555 txt = self.__get_clipboard_text() + '\n' + txt
556 self.CopyText(len(txt), txt)
557
558 #-------------------------------------------------------
562
563 #-------------------------------------------------------
567
568 #-------------------------------------------------------
572
573 #-------------------------------------------------------
577
578 #-------------------------------------------------------
582
583 #-------------------------------------------------------
587
588 #-------------------------------------------------------
590 self.sort_by_SOAP()
591
592 #-------------------------------------------------------
593 # marker related helpers
594 #-------------------------------------------------------
598
599 #-------------------------------------------------------
601 for marker_num in cSoapSTC._DEFINED_MARKER_NUMS:
602 if markers & (1 << marker_num):
603 self.MarkerAdd(line, marker_num)
604
605 #-------------------------------------------------------
607 markers = self.MarkerGet(line)
608 for marker_num in cSoapSTC._SOAP_MARKER_NUMS:
609 if markers & (1 << marker_num):
610 return marker_num
611
612 return -1 # should only happen when deleting all lines -> STC empties out INCLUDING existing markers ...
613
614 #-------------------------------------------------------
616 markers = self.MarkerGet(line)
617 for marker_num in cSoapSTC._SOAP_MARKER_NUMS:
618 if markers & (1 << marker_num):
619 return cSoapSTC._MARKER2SOAP[marker_num]
620
621 return -1 # should only happen when deleting all lines -> STC empties out INCLUDING existing markers ...
622
623 #-------------------------------------------------------
624 # def set_soap_cat_of_line(self, line, soap_category, unconditionally=False):
626
627 readd_soap_line = False
628 prev_soap_cat = '-'
629 # if not unconditionally:
630 if True:
631 # need to keep at least one of previous SOAP
632 prev_soap_marker = self.get_soap_marker_of_line(line)
633 if prev_soap_marker != -1:
634 if self.marker_count(prev_soap_marker) < 2:
635 prev_soap_cat = cSoapSTC._MARKER2SOAP[prev_soap_marker]
636 readd_soap_line = True
637 # return False
638
639 # remove all SOAP markers of this line
640 for marker_num in cSoapSTC._SOAP_MARKER_NUMS:
641 self.MarkerDelete(line, marker_num)
642 self.MarkerDelete(line, cSoapSTC._MARKER_LINE_BG_LIGHT_GREY)
643
644 new_marker_num = cSoapSTC._SOAP2MARKER[soap_category]
645 self.MarkerAdd(line, new_marker_num)
646 if cSoapSTC._SOAPMARKER2BACKGROUND[new_marker_num]:
647 self.MarkerAdd(line, cSoapSTC._MARKER_LINE_BG_LIGHT_GREY)
648
649 # for some reason the marker is now set but the change is not always displayed ?
650 # -> this happens if wx.CallAfter is used on this method
651
652 if readd_soap_line:
653 wx.CallAfter(self.append_soap_line, prev_soap_cat)
654
655 return True
656
657 #-------------------------------------------------------
659 for marker_num in [ cSoapSTC._MARKER_S, cSoapSTC._MARKER_O, cSoapSTC._MARKER_A, cSoapSTC._MARKER_P ]:
660 if self.MarkerNext(0, (1 << marker_num)) == -1:
661 return False
662 return True
663
664 #-------------------------------------------------------
668
669 #-------------------------------------------------------
671 line_count = 0
672 line_w_marker = -1
673 while True:
674 line_w_marker = self.MarkerNext(line_w_marker + 1, (1 << marker))
675 if line_w_marker == -1:
676 break
677 line_count += 1
678 return line_count
679
680 #-------------------------------------------------------
684
685 #-------------------------------------------------------
686 # key handlers
687 #-------------------------------------------------------
689
690 if evt.HasModifiers():
691 # we only handle DELETE w/o modifiers so far
692 evt.Skip()
693 return False
694
695 sel_start, sel_end = self.GetSelection()
696 if sel_start != sel_end:
697 evt.Skip()
698 sel_start_line = self.LineFromPosition(sel_start)
699 sel_end_line = self.LineFromPosition(sel_end)
700 # within one line -> allow in any case
701 if sel_start_line == sel_end_line:
702 return
703 sel_start_soap_marker = self.get_soap_marker_of_line(sel_start_line)
704 sel_end_soap_marker = self.get_soap_marker_of_line(sel_end_line)
705 if sel_start_soap_marker == sel_end_soap_marker:
706 # across lines of the same SOAP type -> allow
707 return
708 self.__ensure_has_all_soap_types = True
709 return
710
711 curr_line = self.CurrentLine
712 if (curr_line + 1) == self.LineCount: # adjust for line index being 0-based
713 # we are on the last line, therefore we cannot end up
714 # pulling up a next line (and thereby remove the only
715 # line with a given SOAP category in case the last+1
716 # line would happen to be the only one of that category)
717 evt.Skip()
718 return False
719
720 # in last column
721 caret_pos = self.GetColumn(self.CurrentPos)
722 max_pos = self.LineLength(curr_line) - 1
723 if caret_pos < max_pos:
724 # DELETE _inside_ a line (as opposed to at the
725 # _end_ of one) will not pull up the next line
726 # so no special SOAP checking
727 evt.Skip()
728 return False
729
730 soap_marker_current_line = self.get_soap_marker_of_line(curr_line)
731 soap_marker_next_line = self.get_soap_marker_of_line(curr_line + 1)
732 if soap_marker_current_line == soap_marker_next_line:
733 # pulling up a line of the _same_ SOAP category
734 # is fine - and exactly what the user intended
735 # so allow that to happen (IOW no special DELETE
736 # handling)
737 evt.Skip()
738 return False
739
740 # now we have got
741 # - a DELETE
742 # - without modifier keys
743 # - _not_ on the last line
744 # - in the last column of the current line
745 # - but the next line is of a different SOAP category
746 # so, do NOT evt.Skip() - IOW, ignore this DELETE
747 return True
748
749 #-------------------------------------------------------
751
752 if evt.HasModifiers():
753 # we only handle BACKSPACE w/o modifiers so far
754 evt.Skip()
755 return False
756
757 sel_start, sel_end = self.GetSelection()
758 if sel_start != sel_end:
759 evt.Skip()
760 sel_start_line = self.LineFromPosition(sel_start)
761 sel_end_line = self.LineFromPosition(sel_end)
762 # within one line -> allow in any case
763 if sel_start_line == sel_end_line:
764 return
765 sel_start_soap_marker = self.get_soap_marker_of_line(sel_start_line)
766 sel_end_soap_marker = self.get_soap_marker_of_line(sel_end_line)
767 if sel_start_soap_marker == sel_end_soap_marker:
768 # across lines of the same SOAP type -> allow
769 return
770 self.__ensure_has_all_soap_types = True
771 return
772
773 curr_line = self.LineFromPosition(self.CurrentPos)
774 if curr_line == 0:
775 # cannot BACKSPACE into line -1 anyway
776 evt.Skip()
777 return False
778
779 if self.GetColumn(self.CurrentPos) > 0:
780 # not in first column, so not BACKSPACing into previous line
781 evt.Skip()
782 return False
783
784 soap_marker_current_line = self.get_soap_marker_of_line(curr_line)
785 soap_marker_next_line = self.get_soap_marker_of_line(curr_line - 1)
786 if soap_marker_current_line == soap_marker_next_line:
787 # backspacing into previous line of the _same_ SOAP
788 # category is fine - and exactly what the user
789 # intended so allow that to happen (IOW no special
790 # DELETE handling)
791 evt.Skip()
792 return False
793
794 # now we have got
795 # - a BACKSPACE
796 # - without modifier keys
797 # - _not_ on the first line
798 # - in the first column of the current line
799 # - but the previous line is of a different SOAP category
800 # so, do NOT evt.Skip() - IOW, ignore this BACKSPACE
801 return True
802
803 #-------------------------------------------------------
805 # currently we always want to pass on the RETURN (but remember the markers)
806 evt.Skip()
807 if evt.HasModifiers():
808 # we only handle RETURN w/o modifiers so far
809 self.__markers_of_prev_line = None
810 return
811 self.__markers_of_prev_line = self.MarkerGet(self.CurrentLine)
812
813 #-------------------------------------------------------
815 self.__changing_SOAP_cat = False
816 try:
817 soap_category = gmSoapDefs.l10n2soap_cat[key]
818 except KeyError:
819 if key.islower():
820 key = key.upper()
821 else:
822 key = key.lower()
823 try:
824 soap_category = gmSoapDefs.l10n2soap_cat[key]
825 except KeyError:
826 return
827 self.set_soap_cat_of_line(line, soap_category)
828 wx.CallAfter(self.sort_by_SOAP)
829
830 #-------------------------------------------------------
838
839 #-------------------------------------------------------
847
848 #-------------------------------------------------------
849 # event setup and handlers
850 #-------------------------------------------------------
852 # wxPython events
853 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down) # happens before key gets into STC
854 #self.Bind(wx.EVT_CHAR, self._on_wx_char) # happens before char gets into STC
855 self.Bind(wx.EVT_CONTEXT_MENU, self._on_context_menu_activated)
856
857 # STC events
858 self.Bind(wx.stc.EVT_STC_CHARADDED, self._on_stc_char_added)
859 self.Bind(wx.stc.EVT_STC_CHANGE, self._on_stc_change)
860
861 #self.Bind(stc.EVT_STC_DO_DROP, self.OnDoDrop)
862 #self.Bind(stc.EVT_STC_DRAG_OVER, self.OnDragOver)
863 #self.Bind(stc.EVT_STC_START_DRAG, self.OnStartDrag)
864 #self.Bind(stc.EVT_STC_MODIFIED, self.OnModified)
865
866 #self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
867
868 #-------------------------------------------------------
871
872 #-------------------------------------------------------
874
875 # CTRL-T has been pressed last, now another character has been pressed
876 if self.__changing_SOAP_cat:
877 self.__handle_soap_category_key_down(chr(evt.GetUniChar()).lower(), self.CurrentLine)
878 # somehow put cursor into the changed (and possibly moved) line
879 return
880
881 key = evt.KeyCode
882
883 # ENTER
884 if key == wx.WXK_RETURN:
885 self.__handle_return_key_down(evt)
886 return
887
888 # BACKSPACE
889 if key == wx.WXK_BACK:
890 self.__handle_backspace_key(evt)
891 return
892
893 # DELETE
894 if key == wx.WXK_DELETE:
895 self.__handle_delete_key(evt)
896 return
897
898 # MENU
899 if key == wx.WXK_MENU:
900 self.__handle_menu_key_down(evt)
901 return
902
903 # CTRL-T: set Type
904 if key == ord('T'):
905 if evt.HasModifiers():
906 if evt.CmdDown(): # CTRL-T or APPLE-T
907 self.__changing_SOAP_cat = True
908 return
909
910 evt.Skip() # make sure unhandled keys get to the STC
911
912 #-------------------------------------------------------
914 evt.Skip()
915 key = evt.GetKey()
916 if key == 10:
917 # we cannot simply transfer the markers of the previous
918 # line (where we pressed RETURN) into the current line
919 # (which appeared after the RETURN) because the STC handles
920 # creating the "new" line differently based on where in the
921 # previous line RETURN was pressed (!) -- if it happened
922 # to be in position 0 (at the start of the line) the previous
923 # line is pushed DOWN and an empty line is inserted BEFORE
924 # the previous line (likely an optimization)
925 # hence we need to remember the markers of the real previous
926 # line from _before_ the new line gets created and use that
927 # auto-set the markers of the new line... |-)
928 if self.__markers_of_prev_line is None:
929 return
930 self.__set_markers_of_line(self.CurrentLine - 1, self.__markers_of_prev_line)
931 self.__set_markers_of_line(self.CurrentLine, self.__markers_of_prev_line)
932 self.__markers_of_prev_line = None
933 return
934
935 #-------------------------------------------------------
939
940 #-------------------------------------------------------
941 #-------------------------------------------------------
942 #-------------------------------------------------------
943 #-------------------------------------------------------
944 # unused:
945 #-------------------------------------------------------
947 # This is how the clipboard contents can be preserved after
948 # the app has exited.
949 wx.TheClipboard.Flush()
950 evt.Skip()
951
952
954 #self.log.write("OnStartDrag: %d, %s\n"
955 # % (evt.GetDragAllowMove(), evt.GetDragText()))
956
957 if debug and evt.GetPosition() < 250:
958 evt.SetDragAllowMove(False) # you can prevent moving of text (only copy)
959 evt.SetDragText("DRAGGED TEXT") # you can change what is dragged
960 #evt.SetDragText("") # or prevent the drag with empty text
961
962
964 #self.log.write(
965 # "OnDragOver: x,y=(%d, %d) pos: %d DragResult: %d\n"
966 # % (evt.GetX(), evt.GetY(), evt.GetPosition(), evt.GetDragResult())
967 # )
968
969 if debug and evt.GetPosition() < 250:
970 evt.SetDragResult(wx.DragNone) # prevent dropping at the beginning of the buffer
971
972
974 #self.log.write("OnDoDrop: x,y=(%d, %d) pos: %d DragResult: %d\n"
975 # "\ttext: %s\n"
976 # % (evt.GetX(), evt.GetY(), evt.GetPosition(), evt.GetDragResult(),
977 # evt.GetDragText()))
978
979 if debug and evt.GetPosition() < 500:
980 evt.SetDragText("DROPPED TEXT") # Can change text if needed
981 #evt.SetDragResult(wx.DragNone) # Can also change the drag operation, but it
982 # is probably better to do it in OnDragOver so
983 # there is visual feedback
984
985 #evt.SetPosition(25) # Can also change position, but I'm not sure why
986 # you would want to...
987
988
990 #self.log.write("""OnModified
991 # Mod type: %s
992 # At position: %d
993 # Lines added: %d
994 # Text Length: %d
995 # Text: %s\n""" % ( self.transModType(evt.GetModificationType()),
996 # evt.GetPosition(),
997 # evt.GetLinesAdded(),
998 # evt.GetLength(),
999 # repr(evt.GetText()) ))
1000 pass
1001
1002
1004 st = ""
1005 table = [(stc.STC_MOD_INSERTTEXT, "InsertText"),
1006 (stc.STC_MOD_DELETETEXT, "DeleteText"),
1007 (stc.STC_MOD_CHANGESTYLE, "ChangeStyle"),
1008 (stc.STC_MOD_CHANGEFOLD, "ChangeFold"),
1009 (stc.STC_PERFORMED_USER, "UserFlag"),
1010 (stc.STC_PERFORMED_UNDO, "Undo"),
1011 (stc.STC_PERFORMED_REDO, "Redo"),
1012 (stc.STC_LASTSTEPINUNDOREDO, "Last-Undo/Redo"),
1013 (stc.STC_MOD_CHANGEMARKER, "ChangeMarker"),
1014 (stc.STC_MOD_BEFOREINSERT, "B4-Insert"),
1015 (stc.STC_MOD_BEFOREDELETE, "B4-Delete")
1016 ]
1017
1018 for flag,text in table:
1019 if flag & modType:
1020 st = st + text + " "
1021
1022 if not st:
1023 st = 'UNKNOWN'
1024
1025 return st
1026
1027 #----------------------------------------------------------------------
1028 #----------------------------------------------------------------------
1029 #----------------------------------------------------------------------
1030 if wx.Platform == '__WXMSW__':
1031 face1 = 'Arial'
1032 face2 = 'Times New Roman'
1033 face3 = 'Courier New'
1034 pb = 12
1035 else:
1036 face1 = 'Helvetica'
1037 face2 = 'Times'
1038 face3 = 'Courier'
1039 pb = 14
1040
1041
1042 _USE_PANEL = 1
1043
1045 if not _USE_PANEL:
1046 ed = p = cSoapSTC(nb, -1)
1047
1048 else:
1049 p = wx.Panel(nb, -1, style=wx.NO_FULL_REPAINT_ON_RESIZE)
1050 ed = cSoapSTC(p, -1, log)
1051 s = wx.BoxSizer(wx.HORIZONTAL)
1052 s.Add(ed, 1, wx.EXPAND)
1053 p.SetSizer(s)
1054 p.SetAutoLayout(True)
1055
1056
1057 #ed.SetBufferedDraw(False)
1058 #ed.StyleClearAll()
1059 #ed.SetScrollWidth(800)
1060 #ed.SetWrapMode(True)
1061 #ed.SetUseAntiAliasing(False)
1062 #ed.SetViewEOL(True)
1063
1064 #ed.CmdKeyClear(stc.STC_KEY_BACK,
1065 # stc.STC_SCMOD_CTRL)
1066 #ed.CmdKeyAssign(stc.STC_KEY_BACK,
1067 # stc.STC_SCMOD_CTRL,
1068 # stc.STC_CMD_DELWORDLEFT)
1069
1070 ed.SetText(demoText)
1071
1072 if wx.USE_UNICODE:
1073 import codecs
1074 decode = codecs.lookup("utf-8")[1]
1075
1076 ed.GotoPos(ed.GetLength())
1077 ed.AddText("\n\nwx.StyledTextCtrl can also do Unicode:\n")
1078 uniline = ed.GetCurrentLine()
1079 unitext, l = decode('\xd0\x9f\xd0\xb8\xd1\x82\xd0\xbe\xd0\xbd - '
1080 '\xd0\xbb\xd1\x83\xd1\x87\xd1\x88\xd0\xb8\xd0\xb9 '
1081 '\xd1\x8f\xd0\xb7\xd1\x8b\xd0\xba \xd0\xbf\xd1\x80\xd0\xbe\xd0\xb3\xd1\x80\xd0\xb0\xd0\xbc\xd0\xbc\xd0\xb8\xd1\x80\xd0\xbe\xd0\xb2\xd0\xb0\xd0\xbd\xd0\xb8\xd1\x8f!\n\n')
1082 ed.AddText('\tRussian: ')
1083 ed.AddText(unitext)
1084 ed.GotoPos(0)
1085 #else:
1086 # #ed.StyleSetFontEncoding(stc.STC_STYLE_DEFAULT, wx.FONTENCODING_KOI8)
1087 # #text = u'\u041f\u0438\u0442\u043e\u043d - \u043b\u0443\u0447\u0448\u0438\u0439 \u044f\u0437\u044b\u043a \n\u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f!'
1088 # #text = text.encode('koi8-r')
1089 # #ed.StyleSetFontEncoding(stc.STC_STYLE_DEFAULT, wx.FONTENCODING_BIG5)
1090 # #text = u'Python \u662f\u6700\u597d\u7684\u7de8\u7a0b\u8a9e\u8a00\uff01'
1091 # #text = text.encode('big5')
1092 # ed.GotoPos(ed.GetLength())
1093 # ed.AddText('\n\n' + text)
1094
1095 ed.EmptyUndoBuffer()
1096
1097 # make some styles
1098 ed.StyleSetSpec(stc.STC_STYLE_DEFAULT, "size:%d,face:%s" % (pb, face3))
1099 ed.StyleClearAll()
1100 ed.StyleSetSpec(1, "size:%d,bold,face:%s,fore:#0000FF" % (pb, face1))
1101 ed.StyleSetSpec(2, "face:%s,italic,fore:#FF0000,size:%d" % (face2, pb))
1102 ed.StyleSetSpec(3, "face:%s,bold,size:%d" % (face2, pb))
1103 ed.StyleSetSpec(4, "face:%s,size:%d" % (face1, pb-1))
1104
1105 # Now set some text to those styles... Normally this would be
1106 # done in an event handler that happens when text needs displayed.
1107 ed.StartStyling(98, 0xff)
1108 ed.SetStyling(6, 1) # set style for 6 characters using style 1
1109
1110 ed.StartStyling(190, 0xff)
1111 ed.SetStyling(20, 2)
1112
1113 ed.StartStyling(310, 0xff)
1114 ed.SetStyling(4, 3)
1115 ed.SetStyling(2, 0)
1116 ed.SetStyling(10, 4)
1117
1118
1119 # line numbers in the margin
1120 ed.SetMarginType(0, stc.STC_MARGIN_NUMBER)
1121 ed.SetMarginWidth(0, 22)
1122 ed.StyleSetSpec(stc.STC_STYLE_LINENUMBER, "size:%d,face:%s" % (pb-2, face1))
1123
1124 # setup some markers
1125 ed.SetMarginType(1, stc.STC_MARGIN_SYMBOL)
1126 ed.MarkerDefine(0, stc.STC_MARK_ROUNDRECT, "#CCFF00", "RED")
1127 ed.MarkerDefine(1, stc.STC_MARK_CIRCLE, "FOREST GREEN", "SIENNA")
1128 ed.MarkerDefine(2, stc.STC_MARK_SHORTARROW, "blue", "blue")
1129 ed.MarkerDefine(3, stc.STC_MARK_ARROW, "#00FF00", "#00FF00")
1130
1131 # put some markers on some lines
1132 ed.MarkerAdd(17, 0)
1133 ed.MarkerAdd(18, 1)
1134 ed.MarkerAdd(19, 2)
1135 ed.MarkerAdd(20, 3)
1136 ed.MarkerAdd(20, 0)
1137
1138
1139 # and finally, an indicator or two
1140 ed.IndicatorSetStyle(0, stc.STC_INDIC_SQUIGGLE)
1141 ed.IndicatorSetForeground(0, wx.RED)
1142 ed.IndicatorSetStyle(1, stc.STC_INDIC_DIAGONAL)
1143 ed.IndicatorSetForeground(1, wx.BLUE)
1144 ed.IndicatorSetStyle(2, stc.STC_INDIC_STRIKE)
1145 ed.IndicatorSetForeground(2, wx.RED)
1146
1147 ed.StartStyling(836, stc.STC_INDICS_MASK)
1148 ed.SetStyling(10, stc.STC_INDIC0_MASK)
1149 ed.SetStyling(8, stc.STC_INDIC1_MASK)
1150 ed.SetStyling(10, stc.STC_INDIC2_MASK | stc.STC_INDIC1_MASK)
1151
1152
1153 # some test stuff...
1154 if debug:
1155 print("GetTextLength(): ", ed.GetTextLength(), len(ed.GetText()))
1156 print("GetText(): ", repr(ed.GetText()))
1157 print()
1158 print("GetStyledText(98, 104): ", repr(ed.GetStyledText(98, 104)), len(ed.GetStyledText(98, 104)))
1159 print()
1160 print("GetCurLine(): ", repr(ed.GetCurLine()))
1161 ed.GotoPos(5)
1162 print("GetCurLine(): ", repr(ed.GetCurLine()))
1163 print()
1164 print("GetLine(1): ", repr(ed.GetLine(1)))
1165 print()
1166 ed.SetSelection(25, 35)
1167 print("GetSelectedText(): ", repr(ed.GetSelectedText()))
1168 print("GetTextRange(25, 35): ", repr(ed.GetTextRange(25, 35)))
1169 print("FindText(0, max, 'indicators'): ", end=' ')
1170 print(ed.FindText(0, ed.GetTextLength(), "indicators"))
1171 if wx.USE_UNICODE:
1172 end = ed.GetLength()
1173 start = ed.PositionFromLine(uniline)
1174 print("GetTextRange(%d, %d): " % (start, end), end=' ')
1175 print(repr(ed.GetTextRange(start, end)))
1176
1177
1178 wx.CallAfter(ed.GotoPos, 0)
1179 return p
1180
1181
1182 #----------------------------------------------------------------------
1183 overview = """\
1184 <html><body>
1185 Once again, no docs yet. <b>Sorry.</b> But <a href="data/stc.h.html">this</a>
1186 and <a href="http://www.scintilla.org/ScintillaDoc.html">this</a> should
1187 be helpful.
1188 </body><html>
1189 """
1190
1191 #===================================================
1192 # main
1193 #---------------------------------------------------
1194 if __name__ == '__main__':
1195
1196 if len(sys.argv) < 2:
1197 sys.exit()
1198
1199 if sys.argv[1] != 'test':
1200 sys.exit()
1201
1202 import wx.lib.colourdb
1203
1204 from Gnumed.pycommon import gmI18N
1205 gmI18N.activate_locale()
1206 gmI18N.install_domain(domain = 'gnumed')
1207
1208 #-----------------------------------------------
1210 app = wx.PyWidgetTester(size = (600, 600))
1211 wx.lib.colourdb.updateColourDB()
1212 #print wx.lib.colourdb.getColourList()
1213 app.SetWidget(cSoapSTC, -1, (100,50))
1214 app.MainLoop()
1215 return True
1216
1217 # app = wx.PyWidgetTester(size = (200, 50))
1218 # tc = cTextCtrl(app.frame, -1)
1219 # #tc.enable_keyword_expansions()
1220 # app.frame.Show(True)
1221 # app.MainLoop()
1222 # return True
1223
1224 #-----------------------------------------------
1225 test_stc()
1226
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Thu May 10 01:55:20 2018 | http://epydoc.sourceforge.net |