| Home | Trees | Indices | Help |
|
|---|
|
|
1 """gmResizingWidgets - Resizing widgets for use in GNUmed.
2
3 Design by Richard Terry and Ian Haywood.
4 """
5 #====================================================================
6 # $Source: /home/ncq/Projekte/cvs2git/vcs-mirror/gnumed/gnumed/client/wxpython/gmResizingWidgets.py,v $
7 # $Id: gmResizingWidgets.py,v 1.55 2009-03-10 14:24:15 ncq Exp $
8 __version__ = "$Revision: 1.55 $"
9 __author__ = "Ian Haywood, Karsten Hilbert, Richard Terry"
10 __license__ = 'GPL v2 or later (details at http://www.gnu.org)'
11
12 import sys, logging, re as regex
13
14
15 import wx
16 import wx.stc
17
18
19 from Gnumed.pycommon import gmI18N, gmDispatcher, gmPG2
20 from Gnumed.wxpython import gmGuiHelpers, gmTimer
21
22 _log = logging.getLogger('gm.ui')
23 _log.info(__version__)
24
25 STYLE_ERROR=1
26 STYLE_TEXT=2
27 STYLE_EMBED=4
28
29 #====================================================================
32 wx.ListBox.__init__(self, parent, -1, pos, size, style=wx.LB_SINGLE | wx.LB_NEEDED_SB)
33 self.callback = callback
34 self.alive = 1 # 0=dead, 1=alive, 2=must die
35 wx.EVT_LISTBOX (self, self.GetId(), self.OnList)
36 #------------------------------------------------
38 """
39 Sets the items, Items is a dict with label, data, weight items
40 """
41 items.sort (lambda a,b: cmp(b['weight'], a['weight']))
42 self.Clear()
43 self.Set([item['label'] for item in items])
44 n = 0
45 for item in items:
46 self.SetClientData(n, item['data'])
47 # n += 1 ??
48 self.SetSelection(0)
49 #------------------------------------------------
54 #------------------------------------------------
59 #------------------------------------------------
61 line = self.GetSelection()
62 if line >= 0:
63 text = self.GetString(line)
64 data = self.GetClientData(line)
65 self.callback(text, data)
66 self.alive = 2
67 self.Destroy() # this is only safe when in the event handler of another widget
68 #------------------------------------------------
70 event.Skip()
71 if self.alive != 2:
72 line = self.GetSelection()
73 if line >= 0:
74 text = self.GetString(line)
75 data = self.GetClientData(line)
76 self.callback (text, data)
77 self.alive = 2
78 else:
79 wx.CallAfter (self.Destroy) # in theory we shouldn't have to do this,
80 # but when we don't, wx segfaults.
81 #------------------------------------------------
85 #====================================================================
86 # according to Ian there isn't really a particular reason
87 # why we do not use wxMiniFrame instead of wx.Frame or even a wxWindow
89 # def __init__ (self, embed_header, widget_class, originator=None, pos=wx.DefaultPosition):
90 # wx.Frame.__init__(self, None, wxNewId(), widget_class.__name__, pos=pos, style=wx.SIMPLE_BORDER)
91 # self.win = widget_class(self, -1, pos = pos, size = wx.Size(300, 150), complete = self.OnOK)
93 wx.Frame.__init__(self, None, wx.NewId(), widget.__class__.__name__, pos=pos, style=wx.SIMPLE_BORDER)
94 widget.set_completion_callback(self.OnOK)
95 self.win = widget
96 self.embed_header = embed_header
97 self.originator = originator
98
99 self.__do_layout()
100
101 wx.EVT_BUTTON(self.__BTN_OK, self.__BTN_OK.GetId(), self.OnOK)
102 wx.EVT_BUTTON(self.__BTN_Cancel, self.__BTN_Cancel.GetId(), self._on_close)
103 self.win.SetFocus ()
104 #------------------------------------------------
106 self.__BTN_OK = wx.Button (self, -1, _("OK"), style=wx.BU_EXACTFIT)
107 self.__BTN_Cancel = wx.Button (self, -1, _("Cancel"), style=wx.BU_EXACTFIT)
108 szr_btns = wx.BoxSizer (wx.HORIZONTAL)
109 szr_btns.Add(self.__BTN_OK, 0, 0)
110 szr_btns.Add(self.__BTN_Cancel, 0, 0)
111
112 szr_main = wx.BoxSizer(wx.VERTICAL)
113 szr_main.Add(self.win, 1, wx.EXPAND, 0)
114 szr_main.Add(szr_btns, 0, wx.EXPAND)
115
116 self.SetAutoLayout(1)
117 self.SetSizer(szr_main)
118 szr_main.Fit(self)
119 szr_main.SetSizeHints(self)
120 self.Layout()
121 #------------------------------------------------
124 #------------------------------------------------
126 if self.originator:
127 self.originator.Embed ("%s: %s" % (self.embed_header, self.win.GetSummary()))
128 self.Close ()
129 #====================================================================
134 #====================================================================
136 """A vertically-scrolled window which allows subwindows
137 to change their size, and adjusts accordingly.
138 """
140
141 wx.ScrolledWindow.__init__(self, parent, id, pos = pos, size = size, style=wx.VSCROLL)
142 self.SetScrollRate(0, 20) # suppresses X scrolling by setting X rate to zero
143
144 # self.__list = None
145 # self.complete = complete # ??
146
147 self.__input_lines = [[]]
148 self.__szr_main = None
149 self.DoLayout()
150 self.__szr_main = wx.FlexGridSizer(len(self.__input_lines), 2)
151 for line in self.__input_lines:
152 if len(line) != 0:
153 # first label goes into column 1
154 if line[0]['label'] is not None:
155 self.__szr_main.Add(line[0]['label'], 1)
156 else:
157 self.__szr_main.Add((1, 1))
158 # the rest gets crammed into column 2
159 h_szr = wx.BoxSizer (wx.HORIZONTAL)
160 h_szr.Add(line[0]['instance'], 1, wx.EXPAND)
161 for widget in line[1:]:
162 if widget['label'] is not None:
163 h_szr.Add(widget['label'], 0)
164 h_szr.Add(widget['instance'], 1, wx.EXPAND)
165 self.__szr_main.Add(h_szr, 1, wx.EXPAND)
166 self.__szr_main.AddGrowableCol(1)
167 self.__szr_main.Add((1, 1))
168
169 self.SetSizer(self.__szr_main)
170 self.__szr_main.Fit(self)
171 self.FitInside()
172 #------------------------------------------------
174 """
175 Adds a widget, optionally with label
176
177 @type label: string
178 @param label: text of the label
179 @type widgets: wx.Window descendant
180 """
181 if label is None:
182 textbox = None
183 else:
184 textbox = wx.StaticText(self, -1, label, style=wx.ALIGN_RIGHT)
185 # append to last line
186 self.__input_lines[-1].append({'ID': label, 'label': textbox, 'instance': widget})
187 #------------------------------------------------
193 #------------------------------------------------
195 """
196 Overridden by descendants, this function uses AddWidget and Newline to form
197 the outline of the widget
198 """
199 _log.error('[%s] forgot to override DoLayout()' % self.__class__.__name__)
200 #------------------------------------------------
202 """Called when a child widget has a new height, redoes the layout.
203 """
204 if self.__szr_main is not None:
205 self.__szr_main.SetItemMinSize(widget, -1, new_height)
206 self.__szr_main.FitInside(self)
207 #------------------------------------------------
209 """
210 Ensures widget is visible
211
212 @param widget: a child widget
213 @type cur_x: integer
214 @param cur_x: the X co-ordinate of the cursor inside widget, if applicable
215 @type cur_y: integer
216 @param cur_y: the Y co-ordinate of the cursor inside widget
217 """
218 # get widget position
219 x, y = widget.GetPositionTuple()
220 # adjust for cursor offset
221 x += cur_x
222 y += cur_y
223 # convert to virtual coordinates
224 x, y = self.CalcUnscrolledPosition(x, y)
225 x_dimension, y_dimension = self.GetScrollPixelsPerUnit()
226 y = y / y_dimension
227 # currently, don't bother with X direction
228 self.Scroll (-1, y)
229 #------------------------------------------------
231 """
232 Runs SetValue() on all the fields
233
234 @type values: dictionary
235 @param values: keys are the labels, values are passed to SetValue()
236 """
237 # FIXME: adapt to cSTCval
238 for line in self.__input_lines:
239 for widget in line:
240 if values.has_key(widget['ID']):
241 if isinstance(widget['instance'], wx.stc.StyledTextCtrl):
242 widget['instance'].SetText(values[widget['ID']])
243 elif isinstance(widget['instance'], (wx.Choice, wx.RadioBox)):
244 widget['instance'].SetSelection(values[widget['ID']])
245 else:
246 widget['instance'].SetValue(values[widget['ID']])
247 #------------------------------------------------
249 """Return dict of values of inner widgets.
250
251 Returns a dictionary of the results of GetValue()
252 called on all widgets, keyed by label
253 Unlabelled widgets don't get called
254 """
255 # FIXME: this does not detect ID collisions between lines
256 vals = {}
257 for line in self.__input_lines:
258 for widget in line:
259 if widget['ID'] is None:
260 continue
261 result = cSTCval()
262 if isinstance(widget['instance'], cResizingSTC):
263 result.text = widget['instance'].GetText()
264 result.data = widget['instance'].GetData()
265 elif isinstance(widget['instance'], wx.stc.StyledTextCtrl):
266 result.text = widget['instance'].GetText()
267 elif isinstance(widget['instance'], (wx.Choice, wx.RadioBox)):
268 result.selection = widget['instance'].GetSelection()
269 else:
270 result.value = widget['instance'].GetValue()
271 vals[widget['ID']] = result
272 return vals
273 #------------------------------------------------
275 """
276 Clears all widgets where this makes sense
277 """
278 for line in self.__input_lines:
279 for widget in line:
280 if isinstance (widget['instance'], wx.stc.StyledTextCtrl):
281 widget['instance'].ClearAll()
282 elif isinstance (widget['instance'], wx.TextCtrl):
283 widget['instance'].Clear()
284 elif isinstance (widget['instance'], (wx.ToggleButton, wx.CheckBox, wx.RadioButton, wx.Gauge)):
285 widget['instance'].SetValue(0)
286 elif isinstance (widget['instance'], (wx.Choice, wx.ComboBox, wx.RadioBox)):
287 widget['instance'].SetSelection(0)
288 elif isinstance (widget['instance'], wx.SpinCtrl):
289 widget['instance'].SetValue(widget['instance'].GetMin())
290 #------------------------------------------------
292 # try to focus on the first line if we can.
293 try:
294 self.lines[0][0]['instance'].SetFocus()
295 except IndexError:
296 pass
297 except AttributeError:
298 pass
299 #------------------------------------------------
301 """
302 Returns a pick list, destroying a pre-existing pick list for this widget
303
304 the alive member is true until the object is Destroy ()'ed
305
306 @param callback: called when a item is selected,
307 @type callback: callable
308 @param x_intended: the X-position where the list should appear
309 @type x_intended: int
310 @param x: the Y-position where the list should appear
311 @type y_intended: int
312
313 @return: PickList
314 """
315 # # retire previous pick list
316 # if self.__list and self.__list.alive:
317 # self.__list.Destroy()
318 our_width, our_height = self.GetSizeTuple()
319 char_height = self.GetCharHeight()
320 # make list 9 lines of height char_height high
321 list_height = char_height * 9
322 # and find best placement
323 # - height
324 if (list_height + char_height) > our_height:
325 list_height = our_height
326 y_final = 0
327 elif (y_intended + list_height + char_height) > our_height:
328 y_final = our_height - list_height
329 else:
330 y_final = y_intended + char_height
331 # - width
332 list_width = int(list_height / 1.4)
333 if list_width > our_width:
334 list_width = our_width
335 x_final = 0
336 elif (x_intended + list_width) > our_width:
337 x_final = our_width - list_width
338 else:
339 x_final = x_intended
340 # self.__list = cPickList(self, wx.Point(x_final, y_final), wx.Size(list_width, list_height), callback=callback)
341 # return self.__list
342 list = cPickList(self, wx.Point(x_final, y_final), wx.Size(list_width, list_height), callback=callback)
343 return list
344 #------------------------------------------------
345 # def set_completion_callback(self, callback):
346 # self.complete = callback
347 #------------------------------------------------
351 #====================================================================
353 """
354 A StyledTextCrl that monitors the size of its internal text and
355 resizes the parent accordingly.
356
357 MUST ONLY be used inside ResizingWindow !
358
359 FIXME: override standard STC popup menu
360 """
361 - def __init__ (self, parent, id, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0, data=None):
362 if not isinstance(parent, cResizingWindow):
363 raise ValueError, 'parent of %s MUST be a ResizingWindow' % self.__class__.__name__
364
365 wx.stc.StyledTextCtrl.__init__ (self, parent, id, pos, size, style)
366
367 self.SetWrapMode (wx.stc.STC_WRAP_WORD)
368 # FIXME: configure
369 self.StyleSetSpec (STYLE_ERROR, "fore:#7F11010,bold")
370 self.StyleSetSpec (STYLE_EMBED, "fore:#4040B0")
371 self.StyleSetChangeable (STYLE_EMBED, 0)
372 # self.StyleSetHotSpot (STYLE_EMBED, 1)
373 self.SetEOLMode (wx.stc.STC_EOL_LF)
374
375 self.__register_interests()
376
377 self.next_in_tab_order = None
378 self.prev_in_tab_order = None
379
380 self.__parent = parent
381
382 self.__popup_keywords = {}
383
384 # FIXME: delay configurable
385 # self.__timer = gmTimer.cTimer (
386 # callback = self._on_timer_fired,
387 # delay = 300
388 # )
389 self.__matcher = None
390
391 self.__show_list = 1
392 self.__embed = {}
393 self.list = None
394 self.no_list = 0 # ??
395
396 self.__data = data # this is just a placeholder for data to be attached to this STC, will be returned from GetData()
397
398 self.__keyword_separators = regex.compile("[!?'\".,:;)}\]\r\n\s\t]+")
399 #------------------------------------------------
400 # public API
401 #------------------------------------------------
406 #------------------------------------------------
411 #------------------------------------------------
413 self.replace_text(start, end, text, style)
414 #------------------------------------------------
416 self.no_list = 1
417 self.ReplaceText(self.fragment_start, self.fragment_end, text+';', STYLE_EMBED)
418 self.GotoPos(self.fragment_start+len (text)+1)
419 self.SetFocus()
420 # if data:
421 # self.__embed[text] = data
422 self.no_list = 0
423 #------------------------------------------------
425 # FIXME: optimize
426 end = pos+1
427 while (end < self.GetLength()) and (self.GetCharAt(end) != ord(';')):
428 end += 1
429 start = pos
430 while (start > 0) and (self.GetCharAt(start and start-1) != ord(';')):
431 start -= 1
432 self.SetTargetStart(start)
433 self.SetTargetEnd(end)
434 self.ReplaceTarget('')
435 #------------------------------------------------
437 """Set focus to current position in STC.
438
439 - make sure that's visible, too
440 """
441 wx.stc.StyledTextCtrl.SetFocus(self)
442 # goto first line ?
443 if line == 1:
444 if x is None:
445 x = 0
446 self.GotoPos(self.PositionFromPoint(wx.Point(x,0)))
447 return
448 # goto last line ?
449 if line == -1:
450 _log.debug('going to last line in STC')
451 last_char_pos = self.GetLength()
452 if x is None:
453 self.GotoPos(last_char_pos)
454 _log.debug('no X given, use X=%s' % last_char_pos.x)
455 return
456 y = self.PointFromPosition(last_char_pos).y
457 _log.debug('going to given X=%s' % x)
458 self.GotoPos(self.PositionFromPoint(wx.Point(x,y)))
459 return
460 # goto last current position
461 cur = self.PointFromPosition(self.GetCurrentPos())
462 self.__parent.EnsureVisible (self, cur.x, cur.y)
463 #------------------------------------------------
465 """
466 Attaches a gmMatchProvider to the STC,this will be used to drive auto-completion
467 """
468 self.__matcher = matcher
469 #------------------------------------------------
471 """
472 Configures the data associated with this STC
473 @param data The associated data
474 @type data Any object
475 """
476 self.__data = data
477 #------------------------------------------------
483 #------------------------------------------------
485 """
486 Oddly, the otherwise very rich wx.STC API does not provide an
487 easy way to replace text, so we provide it here.
488
489 @param start: the position in the text to start from
490 @param length: the length of the string to replace
491 @param text: the new string
492 @param style: the style for the replaced string
493 """
494 self.SetTargetStart(start)
495 self.SetTargetEnd(end)
496 self.ReplaceTarget(text)
497 if style is not None:
498 self.StartStyling(start, 0xFF)
499 self.SetStyling(len(text), style)
500 #------------------------------------------------
502
503 if keyword == u'$$steffi': # Easter Egg ;-)
504 expansion = u'Hai, play! Versucht das! (Keks dazu?) :-)'
505 else:
506 expansion = gmPG2.expand_keyword(keyword = keyword)
507
508 if expansion is None:
509 return
510
511 if expansion == u'':
512 return
513
514 self.replace_text (
515 start = position,
516 end = position + len(keyword),
517 text = expansion
518 )
519
520 self.GotoPos(position + len(expansion) + 1)
521 #wx.stc.StyledTextCtrl.SetFocus(self)
522 cur = self.PointFromPosition(position + len(expansion) + 1)
523 self.__parent.EnsureVisible(self, cur.x, cur.y)
524 #------------------------------------------------
525 # event handling
526 #------------------------------------------------
528 self.SetModEventMask (wx.stc.STC_MOD_INSERTTEXT | wx.stc.STC_MOD_DELETETEXT | wx.stc.STC_PERFORMED_USER)
529
530 wx.stc.EVT_STC_MODIFIED (self, self.GetId(), self.__on_STC_modified)
531
532 wx.EVT_KEY_DOWN (self, self.__on_key_down)
533 wx.EVT_KEY_UP (self, self.__OnKeyUp)
534 wx.EVT_CHAR(self, self.__on_char)
535 #------------------------------------------------
537
538 # did the user do anything of note to us ?
539 if not (event.GetModificationType() & (wx.stc.STC_MOD_INSERTTEXT | wx.stc.STC_MOD_DELETETEXT)):
540 event.Skip()
541 return
542
543 last_char_pos = self.GetLength()
544
545 # stop timer if empty
546 if last_char_pos == 0:
547 # self.__timer.Stop()
548 return
549
550 # do we need to resize ?
551 line_height = self.TextHeight(0)
552 true_txt_height = (self.PointFromPosition(last_char_pos).y - self.PointFromPosition(0).y) + line_height
553 x, visible_height = self.GetSizeTuple()
554 if visible_height < true_txt_height:
555 # print "line:", line_height
556 # print "before resize: too small"
557 # print "visible height", visible_height
558 # print "true text hgt", true_txt_height
559 n, remainder = divmod((true_txt_height - visible_height), line_height)
560 if remainder > 0: n = n + 1
561 target_height = visible_height + (n * line_height)
562 self.__parent.ReSize(self, target_height)
563 # print "after resize"
564 x, y = self.GetSizeTuple()
565 # print "visible height", y
566
567 if ((visible_height - line_height) > true_txt_height):
568 # print "line:", line_height
569 # print "before resize: too big"
570 # print "visible height", visible_height
571 # print "true text hgt", true_txt_height
572 # n, delta = divmod((visible_height - true_txt_height), line_height)
573 # target_height = visible_height - (n * line_height)
574 target_height = visible_height - line_height
575 self.__parent.ReSize(self, target_height)
576 # print "after resize"
577 x, y = self.GetSizeTuple()
578 # print "visible height", y
579
580 # is currently relevant term a keyword for popping up an edit area or something ?
581 fragment = self.__get_focussed_fragment()
582 if fragment in self.__popup_keywords.keys():
583 # self.__timer.Stop()
584 self.__handle_keyword(fragment)
585 return
586 # else restart timer for match list
587 # self.__timer.Start(oneShot = True)
588 # event.Skip()
589 return
590 #------------------------------------------------
592 """Act on some key presses we want to process ourselves."""
593
594 # if (self.list is not None) and not self.list.alive:
595 # self.list = None # someone else has destroyed our list!
596
597 # curs_pos = self.GetCurrentPos()
598
599 # <DOWN>
600 # - if in list: scroll list
601 # - if in last line: goto first line, same character, in next_in_tab_order
602 # - else standard behaviour
603 #if event.GetKeyCode() == wx.WXK_DOWN:
604 # if (self.list is not None) and self.list.alive:
605 # self.list.Down()
606 # return
607 # print "arrow down @ %s (line %s of %s)" % (curs_pos, self.LineFromPosition(curs_pos), self.GetLineCount())
608 # if self.LineFromPosition(curs_pos)+1 == self.GetLineCount():
609 # if self.next_in_tab_order is not None:
610 # curs_coords = self.PointFromPosition(curs_pos)
611 # self.next_in_tab_order.SetFocus(x=curs_coords.x, line=1)
612 # return
613
614 # <UP>
615 # - if in list: scroll list
616 # - if in first line: goto last line, same character, in prev_in_tab_order
617 # - else standard behaviour
618 #if event.GetKeyCode() == wx.WXK_UP:
619 # _log.debug('<UP-ARROW> key press detected')
620 # if (self.list is not None) and self.list.alive:
621 # self.list.Up()
622 # return
623 # _log.debug('pos %s = line %s' % (curs_pos, self.LineFromPosition(curs_pos)))
624 # if self.LineFromPosition(curs_pos) == 0:
625 # _log.debug('first line of STC - special handling')
626 # if self.prev_in_tab_order is not None:
627 # _log.debug('prev_in_tab_order = %s' % str(self.prev_in_tab_order))
628 # curs_coords = self.PointFromPosition(curs_pos)
629 # _log.debug('cursor coordinates in current STC: %s:%s' % (curs_coords.x, curs_coords.y))
630 # self.prev_in_tab_order.SetFocus(x=curs_coords.x, line=-1)
631 # return
632 # else:
633 # _log.debug('not first line of STC - standard handling')
634
635 # <TAB> key
636 # - move to next/prev_in_tab_order
637 # FIXME: what about inside a list ?
638 if event.GetKeyCode() == wx.WXK_TAB:
639 if event.m_shiftDown:
640 if self.prev_in_tab_order is not None:
641 self.prev_in_tab_order.SetFocus()
642 else:
643 if self.next_in_tab_order is not None:
644 self.next_in_tab_order.SetFocus()
645 return
646
647 # <DEL>
648 # - if inside embedded string
649 # - delete entire string and data dict
650 # - else standard behaviour
651 # if event.GetKeyCode() == wx.WXK_DELETE:
652 # # FIXME: perhaps add check for regex, too ?
653 # if self.GetStyleAt(curs_pos) == STYLE_EMBED:
654 # self.DelPhrase(curs_pos)
655 # # FIXME: also delete corresponding "additional data" dict ...
656 # return
657
658 # <BACKSPACE>
659 # - if inside embedded string
660 # - delete entire string and data dict
661 # - else standard behaviour
662 # if event.GetKeyCode() == wx.WXK_BACK:
663 # # FIXME: perhaps add check for regex, too ?
664 # if self.GetStyleAt(curs_pos-1) == STYLE_EMBED:
665 # self.DelPhrase (curs_pos-1)
666 # # FIXME: also delete corresponding "additional data" dict ...
667 # return
668
669 event.Skip() # skip to next event handler to keep processing
670 #------------------------------------------------
672 if not self.list:
673 curs_pos = self.PointFromPosition(self.GetCurrentPos())
674 self.__parent.EnsureVisible (self, curs_pos.x, curs_pos.y)
675 #------------------------------------------------
677
678 char = unichr(evt.GetUnicodeKey())
679
680 if self.__keyword_separators.match(char) is not None:
681 if self.GetLength() == 1:
682 evt.Skip()
683 return
684
685 line, caret_pos = self.GetCurLine()
686 word = self.__keyword_separators.split(line[:caret_pos])[-1]
687 if (word not in [ r[0] for r in gmPG2.get_text_expansion_keywords() ]) and (word != u'$$steffi'): # Easter Egg ;-)
688 evt.Skip()
689 return
690
691 start = self.GetCurrentPos() - len(word)
692 wx.CallAfter(self.replace_keyword_with_expansion, word, start)
693 evt.Skip()
694 return
695
696 evt.Skip()
697 #------------------------------------------------
698 # def _cb_on_popup_completion(self, was_cancelled=False):
699 # """Callback for popup completion.
700 #
701 # - this is called when the user has signalled
702 # being done interacting with the popup
703 # - if was_cancelled is True the popup content should
704 # be ignored and no further action taken on it
705 # """
706 # print "popup interaction completed"
707 # if was_cancelled:
708 # print "popup cancelled, ignoring data"
709 ## self.__popup.Destroy()
710 # self.__popup = None
711 # return
712 # print "getting data from popup and acting on it"
713 # print self.__popup.GetData()
714 # # FIXME: wxCallAfter(embed) and store
715 # # maybe be a little smarter here
716 # self.__popup.Destroy()
717 # self.__popup = None
718 #------------------------------------------------
720 # print 'timer <%s> fired' % cookie
721 fragment = self.__get_focussed_fragment()
722 if fragment.strip() == '':
723 return 1
724 # print 'should popup context pick list on <%s> now' % fragment
725
726 return 1
727
728 # - get matches and popup select list
729 if self.no_list:
730 return
731 if self.__matcher is None:
732 return
733 if not self.__show_list:
734 return
735
736 # do indeed show list
737 if len(fragment) == 0:
738 if (self.list is not None) and self.list.alive:
739 self.list.Destroy()
740 return
741 matches_found, matches = self.__matcher.getMatches(fragment)
742 if not matches_found:
743 if (self.list is not None) and self.list.alive:
744 self.list.Destroy()
745 return
746 if not ((self.list is not None) and self.list.alive):
747 x, y = self.GetPositionTuple()
748 p = self.PointFromPosition(curs_pos)
749 self.list = self.__parent.GetPickList(self.__userlist, x+p.x, y+p.y)
750 self.list.SetItems(matches)
751 #------------------------------------------------
752 # internal API
753 #------------------------------------------------
755 curs_pos = self.GetCurrentPos()
756 text = self.GetText()
757 self.fragment_start = text.rfind(';', 0, curs_pos) # FIXME: ';' hardcoded as separator
758 if self.fragment_start == -1:
759 self.fragment_start = 0
760 else:
761 self.fragment_start += 1
762 last_char_pos = self.GetLength()
763 self.fragment_end = text.find(';', curs_pos, last_char_pos) # FIXME: ';' hardcoded as separator
764 if self.fragment_end == -1:
765 self.fragment_end = last_char_pos
766 return text[self.fragment_start:self.fragment_end].strip()
767 #------------------------------------------------
769 # print "calculating optimal popup geometry"
770 parent_width, parent_height = self.__parent.GetSizeTuple()
771 # print "parent size is %sx%s pixel" % (parent_width, parent_height)
772 # FIXME: this should be gotten from ourselves, not the parent, but how ?
773 parent_char_height = self.__parent.GetCharHeight()
774 # print "char height in parent is", parent_char_height, "pixel"
775 # make popup 9 lines of height parent_char_height high
776 # FIXME: better detect this, but how ?
777 popup_height = parent_char_height * 9
778 # print "hence intended popup height is", popup_height, "pixel"
779 # get STC displacement inside parent
780 stc_origin_x, stc_origin_y = self.GetPositionTuple()
781 # print "inside parent STC is @ %s:%s" % (stc_origin_x, stc_origin_y)
782 # get current cursor position inside STC in pixels
783 curs_pos = self.PointFromPosition(self.GetCurrentPos())
784 # print "inside STC cursor is @ %s:%s" % (curs_pos.x, curs_pos.y)
785 # find best placement
786 # - height
787 if (popup_height + parent_char_height) > parent_height:
788 # don't let popup get bigger than parent window
789 popup_height = parent_height
790 popup_y_pos = 0
791 elif ((popup_height + parent_char_height) + (curs_pos.y + stc_origin_y)) > parent_height:
792 # if would fit inside but forced (partially) outside
793 # by cursor position then move inside
794 popup_y_pos = parent_height - popup_height
795 else:
796 popup_y_pos = (curs_pos.y + stc_origin_y) + parent_char_height
797 # - width
798 popup_width = int(popup_height / 1.4) # Golden Cut
799 if popup_width > parent_width:
800 # don't let popup get bigger than parent window
801 popup_width = parent_width
802 popup_x_pos = 0
803 elif (popup_width + (curs_pos.x + stc_origin_x)) > parent_width:
804 # if would fit inside but forced (partially) outside
805 # by cursor position then move inside
806 popup_x_pos = parent_width - popup_width
807 else:
808 popup_x_pos = curs_pos.x + stc_origin_x
809 # print "optimal geometry = %sx%s @ %s:%s" % (popup_width, popup_height, popup_x_pos, popup_y_pos)
810 return (wx.Point(popup_x_pos, popup_y_pos), wx.Size(popup_width, popup_height))
811 #------------------------------------------------
813 try:
814 create_widget = self.__popup_keywords[kwd]['widget_factory']
815 except KeyError:
816 gmDispatcher.send(signal='statustext', msg=_('No action configured for keyword [%s].') % kwd)
817 return False
818
819 # best_pos, best_size = self.__get_best_popup_geom()
820 screen_pos = self.ClientToScreen(self.PointFromPosition(self.GetCurrentPos()))
821 top_parent = wx.GetTopLevelParent(self)
822 best_pos = top_parent.ScreenToClient(screen_pos)
823 try:
824 popup = create_widget (
825 parent = top_parent,
826 pos = best_pos,
827 size = wx.Size(400, 300),
828 style = wx.SUNKEN_BORDER,
829 data_sink = self.__popup_keywords[kwd]['widget_data_sink']
830 )
831 except StandardError:
832 _log.exception('cannot call [%s] on keyword [%s] to create widget' % (create_widget, kwd))
833 gmGuiHelpers.gm_show_error (
834 aMessage = _('Cannot invoke [%s] for keyword [%s].') % (create_widget, kwd),
835 aTitle = _('showing keyword popup')
836 )
837 return False
838
839 if not isinstance(popup, wx.Dialog):
840 gmDispatcher.send(signal='statustext', msg=_('Action [%s] on keyword [%s] is invalid.') % (create_widget, kwd))
841 _log.error('keyword [%s] triggered action [%s]' % (kwd, create_widget))
842 _log.error('the result (%s) is not a wx.Dialog subclass instance, however' % str(popup))
843 return False
844
845 # display widget
846 result = popup.ShowModal()
847 if result == wx.ID_OK:
848 summary = popup.get_summary()
849 wx.CallAfter(self.Embed, summary)
850 popup.Destroy()
851 #------------------------------------------------
853 # this is a callback
854 # --- old --------------
855 # # FIXME: need explanation on instance/callable business, it seems complicated
856 # if issubclass(data, cResizingWindow):
857 # win = data (
858 # self,
859 # -1,
860 # pos = self.ClientToScreen(self.PointFromPosition(self.GetCurrentPos())),
861 # size = wx.Size(300, 150)
862 # )
863 # cPopupFrame (
864 # embed_header = text,
865 # widget = win,
866 # originator = self,
867 # pos = self.ClientToScreen(self.PointFromPosition(self.GetCurrentPos()))
868 # ).Show()
869 # elif callable(data):
870 # data (text, self.__parent, self, self.ClientToScreen (self.PointFromPosition (self.GetCurrentPos ())))
871 # --- old --------------
872 if self.MakePopup (text, data, self, self.ClientToScreen (self.PointFromPosition (self.GetCurrentPos ()))):
873 pass
874 else:
875 self.Embed (text, data)
876 #--------------------------------------------------
878 """
879 An overrideable method, called whenever a match is made in this STC
880 Designed for producing popups, but the overrider can in fact, do
881 whatever they please.
882
883 @return True if a poup-up or similar actually happened (which suppresses inserting the match string in the text
884 @rtype boolean
885 """
886 #cPopupFrame(text, win, self, cursor_position)).Show()
887 return False
888 #====================================================================
889 #====================================================================
890 if __name__ == '__main__':
891
892 # from Gnumed.pycommon.gmMatchProvider import cMatchProvider_FixedList
893 # from Gnumed.pycommon import gmI18N
894
896 print "test keyword must have been typed..."
897 print "actually this would have to return a suitable wx.Window subclass instance"
898 print "args:", args
899 print "kwd args:"
900 for key in kwargs.keys():
901 print key, "->", kwargs[key]
902 #================================================================
904 msg = (
905 "test keyword must have been typed...\n"
906 "actually this would have to return a suitable wx.Window subclass instance\n"
907 )
908 for arg in args:
909 msg = msg + "\narg ==> %s" % arg
910 for key in kwargs.keys():
911 msg = msg + "\n%s ==> %s" % (key, kwargs[key])
912 gmGuiHelpers.gm_show_info (
913 aMessage = msg,
914 aTitle = 'msg box on create_widget from test_keyword'
915 )
916 #================================================================
919 wx.Panel.__init__ (
920 self,
921 parent,
922 -1,
923 pos,
924 size,
925 style
926 )
927 self.__completion_callback = completion_callback
928 self._wx.ID_BTN_OK = wx.NewId()
929 self._wx.ID_BTN_Cancel = wx.NewId()
930 self.__do_layout()
931 self.__register_interests()
932 self.Show()
933
935 # message
936 msg = "test keyword popup"
937 text = wx.StaticText (self, -1, msg)
938 # buttons
939 self.btn_OK = wx.Button(self, self._wx.ID_BTN_OK, _("OK"))
940 self.btn_OK.SetToolTipString(_('dismiss popup and embed data'))
941 self.btn_Cancel = wx.Button(self, self._wx.ID_BTN_Cancel, _("Cancel"))
942 self.btn_Cancel.SetToolTipString(_('dismiss popup and throw away data'))
943 szr_buttons = wx.BoxSizer(wx.HORIZONTAL)
944 szr_buttons.Add(self.btn_OK, 1, wx.EXPAND | wx.ALL, 1)
945 szr_buttons.Add(5, 0, 0)
946 szr_buttons.Add(self.btn_Cancel, 1, wx.EXPAND | wx.ALL, 1)
947 # arrange
948 szr_main = wx.BoxSizer(wx.VERTICAL)
949 szr_main.Add(text, 1, wx.EXPAND | wx.ALL, 1)
950 szr_main.Add(szr_buttons, 0)
951 # layout
952 self.SetAutoLayout(True)
953 self.SetSizer(szr_main)
954 szr_main.Fit(self)
955
957 wx.EVT_BUTTON(self.btn_OK, self._wx.ID_BTN_OK, self._on_ok)
958 wx.EVT_BUTTON(self.btn_Cancel, self._wx.ID_BTN_Cancel, self._on_cancel)
959
962
965 #================================================================
967 pnl = cTestKwdPopupPanel (
968 parent = parent,
969 pos = pos,
970 size = size,
971 style = style,
972 completion_callback = completion_callback
973 )
974 return pnl
975 #================================================================
978 self.input1 = cResizingSTC(self, -1)
979 self.input2 = cResizingSTC(self, -1)
980 self.input3 = cResizingSTC(self, -1)
981
982 self.input1.prev_in_tab_order = None
983 self.input1.next_in_tab_order = self.input2
984 self.input2.prev_in_tab_order = self.input1
985 self.input2.next_in_tab_order = self.input3
986 self.input3.prev_in_tab_order = self.input2
987 self.input3.next_in_tab_order = None
988
989 self.AddWidget (widget=self.input1, label="S")
990 self.Newline()
991 self.AddWidget (widget=self.input2, label="O")
992 self.Newline()
993 self.AddWidget (widget=self.input3, label="A+P")
994
995 kwds = {}
996 kwds['$test_keyword'] = {'widget_factory': create_widget_on_test_kwd3}
997 self.input2.set_keywords(popup_keywords=kwds)
998 #================================================================
1001 wx.Panel.__init__(self, parent, id)
1002 sizer = wx.BoxSizer(wx.VERTICAL)
1003 self.soap = cSoapWin(self, -1)
1004 self.save = wx.Button (self, -1, _(" Save "))
1005 self.delete = wx.Button (self, -1, _(" Delete "))
1006 self.new = wx.Button (self, -1, _(" New "))
1007 # self.list = wx.ListBox (self, -1, style=wx.LB_SINGLE | wx.LB_NEEDED_SB)
1008 wx.EVT_BUTTON (self.save, self.save.GetId (), self.OnSave)
1009 wx.EVT_BUTTON (self.delete, self.delete.GetId (), self.OnDelete)
1010 wx.EVT_BUTTON (self.new, self.new.GetId (), self.OnNew)
1011 # wx.EVT_LISTBOX (self.list, self.list.GetId (), self.OnList)
1012 self.__do_layout()
1013
1015 sizer_1 = wx.BoxSizer(wx.VERTICAL)
1016 sizer_1.Add(self.soap, 3, wx.EXPAND, 0)
1017 sizer_2 = wx.BoxSizer (wx.HORIZONTAL)
1018 sizer_2.Add(self.save, 0, 0)
1019 sizer_2.Add(self.delete, 0, 0)
1020 sizer_2.Add(self.new, 0, 0)
1021 sizer_1.Add(sizer_2, 0, wx.EXPAND)
1022 # sizer_1.Add(self.list, 3, wx.EXPAND, 0)
1023 self.SetAutoLayout(1)
1024 self.SetSizer(sizer_1)
1025 sizer_1.Fit(self)
1026 sizer_1.SetSizeHints(self)
1027 self.Layout()
1028
1031 # sel = self.list.GetSelection ()
1032 # if sel >= 0:
1033 # self.list.Delete (sel)
1034
1036 # sel = self.list.GetSelection ()
1037 # if sel >= 0:
1038 # self.OnSave (None)
1039 self.soap.Clear()
1040 # self.list.SetSelection (sel, 0)
1041
1046 # sel = self.list.GetSelection ()
1047 # if sel < 0:
1048 # self.list.Append (title, data)
1049 # else:
1050 # self.list.SetClientData (sel, data)
1051 # self.list.SetString (sel, title)
1052
1053 # def OnList (self, event):
1054 # self.soap.SetValues (event.GetClientData ())
1055 #================================================================
1058 wx.Frame.__init__ (self, None, wx.NewId(), "test SOAP", size = wx.Size (350, 500)) # this frame will have big fat borders
1059 wx.EVT_CLOSE (self, self.OnClose)
1060 panel = cSoapPanel(self, -1)
1061 sizer = wx.BoxSizer(wx.VERTICAL)
1062 sizer.Add (panel, 1, wx.GROW)
1063 self.SetSizer(sizer)
1064 self.SetAutoLayout(1)
1065 sizer.Fit (self)
1066 self.Layout ()
1067
1069 self.Destroy()
1070 #================================================================
1076 #================================================================
1077 app = testApp(0)
1078 app.MainLoop()
1079 #====================================================================
1080
| Home | Trees | Indices | Help |
|
|---|
| Generated by Epydoc 3.0.1 on Mon Jun 25 03:58:34 2012 | http://epydoc.sourceforge.net |