Change 'values written by' to use the original Program's environment.
[l3full.git] / l3gui / widgets.py
blobe39db24564737ce1528ef08cfbef68bab3ed924e
2 # Author: Michael H. Hohn (mhhohn@lbl.gov)
4 # Copyright (c) 2006, The Regents of the University of California
6 # See legal.txt and license.txt
9 """
10 Canvas "widgets". These come in groups:
12 graphical analogs to l3's core types
13 as found in l3lang.ast.
14 They provide editing, dragging, and composition functionality,
15 and allow for graphical program construction (useful for
16 higher-level functions), and graphical program and data
17 examination.
19 simple display support
20 These are additional items needed for display, like
21 Placeholder and Header.
23 """
24 #* Header
26 # policy for flush_events
27 # To ensure proper sizing, flush_events is called after display
28 # additions are made.
29 # Using flush_events in every get_bounds() call slows display down
30 # significantly.
32 import sys, weakref, traceback, string, types, os
33 from math import floor, sqrt
34 from pprint import pprint
36 import pango
37 import gtk
38 import gobject
39 try:
40 import gnomecanvas as canvas
41 from gnomecanvas import MOVETO_OPEN, MOVETO, LINETO, CURVETO
42 except:
43 # Try backported version used in sparx.
44 import canvas
45 from canvas import MOVETO_OPEN, MOVETO, LINETO, CURVETO
47 import l3lang.globals as G
48 from l3lang import reader, ast, view, utils
49 from l3lang.ast import empty_parent, Set, Marker, Int, Call, Symbol, \
50 Macro, aList, List, Return, Member
51 import l3gui.misc as misc
52 from l3gui.misc import \
53 print_, \
54 warn_, \
55 DisplayError, \
56 flush_events, \
57 _get_props, \
58 _set_props, \
59 path_rectangle_rounded, \
60 canvas_item_get_bounds_world, \
61 destroy_if_last, \
62 _safed, \
63 Selectable,\
64 slot_available
67 #* key mappings
68 kn = gtk.gdk.keyval_from_name
69 key_Left = kn("Left")
70 key_Right = kn("Right")
71 key_Up = kn("Up")
72 key_Down = kn("Down")
73 key_Return = kn("Return")
74 key_Escape = kn("Escape")
75 key_Windows_L = kn("Super_L")
76 key_Alt_L = kn("Alt_L")
79 #* control flow lines (element display)
80 class ContinueLine:
81 pass
83 class BreakLine:
84 pass
86 def __init__(self, w_, cnvs, child, parent):
87 x1, y1 = child.continue_start_pt()
88 xn, yn = parent.continue_stop_pt()
90 # World to parent coordinates.
91 _, _, _, _, sx, sy = parent.i2w_affine(tuple(range(0,6)))
92 x1 -= sx
93 y1 -= sy
94 xn -= sx
95 yn -= sy
97 # Line moves left and up.
98 points = [x1, y1,
99 xn, y1,
100 xn, yn,]
101 self._line = parent._root_group.add(
102 canvas.CanvasLine,
103 fill_color = "green",
104 points = points,
105 width_units = 0.3,
106 cap_style = gtk.gdk.CAP_ROUND,
107 join_style = gtk.gdk.JOIN_ROUND,
109 ContinueLine.__init__ = __init__
112 def __init__(self, w_, cnvs, child, parent):
113 x1, y1 = child.break_start_pt()
114 xn, yn = parent.break_stop_pt()
116 # World to parent coordinates.
117 _, _, _, _, sx, sy = parent.i2w_affine(tuple(range(0,6)))
118 x1 -= sx
119 y1 -= sy
120 xn -= sx
121 yn -= sy
123 # Line moves right and down.
124 points = [x1, y1,
125 xn, y1,
126 xn, yn,]
127 self._line = parent._root_group.add(
128 canvas.CanvasLine,
129 fill_color = "red",
130 points = points,
131 width_units = 0.3,
132 cap_style = gtk.gdk.CAP_ROUND,
133 join_style = gtk.gdk.JOIN_ROUND,
135 BreakLine.__init__ = __init__
137 def destroy(self):
138 _safed(self._line)
139 self._line = None
140 ContinueLine.destroy = destroy
141 BreakLine.destroy = destroy
143 def refresh(self):
144 pass
145 ContinueLine.refresh = refresh
146 BreakLine.refresh = refresh
149 #* Label ( element header display )
150 class Label:
151 # Display of a textual element, inteded to be part of another
152 # construct. Thus, Label has no event handling of its own, no
153 # menu, no independent hiding, etc.
155 # Appearance similar to l3Rawtext.
156 pass
158 def __init__(self, w_, cnvs, root_group, header_text,
159 x = 0, y = 0, font = None, fill_color = "white"):
160 if font is None:
161 font = w_.cp_.label_font
163 # Outline follows l3Rawtext.__init__, but the contents are too
164 # distinct to merge.
165 self._destroy_hook = [] # (func -> None) list
166 self._reparent_to_root_hook = [] # (func -> None) list
168 self.w_ = w_
169 self._canvas = cnvs
171 # l3 tree.
173 # Item cross-referencing.
175 # Text edit state.
177 # Focus redirection.
179 # Display Group.
180 self._root_group = root_group.add(canvas.CanvasGroup)
182 # Display self.
183 if 1:
185 # Text content / format / scale
187 self._ltext = misc.GtkObjProxy(self._root_group.add(
188 canvas.CanvasText,
189 anchor = gtk.ANCHOR_NORTH_WEST,
190 x = x,
191 y = y,
192 size = self.w_.cp_.font_size_labels * 1.0 * pango.SCALE,
193 scale = 1.0,
194 scale_set = True,
195 font = font,
196 size_set = True,
197 markup = header_text,
199 self._ltext.show()
200 flush_events()
201 x1, y1, x2, y2 = self._ltext.get_bounds() # world units
202 # Reference size.
203 self._ltext_size_1 = ((x2 - x1) * self._canvas._pixpu,
204 (y2 - y1) * self._canvas._pixpu)
206 # Current scale size.
207 self._ltext.set_property(
208 "size",
209 self.w_.cp_.font_size_labels * self._canvas._abs_zoom * pango.SCALE)
211 self._canvas.add_zoom_text(self)
213 # Text events.
215 # Text outline.
216 x1, y1, x2, y2 = self._ltext.get_bounds() # world units
217 pad = w_.cp_.textview.outline_padding / cnvs._pixpu
218 x1 -= pad
219 y1 -= pad
220 x2 += pad
221 y2 += pad
223 self._loutline = self._root_group.add(
224 canvas.CanvasRect,
225 x1 = x1, y1 = y1,
226 x2 = x2, y2 = y2,
227 fill_color = fill_color,
228 outline_color = fill_color,
229 width_pixels = w_.cp_.header_outline_width,
231 self._loutline.lower(1)
232 self._loutline.show()
234 # Outline events.
236 # Display subtrees.
238 # Highlight outline.
240 # Borders etc.
241 Label.__init__ = __init__
243 def connect(self, *args):
244 self._root_group.connect(*args)
245 Label.connect = connect
247 def get_property(self, prop):
248 return self._root_group.get_property(prop)
249 Label.get_property = get_property
251 def set_property(self, prop, val):
252 return self._root_group.set_property(prop, val)
253 Label.set_property = set_property
255 #* Image ( element header display )
256 class Image:
257 # Display of an image, intended to be part of another
258 # construct. Thus, Image has no event handling of its own, no
259 # menu, no independent hiding, etc.
260 # See also class Label.
261 pass
263 def __init__(self, w_, cnvs, root_group, pixbuf,
264 x = 0, y = 0, parent = None):
266 # Outline follows l3Rawtext.__init__, but the contents are too
267 # distinct to merge.
268 self._destroy_hook = [] # (func -> None) list
269 self._reparent_to_root_hook = [] # (func -> None) list
271 self._parent = parent
272 self.w_ = w_
273 self._canvas = cnvs
274 self._img_scale = w_.cp_.image_scale
276 # l3 tree.
278 # Item cross-referencing.
280 # Text edit state.
282 # Focus redirection.
284 # Display Group.
285 self._root_group = root_group.add(canvas.CanvasGroup)
287 # Display self.
288 if w_.cp_.image_natural_size:
290 # Image content at original pixel size
292 # Display without aspect distortion.
293 pixwid = pixbuf.get_width()
294 pixhei = pixbuf.get_height()
295 self._ltext = misc.GtkObjProxy(self._root_group.add(
296 canvas.CanvasPixbuf,
297 x = x,
298 y = y,
299 anchor = gtk.ANCHOR_NORTH_WEST,
300 height_set = True,
301 width_set = True,
302 height_in_pixels = True,
303 width_in_pixels = True,
304 width = pixwid * self._img_scale,
305 height = pixhei * self._img_scale,
306 pixbuf = pixbuf,
308 self._ltext.show()
309 flush_events()
310 x1, y1, x2, y2 = self._ltext.get_bounds() # world units
312 else:
314 # Image content / format / scale
316 # Display without aspect distortion.
317 pixwid = pixbuf.get_width()
318 pixhei = pixbuf.get_height()
319 woh = min(1.0 * pixwid / pixhei, 1)
320 how = min(1.0 * pixhei / pixwid, 1)
321 self._ltext = misc.GtkObjProxy(self._root_group.add(
322 canvas.CanvasPixbuf,
323 x = x,
324 y = y,
325 anchor = gtk.ANCHOR_NORTH_WEST,
326 height_set = True,
327 width_set = True,
328 width = w_.cp_.image_width * woh * self._img_scale,
329 height = w_.cp_.image_height * how * self._img_scale,
330 pixbuf = pixbuf,
332 self._ltext.show()
333 flush_events()
334 x1, y1, x2, y2 = self._ltext.get_bounds() # world units
336 # Display subtrees.
338 # Highlight outline.
340 # Borders etc.
341 Image.__init__ = __init__
343 # # def resize(self, which, value):
344 # # # UNTESTED
345 # # ''' Resize the pixmap to `value` world units.
346 # # `which` is "width" or "height"
347 # # '''
348 # # assert which in ['width', 'height'], "`which` must be width or height"
349 # # self._ltext.set_property(which, value)
350 # # # Move following items in parent.
351 # # if self._parent:
352 # # self._parent.new_size_for(self)
353 # # Image.resize = resize
355 def scale(self, ratio):
356 get = self._ltext.get_property
357 set = self._ltext.set_property
359 set('height', get('height') * ratio)
360 set('width', get('width') * ratio)
362 # Move following items in parent.
363 if self._parent:
364 self._parent.new_size_for(self)
366 self._img_scale *= ratio
367 Image.scale = scale
369 def set_default_size(self):
370 self.w_.cp_.image_scale = self._img_scale
371 Image.set_default_size = set_default_size
374 def connect(self, *args):
375 self._root_group.connect(*args)
376 Image.connect = connect
378 def get_property(self, prop):
379 return self._root_group.get_property(prop)
380 Image.get_property = get_property
382 def set_property(self, prop, val):
383 return self._root_group.set_property(prop, val)
384 Image.set_property = set_property
386 #* Widget ( element header display )
387 class Widget:
388 # Display of an embedded widget, intended to be part of another
389 # canvas construct. Thus, Widget has no event handling of its own, no
390 # menu, no independent hiding, etc.
391 # See also class Label.
392 pass
394 def __init__(self, w_, cnvs, root_group, widget,
395 x = 0, y = 0, parent = None):
397 # Outline follows l3Rawtext.__init__, but the contents are too
398 # distinct to merge.
399 self._destroy_hook = [] # (func -> None) list
400 self._reparent_to_root_hook = [] # (func -> None) list
402 self._parent = parent
403 self.w_ = w_
404 self._canvas = cnvs
406 # l3 tree.
408 # Item cross-referencing.
410 # Text edit state.
412 # Focus redirection.
414 # Display Group.
415 self._root_group = root_group.add(canvas.CanvasGroup)
417 # Display self.
419 # Widget content at original pixel size
421 # Display without aspect distortion.
422 self._ltext = misc.GtkObjProxy(self._root_group.add(
423 canvas.CanvasWidget,
424 x = x,
425 y = y,
426 anchor = gtk.ANCHOR_NORTH_WEST,
427 size_pixels = False,
428 width = 40,
429 height = 30,
430 widget = widget,
432 self._ltext.show()
433 widget.show()
434 flush_events()
435 x1, y1, x2, y2 = self._ltext.get_bounds() # world units
437 # Display subtrees.
439 # Highlight outline.
441 # Borders etc.
442 pass
443 Widget.__init__ = __init__
445 def scale(self, ratio):
446 get = self._ltext.get_property
447 set = self._ltext.set_property
449 set('height', get('height') * ratio)
450 set('width', get('width') * ratio)
452 # Move following items in parent.
453 if self._parent:
454 self._parent.new_size_for(self)
456 Widget.scale = scale
459 def connect(self, *args):
460 self._root_group.connect(*args)
461 Widget.connect = connect
463 def get_property(self, prop):
464 return self._root_group.get_property(prop)
465 Widget.get_property = get_property
467 def set_property(self, prop, val):
468 return self._root_group.set_property(prop, val)
469 Widget.set_property = set_property
471 #* uWidget ( element header display )
472 class uWidget:
473 # Base class for trivial "widgets" like buttons.
475 # Full widgets
476 # a) make the display sluggish
477 # b) cannot mix (overlap) with the canvas elements
478 # c) do not nest with canvas items
480 # uWidgets are always added to another construct (like l3List) for
481 # a) convenience
482 # b) display aestetics
484 # uWidgets handle events only to call appropriate functions of the
485 # main construct.
486 # uWidgets provide no independent hiding, etc.
488 # See also class Label.
489 pass
491 def __init__(self, w_, cnvs, root_group,
492 action = (lambda _: None)):
494 # Comment outline follows l3Rawtext.__init__.
496 self._destroy_hook = [] # (func -> None) list
497 self._reparent_to_root_hook = [] # (func -> None) list
499 self.w_ = w_
500 self._canvas = cnvs
501 self._action = action
503 # l3 tree.
505 # Item cross-referencing.
507 # Text edit state.
509 # Focus redirection.
511 # Display Group.
512 self._root_group = root_group.add(canvas.CanvasGroup)
513 grp_trans = self._root_group.add(canvas.CanvasGroup)
514 grp_scale = grp_trans.add(canvas.CanvasGroup)
515 grp_obj = grp_scale.add(canvas.CanvasGroup)
517 self._grp_trans = grp_trans # Use to move
518 self._grp_scale = grp_scale # Use to scale.
519 self._grp_obj = grp_obj # Draw objects into this.
520 # Use .move to reposition for scaling center.
522 # Display self.
523 self.draw()
525 # Display subtrees.
527 # Highlight outline.
529 # Borders etc.
530 uWidget.__init__ = __init__
532 def connect(self, *args):
533 self._root_group.connect(*args)
534 uWidget.connect = connect
536 def get_property(self, prop):
537 return self._root_group.get_property(prop)
538 uWidget.get_property = get_property
540 def set_property(self, prop, val):
541 return self._root_group.set_property(prop, val)
542 uWidget.set_property = set_property
544 def draw(self):
545 raise Exception("uWidget.draw must be overridden.")
546 # self.draw is to be overriden in subclass.
547 # The following code serves as template.
549 # Default uWidget content.
551 self._ltext = misc.GtkObjProxy(self._grp_obj.add(
552 canvas.CanvasText,
553 anchor = gtk.ANCHOR_NORTH_WEST,
554 x = 0,
555 y = 0,
556 size = self.w_.cp_.font_size * 1.0 * pango.SCALE,
557 scale = 1.0,
558 scale_set = True,
559 font = None,
560 size_set = True,
561 markup = "uW",
563 self._ltext.show()
565 # Reference size.
566 flush_events()
567 x1, y1, x2, y2 = self._ltext.get_bounds() # world units
568 self._ltext_size_1 = ((x2 - x1) * self._canvas._pixpu,
569 (y2 - y1) * self._canvas._pixpu)
571 # Current scale size.
572 self._ltext.set_property(
573 "size",
574 self.w_.cp_.font_size * self._canvas._abs_zoom * pango.SCALE)
576 # Register for zooming.
577 # # self._canvas.add_zoom_text(self)
579 # Content events.
580 self.connect("event", lambda *a: self.button_event(*a))
581 uWidget.draw = draw
584 def button_event(self, item, event):
585 if event.type == gtk.gdk.BUTTON_RELEASE:
586 if event.button == 1:
587 # # print 'uWidget button press. Override method.'
588 self._action(event)
589 return True
590 uWidget.button_event = button_event
594 #** uBlank
595 class uBlank(uWidget):
596 '''Display a 1x1 pixel. Use to experiment with layout, without
597 removing code.
599 pass
601 #** uVisibleSymbol ( element header display )
602 class uVisibleSymbol(uWidget):
603 pass
606 #** uPartiallyVisible ( element header display )
607 class uPartiallyVisible(uWidget):
608 pass
611 #** uInvisibleSymbol ( element header display )
612 class uInvisibleSymbol(uWidget):
613 pass
616 #** common drawing
617 def _draw_common_triangle(self):
619 # Triangle right.
622 # This kind of glyph design is silly; using a predefined character
623 # is far preferable.
624 cp = self.w_.cp_
625 # width 1, height 2
626 points = [0 , 0,
627 1 , 1,
628 0 , 2,
629 0 , 0,
632 # Draw triangle.
633 self._ltext = misc.GtkObjProxy(self._grp_obj.add(
634 canvas.CanvasPolygon,
635 fill_color = "black",
636 points = points,
637 width_units = 0.2,
638 cap_style = gtk.gdk.CAP_ROUND,
639 join_style = gtk.gdk.JOIN_ROUND,
642 # Draw background / padding. This will also rotate / scale.
643 pad = self.w_.cp_.symbol_padding
645 # pad = 0.0
646 # self._loutline = misc.GtkObjProxy(self._grp_obj.add(
647 # canvas.CanvasRect,
648 # x1 = - pad,
649 # y1 = - pad,
650 # x2 = 1 + pad,
651 # y2 = 2 + pad,
652 # fill_color = "white",
653 # outline_color = "white",
654 # width_pixels = self.w_.cp_.outline_width_normal,
655 # ))
656 # self._loutline.lower(1)
658 # Reposition for scaling around center.
659 ll, tt, rr, bb = canvas_item_get_bounds_world(self._grp_obj)
660 self._grp_obj.move( -(ll + rr)/2, -(tt + bb)/2 )
662 # Scale.
663 sx = sy = cp.symbol_width / (1.0 - 0.0) # width from `points`
664 self._grp_scale.affine_relative( (sx, 0.0, 0.0, sy, 0.0, 0.0) )
666 # Now add background.
667 ll, tt, rr, bb = canvas_item_get_bounds_world(self._grp_scale)
668 self._loutline = misc.GtkObjProxy(self._grp_trans.add(
669 canvas.CanvasRect,
670 x1 = ll - pad,
671 y1 = tt - pad,
672 x2 = rr + pad,
673 y2 = bb + pad,
674 fill_color = "white",
675 outline_color = "white",
676 width_pixels = self.w_.cp_.outline_width_normal,
678 self._loutline.lower(1)
680 self._ltext.show()
682 # Content events.
683 self.connect("event", lambda *a: self.button_event(*a))
684 uWidget._draw_common_triangle = _draw_common_triangle
686 def draw(self):
687 self._draw_common_triangle()
688 uInvisibleSymbol.draw = draw
690 def draw(self):
692 # Triangle down & right.
694 self._draw_common_triangle()
696 # Rotate 45 deg.
697 self._grp_scale.affine_relative( misc.affine_rotate(45) )
698 uPartiallyVisible.draw = draw
700 def draw(self):
702 # Triangle down.
704 self._draw_common_triangle()
706 # Rotate 90 deg.
707 self._grp_scale.affine_relative( misc.affine_rotate(90) )
708 uVisibleSymbol.draw = draw
711 def draw(self):
712 # Draw triangle.
713 self._ltext = misc.GtkObjProxy(self._grp_obj.add(
714 canvas.CanvasRect,
715 x1 = 0,
716 y1 = 0,
717 x2 = 0.1,
718 y2 = 0.1,
719 fill_color = "white",
720 outline_color = "white",
721 width_pixels = self.w_.cp_.outline_width_normal,
724 self._ltext.show()
725 # Content events.
726 ## self.connect("event", lambda *a: self.button_event(*a))
727 uBlank.draw = draw
731 #* l3Base
732 class l3Base(Selectable):
733 pass
735 def __init__(self):
736 self._parent = None
737 self._destroy_hook = [] # (func -> None) list
738 self._reparent_to_root_hook = [] # (func -> None) list
739 self._vis_indic = None # visibility status indicator
741 # Selection menu.
742 self._selection_popup_cb = {} # menu item -> connection args
743 self._selection_popup = gtk.Menu() # dummy
744 self._selection_popup_list = [
745 [ gtk.MenuItem("print values in context"),
746 ("activate", lambda *args: self.selection_values_in_context(args) )],
748 [ gtk.MenuItem("evaluate locally"),
749 ("activate", lambda *args: self.selection_eval_local(args))],
751 [ gtk.MenuItem("evaluate locally, use console"),
752 ("activate", lambda *args: self.eval_local_console(args))],
754 [ gtk.MenuItem("item screenshot to /tmp/foo.png"),
755 ("activate", lambda *args: self.item_screenshot("/tmp/foo.png"))],
758 l3Base.__init__ = __init__
761 #** Screenshot of selected item to file
762 def item_screenshot(self, filename):
763 print "capturing image from RIGHT canvas."
764 # todo: get item's canvas
765 # todo: remove popup menu before capture.
766 view = self.w_.canvas.view
767 selection = self.w_.selector.get_selection()
768 (sl, st, sr, sb) = selection.get_bounds()
770 # Get selection corners.
771 # These are the canvas window coordinates.
772 (wl, wt) = map(int, view.world_to_window(sl, st))
773 (wr, wb) = map(int, view.world_to_window(sr, sb))
775 # Compute selection dimensions.
776 width = wr - wl
777 height = wb - wt
779 # Prepare pixbuf.
780 pixbuf = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB,
781 False, # has_alpha
782 8, # bits_per_sample
783 width,
784 height)
786 # get_from_drawable(src, cmap, src_x, src_y, dest_x, dest_y, width, height)
787 status = pixbuf.get_from_drawable(
788 view.window,
789 gtk.gdk.colormap_get_system(),
790 wl, wt,
791 0, 0,
792 width, height
795 if status == None:
796 print "capture failed"
798 pixbuf.save("/tmp/foo.png", "png")
799 l3Base.item_screenshot = item_screenshot
803 #** value display from selection
804 def selection_values_in_context(self, args):
805 # args: (menuitem)
806 st = self.w_.state_.storage
807 tw = ast.TreeWork(st)
809 l3nd = self.w_.selector.get_selection_list()
811 # Self is data source.
812 cctxt = tw.get_call_ctxt(self.getid())
814 # Form the call graph pruning list.
815 l3nd.remove(self)
817 # Show pruning list.
818 print "------------------------------------------------------------------"
819 print "Showing only data created through:"
820 for nd in l3nd:
821 print nd.l3tree().source_string()
823 # Get reduced calling context.
824 pruned = tw.prune_cctxt(
825 cctxt,
826 map(l3Base.getid, l3nd)) # Remaining selection for pruning.
828 # Display values
829 def id_v_ts(leaves):
830 print "clone id, clone timestamp, value"
831 print "--------------------------------------------"
832 for (lid, val) in leaves:
833 print "%s, %s, %s" % (lid, st.ie_.get_timestamp(lid), val)
834 id_v_ts(tw.cctxt_leaves(pruned))
836 pass
837 l3Base.selection_values_in_context = selection_values_in_context
841 #** ast related
842 def contains_recursive(self, other):
843 oid = other._l3tree._id
844 for node in self._l3tree.top_down():
845 # Note: aList([]) == aList([]) is always True
846 if node._id == oid:
847 return True
848 return False
849 l3Base.contains_recursive = contains_recursive
851 def l3tree(self):
852 return self._l3tree
853 l3Base.l3tree = l3tree
855 def getid(self):
856 return self._l3tree._id
857 l3Base.getid = getid
861 #** canvas display functions
862 def dump_bbox(self):
863 print "Bounds:", self.get_bounds_world()
864 l3Base.dump_bbox = dump_bbox
866 def zoom(self, factor):
867 return (factor, factor)
868 l3Base.zoom = zoom
870 def i2w_affine(self, tuple_):
871 return self._root_group.i2w_affine(tuple_)
872 l3Base.i2w_affine = i2w_affine
874 def get_anchor(self):
875 # Use upper left.
876 x, y, u, v = self.get_bounds()
877 return x, y
878 l3Base.get_anchor = get_anchor
881 #** structural
882 def reparent(self, newp):
883 assert isinstance(newp, (l3aList, l3Base))
885 # Remove from current parent.
886 if self._parent:
887 self._parent.detach(self)
888 self._parent = None
890 # Attach to new parent.
891 self._parent = newp
892 self._root_group.reparent(newp._root_group)
893 if self._outline:
894 # Avoid repositioning mess.
895 self.plain_view()
896 self.highlight()
897 l3Base.reparent = reparent
899 def reparent_to_root(self):
900 # Graphics.
901 self._run_reparent_to_root_hook()
903 _, _, _, _, x1, y1 = self.i2w_affine(tuple(range(0,6)))
904 self._root_group.reparent(self._canvas.root())
905 _, _, _, _, nx1, ny1 = self.i2w_affine(tuple(range(0,6)))
906 self._root_group.move(x1 - nx1, y1 - ny1)
908 # State.
909 self._parent = None
910 l3Base.reparent_to_root = reparent_to_root
913 #** Selection handling
914 def plain_view(self):
915 if not self._outline:
916 return
917 destroy_if_last(self._outline)
918 self._outline = None
919 l3Base.plain_view = plain_view
921 def highlight(self):
922 if self._outline:
923 return
924 flush_events()
926 # Draw outline.
927 x1, y1, x2, y2 = self.get_bounds_world()
928 pad = self.w_.cp_.highlight_padding
929 x1 -= pad
930 y1 -= pad
931 x2 += pad
932 y2 += pad
934 ### self._outline = self._root_group.get_property("parent").add(
935 self._outline = (self._canvas.root().add(
936 canvas.CanvasLine,
937 fill_color = "orange",
938 points = [x1, y1,
939 x2, y1,
940 x2, y2,
941 x1, y2,
942 x1, y1,
944 width_units = self.w_.cp_.highlight_thickness,
945 line_style = gtk.gdk.SOLID,
946 cap_style = gtk.gdk.CAP_ROUND,
947 join_style = gtk.gdk.JOIN_ROUND,
948 smooth = True,
949 spline_steps = 12,
951 ### self._loutline.set_property("fill-color", 'beige')
952 ### self._ltext.set_property("fill-color", 'beige')
954 # Event handling.
955 self._outline.connect("event", lambda *a: self.selection_popup_event(*a))
956 self._outline.connect("event", lambda *a: self.selection_magnify_event(*a))
957 pass
958 l3Base.highlight = highlight
961 def selection_popup_event(self, widget, event):
962 if event.type == gtk.gdk.BUTTON_PRESS:
963 if event.button == 3:
964 self.selection_init_popup()
965 self._selection_popup.popup(None, None, None,
966 event.button, event.time)
967 return True
968 return False
969 l3Base.selection_popup_event = selection_popup_event
972 def selection_init_popup(self):
973 # Prepare a popup menu including the original menu items (in
974 # _selection_popup_list) plus any inserted via selection_prepend().
976 new_menu = gtk.Menu()
977 for item, conargs in self._selection_popup_list:
979 # To insert menu items in new_menu, re-use existing widgets
980 # after removing them from their old menu.
981 if item.get_parent() == self._selection_popup:
982 if self._selection_popup_cb.has_key(item):
983 # Remove old event handler.
984 item.disconnect(self._selection_popup_cb[item])
985 self._selection_popup.remove(item)
986 new_menu.append( item )
988 # Connect new event handler.
989 if conargs != None:
990 # Callback arguments are (event name, handler)
991 args = tuple(conargs)
992 self._selection_popup_cb[item] = item.connect(*args)
993 item.show()
995 if self._selection_popup:
996 self._selection_popup.destroy()
998 self._selection_popup = new_menu
999 l3Base.selection_init_popup = selection_init_popup
1001 def selection_magnify_event(self, item, event):
1002 if event.type == gtk.gdk.ENTER_NOTIFY:
1003 if self.w_.fluid_ref(trace_gui_events = False):
1004 print "selection_magnify_event"
1005 item.set_property("width_units",
1006 self.w_.cp_.highlight_thickness *
1007 self.w_.cp_.hover_magnification)
1008 return False
1010 elif event.type == gtk.gdk.LEAVE_NOTIFY:
1011 if self.w_.fluid_ref(trace_gui_events = False):
1012 print "selection_magnify_event"
1013 item.set_property("width_units",
1014 self.w_.cp_.highlight_thickness)
1015 return False
1016 l3Base.selection_magnify_event = selection_magnify_event
1018 #* l3Nested editing
1019 class l3Nested(l3Base):
1020 pass
1022 def __init__(self, w_, cnvs, l3tree, rentab):
1023 l3Base.__init__(self)
1025 # Display.
1026 self._root_group = cnvs.root().add(canvas.CanvasGroup)
1027 self._pup = CommonPopup(self)
1028 self.explicit_title = None
1030 # Rendering options.
1031 self._render_mode = rentab.get_state(l3tree)
1033 # Headerless state.
1034 self._header = None
1036 # Undecorated state.
1037 self._deco = None
1039 # Events
1040 self._root_group.connect("event", lambda *a: self.drag_event(*a))
1042 # movement state
1043 self.remember_x = None
1044 self.remember_y = None
1046 # Canvas registration. Must preceed children's construction.
1047 cnvs.register_l3tree_pic(l3tree._id, self)
1048 l3Nested.__init__ = __init__
1051 #** special display
1052 def zoom(self, factor):
1053 return l3Base.zoom(self, factor)
1054 l3Nested.zoom = zoom
1058 #* Placeholder
1059 class Placeholder(l3Nested):
1060 pass
1062 def __init__(self, w_, cnvs, tree_id, rentab):
1063 # l3 tree.
1064 l3tree = w_.state_.storage.load(tree_id)
1065 l3Nested.__init__(self, w_, cnvs, l3tree, rentab)
1067 # Bindings to keep. ### move to nested.__init__
1068 self.w_ = w_
1069 self._canvas = cnvs
1070 self._marker_points = {} # marker -> point list
1071 self._l3tree = l3tree
1073 # Display elements.
1074 # # self._blank = self.draw_blank()
1075 self._blank = uInvisibleSymbol(w_, cnvs, self._root_group,
1076 action = lambda _: self.show_subtree())
1078 # Highlighting.
1079 self._outline = None
1081 # Item cross-referencing.
1082 self._marker = None
1083 self._container = None
1085 # Borders etc.
1086 # # l3Nested.init_deco(self)
1087 self.init_header(rentab)
1088 self.init_deco(rentab)
1090 # Event handling.
1091 ## self._blank.connect("event", lambda *a: self.expand_event(*a))
1092 Placeholder.__init__ = __init__
1094 #* TextEdit
1095 class TextEdit:
1096 pass
1098 def __init__(self, w_, l3parent, text, tag_table, cnvs, refpos = None):
1100 Interface of `l3parent` must include:
1101 ed_new_text
1102 ed_restore_text
1103 finish_edit
1104 rebuild_tree
1106 get_bounds_world (if `refpos` is not given)
1107 l3parent._canvas
1110 Typical calling sequence from `other`:
1112 other.start_edit -> TextEdit() (te)
1114 te handles its own events; the two possible exits are:
1116 te.text_aborted -> other.ed_restore_text
1117 -> other.finish_edit
1119 te.text_finished -> other.rebuild_tree(new_text)
1120 -> other.ed_new_text(new_text)
1121 -> other.finish_edit()
1123 self.w_ = w_
1124 self._l3parent = l3parent
1125 self._text_orig = text
1126 self._canvas = cnvs
1127 if refpos == None:
1128 l1, t1, _, _ = l3parent.get_bounds_world()
1129 else:
1130 l1, t1 = refpos
1132 # Edit Buffer.
1133 self._edit_buff = buff = gtk.TextBuffer(table = tag_table)
1134 buff.set_text(text)
1135 buff.apply_tag(l3parent._canvas._font_size_tag, *buff.get_bounds() )
1137 buff.connect_after("insert-text",
1138 lambda *a: self.re_tag_inserted_text(*a))
1140 # Edit View.
1141 self._view = gtk.TextView(buffer = buff)
1142 self._view.connect("key-press-event", lambda *a: self.on_keypress(*a))
1144 # Scrollbars.
1145 self._view_s = gtk.ScrolledWindow()
1146 self._view_s.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
1147 self._view_s.add(self._view)
1149 # Ok button.
1150 self._ok = gtk.Button(label = "Ok", stock = gtk.STOCK_OK)
1151 self._ok.connect("clicked", lambda *a: self.text_finished())
1153 # Cancel button.
1154 self._cancel = gtk.Button(label = "Cancel", stock = gtk.STOCK_CANCEL)
1155 self._cancel.connect("clicked", lambda *a: self.text_aborted())
1157 # Assembly.
1158 # Top row.
1159 self._ass_r1 = gtk.HBox()
1160 self._ass_r1.pack_start(self._view_s, expand = True)
1161 # Bottom row.
1162 self._ass_r2 = gtk.HBox()
1163 self._ass_r2.pack_start(self._ok, expand = True)
1164 self._ass_r2.pack_start(self._cancel, expand = True)
1165 # Stack them
1166 self._ass_col = gtk.VBox()
1167 self._ass_col.pack_start(self._ass_r1, expand = True)
1168 self._ass_col.pack_start(self._ass_r2, expand = False)
1170 # Pop up edit window.
1171 # Positioning is controlled by the window manager.
1172 self._window = gtk.Window()
1173 self._window.set_property("title", "edit expression")
1174 self._window.set_property("window-position", gtk.WIN_POS_MOUSE)
1175 self._window.connect("delete-event", lambda *a: self.text_aborted())
1176 self._window.set_default_size(350, 200)
1177 self._window.set_border_width(0)
1178 self._window.add(self._ass_col)
1179 main_win = cnvs.get_parent()
1180 while not isinstance(main_win, gtk.Window):
1181 main_win = main_win.get_parent()
1182 self._window.set_transient_for(main_win)
1183 self._window.show_all()
1184 TextEdit.__init__ = __init__
1186 def on_keypress(self, widget, event):
1187 # Escape.
1188 if event.keyval == key_Escape:
1189 self.text_aborted()
1190 return True
1192 # Ctrl-Return
1193 if event.state & gtk.gdk.CONTROL_MASK:
1194 if event.keyval == key_Return:
1195 self.text_finished()
1196 return True
1198 return False
1199 TextEdit.on_keypress = on_keypress
1201 def text_aborted(self):
1202 # Paired with text_start (focus event?)
1203 if self.w_.fluid_ref(trace_gui_events = False):
1204 print "\ntext_aborted"
1206 # Remove editor before adding new text.
1207 del self._edit_buff
1208 self._window.destroy()
1210 # Update text.
1211 self._l3parent.ed_restore_text()
1213 # Finish.
1214 self._l3parent.finish_edit()
1215 TextEdit.text_aborted = text_aborted
1217 def text_finished(self):
1218 # Paired with text_start (focus event?)
1219 if self.w_.fluid_ref(trace_gui_events = False):
1220 print "\ntext_finished"
1222 # Get text.
1223 buf = self._edit_buff
1224 new_text = buf.get_text(*buf.get_bounds())
1226 # Remove editor before adding new text.
1227 del self._edit_buff
1228 self._window.destroy()
1230 # Update text.
1231 self._l3parent.rebuild_tree(new_text)
1232 self._l3parent.ed_new_text(new_text)
1234 # Finish.
1235 self._l3parent.finish_edit()
1236 TextEdit.text_finished = text_finished
1238 def re_tag_inserted_text(self, buffer, iter, text, length):
1239 # Keep new text at proper size etc.
1240 iter_to = iter.copy()
1241 if not iter.backward_chars(length):
1242 return False
1244 if iter.has_tag(self._l3parent._canvas._font_size_tag):
1245 return False
1247 buffer.apply_tag(self._l3parent._canvas._font_size_tag, iter, iter_to )
1248 return False
1249 TextEdit.re_tag_inserted_text = re_tag_inserted_text
1251 #* ExtProcConfirm
1252 class ExtProcConfirm:
1254 After running an external process (which may not return a useful
1255 status), provide
1256 Success
1257 Failed
1258 options, and a
1259 text area
1260 to attach any desired log information.
1262 pass
1264 def __init__(self, w_, cnvs, answer_store, text = ""):
1265 # See also TextEdit.
1267 # answer_store::
1268 # an empty list, to be populated with the user's response,
1269 # in the form [(status, log)]
1271 self.w_ = w_
1272 self._answer_store = answer_store
1273 self._text_orig = text
1274 self._canvas = cnvs
1276 # Edit Buffer.
1277 self._edit_buff = buff = gtk.TextBuffer(table = cnvs._common_tag_table)
1278 buff.set_text(text)
1279 buff.apply_tag(self._canvas._font_size_tag, *buff.get_bounds() )
1281 buff.connect_after("insert-text",
1282 lambda *a: self.re_tag_inserted_text(*a))
1284 # Edit View.
1285 self._view = gtk.TextView(buffer = buff)
1286 self._view.connect("key-press-event", lambda *a: self.on_keypress(*a))
1287 self._view.set_size_request(w_.cp_.width,
1288 w_.cp_.height) # Minimum viewport size.
1290 # Scrollbars.
1291 self._view_s = gtk.ScrolledWindow()
1292 self._view_s.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
1293 self._view_s.add(self._view)
1295 # Ok button.
1296 self._ok = gtk.Button(stock = gtk.STOCK_YES)
1297 self._ok.connect("clicked", lambda *a: self.cmd_success())
1299 # Failed button.
1300 self._failed = gtk.Button(stock = gtk.STOCK_NO)
1301 self._failed.connect("clicked", lambda *a: self.cmd_failed())
1303 # Assembly.
1304 # Top row.
1305 self._ass_r1 = gtk.HBox()
1306 self._ass_r1.pack_start(self._view_s, expand = True)
1307 # Bottom row.
1308 self._ass_r2 = gtk.HBox()
1309 self._ass_r2.pack_start(self._ok, expand = True)
1310 self._ass_r2.pack_start(self._failed, expand = True)
1311 # Stack them
1312 self._ass_col = gtk.VBox()
1313 self._ass_col.pack_start(self._ass_r1, expand = True)
1314 self._ass_col.pack_start(self._ass_r2, expand = False)
1316 # Pop up edit window.
1317 # Positioning is controlled by the window manager.
1318 self._window = gtk.Window()
1319 self._window.set_property("title", "User query")
1320 self._window.set_property("window-position", gtk.WIN_POS_MOUSE)
1321 self._window.connect("delete-event", lambda *a: self.cmd_failed())
1322 self._window.set_border_width(0)
1323 self._window.add(self._ass_col)
1324 main_win = cnvs.get_parent()
1325 while not isinstance(main_win, gtk.Window):
1326 main_win = main_win.get_parent()
1327 self._window.set_transient_for(main_win)
1328 self._window.show_all()
1329 ExtProcConfirm.__init__ = __init__
1331 def on_keypress(self, widget, event):
1332 # Escape.
1333 if event.keyval == key_Escape:
1334 self.cmd_failed()
1335 return True
1337 # Ctrl-Return
1338 if event.state & gtk.gdk.CONTROL_MASK:
1339 if event.keyval == key_Return:
1340 self.cmd_success()
1341 return True
1343 return False
1344 ExtProcConfirm.on_keypress = on_keypress
1346 def cmd_failed(self):
1347 # Remove editor before adding new text.
1348 del self._edit_buff
1349 self._window.destroy()
1351 # Finish.
1352 self._answer_store.append( (1, "") ) # (status, log)
1353 ExtProcConfirm.cmd_failed = cmd_failed
1355 def cmd_success(self):
1356 # Get text.
1357 buf = self._edit_buff
1358 new_text = buf.get_text(*buf.get_bounds())
1360 # Remove editor before adding new text.
1361 del self._edit_buff
1362 self._window.destroy()
1364 # Finish.
1365 if new_text == self._text_orig:
1366 self._answer_store.append( (0, "success") ) # (status, log)
1367 else:
1368 self._answer_store.append( (0, new_text) ) # (status, log)
1369 ExtProcConfirm.cmd_success = cmd_success
1371 def re_tag_inserted_text(self, buffer, iter, text, length):
1372 # Keep new text at proper size etc.
1373 iter_to = iter.copy()
1374 if not iter.backward_chars(length):
1375 return False
1377 if iter.has_tag(self._canvas._font_size_tag):
1378 return False
1380 buffer.apply_tag(self._canvas._font_size_tag, iter, iter_to )
1381 return False
1382 ExtProcConfirm.re_tag_inserted_text = re_tag_inserted_text
1384 #* CommonPopup
1385 class CommonPopup:
1386 # Add-on for classes supporting the standard popups.
1388 def verify_interface(self, prim_node):
1389 # An interface base class check will not verify actual presence of
1390 # needed members. This function does.
1392 # However, constructors may not construct these in time for
1393 # this check...
1394 assert prim_node._root_group
1395 assert prim_node.destroy
1396 assert prim_node._l3tree
1397 assert prim_node.w_
1398 assert prim_node.mark_as
1401 def destroy_selected(self, node):
1402 sel = node.w_.selector
1403 items = sel.get_selection_list()
1404 sel.unselect()
1405 [self.start_destroy(itm) for itm in items]
1406 CommonPopup.destroy_selected = destroy_selected
1409 def start_destroy(self, node):
1410 w_ = node.w_
1412 # Hide the tree.
1413 node._root_group.hide()
1415 # Delete in steps.
1416 def body():
1417 node.destroy()
1418 node._l3tree.delete(w_.state_.storage)
1420 w_.with_fluids(body,
1421 detach_l3_tree = False,
1422 insert_l3_tree = False,
1424 pass
1425 CommonPopup.start_destroy = start_destroy
1427 def _insert_list(self, shape = 'list'):
1428 # Setup.
1429 from l3gui.l3canvas import RenderTable
1430 storage = self._prim_node.w_.state_.storage
1431 cnvs = self._prim_node._canvas
1433 # Get the values in the current calling context (path must pass
1434 # multiple selections), ignore None values.
1435 if 1:
1436 # From selection_values_in_context
1437 tw = ast.TreeWork(storage)
1438 l3nd = self._prim_node.w_.selector.get_selection_list()
1440 # Self._prim_node is data source.
1441 cctxt = tw.get_call_ctxt(self._prim_node.getid())
1443 # Form the call graph pruning list.
1444 try: l3nd.remove(self._prim_node)
1445 except ValueError: pass
1447 # Get reduced calling context.
1448 pruned = tw.prune_cctxt(
1449 cctxt,
1450 map(l3Base.getid, l3nd))
1452 # Warn if context is not unique.
1453 # TODO.
1455 # Available info: clone id, clone timestamp, value
1456 # (cid, storage.ie_.get_timestamp(cid), val)
1457 vals = [ val
1458 for (cid, val) in tw.cctxt_leaves(pruned)
1459 if val != None]
1461 # Use values of top-level node if nothing remains.
1462 if len(vals) == 0:
1463 vals = [val
1464 for (id_, val) in self._prim_node.get_values_list()
1465 if val != None]
1467 # For a list of length M, try a sqrt(M) * sqrt(M) layout.
1468 M = len(vals)
1469 if (M > 3) and shape == 'square':
1470 cols = int(floor(sqrt(M)))
1471 vals = [tuple(vals[ii : ii + cols]) for ii in range(0, M, cols)]
1473 # Convert to l3 ast
1474 val, val_id = \
1475 ast.val2ast( vals,
1476 file_contents =
1477 self._prim_node.w_.cp_.display_file_content)\
1478 .setup(empty_parent(),
1479 ast.Env('dummy_env', None, None, storage),
1480 storage)
1481 val.set_outl_edges(self._prim_node.w_, None)
1483 # Special emphasis for this non-permanent value list.
1484 val.set_emphasis("valuelist")
1486 # Add label with source string, or id if not available.
1487 ptree = self._prim_node._l3tree
1488 desc = ptree.source_substring()
1489 if desc == 'no_source':
1490 val.set_label('Value(s) for %d' % self._prim_node._l3tree._id )
1491 else:
1492 val.set_label('Value(s) for %s' % desc )
1494 # Retain source node id for later.
1495 val.setthe(value_source_tree_id =
1496 [v.l3tree()._id for v in l3nd + [self._prim_node]])
1498 # Display the structure.
1499 pic = cnvs.add_l3tree(val, RenderTable())
1501 # Position to the right of expression.
1502 lp, tp, rp, bp = pic.get_bounds_world()
1503 lo, to, ro, bo = self._prim_node.get_bounds_world()
1504 pic.move(ro - lp, to - tp)
1505 CommonPopup._insert_list = _insert_list
1508 def __init__(self, prim_node):
1509 self._prim_node = prim_node
1510 self._widget_cb = {}
1511 self._widget = gtk.Menu() # dummy
1513 self._popup_list = [
1515 [ gtk.MenuItem("add comment"),
1516 ("activate", lambda *args: prim_node.deco_add_comment() )],
1518 [ gtk.MenuItem("copy to clipboard (as raw text)"),
1519 ("activate", lambda *args: prim_node.w_.selector.copy_selection())],
1521 [ gtk.MenuItem("delete"),
1522 ("activate", lambda *args: self.start_destroy(prim_node))],
1524 [ gtk.MenuItem("delete selected"),
1525 ("activate", lambda *args: self.destroy_selected(prim_node))],
1527 [ gtk.MenuItem("dump code"),
1528 ("activate",
1529 lambda *args: view.print_info(prim_node.w_.state_.storage,
1530 prim_node._l3tree))],
1531 [ gtk.MenuItem("dump values"),
1532 ("activate",
1533 lambda *args: prim_node.dump_values() ) ],
1535 [ gtk.MenuItem("export values as string"),
1536 ("activate",
1537 lambda *args: prim_node.string_export() ) ],
1539 # # [ gtk.MenuItem("dump [values list]"),
1540 # # ("activate",
1541 # # lambda *args: pprint(prim_node.get_values_list()))],
1543 # # [ gtk.MenuItem("dump (values list) as l3 AST"),
1544 # # ("activate",
1545 # # lambda *args: pprint(ast.val2ast(prim_node.get_values_list())) )],
1547 [ gtk.MenuItem("insert values as l3 list"),
1548 ("activate", lambda *args: self._insert_list() )],
1550 [ gtk.MenuItem("insert values as l3 list list "),
1551 ("activate", lambda *args: self._insert_list(shape = 'square') )],
1553 [ gtk.MenuItem("evaluate locally"),
1554 ("activate", lambda *args: prim_node.eval_local(args))],
1556 [ gtk.MenuItem("evaluate locally, use console"),
1557 ("activate", lambda *args: prim_node.eval_local_console(args))],
1559 [ gtk.MenuItem("exit"),
1560 ("activate", lambda *args : gtk.main_quit() )],
1562 [ gtk.MenuItem("hide"),
1563 ("activate", lambda *args: prim_node.hide_subtree() )],
1565 [ gtk.MenuItem("select value"),
1566 ("activate",
1567 lambda *args: DataSelPopup(prim_node.w_, prim_node._l3tree))],
1570 # Pop up when l3 node is pressed.
1571 prim_node._root_group.connect("event", lambda *a: self.popup_event(*a))
1572 CommonPopup.__init__ = __init__
1575 #** pop-up base menu
1576 def init_popup(self):
1577 # Start new Menu.
1578 new_menu = gtk.Menu()
1580 # Tear-off item.
1581 mi = gtk.TearoffMenuItem()
1582 new_menu.append(mi)
1583 mi.show()
1585 # Run pre_popup_hook, if any.
1586 getattr(self._prim_node, "pre_popup_hook", lambda : None)()
1588 # Populate the Menu.
1589 for mi, conargs in self._popup_list:
1590 # Insert in new menu, re-using existing widgets.
1591 if mi.get_parent() == self._widget:
1592 if self._widget_cb.has_key(mi):
1593 mi.disconnect(self._widget_cb[mi]) # remove old event handler
1594 self._widget.remove(mi)
1595 new_menu.append( mi )
1596 # Connect new event handler.
1597 if conargs != None:
1598 self._widget_cb[mi] = mi.connect(*conargs)
1599 mi.show()
1601 if self._widget:
1602 self._widget.destroy()
1604 self._widget = new_menu
1605 CommonPopup.init_popup = init_popup
1607 def popup_event(self, widget, event):
1608 # Pop up on button-3 press.
1609 if event.type == gtk.gdk.BUTTON_PRESS:
1610 if event.button == 3:
1611 self.init_popup()
1612 self._widget.popup(None, None, None,
1613 event.button, event.time)
1614 return True
1615 return False
1616 CommonPopup.popup_event = popup_event
1619 #** menu additions
1620 def prepend(self, item_init):
1621 # Menu additions can be made at any time.
1623 # item_init has form
1624 # [ gtk.MenuItem("dump code"),
1625 # ("activate",
1626 # lambda *args: view.print_info(prim_node.w_.state_.storage,
1627 # prim_node._l3tree))]
1628 # or
1629 # [gtk.SeparatorMenuItem(), None]
1631 self._popup_list.insert(0, item_init)
1632 CommonPopup.prepend = prepend
1634 #* DataSelPopup
1635 class DataSelPopup:
1636 pass
1639 def __init__(self, w_, l3tree):
1640 self.w_ = w_
1641 self._l3tree = l3tree
1642 self._widget_cb = {}
1643 self._widget = gtk.Menu() # dummy
1645 self.init_popup()
1647 self._widget.popup(None, None, None, 1, 1)
1648 ## event.button, event.time)
1649 DataSelPopup.__init__ = __init__
1652 def traverse_values_list(self):
1653 # Produce a (menuitem, function) list for datum selection.
1654 st = self.w_.state_.storage
1655 c_id = self._l3tree._id
1657 # Dynamic id(s).
1658 clone_l = st.get_attribute(c_id, "interp_clone")
1659 if clone_l:
1660 G.logger.info("%d has %d clones.\nValues:" % (c_id, len(clone_l)))
1661 # todo: this should recurse?
1662 for id in clone_l:
1663 # todo: use loop (var, value) pairs, not just id
1664 yield [ gtk.MenuItem("clone %d" % id),
1665 ("activate",
1666 lambda *args: DataSelPopup(self.w_, st.load(id)))]
1667 else:
1668 # Toplevel or final id.
1669 yield [ gtk.MenuItem("value operations"),
1670 ("activate",
1671 # todo: ValueOperPopup
1672 lambda *args: DataSelPopup(self.w_, self._l3tree))]
1673 DataSelPopup.traverse_values_list = traverse_values_list
1676 def init_popup(self):
1677 # Start new Menu.
1678 new_menu = gtk.Menu()
1680 # Tear-off item.
1681 mi = gtk.TearoffMenuItem()
1682 new_menu.append(mi)
1683 mi.show()
1685 # Populate the Menu.
1686 for mi, conargs in self.traverse_values_list():
1687 # Insert in new menu, re-using existing widgets.
1688 if mi.get_parent() == self._widget:
1689 if self._widget_cb.has_key(mi):
1690 mi.disconnect(self._widget_cb[mi]) # remove old event handler
1691 self._widget.remove(mi)
1692 new_menu.append( mi )
1693 # Connect new event handler.
1694 if conargs != None:
1695 self._widget_cb[mi] = mi.connect(*conargs)
1696 mi.show()
1697 # Remove duplicate popups.
1698 if self._widget:
1699 self._widget.destroy()
1701 self._widget = new_menu
1702 DataSelPopup.init_popup = init_popup
1706 #* l3Loop
1707 # This construct is a MACRO using other l3 constructs.
1708 # Current restrictions:
1709 # - no editing of the loop name
1710 # a. requires synchronized replacements
1711 # b. but then, no customizable loop template is possible
1712 # c. so custom editing support is required
1714 class l3Loop(l3Nested):
1715 pass
1717 def __init__(self, w_, cnvs, tree_id, rentab):
1718 # layout:
1719 # loop CALL from FROMARGS
1720 # BODY
1722 # l3 tree.
1723 l3tree = w_.state_.storage.load(tree_id)
1724 assert isinstance(l3tree, ast.Loop)
1725 l3Nested.__init__(self, w_, cnvs, l3tree, rentab)
1727 # Bindings to keep.
1728 self.w_ = w_
1729 self._canvas = cnvs
1730 self._l3tree = l3tree
1732 # Highlighting.
1733 self._outline = None
1735 # Item cross-referencing.
1736 self._marker = None
1737 self._container = None
1739 # # self._marker_points = {} # marker -> point list
1742 # Create display elements.
1744 # loop
1745 d_loop = Label(w_, cnvs, self._root_group, "loop")
1747 # CALL
1749 ### Editing this tree requires updates to two other trees and is
1750 ### deferred for now.
1751 d_call = cnvs.add_l3tree(l3tree.l_view_call, rentab)
1752 d_call.reparent(self)
1754 # from
1755 d_from = Label(w_, cnvs, self._root_group, "from")
1757 # FROMARGS
1758 d_from_args = l3aList(w_, cnvs,
1759 l3tree.l_from_args[0],
1760 rentab,
1761 root_group = self._root_group,
1762 draggable = False,
1763 parent = self,
1766 # BODY
1767 d_body = l3aList(w_, cnvs,
1768 l3tree.l_body[0],
1769 rentab,
1770 root_group = self._root_group,
1771 draggable = False,
1772 parent = self,
1775 # Marker(s).
1776 # Only editing of the macro leaves makes sense. This implies
1777 # that the macro itself cannot be edited, greatly simplifying
1778 # the code.
1780 # Inform obj of marker.
1781 d_call.setup_marker(None, self) ### need marker substitute.
1783 # Bindings to keep.
1784 # Indexed access. Match l3tree indexing where applicable.
1785 self._d_elements = [
1786 # l3 ast.
1787 d_call,
1788 d_from_args,
1789 d_body,
1791 # Rest
1792 d_loop,
1793 d_from,
1795 self._update_refs()
1798 # Align display elements, starting from d_loop.
1800 self._align_display()
1802 # Event handling.
1804 # Borders etc.
1805 self.init_header(rentab)
1806 self.init_deco(rentab)
1808 pass
1809 l3Loop.__init__ = __init__
1811 def _update_refs(self):
1812 (self._d_call,
1813 self._d_from_args ,
1814 self._d_body ,
1816 self._d_loop ,
1817 self._d_from,
1818 ) = self._d_elements
1819 l3Loop._update_refs = _update_refs
1821 def _align_display(self):
1823 # Align display elements, starting from d_loop.
1825 flush_events()
1827 [d_call,
1828 d_from_args,
1829 d_body,
1831 d_loop,
1832 d_from,
1833 ] = self._d_elements
1834 # d_call
1835 l1, t1, r1, b1 = d_loop.get_bounds()
1836 l2, t2, r2, b2 = d_call.get_bounds()
1837 d_call.move(r1 - l2, t1 - t2)
1839 # d_from
1840 l1, t1, r1, b1 = d_call.get_bounds()
1841 l2, t2, r2, b2 = d_from.get_bounds()
1842 d_from.move(r1 - l2, t1 - t2)
1844 # d_from_args
1845 l1, t1, r1, b1 = d_from.get_bounds()
1846 l2, t2, r2, b2 = d_from_args.get_bounds()
1847 d_from_args.move(l1 - l2, b1 - t2)
1849 # d_body
1850 l1, t1, r1, b1 = d_call.get_bounds()
1851 l2, t2, r2, b2 = d_from_args.get_bounds()
1852 l3, t3, r3, b3 = d_body.get_bounds()
1853 # Should really use the lowest bound of (loop CALL from FROMARGS)...
1854 d_body.move(l1 - l3, max(b1, b2) - t3)
1855 pass
1856 l3Loop._align_display = _align_display
1859 def new_size_for(self, child):
1860 self._align_display()
1862 # Refresh decoration.
1863 self.refresh_deco()
1865 # Propagate.
1866 if self._parent:
1867 self._parent.new_size_for(self)
1868 return
1869 l3Loop.new_size_for = new_size_for
1871 def destroy(self):
1872 l3Nested.destroy(self)
1874 _safed(self._d_loop)
1875 _safed(self._d_from)
1876 _safed(self._d_call)
1877 _safed(self._d_from_args)
1878 _safed(self._d_body)
1879 ### self._l3tree.delete(self.w_.state_.storage)
1880 l3Nested.destroy_deco(self)
1881 l3Loop.destroy = destroy
1884 #* marker / label pair
1885 # Labels are the second type loosely connected to markers (display
1886 # items being the other). As for the marker/display connection,
1887 # separate lists are used to keep each, instead of one list holding
1888 # tuples.
1890 # Markers and Labels may move independently -- so they are not in a
1891 # canvasgroup.
1893 # class MarkerLabel:
1894 # __slots__ = ['marker', 'label']
1896 #* l3aList
1897 class l3aList(Selectable):
1898 ''' An l3aList is the actual display of aList content. All
1899 entries are stacked vertically.
1901 Every entry may be removed by dragging it out of the list.
1903 New entries are added by pasting (mouse-2) on an item separator.
1905 pass
1908 def l3tree(self):
1909 return self._l3tree
1910 l3aList.l3tree = l3tree
1913 #** init
1914 def __init__(self, w_, cnvs, l3tree, rentab,
1915 root_group = None,
1916 draggable = True,
1917 parent = None,
1918 insertion_on_markers = True,
1919 hide_markers = False,
1921 assert isinstance(l3tree, ast.aList)
1922 self._insertion_on_markers = insertion_on_markers
1923 self._hide_markers = hide_markers
1924 self._parent = parent
1925 self._canvas = cnvs
1926 self.w_ = w_
1927 self._l3tree = l3tree
1928 self._destroy_hook = [] # (func -> None) list
1929 self._reparent_to_root_hook = [] # (func -> None) list
1930 self._dlist = [] # child element list
1931 self._marker_lst = [] # (marker list)
1932 self._mark_ = 0 # (int) current mark position
1934 # Set up graphical base.
1935 if root_group:
1936 self._root_group = (root_group.add(canvas.CanvasGroup))
1937 else:
1938 self._root_group = (cnvs.root().add(canvas.CanvasGroup))
1940 if draggable:
1941 self._root_group.connect("event", lambda *a: self.drag_event(*a))
1943 # Canvas registration. Must preceed children's construction.
1944 cnvs.register_l3tree_pic(self._l3tree._id, self)
1946 # Set up nested elements.
1947 # The marker entry preceeds the list entry.
1948 self._dlist = [cnvs.add_l3tree(child, rentab) for child in l3tree.entries()]
1949 for itm in self._dlist:
1950 itm.reparent(self)
1952 if hide_markers:
1953 self._marker_lst = [ self.draw_marker(0, 0, hidden_color = 'white')]+\
1954 [ self.draw_marker(0, 0, hidden_color = 'white')
1955 for _ in l3tree.entries()]
1956 else:
1957 self._marker_lst = [ self.draw_marker(0, 0)]+\
1958 [ self.draw_marker(0, 0) for _ in l3tree.entries()]
1960 # Insert leader on left to force proper bounding box of self in parent.
1961 self._leader = self._root_group.add(
1962 canvas.CanvasLine,
1963 fill_color = 'white',
1964 first_arrowhead = 0,
1965 last_arrowhead = 0,
1966 width_pixels = 1,
1967 points = [ 0,0, self.w_.cp_.label_indent,0 ],
1970 # Position elements for bounding box correctness. See also .insert().
1971 flush_events()
1972 vpad = w_.cp_.marker_padding
1973 if self._l3tree.getthe('layout') == 'horizontal':
1974 self._adj_h(self.w_.cp_.label_indent, 0, self._marker_lst, self._dlist)
1975 else:
1976 self._adj_v(self.w_.cp_.label_indent, 0, self._marker_lst, self._dlist)
1978 # Marker menu.
1979 self._marker_widget_cb = {} # menu item -> connection args
1980 self._marker_widget = gtk.Menu() # dummy
1981 self._marker_popup_list = ([
1982 # args are (menuitem, marker index) (for now)
1983 [ gtk.MenuItem("evaluate locally"),
1984 ("activate", lambda *args: self.marker_eval_local(args))],
1986 [ gtk.MenuItem("set mark"),
1987 ("activate", lambda *args: self.marker_set_mark(*args))],
1989 [ gtk.MenuItem("select region (from here to mark)"),
1990 ("activate", lambda *args: self.marker_select_region(*args))],
1994 # Insert spacer under last marker to force proper bounding box of
1995 # self in parent.
1996 self._spacer_bottom = self._root_group.add(
1997 canvas.CanvasLine,
1998 fill_color = 'white',
1999 first_arrowhead = 0,
2000 last_arrowhead = 0,
2001 width_pixels = 1,
2002 points = [ 0, 0, self.w_.cp_.marker_width, 0],
2004 self._mv_spacer()
2006 # Event handling.
2007 if insertion_on_markers:
2008 for marker in self._marker_lst:
2009 self.bind_marker_events(marker)
2010 l3aList.__init__ = __init__
2012 def _mv_spacer(self):
2013 flush_events()
2014 spcr = self._spacer_bottom
2015 spcr.lower_to_bottom()
2016 lm, tm, rm, bm = marker_get_bounds(self.w_, self._marker_lst[-1]).pad_tb()
2017 ls, ts, rs, bs = spcr.get_bounds()
2018 spcr.move(lm - ls, bm - ts)
2019 l3aList._mv_spacer = _mv_spacer
2021 def _adj_v(self, ax, ay, newmarker, newobj):
2022 # Position child elements for vertical list.
2024 # Move marker.
2025 newmarker[0].move(ax, ay)
2027 if newobj:
2028 # Inform obj of marker
2029 newobj[0].setup_marker(newmarker[0], self)
2031 # Move object.
2032 ml, _, _, mb = marker_get_bounds(self.w_, newmarker[0]).pad_tb()
2033 ol, ot, _, ob = newobj[0].get_bounds()
2034 newobj[0].move(ml - ol, mb - ot)
2036 # Move following markers and objects
2037 self._adj_v(ml,
2038 mb + (ob - ot), # + vpad,
2039 newmarker[1:], newobj[1:])
2040 l3aList._adj_v = _adj_v
2043 def _adj_h(self, ax, ay, newmarker, newobj):
2044 # Position child elements for horizontal list.
2046 # Move marker.
2047 newmarker[0].move(ax, ay)
2049 if newobj:
2050 # Inform obj of marker
2051 newobj[0].setup_marker(newmarker[0], self)
2053 # Move object.
2054 ml, mt, mr, mb = marker_get_bounds(self.w_, newmarker[0]).pad_lr()
2055 ol, ot, or_, ob = newobj[0].get_bounds()
2056 newobj[0].move(mr - ol, mt - ot)
2058 # Move following markers and objects
2059 self._adj_h(mr + (or_ - ol), # + hpad,
2061 newmarker[1:], newobj[1:])
2062 l3aList._adj_h = _adj_h
2065 #** TextEdit interface
2066 def rebuild_tree(self, text):
2067 pass
2068 l3aList.rebuild_tree = rebuild_tree
2071 #** marker event handlers
2073 def marker_set_mark(self, menuitem, index):
2074 self._mark_ = index
2075 l3aList.marker_set_mark = marker_set_mark
2077 def marker_select_region(self, menuitem, index):
2078 self.verify_mark()
2079 # Order range.
2080 if index <= self._mark_:
2081 first, last = index, self._mark_
2082 else:
2083 first, last = self._mark_, index
2084 # Add items to selection.
2085 add = self.w_.selector.toggle_selection
2086 for ii in range(first, last):
2087 add(self._dlist[ii])
2088 l3aList.marker_select_region = marker_select_region
2090 def verify_mark(self):
2091 'Ensure the mark is a valid index.'
2092 mx = len(self._dlist)
2093 if self._mark_ > mx: self._mark_ = mx
2094 if self._mark_ < 0: self._mark_ = 0
2095 l3aList.verify_mark = verify_mark
2098 def bind_marker_events(self, marker):
2099 marker.connect("event", lambda *a: self.marker_handle_event(*a))
2100 l3aList.bind_marker_events = bind_marker_events
2102 def marker_handle_event(self, item, event):
2104 # Destroy item key sequence.
2106 if event.type == gtk.gdk._2BUTTON_PRESS:
2107 if event.button == 3:
2108 ### The whole list, all entries, or a single entry.
2109 pass
2110 return True
2112 if event.type == gtk.gdk.BUTTON_PRESS:
2114 # Range selection.
2116 if event.button == 1:
2117 if event.state & gtk.gdk.SHIFT_MASK:
2118 # shift-button-1: select range
2119 self.marker_select_region(None, self._marker_lst.index(item))
2120 return True
2122 else:
2123 # button-1: Start selection
2124 self.marker_set_mark(None, self._marker_lst.index(item))
2125 return True
2127 # Item insertion.
2129 if event.button == 2:
2130 if self.w_.fluid_ref(trace_gui_events = False):
2131 print "insert_event"
2132 if not self.w_.selector.have_selection():
2133 self.w_.ten_second_message("No node selected.")
2134 return True
2135 else:
2136 # Insert all selected nodes, retaining selection order.
2137 offset = self._marker_lst.index(item)
2138 for (index, node) in enumerate(self.w_.selector.
2139 get_selection_list()):
2140 self.insert(offset + index, node)
2141 return True
2143 # Marker popup menu.
2145 if event.button == 3:
2146 self.marker_init_popup(self._marker_lst.index(item))
2147 self._marker_widget.popup(None, None, None,
2148 event.button, event.time)
2149 return True
2151 # Marker magnification.
2153 if event.type == gtk.gdk.ENTER_NOTIFY:
2154 item.set_property("width_units",
2155 self.w_.cp_.marker_thickness *
2156 self.w_.cp_.hover_magnification)
2157 return True
2159 if event.type == gtk.gdk.LEAVE_NOTIFY:
2160 # canvas quirk: A button click on the enlarged marker
2161 # qualifies as LEAVE_NOTIFY only...
2162 gobject.timeout_add(
2163 100, lambda : item.set_property("width_units",
2164 self.w_.cp_.marker_thickness) )
2165 return True
2166 return False
2167 l3aList.marker_handle_event = marker_handle_event
2170 def marker_prepend(self, item_init):
2171 # Menu additions can be made at any time.
2173 # item_init has form
2174 # [ gtk.MenuItem("dump code"),
2175 # ("activate",
2176 # lambda *args: view.print_info(prim_node.w_.state_.storage,
2177 # prim_node._l3tree))]
2178 # or
2179 # [gtk.SeparatorMenuItem(), None]
2181 self._marker_popup_list.insert(0, item_init)
2182 l3aList.marker_prepend = marker_prepend
2184 def marker_init_popup(self, marker_idx):
2185 # Prepare a popup menu including the original menu items (in
2186 # _marker_popup_list) plus any inserted via marker_prepend().
2188 # The marker index is passed to the popup's handler.
2189 new_menu = gtk.Menu()
2190 for item, conargs in self._marker_popup_list:
2191 # Insert menu items in new menu, re-using existing widgets.
2192 if item.get_parent() == self._marker_widget:
2193 if self._marker_widget_cb.has_key(item):
2194 # remove old event handler
2195 item.disconnect(self._marker_widget_cb[item])
2196 self._marker_widget.remove(item)
2197 new_menu.append( item )
2198 # Connect new event handler.
2199 if conargs != None:
2200 # (event name, handler, marker_idx)
2201 args = tuple(list(conargs) + [marker_idx])
2202 self._marker_widget_cb[item] = item.connect(*args)
2203 item.show()
2205 if self._marker_widget:
2206 self._marker_widget.destroy()
2208 self._marker_widget = new_menu
2209 l3aList.marker_init_popup = marker_init_popup
2212 #** marker functions
2213 # # l3alist local marker tests
2214 # from widgets import marker_get_bounds, marker_get_anchor
2215 # ma = self.draw_marker(2,3)
2216 # print marker_get_bounds(self.w_, ma)
2217 # print marker_get_anchor(self.w_, ma)
2219 def draw_marker(self, ulx, uly, hidden_color = None):
2220 def _hline():
2221 # Horizontal line (Canvas object).
2222 cp = self.w_.cp_
2223 vpad = self.w_.cp_.marker_padding
2224 _marker = self._root_group.add(
2225 canvas.CanvasLine,
2226 fill_color = hidden_color or cp.marker_color,
2227 points = [ulx,
2228 uly + vpad,
2229 ulx + cp.marker_width,
2230 uly + cp.marker_height + vpad,
2232 width_units = cp.marker_thickness,
2233 line_style = gtk.gdk.SOLID,
2234 cap_style = gtk.gdk.CAP_ROUND,
2235 join_style = gtk.gdk.JOIN_ROUND,
2236 smooth = False,
2237 spline_steps = 12,
2239 return _marker
2240 def _vline():
2241 # Vertical line (Canvas object).
2242 cp = self.w_.cp_
2243 hpad = self.w_.cp_.marker_padding
2244 _marker = self._root_group.add(
2245 canvas.CanvasLine,
2246 fill_color = hidden_color or cp.marker_color,
2247 points = [ulx + hpad,
2248 uly,
2249 ulx + cp.tuple_marker_width + hpad,
2250 uly + cp.tuple_marker_height,
2252 width_units = cp.marker_thickness,
2253 line_style = gtk.gdk.SOLID,
2254 cap_style = gtk.gdk.CAP_ROUND,
2255 join_style = gtk.gdk.JOIN_ROUND,
2256 smooth = False,
2257 spline_steps = 12,
2259 return _marker
2260 return { None : _hline,
2261 'vertical': _hline,
2262 'horizontal': _vline,
2263 }[self._l3tree.getthe('layout')]()
2264 l3aList.draw_marker = draw_marker
2267 def i2w_affine(self, tuple_):
2268 return self._root_group.i2w_affine(tuple_)
2269 l3aList.i2w_affine = i2w_affine
2271 def reparent_to_root(self):
2272 # Graphics.
2273 self._run_reparent_to_root_hook()
2275 _, _, _, _, x1, y1 = self.i2w_affine(tuple(range(0,6)))
2276 self._root_group.reparent(self._canvas.root())
2277 _, _, _, _, nx1, ny1 = self.i2w_affine(tuple(range(0,6)))
2278 self._root_group.move(x1 - nx1, y1 - ny1)
2280 # State.
2281 self._parent = None
2282 l3aList.reparent_to_root = reparent_to_root
2285 #* generic marker functions
2286 def marker_get_properties(marker):
2287 # Collect opaque info for marker_to_head/plain
2288 return marker.get_property("points")
2290 def marker_get_anchor(w_, marker):
2291 x, y, u, v = marker.get_bounds()
2292 ### magic offset values...
2293 try:
2294 wid = marker.get_property("width_units") # get CURRENT setting
2295 except TypeError:
2296 wid = 0
2297 return (x + wid, y + wid)
2299 class mBounds(tuple):
2300 def set(self, **kw):
2301 self.__dict__.update(kw)
2302 return self
2304 def pad_tb(self):
2305 # Pad (marker) bounds on top/bottom.
2306 x, y, u, v = self
2307 vpad = self.w_.cp_.marker_padding
2308 return mBounds((x , y - vpad,
2309 u , v + vpad)).set(w_ = self.w_)
2310 mBounds.pad_tb = pad_tb
2312 def pad_lr(self):
2313 # Pad (marker) bounds on left / right.
2314 x, y, u, v = self
2315 vpad = self.w_.cp_.marker_padding
2316 return mBounds((x - vpad, y, u + vpad, v)).set(w_ = self.w_)
2317 mBounds.pad_lr = pad_lr
2319 ## mBounds((1,2, 3,4)).set(w_ = w_)
2320 ## mBounds((1,2, 3,4)).set(w_ = w_).pad_lr()
2322 def marker_get_bounds(w_, marker):
2323 x, y, u, v = marker.get_bounds()
2324 ### magic offset values...
2325 try:
2326 wid = marker.get_property("width_units") # get CURRENT setting
2327 except TypeError:
2328 wid = 0
2329 return mBounds((x + wid, y + wid, u - wid, v - wid)).set(w_ = w_)
2331 def marker_pad_tb(w_, bounds):
2332 # Pad (marker) bounds on top/bottom.
2333 x, y, u, v = bounds
2334 vpad = w_.cp_.marker_padding
2335 return (x , y - vpad, u , v + vpad)
2337 def marker_pad_lr(w_, bounds):
2338 # Pad (marker) bounds on left / right.
2339 x, y, u, v = bounds
2340 vpad = w_.cp_.marker_padding
2341 return (x - vpad, y, u + vpad, v)
2344 #* l3Rawtext
2345 #** init & misc.
2346 class l3Rawtext(l3Base):
2347 pass
2349 def text_and_outline(self):
2351 # Text content / format / scale
2353 ## rendering speed up
2354 if 0:
2355 self._root_group.move(+1000,0)
2356 if isinstance(self, l3Comment):
2357 # Allow pango text markup for comments.
2358 self._ltext = misc.GtkObjProxy(self._root_group.add(
2359 canvas.CanvasText,
2360 anchor = gtk.ANCHOR_NORTH_WEST,
2361 x = 0,
2362 y = 0,
2363 size = self._font_size,
2364 scale = 1.0,
2365 scale_set = True,
2366 ## font = self._font_desc,
2367 size_set = True,
2368 markup = "\n".join(view.leading_lines(self._text_orig,
2369 num_lines=10)),
2371 else:
2372 self._ltext = misc.GtkObjProxy(self._root_group.add(
2373 canvas.CanvasText,
2374 anchor = gtk.ANCHOR_NORTH_WEST,
2375 x = 0,
2376 y = 0,
2377 size = self._font_size,
2378 scale = 1.0,
2379 scale_set = True,
2380 font = self._font_desc,
2381 size_set = True,
2382 text = "\n".join(view.leading_lines(self._text_orig, num_lines=10)),
2384 # .destroy order checks.
2385 self._under_destruction = False
2386 def _check_destroy_order(widget):
2387 if not self._under_destruction:
2388 # An exception here will not give a useful
2389 # traceback. (event handling interferes?)
2390 print (".destroy from external source.")
2391 import pdb; pdb.set_trace()
2392 self._ltext.connect("destroy", _check_destroy_order)
2394 if self._new_text_props != None:
2395 _set_props(self._ltext, self._new_text_props)
2396 self._new_text_props = None
2398 self._ltext.show()
2399 flush_events()
2400 x1, y1, x2, y2 = self._ltext.get_bounds() # world units
2401 # Reference size.
2402 self._ltext_size_1 = ((x2 - x1) * self._canvas._pixpu,
2403 (y2 - y1) * self._canvas._pixpu)
2405 # Current scale size.
2406 self._ltext.set_property(
2407 "size",
2408 self.w_.cp_.font_size * self._canvas._abs_zoom * pango.SCALE)
2410 # Text events.
2411 self._ltext.connect("event", lambda *a: self.destroy_event(*a))
2412 self._ltext.connect("event", lambda *a: self.start_edit(*a))
2413 self._ltext.connect("event", lambda *a: self.drag_event(*a))
2415 # Text outline.
2416 # The CanvasText has TRANSPARENT background; here, it
2417 # needs to be opaque.
2418 x1, y1, x2, y2 = self._ltext.get_bounds() # world units
2419 pad = self.w_.cp_.textview.outline_padding / self._canvas._pixpu
2420 x1 -= pad
2421 y1 -= pad
2422 x2 += pad
2423 y2 += pad
2425 self._loutline = self._root_group.add(
2426 canvas.CanvasRect,
2427 x1 = x1, y1 = y1,
2428 x2 = x2, y2 = y2,
2429 fill_color = self._fill_color,
2430 outline_color = self._outline_color, ### black
2431 width_pixels = self.w_.cp_.outline_width_normal,
2434 self._loutline.lower(1)
2435 self._loutline.show()
2437 # Outline events.
2438 self._loutline.connect("event", lambda *a: self.drag_event(*a))
2440 # Ensure correct sizing.
2441 # # flush_events()
2442 # # self._root_group.move(-1000,0)
2443 ## rendering speed up
2444 if 0:
2445 self._root_group.move(-1000,0)
2446 flush_events()
2448 def __init__(self, w_, cnvs, tree_id, rentab, render_only = []):
2449 l3Base.__init__(self)
2450 self._canvas = cnvs
2451 self.w_ = w_
2453 # Parameters
2454 self.init_params()
2456 # l3 tree.
2457 l3tree = w_.state_.storage.load(tree_id)
2458 assert isinstance(l3tree, (ast.astType, ast.aNone, ast.aList))
2459 if isinstance(l3tree, (ast.aNone, ast.aList)):
2460 warn_("displaying internal types as text; "
2461 "expect clobbered display for:")
2462 warn_(l3tree.get_infix_string(40))
2464 self._text_orig = l3tree.l3_repr_dedented()
2465 self._l3tree = l3tree
2466 self._subtrees = [] # l3Base nodes.
2468 # Rendering options.
2469 self._render_mode = rentab.get_state(l3tree)
2471 # Item cross-referencing.
2472 self._marker = None
2473 self._container = None
2475 # Headerless state.
2476 self._header = None
2478 # Undecorated state.
2479 self._deco = None
2481 # Text edit state.
2482 self._new_text_props = None
2484 # Focus redirection.
2485 self._focus_dummy = cnvs.root().add(canvas.CanvasItem)
2486 self._focus_dummy.hide()
2487 self._focus_dummy.grab_focus()
2489 # Display Group
2490 self._root_group = cnvs.root().add(canvas.CanvasGroup)
2491 self._pup = CommonPopup(self)
2492 self._editor = None
2494 # Canvas registration. Must preceed children's construction.
2495 cnvs.register_l3tree_pic(l3tree._id, self)
2497 # Display self.
2498 text_and_outline(self)
2501 # Display subtrees.
2502 (row_par, col_par), _ = l3tree.get_char_range()
2503 l_par, t_par, r_par, b_par = self._ltext.get_bounds()
2504 _, _, unit_y = self._canvas.font_wh()
2505 if render_only:
2506 do_subtrees = render_only
2507 else:
2508 do_subtrees = l3tree.subtrees1()
2510 for sub in do_subtrees:
2511 # # print "Now:", sub
2512 # Display elements.
2513 disp_ch = cnvs.add_l3tree(sub, rentab, force_text = True)
2514 disp_ch.reparent(self)
2515 l_ch, t_ch, r_ch, b_ch = disp_ch._ltext.get_bounds()
2517 # Align.
2518 # Get row, column offset.
2519 (row_ch, col_ch), _ = sub.get_char_range()
2521 # Move into character position.
2522 disp_ch.move((l_par - l_ch) + (col_ch - col_par),
2523 ((t_par - t_ch) + (row_ch - row_par)) * unit_y)
2525 # Track for later.
2526 self._subtrees.append(disp_ch)
2528 # Highlight outline.
2529 self._outline = None
2531 # Register for zooming.
2532 cnvs.add_zoom_text(self)
2534 # Borders etc.
2535 self.init_header(rentab)
2536 self.init_deco(rentab)
2537 self._root_group.show()
2539 # movement state
2540 self.remember_x = None
2541 self.remember_y = None
2543 pass
2544 l3Rawtext.__init__ = __init__
2547 def pre_popup_hook(self):
2548 ''' Conditional popup menu additions.
2549 Additions for
2550 1. Possible file names
2551 2. Known Filepaths
2553 # 2. Known Filepaths
2554 import l3lang.external.xmipp_wrap as xm
2555 from l3gui.l3canvas import RenderTable
2556 def _display_img(fname):
2557 storage = self.w_.state_.storage
2558 tree, tid = ast.Native(fname).setup(
2559 empty_parent(),
2560 ast.Env('dummy_env', None, None, storage),
2561 storage)
2562 tree.set_outl_edges(self.w_, None)
2564 pic = self._canvas.add_l3tree(tree, RenderTable())
2565 lp, tp, rp, bp = pic.get_bounds_world()
2566 lo, to, ro, bo = self.get_bounds_world()
2567 pic.move(ro - lp, to - tp)
2569 if (isinstance(self._l3tree, (ast.String)) and
2570 self._l3tree.isfile() and
2571 not getattr(self, '_popup_known_filepaths_added', False)):
2572 map(self._pup.prepend, [
2573 [gtk.SeparatorMenuItem(), None],
2575 [ gtk.MenuItem(".spi -> xmipp_show -sel <path>"),
2576 ( "activate",
2577 lambda *args: xm.show(sel = self._l3tree.py_string()))],
2579 [ gtk.MenuItem(".spi -> xmipp_show -vol <path>"),
2580 ( "activate",
2581 lambda *args: xm.show(vol = self._l3tree.py_string()))],
2583 [ gtk.MenuItem(".txt -> $EDITOR <path>"),
2584 ( "activate",
2585 lambda *args:
2586 os.system(os.path.expandvars("$EDITOR %s &" % # clean up...
2587 self._l3tree.py_string())))],
2589 [ gtk.MenuItem(".png -> view image"),
2590 ( "activate",
2591 lambda *args:
2592 _display_img(self._l3tree))],
2594 # The table display restructures a simple list; for file
2595 # content, this requires additions in file2ast, for every
2596 # file content type.
2597 [ gtk.MenuItem(".hdf file -> insert as l3 list list"),
2598 ("activate", lambda *args: self._insert_contents(shape='square'))],
2600 [ gtk.MenuItem(".hdf file -> insert as l3 list"),
2601 ("activate", lambda *args: self._insert_contents() )],
2604 self._popup_known_filepaths_added = True
2606 # 1. Possible file names
2607 elif (isinstance(self._l3tree, (ast.String, ast.Symbol, ast.Member)) and
2608 not getattr(self, '_popup_file_paths_added', False)):
2609 # Get values.
2610 va_l = self.get_values_list()
2612 # Remove invalid elements.
2613 va_l = filter(lambda (id, va):
2614 isinstance(va, (ast.String, ast.Symbol,
2615 types.StringType)) and (id,va),
2616 va_l)
2617 # Is there any content?
2618 if len(va_l) > 0:
2619 map(self._pup.prepend, [
2620 [gtk.SeparatorMenuItem(), None],
2621 [ gtk.MenuItem(".any -> insert (file path) list"),
2622 ( "activate", lambda *args: self.get_filepaths_list())],
2623 [ gtk.MenuItem(".png -> insert (file path, image) list"),
2624 ( "activate", lambda *args: self.filep_img_list())],
2626 self._popup_file_paths_added = True
2627 l3Rawtext.pre_popup_hook = pre_popup_hook
2629 def _insert_contents(self, shape = 'list'):
2630 # See also CommonPopup._insert_list
2631 from l3gui.l3canvas import RenderTable
2632 storage = self.w_.state_.storage
2633 cnvs = self._canvas
2634 fname = self._l3tree
2636 # Convert to l3 ast
2637 val, val_id = misc.file2ast(fname, shape = shape)\
2638 .setup(empty_parent(),
2639 ast.Env('dummy_env', None, None, storage),
2640 storage)
2641 val.set_outl_edges(self.w_, None)
2643 # Special emphasis for this non-permanent value list.
2644 val.set_emphasis("valuelist")
2645 ## val.set_label('File contents of %d' % self._l3tree._id )
2646 val.set_label('File contents of %s' % self._l3tree.py_string())
2648 # Form display.
2649 pic = cnvs.add_l3tree(val, RenderTable())
2651 # Position to the right of expression.
2652 lp, tp, rp, bp = pic.get_bounds_world()
2653 lo, to, ro, bo = self.get_bounds_world()
2654 pic.move(ro - lp, to - tp)
2655 l3Rawtext._insert_contents = _insert_contents
2658 def get_filepaths_list(self):
2659 ''' Examine all values associated with self, and display those
2660 denoting valid file paths as list.
2662 from l3gui.l3canvas import RenderTable
2663 storage = self.w_.state_.storage
2664 cnvs = self._canvas
2665 # Get values.
2666 va_l = self.get_values_list()
2668 # Remove invalid elements.
2669 va_l = filter(lambda (id, va):
2670 isinstance(va, (ast.String, ast.Symbol,
2671 types.StringType)) and (id,va),
2672 va_l)
2674 # Form "path/name" list.
2675 path_l = [("%s/%s" % (storage.load(id).dirpath(self.w_), va)).strip('/')
2676 for (id, va) in va_l]
2678 # Keep only valid path names.
2679 path_l = filter(lambda nm: os.path.exists(nm) and nm, path_l)
2681 # Get l3 list.
2682 # This is a (very) simplified ast.val2ast(), using ast.Filepath
2683 # instead of ast.String.
2685 val, val_id = ast.List(ast.aList(map(ast.FilepathString, path_l))).setup(
2686 empty_parent(),
2687 ast.Env('dummy_env', None, None, storage),
2688 storage)
2689 val.set_outl_edges(self.w_, None)
2691 # Special emphasis for this non-permanent value list.
2692 val.set_emphasis("filelist")
2693 val.set_label('File path list for %s' % self._l3tree.py_string() )
2695 # Form and position display.
2696 pic = cnvs.add_l3tree(val, RenderTable())
2697 lp, tp, rp, bp = pic.get_bounds_world()
2698 lo, to, ro, bo = self.get_bounds_world()
2699 pic.move(ro - lp, to - tp)
2700 l3Rawtext.get_filepaths_list = get_filepaths_list
2703 def filep_img_list(self):
2704 ''' Examine all values associated with self, and display those
2705 denoting valid file paths as (name = <image>) list.
2707 from l3gui.l3canvas import RenderTable
2708 storage = self.w_.state_.storage
2709 cnvs = self._canvas
2710 # Get values.
2711 va_l = self.get_values_list()
2713 # Remove invalid elements.
2714 va_l = filter(lambda (id, va):
2715 isinstance(va, (ast.String, ast.Symbol,
2716 types.StringType)) and (id,va),
2717 va_l)
2719 # Form "path/name" list.
2720 path_l = [("%s/%s" % (storage.load(id).dirpath(self.w_), va)).strip('/')
2721 for (id, va) in va_l]
2723 # Keep only valid path names.
2724 path_l = filter(lambda nm: os.path.exists(nm) and nm, path_l)
2726 # Get l3 list.
2727 # This is a (very) simplified ast.val2ast().
2729 from l3lang.ast import Set, Tuple, aList, FilepathString, Native
2730 val, val_id = ast.List(ast.aList(
2731 map(lambda path : Set(Symbol(path), Native(FilepathString(path))),
2732 path_l)
2733 )).setup(
2734 empty_parent(),
2735 ast.Env('dummy_env', None, None, storage),
2736 storage)
2737 val.set_outl_edges(self.w_, None)
2739 # Special emphasis for this non-permanent value list.
2740 val.set_emphasis("filelist")
2741 val.set_label('File path list for `%s`' % self._l3tree.py_string() )
2743 # Form and position display.
2744 pic = cnvs.add_l3tree(val, RenderTable())
2745 lp, tp, rp, bp = pic.get_bounds_world()
2746 lo, to, ro, bo = self.get_bounds_world()
2747 pic.move(ro - lp, to - tp)
2748 l3Rawtext.filep_img_list = filep_img_list
2751 def init_params(self):
2752 # Parameter settings, localized to allow subclass overrides
2753 self._font_desc = self.w_.cp_.rawtext_font
2754 self._font_size = self.w_.cp_.font_size * 1.0 * pango.SCALE
2755 self._fill_color = "white"
2756 self._outline_color = 'white'
2757 l3Rawtext.init_params = init_params
2759 def ed_new_size(self, height):
2760 # # self._edit_frame.set_property( "height", height )
2761 pass
2762 l3Rawtext.ed_new_size = ed_new_size
2765 def zoom(self, factor):
2766 l3Base.zoom(self, factor)
2767 if self._ltext: # zooming while editing?
2768 self._ltext.set_property("size",
2769 self.w_.cp_.font_size * factor * pango.SCALE)
2770 ### flush_events()
2771 u1, v1, u2, v2 = self._ltext.get_bounds()
2772 ox, oy = self._ltext_size_1
2773 if ox == 0: ox = 1
2774 if oy == 0: oy = 1
2775 ppu = self._canvas._pixpu
2776 ratio_h = 1.0 * (u2 - u1) * ppu / ox
2777 ratio_v = 1.0 * (v2 - v1) * ppu / oy
2778 return (ratio_h, ratio_v)
2779 else:
2780 return factor, factor
2781 l3Rawtext.zoom = zoom
2783 def setup_marker(self, marker, container):
2784 self._marker = marker
2785 self._container = container
2786 l3Nested.setup_marker = setup_marker
2787 l3Rawtext.setup_marker = setup_marker
2790 #** events
2793 #*** start_edit
2794 def start_edit(self, _textview, event):
2795 if event.type == gtk.gdk._2BUTTON_PRESS:
2796 if event.button == 1:
2797 # Remove text display.
2798 self._new_text_props = _get_props(self._ltext, [], "x", "y")
2799 self._under_destruction = True
2800 self._ltext.destroy()
2801 self._ltext = None
2802 # ... and the outline.
2803 self._loutline.destroy()
2804 self._loutline = None
2806 # Use new widget for editing.
2807 if isinstance(self, l3Rawtext):
2808 params = self.w_.cp_.textview
2809 else:
2810 params = self.w_.cp_.inline
2812 self._editor = TextEdit(self.w_,
2813 self,
2814 self._text_orig,
2815 self._canvas._common_tag_table,
2816 self._canvas,
2818 self._editor._view.show()
2820 self._editor._view.grab_focus()
2821 self._under_destruction = "partial"
2822 return True
2823 return False
2824 l3Rawtext.start_edit = start_edit
2827 #* l3List / l3Map editing
2828 class l3List(l3Nested):
2829 pass
2831 class l3ViewList(l3List):
2832 pass
2834 class l3Map(l3Nested):
2835 pass
2838 #** l3List.__init__
2839 def __init__(self, w_, cnvs, tree_id, rentab):
2840 # l3 tree.
2841 l3tree = w_.state_.storage.load(tree_id)
2842 assert isinstance(l3tree, (ast.Map, ast.List, ast.Tuple))
2843 assert not isinstance(l3tree, (ast.cls_viewList))
2844 l3Nested.__init__(self, w_, cnvs, l3tree, rentab)
2847 #*** Display elements.
2848 alist = l3aList(w_, cnvs,
2849 l3tree[0],
2850 rentab,
2851 root_group = self._root_group,
2852 draggable = False,
2853 parent = self,
2856 #*** Label.
2857 head = Label(w_, cnvs, self._root_group, l3tree.deco_title_text())
2860 # Expander
2862 expander = uVisibleSymbol(w_, cnvs, self._root_group,
2863 action = lambda _: self.hide_subtree())
2865 # Align.
2866 # Expander Head
2867 # Alist
2868 flush_events() # Update bounding boxes
2869 LE, TE, RE, BE = expander.get_bounds()
2870 LH, TH, RH, BH = head.get_bounds()
2871 head.move(RE - LH, TE - TH)
2873 LA, TA, _, _ = alist.get_bounds()
2874 alist.move(LH - LA, BH - TA)
2876 # Events.
2878 # Bindings to keep.
2879 self.w_ = w_
2880 self._canvas = cnvs
2881 self._alist = alist
2882 self._head = head
2883 self._expander = expander
2884 self._l3tree = l3tree
2886 # Highlighting.
2887 self._outline = None
2889 # Item cross-referencing.
2890 self._marker = None
2891 self._container = None
2893 # Popup menu additions
2894 if isinstance(self, l3Map):
2895 map(self._pup.prepend, [
2896 # Specials.
2897 [ gtk.SeparatorMenuItem(), None],
2899 [ gtk.MenuItem("examine dir"),
2900 ( "activate", lambda *args: self.insert_view())],
2902 [ gtk.MenuItem("print working directory (pwd)"),
2903 ( "activate", lambda *args: self.print_pwd())],
2907 # Borders etc.
2908 self.init_header(rentab)
2909 self.init_deco(rentab)
2910 pass
2911 l3List.__init__ = __init__
2912 l3Map.__init__ = __init__
2914 #** Env inspection
2915 def insert_view(self):
2916 from l3gui.l3canvas import RenderTable
2917 storage = self.w_.state_.storage
2918 # Get the information.
2919 content = self.get_values_list()[0]
2920 if content is None:
2921 content = []
2922 val, val_id = ast.val2ast(content)\
2923 .setup(empty_parent(),
2924 ast.Env('dummy_env', None, None, storage),
2925 storage)
2926 val.set_outl_edges(self.w_, None)
2927 val.set_label("Env listing")
2928 # Form and position display.
2929 pic = self._canvas.add_l3tree(val, RenderTable())
2930 lp, tp, rp, bp = pic.get_bounds_world()
2931 lo, to, ro, bo = self.get_bounds_world()
2932 pic.move(ro - lp, to - tp)
2933 l3Map.insert_view = insert_view
2936 #** pwd
2937 def print_pwd(self):
2938 st = self.w_.state_.storage
2939 val = st.get_attribute(self._l3tree._id, 'interp_result')
2940 if isinstance(val, ast.Env):
2941 G.logger.message("Directory name is %s" % val.directory_name())
2942 l3Map.print_pwd = print_pwd
2945 #* l3Set / l3If / l3Call
2946 class l3Set(l3Nested):
2947 pass
2949 class l3If(l3Nested):
2950 # Layout:
2951 # if COND:
2952 # YES
2953 # else:
2954 # NO
2955 pass
2958 # ---------------------------------
2959 class l3IfOutline(l3Nested):
2960 pass
2962 # ---------------------------------
2963 class l3IfLoop(l3Nested):
2964 # Layout:
2965 # loop
2966 # BODY
2968 pass
2971 def continue_stop_pt(self):
2972 lc, tc, rc, bc = self._d_loop.get_bounds_world()
2973 return lc + (rc - lc)/10, bc ## parameters
2974 l3IfLoop.continue_stop_pt = continue_stop_pt
2976 def break_stop_pt(self):
2977 lc, tc, rc, bc = self.get_bounds_world()
2978 return rc + 4, bc ## parameters
2979 l3IfLoop.break_stop_pt = break_stop_pt
2982 # ---------------------------------
2983 # todo: combine l3If, l3IfLoop and l3IfWhile
2985 class l3IfWhile(l3Nested):
2986 # Layout:
2987 # while COND
2988 # BODY
2990 pass
2992 def continue_stop_pt(self):
2993 lc, tc, rc, bc = self._d_loop.get_bounds_world()
2994 return lc + (rc - lc)/10, bc ## parameters
2995 l3IfWhile.continue_stop_pt = continue_stop_pt
2997 def break_stop_pt(self):
2998 lc, tc, rc, bc = self.get_bounds_world()
2999 return rc + 4, bc ## parameters
3000 l3IfWhile.break_stop_pt = break_stop_pt
3003 # ---------------------------------
3004 class l3IfLoopContinue(l3Nested):
3005 pass
3006 def continue_start_pt(self):
3007 lc, tc, rc, bc = self.get_bounds_world()
3008 return lc, (tc + bc) / 2
3009 l3IfLoopContinue.continue_start_pt = continue_start_pt
3012 # ---------------------------------
3013 class l3IfLoopBreak(l3Nested):
3014 pass
3015 def break_start_pt(self):
3016 lc, tc, rc, bc = self.get_bounds_world()
3017 return rc, (tc + bc) / 2
3018 l3IfLoopBreak.break_start_pt = break_start_pt
3021 # ---------------------------------
3022 class l3Call(l3Nested):
3023 pass
3026 #** if_chooser
3027 def l3if_chooser(w_, cnvs, tree_id, rentab):
3029 Choose appropriate display class based on subtree structure.
3031 from l3lang.ast import Call, Set, MarkerTyped, Symbol, Function, \
3032 aList, If, String, Return, Macro, aNone, Tuple
3033 l3tree = w_.state_.storage.load(tree_id)
3034 assert isinstance(l3tree, ast.If)
3036 ma = ast.Matcher()
3038 # todo: use rentab to distinguish compact/expanded form?
3040 # Traverse parents' displays to see if self is inside a loop.
3041 in_l3ifloop = False
3042 for l3pic in cnvs.displayed_parents(l3tree):
3043 if isinstance(l3pic, l3IfLoop):
3044 in_l3ifloop = True
3045 l3ifloop = l3pic
3046 break
3048 #*** if in_l3ifloop:
3049 if in_l3ifloop:
3050 # loop branches?
3051 # if COND:
3052 # return LNAME(LARGS)
3054 if ma.match(l3tree,
3055 If(Marker('_'),
3056 aList([Return(Call(MarkerTyped(Symbol('LNAME'),
3057 Symbol('_')),
3058 MarkerTyped(Symbol('LARGS'),
3059 aList([]))))]),
3060 Marker('_'))):
3061 if ma['LNAME'] == l3ifloop._matcher['LNAME']:
3062 # loop `continue`.
3063 return l3IfLoopContinue(w_, cnvs, tree_id, rentab)
3064 else:
3065 # loop `break`.
3066 return l3IfLoopBreak(w_, cnvs, tree_id, rentab)
3068 # loop break?
3069 # if COND:
3070 # return ARGS
3072 if ma.match(l3tree,
3073 If(Marker('_'),
3074 aList([Return(Marker('_'))]),
3075 Marker('_'))):
3076 return l3IfLoopBreak(w_, cnvs, tree_id, rentab)
3080 #*** loop form?
3081 # loop form?
3082 # if "loop":
3083 # def LNAME(LARGS):
3084 # LBODY
3085 # LNAME2(CARGS)
3086 if ma.match(l3tree,
3087 If(String('loop'),
3088 aList([Set(MarkerTyped(Symbol('LNAME'), Symbol('symbol')),
3089 Function(MarkerTyped(Symbol('LARGS'), aList([])),
3090 MarkerTyped(Symbol('LBODY'), aList([])))),
3091 Call(MarkerTyped(Symbol('LNAME2'), Symbol('symbol')),
3092 MarkerTyped(Symbol('CARGS'), aList([])))]),
3093 Marker('_'))):
3094 if ma['LNAME'] != ma['LNAME2']:
3095 warn_("loop name mismatch; no special display")
3096 return l3If(w_, cnvs, tree_id, rentab)
3097 elif len(ma['LARGS']) != len(ma['CARGS']):
3098 warn_("arguments mismatched between loop definition and call")
3099 return l3If(w_, cnvs, tree_id, rentab)
3100 else:
3101 return l3IfLoop(w_, cnvs, tree_id, rentab, ma)
3104 #*** while loop?
3105 # while loop?
3106 # if "while":
3107 # def "_while_ID"():
3108 # if not COND:
3109 # return
3110 # else:
3111 # BODY
3112 # return _while_ID()
3113 # _while_ID()
3115 elif ma.match(l3tree,
3116 If(String('while'),
3117 aList([Set(Marker('_'),
3118 Macro(aList([]),
3119 aList([If(Call(Symbol('not'),
3120 Marker('COND-PARENT')),
3121 aList([Return(Marker('_'))]),
3122 Marker('BODY')),
3123 Return(Call(Marker('_'),
3124 aList([])))]))),
3125 Call(Marker('_'),
3126 aList([]))]),
3127 aList([]))):
3128 if ma.match(ma['COND-PARENT'], aList([Marker('COND')])):
3129 ma['COND-IDX'] = 0
3130 def get_cond():
3131 return ma['COND-PARENT'][ ma['COND-IDX'] ]
3132 ma.get_cond = get_cond
3133 return l3IfWhile(w_, cnvs, tree_id, rentab, ma)
3136 #*** for loop?
3138 # For display of
3140 # for V in SEQ:
3143 # identify V, SEQ, and B. Only self and those are displayed.
3145 # See also l3lang/reader.py and If.setup()
3147 # print reader.parse('''
3148 # if "for":
3149 # ! ITEMS = ! SEQ
3150 # ! IDX = 0
3151 # ! LEN = len(! ITEMS)
3152 # # orig. for
3153 # def "LOOP"():
3154 # if ! IDX < ! LEN:
3155 # ! IDX = ! IDX + 1
3156 # # V in S.
3157 # ! V = ! ITEMS[ ! IDX - 1 ]
3158 # # Body B
3159 # [ ! B ]
3160 # # Iterate.
3161 # return ! LOOP()
3162 # ! LOOP()
3163 # ''')
3164 # The following pattern is from above, with manual fixes for
3165 # def "LOOP"() [ Set(Symbol('LOOP') ...) -> Set(Marker('LOOP'),...)]
3166 # and
3167 # Marker('B') [ aList([Marker('B')]) -> Marker('B') ]
3169 elif ma.match(l3tree,
3170 If(String('for'), aList([Set(Marker('ITEMS'), Marker('SEQ')), Set(Marker('IDX'), Int(0)), Set(Marker('LEN'), Call(Symbol('len'), aList([Marker('ITEMS')]))), Set(Marker('LOOP'), Macro(aList([]), aList([If(Call(Symbol('<'), aList([Marker('IDX'), Marker('LEN')])), aList([Set(Marker('IDX'), Call(Symbol('+'), aList([Marker('IDX'), Int(1)]))), Set(Marker('V'), Call(Member(Symbol('operator'), Symbol('getitem')), aList([Marker('ITEMS'), Call(Symbol('-'), aList([Marker('IDX'), Int(1)]))]))), List(Marker('B')), Return(Call(Marker('LOOP'), aList([])))]), aList([]))]))), Call(Marker('LOOP'), aList([]))]), aList([]))):
3172 v_parent = w_.state_.storage.load(ma['V']._parent)
3173 ma.get_current_V = (lambda :
3174 v_parent[0])
3175 ma.set_V = ( lambda new:
3176 v_parent.replace_child(ma.get_current_V()._id, new))
3177 seq_parent = w_.state_.storage.load(ma['SEQ']._parent)
3178 ma.get_current_SEQ = (lambda :
3179 seq_parent[1])
3180 ma.set_SEQ = ( lambda new:
3181 seq_parent.replace_child(ma.get_current_SEQ()._id, new))
3182 ma.get_B = lambda : ma['B']
3184 return l3IfFor(w_, cnvs, tree_id, rentab, ma)
3185 # # return l3Rawtext(w_, cnvs, tree_id, rentab,
3186 # # render_only = [ma['V'], ma['SEQ'], ma['B']])
3188 else:
3189 # plain if
3190 return l3If(w_, cnvs, tree_id, rentab)
3192 ## paste
3193 ## wdgt.l3if_chooser = l3if_chooser
3196 #** init
3197 def __init__(self, w_, cnvs, tree_id, rentab):
3198 # layout:
3199 # Lhs = Rhs
3201 # l3 tree.
3202 l3tree = w_.state_.storage.load(tree_id)
3203 assert isinstance(l3tree, ast.Set)
3204 l3Nested.__init__(self, w_, cnvs, l3tree, rentab)
3206 # Bindings to keep.
3207 self._marker_points = {} # marker -> point list
3208 self.w_ = w_
3209 self._canvas = cnvs
3212 # Display elements.
3213 # Lhs
3214 d_lhs = cnvs.add_l3tree(l3tree[0], rentab)
3215 d_lhs.reparent(self)
3217 # Equal
3218 d_equal = Label(w_, cnvs, self._root_group, " = ")
3220 # Rhs
3221 d_rhs = cnvs.add_l3tree(l3tree[1], rentab)
3222 d_rhs.reparent(self)
3224 # Marker(s).
3225 d_lhs_marker = self.draw_marker()
3226 d_rhs_marker = self.draw_marker()
3227 d_lhs_marker.lower_to_bottom()
3228 d_rhs_marker.lower_to_bottom()
3230 # Inform obj of marker.
3231 d_lhs.setup_marker(d_lhs_marker, self)
3232 d_rhs.setup_marker(d_rhs_marker, self)
3234 # Bindings to keep.
3235 self._d_equal = d_equal
3236 self._d_lhs = d_lhs
3237 self._d_rhs = d_rhs
3238 self._d_lhs_marker = d_lhs_marker
3239 self._d_rhs_marker = d_rhs_marker
3240 self._l3tree = l3tree
3242 # Align.
3243 self._align_to_lhs()
3245 # Highlighting.
3246 self._outline = None
3248 # Item cross-referencing.
3249 self._marker = None
3250 self._container = None
3252 # Event handling.
3253 d_lhs_marker.connect("event", lambda *a: self.insert_event(*a))
3254 d_rhs_marker.connect("event", lambda *a: self.insert_event(*a))
3256 # Borders etc.
3257 self.init_header(rentab)
3258 self.init_deco(rentab)
3260 # Hide markers.
3261 d_lhs_marker.hide()
3262 d_rhs_marker.hide()
3263 pass
3264 l3Set.__init__ = __init__
3266 def __init__(self, w_, cnvs, tree_id, rentab, matcher):
3267 # l3 tree.
3268 l3tree = w_.state_.storage.load(tree_id)
3269 assert isinstance(l3tree, ast.If)
3270 l3Nested.__init__(self, w_, cnvs, l3tree, rentab)
3272 # Bindings to keep.
3273 self.w_ = w_
3274 self._canvas = cnvs
3275 self._marker_points = {} # marker -> point list
3276 self._matcher = ma = matcher
3277 self._l3tree = l3tree
3278 self._deco_cb_buffer = [] # callback list for add_item()
3281 # Display elements.
3282 d_loop = Label(w_, cnvs, self._root_group, "loop")
3283 d_body = l3aList(w_, cnvs,
3284 ma['LBODY'],
3285 rentab,
3286 root_group = self._root_group,
3287 draggable = False,
3288 parent = self,
3291 # Marker(s).
3292 # # d_cond_marker = self.draw_marker()
3293 # # d_cond_marker.lower_to_bottom()
3295 # Inform obj of marker.
3296 # # d_cond.setup_marker(d_cond_marker, self)
3299 # Align with respect to d_loop.
3301 flush_events()
3303 ll, tl, rl, bl = d_loop.get_bounds()
3304 lb, tb, rb, bb = d_body.get_bounds()
3305 d_body.move(ll - lb, bl - tb)
3307 # COND marker for child replacement.
3308 # # _, y, x, _ = d_if.get_bounds()
3309 # # u, v, _, _ = d_cond_marker.get_bounds()
3310 # # d_cond_marker.move(x - u + w_.cp_.exp_marker_hoff,
3311 # # y - v + w_.cp_.exp_marker_voff)
3313 # Bindings to keep.
3314 # Indexed access. Match l3tree indexing where applicable.
3315 self._d_elements = [
3316 # l3 ast.
3317 d_loop,
3318 d_body,
3320 # Rest
3321 # # d_else,
3322 # # d_if,
3323 # # d_cond_marker,
3325 self._update_refs()
3327 # Highlighting.
3328 self._outline = None
3330 # Item cross-referencing.
3331 self._marker = None
3332 self._container = None
3334 # Event handling.
3335 # # d_cond_marker.connect("event", lambda *a: self.insert_event(*a))
3337 # Borders etc.
3338 self.init_header(rentab)
3339 self.init_deco(rentab)
3341 pass
3342 l3IfOutline.__init__ = __init__
3344 def __init__(self, w_, cnvs, tree_id, rentab, matcher):
3345 # l3 tree.
3346 l3tree = w_.state_.storage.load(tree_id)
3347 assert isinstance(l3tree, ast.If)
3348 l3Nested.__init__(self, w_, cnvs, l3tree, rentab)
3350 # Bindings to keep.
3351 self.w_ = w_
3352 self._canvas = cnvs
3353 self._marker_points = {} # marker -> point list
3354 self._matcher = ma = matcher
3355 self._l3tree = l3tree
3356 self._deco_cb_buffer = [] # callback list for add_item()
3359 # Display elements.
3360 d_loop = Label(w_, cnvs, self._root_group, "loop")
3361 d_body = l3aList(w_, cnvs,
3362 ma['LBODY'],
3363 rentab,
3364 root_group = self._root_group,
3365 draggable = False,
3366 parent = self,
3369 # Marker(s).
3370 # # d_cond_marker = self.draw_marker()
3371 # # d_cond_marker.lower_to_bottom()
3373 # Inform obj of marker.
3374 # # d_cond.setup_marker(d_cond_marker, self)
3377 # Align with respect to d_loop.
3379 flush_events()
3381 ll, tl, rl, bl = d_loop.get_bounds()
3382 lb, tb, rb, bb = d_body.get_bounds()
3383 d_body.move(ll - lb, bl - tb)
3385 # COND marker for child replacement.
3386 # # _, y, x, _ = d_if.get_bounds()
3387 # # u, v, _, _ = d_cond_marker.get_bounds()
3388 # # d_cond_marker.move(x - u + w_.cp_.exp_marker_hoff,
3389 # # y - v + w_.cp_.exp_marker_voff)
3391 # Bindings to keep.
3392 # Indexed access. Match l3tree indexing where applicable.
3393 self._d_elements = [
3394 # l3 ast.
3395 d_loop,
3396 d_body,
3398 # Rest
3399 # # d_else,
3400 # # d_if,
3401 # # d_cond_marker,
3403 self._update_refs()
3405 # Highlighting.
3406 self._outline = None
3408 # Item cross-referencing.
3409 self._marker = None
3410 self._container = None
3412 # Event handling.
3413 # # d_cond_marker.connect("event", lambda *a: self.insert_event(*a))
3415 # Borders etc.
3416 self.init_header(rentab)
3417 self.init_deco(rentab)
3419 pass
3420 l3IfLoop.__init__ = __init__
3422 def _update_refs(self):
3423 (self._d_loop,
3424 self._d_body ,
3425 ) = self._d_elements
3426 l3IfLoop._update_refs = _update_refs
3429 def __init__(self, w_, cnvs, tree_id, rentab, matcher):
3430 # l3 tree.
3431 l3tree = w_.state_.storage.load(tree_id)
3432 assert isinstance(l3tree, ast.If)
3433 l3Nested.__init__(self, w_, cnvs, l3tree, rentab)
3435 # Bindings to keep.
3436 self.w_ = w_
3437 self._canvas = cnvs
3438 self._marker_points = {} # marker -> point list
3439 self._matcher = ma = matcher
3440 self._l3tree = l3tree
3441 self._deco_cb_buffer = [] # callback list for add_item()
3444 # Display elements.
3445 d_loop = Label(w_, cnvs, self._root_group, "while")
3447 d_cond = cnvs.add_l3tree(ma.get_cond(), rentab)
3448 d_cond.reparent(self)
3450 d_body = l3aList(w_, cnvs,
3451 ma['BODY'],
3452 rentab,
3453 root_group = self._root_group,
3454 draggable = False,
3455 parent = self,
3458 # Marker(s).
3459 d_cond_marker = self.draw_marker()
3460 d_cond_marker.lower_to_bottom()
3462 # Inform obj of marker.
3463 d_cond.setup_marker(d_cond_marker, self)
3466 # Align with respect to d_loop.
3468 flush_events()
3470 llo, tlo, rlo, blo = d_loop.get_bounds()
3471 lco, tco, rco, bco = d_cond.get_bounds()
3472 d_cond.move(rlo + w_.cp_.loop_cond_sep - lco, tlo - tco)
3474 lco, tco, rco, bco = d_cond.get_bounds()
3475 lbo, tbo, rbo, bbo = d_body.get_bounds()
3476 d_body.move(lco - lbo, bco - tbo)
3478 # COND marker for child replacement.
3479 lco, tco, rco, bco = d_cond.get_bounds()
3480 lma, tma, _, _ = d_cond_marker.get_bounds()
3481 d_cond_marker.move(lco - lma + w_.cp_.exp_marker_hoff,
3482 tco - tma + w_.cp_.exp_marker_voff)
3484 # Bindings to keep.
3485 # Indexed access. Match l3tree indexing where applicable.
3486 self._d_elements = [
3487 # l3 ast.
3488 d_cond,
3489 d_body,
3491 # Rest
3492 d_loop,
3493 d_cond_marker,
3495 self._update_refs()
3497 # Highlighting.
3498 self._outline = None
3500 # Item cross-referencing.
3501 self._marker = None
3502 self._container = None
3504 # Event handling.
3505 d_cond_marker.connect("event", lambda *a: self.insert_event(*a))
3507 # Borders etc.
3508 self.init_header(rentab)
3509 self.init_deco(rentab)
3511 pass
3512 l3IfWhile.__init__ = __init__
3514 def _update_refs(self):
3515 (self._d_cond,
3516 self._d_body,
3518 self._d_loop,
3519 self._d_cond_marker,
3520 ) = self._d_elements
3521 l3IfWhile._update_refs = _update_refs
3525 def __init__(self, w_, cnvs, tree_id, rentab):
3526 # l3 tree.
3527 l3tree = w_.state_.storage.load(tree_id)
3528 assert isinstance(l3tree, ast.If)
3529 l3Nested.__init__(self, w_, cnvs, l3tree, rentab)
3531 # Bindings to keep.
3532 self.w_ = w_
3533 self._canvas = cnvs
3534 self._marker_points = {} # marker -> point list
3535 self._l3tree = l3tree
3538 # Display elements.
3539 self._d_label = Label(w_, cnvs, self._root_group, "break")
3541 # Flow edge drawing.
3542 # The actual line is drawn after the enclosing l3IfLoop is
3543 # complete.
3544 def search_l3ifloop():
3545 for l3pic in cnvs.displayed_parents(l3tree):
3546 if isinstance(l3pic, l3IfLoop):
3547 return l3pic
3548 return None
3549 ifloop = search_l3ifloop()
3551 if ifloop is not None:
3552 def body():
3553 return BreakLine(w_, cnvs, self, ifloop)
3554 ifloop._deco_cb_buffer.append(body)
3555 # The line must not be drawn after self (or ifloop) is
3556 # deleted, or self (or an intermediate tree containing self)
3557 # is detached from ifloop.
3558 wref_ifloop = weakref.ref(ifloop)
3559 def rm_linedraw():
3560 if wref_ifloop() is None: return
3561 try:
3562 wref_ifloop()._deco_cb_buffer.remove(body)
3563 except ValueError: # Body removed already.
3564 pass
3565 self.add_reparent_to_root_hook(rm_linedraw)
3566 # Parents up to ifloop (excluded).
3567 tw = ast.TreeWork(self.w_.state_.storage)
3568 for par in tw.all_parents(l3tree):
3569 if par is ifloop._l3tree: break
3570 l3pic = cnvs._nodes.get(par._id)
3571 if l3pic is not None:
3572 l3pic.add_reparent_to_root_hook(rm_linedraw)
3574 # Highlighting.
3575 self._outline = None
3577 # Item cross-referencing.
3578 self._marker = None
3579 self._container = None
3581 # Event handling.
3582 # # d_cond_marker.connect("event", lambda *a: self.insert_event(*a))
3584 # Borders etc.
3585 self.init_header(rentab)
3586 self.init_deco(rentab)
3588 pass
3589 l3IfLoopBreak.__init__ = __init__
3591 def __init__(self, w_, cnvs, tree_id, rentab):
3592 # l3 tree.
3593 l3tree = w_.state_.storage.load(tree_id)
3594 assert isinstance(l3tree, ast.If)
3595 l3Nested.__init__(self, w_, cnvs, l3tree, rentab)
3597 # Bindings to keep.
3598 self.w_ = w_
3599 self._canvas = cnvs
3600 self._marker_points = {} # marker -> point list
3601 self._l3tree = l3tree
3604 # Display elements.
3605 self._d_label = Label(w_, cnvs, self._root_group, "continue")
3607 # Flow edge drawing.
3608 # The actual line is drawn after the enclosing l3IfLoop is
3609 # complete.
3610 def search_l3ifloop():
3611 for l3pic in cnvs.displayed_parents(l3tree):
3612 if isinstance(l3pic, l3IfLoop):
3613 return l3pic
3614 return None
3615 ifloop = search_l3ifloop()
3617 if ifloop is not None:
3618 def body():
3619 return ContinueLine(w_, cnvs, self, ifloop)
3620 ifloop._deco_cb_buffer.append(body)
3621 # The line must not be drawn after self (or ifloop) is
3622 # deleted, or self (or an intermediate tree containing self)
3623 # is detached from ifloop.
3624 wref_ifloop = weakref.ref(ifloop)
3625 def rm_linedraw():
3626 print "******** rm_linedraw: removed ContinueLine"
3627 if wref_ifloop() is None: return
3628 try:
3629 wref_ifloop()._deco_cb_buffer.remove(body)
3630 except ValueError: # Body removed already.
3631 pass
3632 self.add_reparent_to_root_hook(rm_linedraw)
3633 # Parents up to ifloop (excluded).
3634 tw = ast.TreeWork(self.w_.state_.storage)
3635 for par in tw.all_parents(l3tree):
3636 if par is ifloop._l3tree: break
3637 l3pic = cnvs._nodes.get(par._id)
3638 if l3pic is not None:
3639 l3pic.add_reparent_to_root_hook(rm_linedraw)
3641 # Highlighting.
3642 self._outline = None
3644 # Item cross-referencing.
3645 self._marker = None
3646 self._container = None
3648 # Event handling.
3649 # # d_cond_marker.connect("event", lambda *a: self.insert_event(*a))
3651 # Borders etc.
3652 self.init_header(rentab)
3653 self.init_deco(rentab)
3655 pass
3656 l3IfLoopContinue.__init__ = __init__
3659 def __init__(self, w_, cnvs, tree_id, rentab):
3660 # layout:
3661 # +---------+
3662 # |if||COND |
3663 # +---------+
3664 # +----------+
3665 # | YES |
3666 # +----------+
3667 # +----+
3668 # |else|
3669 # +----+
3670 # +----------+
3671 # | NO |
3672 # +----------+
3675 # l3 tree.
3676 l3tree = w_.state_.storage.load(tree_id)
3677 assert isinstance(l3tree, ast.If)
3678 l3Nested.__init__(self, w_, cnvs, l3tree, rentab)
3680 # Bindings to keep.
3681 self.w_ = w_
3682 self._canvas = cnvs
3683 self._marker_points = {} # marker -> point list
3686 # Display elements.
3688 # if
3689 d_if = Label(w_, cnvs, self._root_group, "If")
3691 # COND
3692 d_cond = cnvs.add_l3tree(l3tree[0], rentab)
3693 d_cond.reparent(self)
3695 # YES
3696 d_yes = l3aList(w_, cnvs,
3697 l3tree[1],
3698 rentab,
3699 root_group = self._root_group,
3700 draggable = False,
3701 parent = self,
3704 # else
3705 d_else = Label(w_, cnvs, self._root_group, "else")
3707 # NO
3708 d_no = l3aList(w_, cnvs,
3709 l3tree[2],
3710 rentab,
3711 root_group = self._root_group,
3712 draggable = False,
3713 parent = self,
3716 # Marker(s).
3717 d_cond_marker = self.draw_marker()
3718 d_cond_marker.lower_to_bottom()
3720 # Inform obj of marker.
3721 d_cond.setup_marker(d_cond_marker, self)
3724 # Align with respect to IF.
3726 flush_events()
3727 # COND
3728 _, y, x, _ = d_if.get_bounds()
3729 u, v, _, _ = d_cond.get_bounds()
3730 d_cond.move(x - u, y - v)
3732 # COND marker for child replacement.
3733 _, y, x, _ = d_if.get_bounds()
3734 u, v, _, _ = d_cond_marker.get_bounds()
3735 d_cond_marker.move(x - u + w_.cp_.exp_marker_hoff,
3736 y - v + w_.cp_.exp_marker_voff)
3738 # YES
3739 x, _, _, y = d_cond.get_bounds()
3740 u, v, _, _ = d_yes.get_bounds()
3741 d_yes.move(x - u, y - v)
3743 # else
3744 x, _, _, _ = d_if.get_bounds()
3745 _, _, _, y = d_yes.get_bounds()
3746 u, v, _, _ = d_else.get_bounds()
3747 d_else.move(x - u, y - v)
3749 # NO
3750 _, _, x, _ = d_if.get_bounds()
3751 _, _, _, y = d_else.get_bounds()
3752 u, v, _, _ = d_no.get_bounds()
3753 d_no.move(x - u, y - v)
3755 # Bindings to keep.
3756 # Indexed access. Match l3tree indexing where applicable.
3757 self._d_elements = [
3758 # l3 ast.
3759 d_cond,
3760 d_yes,
3761 d_no,
3763 # Rest
3764 d_else,
3765 d_if,
3766 d_cond_marker,
3768 self._update_refs()
3770 self._l3tree = l3tree
3772 # Highlighting.
3773 self._outline = None
3775 # Item cross-referencing.
3776 self._marker = None
3777 self._container = None
3779 # Event handling.
3780 d_cond_marker.connect("event", lambda *a: self.insert_event(*a))
3782 # Borders etc.
3783 self.init_header(rentab)
3784 self.init_deco(rentab)
3786 pass
3787 l3If.__init__ = __init__
3789 def _update_refs(self):
3790 (self._d_cond,
3791 self._d_yes ,
3792 self._d_no ,
3794 self._d_else,
3795 self._d_if ,
3796 self._d_cond_marker,
3797 ) = self._d_elements
3798 l3If._update_refs = _update_refs
3801 def __init__(self, w_, cnvs, tree_id, rentab):
3802 # layout:
3803 # +-----+
3804 # |func | selector
3805 # +-----+
3806 # +----------+
3807 # | args |
3808 # +----------+
3810 # l3 tree.
3811 l3tree = w_.state_.storage.load(tree_id)
3812 assert isinstance(l3tree, ast.Call)
3813 l3Nested.__init__(self, w_, cnvs, l3tree, rentab)
3815 # Bindings to keep.
3816 self.w_ = w_
3817 self._canvas = cnvs
3818 self._marker_points = {} # marker -> point list
3821 # Display elements.
3823 # func
3824 d_func = cnvs.add_l3tree(l3tree[0], rentab)
3825 d_func.reparent(self)
3827 # Selector
3828 d_selector = Label(w_, cnvs, self._root_group, " () ")
3830 # args
3831 d_args = l3aList(w_, cnvs,
3832 l3tree[1],
3833 rentab,
3834 root_group = self._root_group,
3835 draggable = False,
3836 parent = self,
3839 # Marker(s).
3840 d_func_marker = self.draw_marker()
3841 d_func_marker.lower_to_bottom()
3843 # Inform obj of marker.
3844 d_func.setup_marker(d_func_marker, self)
3847 # Align with respect to FUNC.
3849 flush_events()
3850 # ARGS
3851 lf, tf, rf, bf = d_func.get_bounds()
3852 u, v, _, _ = d_args.get_bounds()
3853 d_args.move(lf - u, bf - v)
3855 # selector
3856 ls, ts, rs, bs = d_selector.get_bounds()
3857 d_selector.move(rf - ls, tf - ts)
3859 # FUNC marker for child replacement.
3860 x, y, _, _ = d_func.get_bounds()
3861 u, v, _, _ = d_func_marker.get_bounds()
3862 d_func_marker.move(x - u + w_.cp_.exp_marker_hoff,
3863 y - v + w_.cp_.exp_marker_voff)
3865 # Bindings to keep.
3866 # Indexed access. Match l3tree indexing where applicable.
3867 self._d_elements = [
3868 # l3 ast.
3869 d_func,
3870 d_args,
3872 # Rest
3873 d_func_marker,
3874 d_selector,
3876 self._update_refs()
3878 self._l3tree = l3tree
3880 # Highlighting.
3881 self._outline = None
3883 # Item cross-referencing.
3884 self._marker = None
3885 self._container = None
3887 # Event handling.
3888 d_func_marker.connect("event", lambda *a: self.insert_event(*a))
3890 # Borders etc.
3891 self.init_header(rentab)
3892 self.init_deco(rentab)
3894 pass
3895 l3Call.__init__ = __init__
3897 def _update_refs(self):
3898 ( self._d_func,
3899 self._d_args,
3901 self._d_func_marker,
3902 self._d_selector,
3903 ) = self._d_elements
3904 l3Call._update_refs = _update_refs
3907 #** misc.
3908 def _func_shift(self):
3909 # Shifts needed when FUNC size changes
3910 if self._d_func:
3911 _, _, fr, fb = self._d_func.get_bounds()
3912 else:
3913 _, _, fr, fb = self._d_func_marker.get_bounds()
3914 # FUNC
3915 _, at, _, _ = self._d_args.get_bounds()
3916 dy = fb - at # Shift between OLD d_args and NEW d_func.
3917 self._d_args.move(0, dy)
3918 # SELECTOR
3919 sl, _, _, _ = self._d_selector.get_bounds()
3920 self._d_selector.move(fr - sl, 0)
3921 l3Call._func_shift = _func_shift
3924 def _cond_shift(self):
3925 # Shifts needed when COND size changes
3926 if self._d_cond:
3927 _, _, _, y = self._d_cond.get_bounds()
3928 else:
3929 _, _, _, y = self._d_cond_marker.get_bounds()
3930 _, v, _, _ = self._d_yes.get_bounds()
3931 dy = y - v # Shift between OLD d_yes and NEW d_cond.
3932 self._d_yes.move(0, dy)
3933 self._d_else.move(0, dy)
3934 self._d_no.move(0, dy)
3935 l3If._cond_shift = _cond_shift
3937 def _cond_shift(self):
3938 # Shifts needed when COND size changes
3939 if self._d_cond:
3940 _, _, _, y = self._d_cond.get_bounds()
3941 else:
3942 _, _, _, y = self._d_cond_marker.get_bounds()
3943 _, v, _, _ = self._d_body.get_bounds()
3944 dy = y - v # Shift between OLD d_yes and NEW d_cond.
3945 self._d_body.move(0, dy)
3946 l3IfWhile._cond_shift = _cond_shift
3949 def _align_to_lhs(self):
3951 # Align.
3953 # +------A---------A------+
3954 # | lhs | equal | rhs |
3955 # +------+---------+------+
3956 flush_events()
3958 # LHS marker
3959 x, y, _, _ = self._d_lhs.get_bounds()
3960 u, _, _, v = self._d_lhs_marker.get_bounds()
3961 self._d_lhs_marker.move(x - u + self.w_.cp_.exp_marker_hoff,
3962 y + self._canvas.font_ascent_units() +
3963 self._canvas.bbox_offset_units() - v )
3965 # EQUAL
3966 _, y, x, _ = self._d_lhs.get_bounds()
3967 u, v, _, _ = self._d_equal.get_bounds()
3968 self._d_equal.move(x - u, y - v)
3970 # RHS
3971 if self._d_rhs != None:
3972 _, y, x, _ = self._d_equal.get_bounds()
3973 u, v, _, _ = self._d_rhs.get_bounds()
3974 self._d_rhs.move(x - u, y - v)
3976 # RHS marker
3977 # also see l3Set.insert
3978 x, y, _, _ = self._d_rhs.get_bounds()
3979 u, _, _, v = self._d_rhs_marker.get_bounds()
3980 ### self._canvas.bbox_wait(self._d_rhs_marker, self._d_rhs)
3981 self._d_rhs_marker.move(x - u + self.w_.cp_.exp_marker_hoff,
3982 y - v + self._canvas.font_ascent_units() +
3983 self._canvas.bbox_offset_units()
3985 l3Set._align_to_lhs = _align_to_lhs
3988 #** Events
3992 #* class l3IfFor members
3994 class l3IfFor(l3Nested):
3995 # Layout:
3996 # for V in SEQ
3999 pass
4002 #* l3Function
4003 class l3Function(l3Nested):
4004 pass
4007 #** init
4008 def __init__(self, w_, cnvs, tree_id, rentab):
4010 # The blandest layout:
4012 # +-----------+
4013 # | function |
4014 # +-----------+
4015 # ----+------+
4016 # | args |
4017 # +------+
4018 # ----+------+
4019 # | body |
4020 # +------+
4022 # l3 tree.
4023 l3tree = w_.state_.storage.load(tree_id)
4024 assert isinstance(l3tree, ast.Function)
4025 l3Nested.__init__(self, w_, cnvs, l3tree, rentab)
4027 # Bindings to keep.
4028 self.w_ = w_
4029 self._canvas = cnvs
4032 # Display elements.
4034 # function
4035 d_function = Label(w_, cnvs, self._root_group, "Function")
4037 # ARGS
4038 d_args = l3aList(w_, cnvs,
4039 l3tree[0],
4040 rentab,
4041 root_group = self._root_group,
4042 draggable = False,
4043 parent = self,
4045 ### incorporate into cnvs.add_l3tree(l3tree[1]) ??
4047 # BODY
4048 d_body = l3aList(w_, cnvs,
4049 l3tree[1],
4050 rentab,
4051 root_group = self._root_group,
4052 draggable = False,
4053 parent = self,
4055 ### incorporate into cnvs.add_l3tree(l3tree[1]) ??
4058 # Marker(s).
4062 # Inform obj of marker.
4066 # Align with respect to FUNCTION.
4068 flush_events()
4070 # ARGS
4071 x, _, _, y = d_function.get_bounds()
4072 u, v, _, _ = d_args.get_bounds()
4073 d_args.move(x - u, y - v)
4075 # BODY
4076 x, _, _, y = d_args.get_bounds()
4077 u, v, _, _ = d_body.get_bounds()
4078 d_body.move(x - u, y - v)
4080 # Bindings to keep.
4081 self._d_function = d_function
4082 self._d_args = d_args
4083 self._d_body = d_body
4084 self._l3tree = l3tree
4086 # Highlighting.
4087 self._outline = None
4089 # Item cross-referencing.
4090 self._marker = None
4091 self._container = None
4093 # Event handling.
4095 # Borders etc.
4096 self.init_header(rentab)
4097 self.init_deco(rentab)
4099 pass
4100 l3Function.__init__ = __init__
4104 #** events
4106 #*** no insertion
4109 #** no insert
4112 #** no detach
4115 #** no draw marker
4118 #* l3Inline
4119 class l3Inline(l3Nested):
4120 pass
4122 def __init__(self, w_, cnvs, tree_id, rentab):
4123 # layout:
4124 # inline STR
4126 # Also see l3Program / l3If.
4128 # l3 tree.
4129 l3tree = w_.state_.storage.load(tree_id)
4130 assert isinstance(l3tree, ast.Inline)
4131 l3Nested.__init__(self, w_, cnvs, l3tree, rentab)
4133 # Bindings to keep.
4134 self.w_ = w_
4135 self._canvas = cnvs
4136 self._l3tree = l3tree
4137 self._marker_points = {} # marker -> point list
4139 # Display elements.
4140 # Label.
4141 head = Label(w_, cnvs, self._root_group, "Inline")
4143 # Python string.
4144 pystr = cnvs.add_l3tree(l3tree[0], rentab)
4145 pystr.reparent(self)
4147 # Marker(s).
4148 pystr_marker = self.draw_marker()
4149 pystr_marker.lower_to_bottom()
4151 # Bindings to keep.
4152 self._pystr = pystr
4153 self._pystr_marker = pystr_marker
4154 self._head = head
4156 # Inform obj of marker.
4157 pystr.setup_marker(pystr_marker, self)
4159 # Align flush left.
4160 # pystr
4161 flush_events()
4162 u, _, _, v = head.get_bounds()
4163 x, y, _, _ = pystr.get_bounds()
4164 head.move(x - u, y - v)
4165 # pystr_marker
4166 sl, st, _, _ = pystr.get_bounds()
4167 ml, mt, _, _ = pystr_marker.get_bounds()
4168 pystr_marker.move(sl - ml + w_.cp_.exp_marker_hoff,
4169 st - mt + w_.cp_.exp_marker_voff)
4171 # Highlighting.
4172 self._outline = None
4174 # Item cross-referencing.
4175 self._marker = None
4176 self._container = None
4178 # Event handling.
4179 ### pystr_marker.connect("event", lambda *a: self.insert_event(*a))
4181 # Popup menu additions
4183 # Borders etc.
4184 self.init_header(rentab)
4185 self.init_deco(rentab)
4186 pass
4187 l3Inline.__init__ = __init__
4190 def rebuild_tree(self, text):
4191 # Form new tree.
4192 print 'inline text update finished'
4194 ### trap parse errors here
4195 tree = reader.parse("'''\n%s\n'''" % text).body()
4196 tree.setup(self._l3tree, self.w_.state_.def_env, self.w_.state_.storage)
4197 tree.set_outl_edges(self.w_, None)
4199 # Replace original raw String().
4200 self._l3tree[0].deep_replace(tree, self.w_.state_.storage)
4201 l3Inline.rebuild_tree = rebuild_tree
4203 #* l3Native
4204 class l3Native(l3Nested):
4205 pass
4207 def __init__(self, w_, cnvs, tree_id, rentab):
4208 # layout:
4209 # header
4210 # value
4212 # Also see l3Inline / l3Program / l3If.
4214 # l3 tree.
4215 l3tree = w_.state_.storage.load(tree_id)
4216 assert isinstance(l3tree, ast.Native)
4217 l3Nested.__init__(self, w_, cnvs, l3tree, rentab)
4219 # Bindings to keep.
4220 self.w_ = w_
4221 self._canvas = cnvs
4222 self._l3tree = l3tree
4224 value = l3tree.value()
4226 # Display elements.
4227 # Label.
4228 val_gen = w_.state_.storage.generator_of(id(value))
4229 if 0:
4230 if val_gen is None:
4231 head = Label(w_, cnvs, self._root_group, "External value")
4232 else:
4233 head = Label(w_, cnvs, self._root_group,
4234 "Value from %d" %
4235 w_.state_.storage.generator_of(id(value))._id
4238 head = uBlank(w_, cnvs, self._root_group)
4240 # todo: actively link the label to the source expression.
4241 # This way, the native display can provide other functions itself,
4242 # and can be a full widget with independent event handling if
4243 # desired.
4245 # Python value.
4246 # Note: canvas operations will destroy these value containers
4247 # occasionally, so values that are gtk widgets can be displayed
4248 # ONLY ONCE.
4250 # Avoid 'import EMAN2' dependency
4251 # Display emdata as image.
4252 if (value.__class__.__name__ == 'EMData'):
4253 # For now, put an image handler here. Add a general handler
4254 # later.
4255 pystr = Image(w_, cnvs, self._root_group, misc.emdata2pixbuf(value),
4256 parent = self)
4258 # Display .png file as image ( display Native(String("foo.png")) )
4259 elif isinstance(value, ast.String) and value.isfile() and \
4260 value.endswith(".png"):
4261 pystr = Image(w_, cnvs, self._root_group,
4262 misc.png2pixbuf(value.py_string()), parent = self)
4264 # Display matplotlib figures via in widgets.
4265 elif (value.__class__.__name__ == 'Figure'):
4266 from matplotlib.backends.backend_gtk import FigureCanvasGTK \
4267 as FigureCanvas
4268 pystr = Widget(w_, cnvs, self._root_group,
4269 FigureCanvas(value), parent = self)
4271 # Avoid numpy dependency.
4272 elif (value.__class__.__name__ == 'ndarray'):
4273 if len(value.shape) == 2:
4274 pystr = Image(w_, cnvs, self._root_group,
4275 misc.arr2pix(value), parent = self)
4276 else:
4277 pystr = Label(w_, cnvs, self._root_group,
4278 misc.escape_markup(repr(value)))
4279 # Generic display.
4280 else:
4281 pystr = Label(w_, cnvs, self._root_group,
4282 misc.escape_markup(str(value)))
4284 # Marker(s).
4286 # Bindings to keep.
4287 self._pystr = pystr
4288 self._head = head
4290 # Inform obj of marker.
4292 # Align flush left.
4293 # pystr
4294 flush_events()
4295 l1, _, _, b1 = head.get_bounds()
4296 l2, t2, _, _ = pystr.get_bounds()
4297 pystr.move(l1 - l2, b1 - t2)
4298 # pystr_marker
4300 # Highlighting.
4301 self._outline = None
4303 # Item cross-referencing.
4304 self._marker = None
4305 self._container = None
4307 # Event handling.
4309 # Popup menu additions
4310 if isinstance(pystr, Image):
4311 map(self._pup.prepend, [
4312 [gtk.SeparatorMenuItem(), None],
4314 [ gtk.MenuItem('set image size as default'),
4315 ( 'activate', lambda *args: pystr.set_default_size())],
4317 [ gtk.MenuItem('shrink image 40%'),
4318 ('activate', lambda *args: pystr.scale(0.6))],
4320 [ gtk.MenuItem('enlarge image 40%'),
4321 ('activate', lambda *args: pystr.scale(1.4))],
4325 # Borders etc.
4326 self.init_header(rentab)
4327 self.init_deco(rentab)
4328 pass
4329 l3Native.__init__ = __init__
4332 #* l3Program
4333 class l3Program(l3Nested):
4334 pass
4336 def __init__(self, w_, cnvs, tree_id, rentab):
4337 # l3 tree.
4338 l3tree = w_.state_.storage.load(tree_id)
4339 if isinstance(self, l3Program):
4340 assert isinstance(l3tree, ast.Program)
4341 else:
4342 assert isinstance(l3tree, (ast.Map, ast.cls_viewList))
4343 l3Nested.__init__(self, w_, cnvs, l3tree, rentab)
4346 #*** Choose display elements.
4347 # alist.
4348 if l3tree._outl_type == 'subtree':
4349 # Full display (the l3List default).
4350 alist = l3aList(w_, cnvs,
4351 l3tree[0],
4352 rentab,
4353 root_group = self._root_group,
4354 draggable = False,
4355 parent = self,
4357 elif l3tree._outl_type == 'flat':
4358 # This heading only; no body additions allowed.
4359 setup_alist = l3tree[0].setup_alist(w_, l3tree[0])
4360 alist = l3aList(w_, cnvs,
4361 setup_alist,
4362 rentab,
4363 root_group = self._root_group,
4364 draggable = False,
4365 parent = self,
4366 insertion_on_markers = False,
4367 hide_markers = True,
4369 elif l3tree._outl_type == 'nested':
4370 # heading + subheadings only; no body additions/insertions
4371 # allowed. Removal is ok.
4372 alist = l3aList(w_, cnvs,
4373 l3tree._outl_children,
4374 rentab,
4375 root_group = self._root_group,
4376 draggable = False,
4377 parent = self,
4378 insertion_on_markers = False,
4379 hide_markers = True,
4381 else:
4382 raise Exception("Invalid _outl_type. Internal error.")
4384 #*** Form the label.
4385 level, index = l3tree.heading_index()
4386 # Use numbers for even levels, letters for odd.
4387 if (level % 2) == 0:
4388 outline_symb = string.ascii_letters[26 + index % 26]
4389 else:
4390 outline_symb = str(index + 1) # count from 1
4392 ttext = l3tree.deco_title_text()
4393 # Strip quotes.
4394 if ttext.startswith("'''"):
4395 ttext = ttext[3:-3]
4396 if ttext.startswith("'") or ttext.startswith('"'):
4397 ttext = ttext[1:-1]
4399 head_text = ("<b>" +
4400 outline_symb +
4401 ". " +
4402 ttext +
4403 "</b>")
4404 head = Label(w_, cnvs, self._root_group, head_text,
4405 font = w_.cp_.viewlist_head_font)
4407 #*** Expander
4409 # The actions cycle to the next view state.
4410 expander_fn, expander_action = {
4411 'flat': (uInvisibleSymbol,
4412 lambda *args: self.view_as('nested')),
4413 'nested': (uPartiallyVisible,
4414 lambda *args: self.view_as('subtree')),
4415 'subtree': (uVisibleSymbol,
4416 lambda *args: self.view_as('flat')),
4417 }[ l3tree.get_outline_type() ]
4419 expander = expander_fn(w_, cnvs, self._root_group,
4420 action = expander_action)
4422 # Align.
4423 # Expander Head
4424 # Alist
4425 flush_events() # Update bounding boxes
4426 LE, TE, RE, BE = expander.get_bounds()
4427 LH, TH, RH, BH = head.get_bounds()
4428 head.move(RE - LH, TE - TH)
4430 LA, TA, _, _ = alist.get_bounds()
4431 alist.move(LH - LA, BH - TA)
4433 # Events.
4434 head.connect("event", lambda *a: self.head_event(*a))
4436 # Bindings to keep.
4437 self.w_ = w_
4438 self._canvas = cnvs
4439 self._alist = alist
4440 self._head = head
4441 self._expander = expander
4442 self._l3tree = l3tree
4444 # Highlighting.
4445 self._outline = None
4447 # Item cross-referencing.
4448 self._marker = None
4449 self._container = None
4451 # Popup menu additions
4452 if isinstance(self, l3Program):
4453 map(self._pup.prepend, [
4454 # Program specials.
4455 [gtk.SeparatorMenuItem(), None],
4456 [ gtk.MenuItem("run code"),
4457 ( "activate", lambda *args: self.run_code())],
4460 map(self._pup.prepend, [
4461 # Outline specials.
4462 [ gtk.SeparatorMenuItem(), None],
4463 [ gtk.MenuItem('view full contents'),
4464 ( 'activate', lambda *args: self.view_as('subtree'))],
4465 [ gtk.MenuItem('view nested outlines'),
4466 ( 'activate', lambda *args: self.view_as('nested'))],
4467 [ gtk.MenuItem('view this level only'),
4468 ( 'activate', lambda *args: self.view_as('flat'))],
4469 [ gtk.SeparatorMenuItem(), None],
4470 [ gtk.MenuItem('show values written'),
4471 ( 'activate', lambda *args: self.show_written_names())],
4472 [ gtk.MenuItem('show values read'),
4473 ( 'activate', lambda *args: self.show_read_names())],
4477 # Borders etc.
4478 self.init_header(rentab)
4479 self.init_deco(rentab)
4480 pass
4481 l3Program.__init__ = __init__
4482 l3ViewList.__init__ = __init__
4485 def view_as(self, type):
4486 def body():
4487 self._l3tree.set_outline(type)
4488 tw = ast.TreeWork(self.w_.state_.storage)
4489 root = tw.find_root(self._l3tree)
4490 if root:
4491 root.set_outl_edges(self.w_, None)
4492 else:
4493 self._l3tree.set_outl_edges(self.w_, None)
4494 self.start_replace_visible()
4495 self.w_.with_fluids(body,
4496 position_tree = False)
4497 l3Program.view_as = view_as
4498 l3ViewList.view_as = view_as
4501 def show_written_names(self):
4502 from l3gui.l3canvas import RenderTable
4503 import re
4505 ignored_syms = re.compile(r'(IDX|ITEMS|LEN|LOOP)[0-9]+')
4507 ma = ast.Matcher()
4508 view, view_id = ast.viewList(aList([])).setup(
4509 empty_parent(),
4510 self._l3tree.eval_env() or self.w_.state_.def_env,
4511 self.w_.state_.storage)
4513 for nd in self._l3tree.top_down_truncate([ast.Function, ast.Call]):
4514 # Several choices exist concerning display of nested
4515 # bindings. These are not visible outside their scope in L3,
4516 # but can be accessed via the interface, and conceivably be
4517 # exported by the language in some way.
4518 # For simplicity, only show those names directly accessible
4519 # here.
4520 if ma.match(nd, Set(Marker('lhs'), Marker('rhs')) ):
4521 if isinstance(ma['lhs'], ast.Symbol):
4522 # Filter out internal names from FOR, WHILE, etc.
4523 if ignored_syms.match(ma['lhs'].py_string()) != None:
4524 continue
4526 node, nid = misc.node_copy(self.w_, ma['lhs'])
4527 view.append_child(node)
4529 view.set_outl_edges(self.w_, None)
4530 view.set_outline('subtree')
4532 # Add label.
4533 view.set_label('Values written by -->')
4535 # Display the structure.
4536 pic = self._canvas.add_l3tree(view, RenderTable())
4538 # Position to the left of expression.
4539 lp, tp, rp, bp = pic.get_bounds_world()
4540 lo, to, ro, bo = self.get_bounds_world()
4541 pic.move(lo - rp, to - tp)
4542 pass
4543 l3Program.show_written_names = show_written_names
4544 l3ViewList.show_written_names = show_written_names
4547 def show_read_names(self):
4548 from l3gui.l3canvas import RenderTable
4549 import re
4551 ignored_syms = re.compile(r'(IDX|ITEMS|LEN|LOOP)[0-9]+')
4552 valid_ident = re.compile(r'[a-zA-Z0-9_]+')
4553 ignored_lhs = [] # Symbols to ignore.
4554 shown = [] # Already included.
4555 ma = ast.Matcher()
4557 # Prepare display list.
4558 view, view_id = ast.viewList(aList([])).setup(
4559 empty_parent(),
4560 self._l3tree.eval_env() or self.w_.state_.def_env,
4561 self.w_.state_.storage)
4563 for nd in self._l3tree.top_down_truncate([]):
4564 if ma.match(nd, Set(Marker('lhs'), Marker('rhs')) ):
4565 if isinstance(ma['lhs'], ast.Symbol):
4566 # Ignore names assigned to.
4567 ignored_lhs.append(ma['lhs'])
4568 # TODO: ignore names bound in Function argument list.
4570 # Filter out internal names from FOR, WHILE, etc.
4571 if ignored_syms.match(ma['lhs'].py_string()) != None:
4572 ignored_lhs.append(ma['lhs'])
4574 if isinstance(nd, ast.Symbol) and \
4575 (nd not in ignored_lhs) and \
4576 (nd not in shown) and \
4577 valid_ident.match(nd.py_string()): # Ignore operators.
4578 node, nid = misc.node_copy(self.w_, nd)
4579 view.append_child(node)
4580 shown.append(nd)
4582 view.set_outl_edges(self.w_, None)
4583 view.set_outline('subtree')
4585 # Add label.
4586 view.set_label('Values read by -->')
4588 # Display the structure.
4589 pic = self._canvas.add_l3tree(view, RenderTable())
4591 # Position to the left of expression.
4592 lp, tp, rp, bp = pic.get_bounds_world()
4593 lo, to, ro, bo = self.get_bounds_world()
4594 pic.move(lo - rp, to - tp)
4595 pass
4596 l3Program.show_read_names = show_read_names
4597 l3ViewList.show_read_names = show_read_names
4599 def run_code(self):
4600 # Attach progress indicator to visible nodes.
4601 for nd in self.iter_visibles():
4602 nd._l3tree._pre_interp_hook = misc.DisplayInterp(self.w_, nd)
4604 # Execute the program, redirecting stdout/err to the message handler.
4605 w_ = self.w_
4606 sys.stdout.flush()
4607 sys.stderr.flush()
4609 w_.stdinouterr.push()
4610 if not w_.opts.gui.native_console:
4611 w_.message.std_to_widget()
4612 ## sys.settrace (w_.message.idle) # MAJOR slowdown!
4613 try:
4614 pprint(self._l3tree.interpret(w_.state_.def_env, w_.state_.storage))
4615 except:
4616 if w_.opts.gui.native_console:
4617 raise # allow debugging via pdb.pm()
4618 try:
4619 tb = sys.exc_traceback
4620 if tb:
4621 tb = tb.tb_next
4622 traceback.print_exception(sys.exc_type, sys.exc_value, tb)
4623 except:
4624 w_.message.std_to_default()
4625 traceback.print_exc()
4626 ## sys.settrace(None)
4627 # Restore stdin/out/err
4628 w_.stdinouterr.pop()
4629 # Remove progress indicators (assuming no editing happened during
4630 # execution...)
4631 for nd in self.iter_visibles():
4632 nd._l3tree._pre_interp_hook = None
4633 l3Program.run_code = run_code
4635 #* visit visible nodes
4636 # Visit visible nodes that have l3trees.
4637 # Taken from `def hide(...)` and adjusted. These need testing.
4638 def iter_visibles(self):
4639 yield self
4640 for ii in self._d_elements:
4641 for prop_iter in ii.iter_visibles():
4642 yield prop_iter
4643 l3Loop.iter_visibles = iter_visibles
4645 def iter_visibles(self):
4646 return []
4647 Label.iter_visibles = iter_visibles
4648 Image.iter_visibles = iter_visibles
4649 Widget.iter_visibles = iter_visibles
4650 uWidget.iter_visibles = iter_visibles
4651 Placeholder.iter_visibles = iter_visibles
4653 def iter_visibles(self):
4654 raise DisplayError("Interface only: " + str(self.__class__))
4655 l3Base.iter_visibles = iter_visibles
4657 def iter_visibles(self):
4658 # aLists are list subclasses and have no custom __deepcopy__, so
4659 # attaching anything to them causes copy problems during .interpret()
4660 ## yield self
4661 for ch in self._dlist:
4662 for prop_iter in ch.iter_visibles():
4663 yield prop_iter
4664 l3aList.iter_visibles = iter_visibles
4666 def iter_visibles(self):
4667 yield self
4668 for sub in self._subtrees:
4669 for prop_iter in sub.iter_visibles():
4670 yield prop_iter
4671 l3Rawtext.iter_visibles = iter_visibles
4673 def iter_visibles(self):
4674 yield self
4675 for prop_iter in self._alist.iter_visibles():
4676 yield prop_iter
4677 for prop_iter in self._head.iter_visibles():
4678 yield prop_iter
4679 l3List.iter_visibles = iter_visibles
4680 l3Map.iter_visibles = iter_visibles
4682 def iter_visibles(self):
4683 yield self
4684 if self._l3tree._outl_type in ['subtree', 'nested']:
4685 for prop_iter in self._alist.iter_visibles():
4686 yield prop_iter
4687 for prop_iter in self._head.iter_visibles():
4688 yield prop_iter
4689 elif self._l3tree._outl_type == 'flat':
4690 pass
4691 else:
4692 raise Exception("Invalid _outl_type. Internal error.")
4693 l3ViewList.iter_visibles = iter_visibles
4695 def iter_visibles(self):
4696 yield self
4697 for prop_iter in self._d_lhs.iter_visibles():
4698 yield prop_iter
4699 for prop_iter in self._d_equal.iter_visibles():
4700 yield prop_iter
4701 for prop_iter in self._d_rhs.iter_visibles():
4702 yield prop_iter
4703 l3Set.iter_visibles = iter_visibles
4705 def iter_visibles(self):
4706 yield self
4707 for prop_iter in self._d_label.iter_visibles():
4708 yield prop_iter
4709 l3IfLoopBreak.iter_visibles = iter_visibles
4710 l3IfLoopContinue.iter_visibles = iter_visibles
4712 def iter_visibles(self):
4713 yield self
4714 for prop_iter in self._d_loop.iter_visibles():
4715 yield prop_iter
4717 for prop_iter in self._d_body.iter_visibles():
4718 yield prop_iter
4719 l3IfLoop.iter_visibles = iter_visibles
4722 def iter_visibles(self):
4723 yield self
4724 for prop_iter in self._d_cond_V.iter_visibles():
4725 yield prop_iter
4727 for prop_iter in self._d_cond_SEQ.iter_visibles():
4728 yield prop_iter
4730 for prop_iter in self._d_body.iter_visibles():
4731 yield prop_iter
4732 l3IfFor.iter_visibles = iter_visibles
4734 def iter_visibles(self):
4735 yield self
4736 for prop_iter in self._d_loop.iter_visibles():
4737 yield prop_iter
4739 for prop_iter in self._d_cond.iter_visibles():
4740 yield prop_iter
4742 for prop_iter in self._d_body.iter_visibles():
4743 yield prop_iter
4744 l3IfWhile.iter_visibles = iter_visibles
4746 def iter_visibles(self):
4747 yield self
4748 for prop_iter in self._d_if.iter_visibles():
4749 yield prop_iter
4751 for prop_iter in self._d_cond.iter_visibles():
4752 yield prop_iter
4754 for prop_iter in self._d_yes.iter_visibles():
4755 yield prop_iter
4757 for prop_iter in self._d_else.iter_visibles():
4758 yield prop_iter
4760 for prop_iter in self._d_no.iter_visibles():
4761 yield prop_iter
4762 l3If.iter_visibles = iter_visibles
4764 def iter_visibles(self):
4765 yield self
4766 for prop_iter in self._d_func.iter_visibles():
4767 yield prop_iter
4769 for prop_iter in self._d_args.iter_visibles():
4770 yield prop_iter
4771 l3Call.iter_visibles = iter_visibles
4773 def iter_visibles(self):
4774 yield self
4775 for prop_iter in self._d_function.iter_visibles():
4776 yield prop_iter
4778 for prop_iter in self._d_args.iter_visibles():
4779 yield prop_iter
4781 for prop_iter in self._d_body.iter_visibles():
4782 yield prop_iter
4783 l3Function.iter_visibles = iter_visibles
4785 def iter_visibles(self):
4786 yield self
4787 for prop_iter in self._alist.iter_visibles():
4788 yield prop_iter
4790 for prop_iter in self._head.iter_visibles():
4791 yield prop_iter
4792 l3Program.iter_visibles = iter_visibles
4794 def iter_visibles(self):
4795 yield self
4796 for prop_iter in self._pystr.iter_visibles():
4797 yield prop_iter
4799 for prop_iter in self._head.iter_visibles():
4800 yield prop_iter
4801 l3Inline.iter_visibles = iter_visibles
4802 l3Native.iter_visibles = iter_visibles
4805 #* hiding
4806 def hide(self):
4807 self._root_group.hide()
4808 for ii in self._d_elements:
4809 ii.hide()
4810 l3Loop.hide = hide
4812 def hide(self):
4813 self._root_group.hide()
4814 Label.hide = hide
4815 Image.hide = hide
4816 Widget.hide = hide
4817 uWidget.hide = hide
4819 def hide(self):
4820 raise DisplayError("Interface only: " + str(self.__class__))
4821 l3Base.hide = hide
4823 def hide(self):
4824 self._root_group.hide()
4825 for ch in self._dlist:
4826 ch.hide()
4827 l3aList.hide = hide
4829 def hide(self):
4830 self._root_group.hide()
4831 self._ltext.hide()
4832 l3Rawtext.hide = hide
4834 def hide(self):
4835 self._root_group.hide()
4836 self._alist.hide()
4837 self._head.hide()
4838 l3List.hide = hide
4839 l3Map.hide = hide
4841 def hide(self):
4842 self._root_group.hide()
4843 self._d_lhs.hide()
4844 self._d_equal.hide()
4845 self._d_rhs.hide()
4846 l3Set.hide = hide
4848 def hide(self):
4849 self._root_group.hide()
4850 self._d_label.hide()
4851 l3IfLoopBreak.hide = hide
4852 l3IfLoopContinue.hide = hide
4854 def hide(self):
4855 self._root_group.hide()
4856 self._d_loop.hide()
4857 self._d_body.hide()
4858 l3IfLoop.hide = hide
4860 def hide(self):
4861 self._root_group.hide()
4862 self._d_loop.hide()
4863 self._d_cond.hide()
4864 self._d_body.hide()
4865 l3IfWhile.hide = hide
4867 def hide(self):
4868 self._root_group.hide()
4869 self._d_if.hide()
4870 self._d_cond.hide()
4871 self._d_yes.hide()
4872 self._d_else.hide()
4873 self._d_no.hide()
4874 l3If.hide = hide
4876 def hide(self):
4877 self._root_group.hide()
4878 self._d_func.hide()
4879 self._d_args.hide()
4880 l3Call.hide = hide
4882 def hide(self):
4883 self._root_group.hide()
4884 self._d_function.hide()
4885 self._d_args.hide()
4886 self._d_body.hide()
4887 l3Function.hide = hide
4889 def hide(self):
4890 self._root_group.hide()
4891 self._alist.hide()
4892 self._head.hide()
4893 l3Program.hide = hide
4895 def hide(self):
4896 self._root_group.hide()
4897 self._pystr.hide()
4898 self._head.hide()
4899 l3Inline.hide = hide
4900 l3Native.hide = hide
4903 #* insert subtree
4904 #** event handler
4905 def insert_event_shared(self, item, event):
4906 if event.type == gtk.gdk.BUTTON_PRESS:
4907 if event.button == 2:
4908 if not self.w_.selector.have_selection():
4909 self.w_.ten_second_message("No node selected.")
4910 return False
4911 else:
4912 self.insert(item, self.w_.selector.get_selection())
4913 return False
4914 l3Call.insert_event = insert_event_shared
4915 l3Set.insert_event = insert_event_shared
4916 l3If.insert_event = insert_event_shared
4917 l3IfWhile.insert_event = insert_event_shared
4920 #** inserter
4921 def append(self, newobj):
4922 self.insert(len(self._l3tree), newobj)
4923 l3aList.append = append
4925 def insert(self, index, newobj):
4926 ### Remove any existing references to newobj in the CALLER --
4927 ### distinguish between MOVE and COPY.
4929 assert isinstance(newobj, l3Base)
4931 # Avoid special cases:
4932 if newobj in self._dlist:
4933 self.w_.ten_second_message("Disconnect element before inserting "
4934 "into the same list.")
4935 return
4937 if newobj.contains_recursive(self):
4938 self.w_.ten_second_message("Cannot insert list into itself.")
4939 return
4941 if newobj._canvas != self._canvas:
4942 self.w_.ten_second_message("Cannot move objects across displays. "
4943 "Copy instead.")
4944 return
4946 # Reparent object.
4947 newobj.reparent(self)
4949 # Draw new marker over insertion marker.
4950 ox1, oy1 = marker_get_anchor(self.w_, self._marker_lst[index])
4952 if self._l3tree.getthe('layout') == 'horizontal':
4953 ox1 -= self.w_.cp_.marker_padding
4954 ## oy1 += self.w_.cp_.marker_padding
4955 else:
4956 oy1 -= self.w_.cp_.marker_padding
4958 if self._hide_markers:
4959 newmarker = self.draw_marker(ox1, oy1, hidden_color = 'white')
4960 else:
4961 newmarker = self.draw_marker(ox1, oy1)
4963 if self._insertion_on_markers:
4964 self.bind_marker_events(newmarker)
4966 # Adjust positions.
4967 if self._l3tree.getthe('layout') == 'horizontal':
4968 self._adj_h_insert(index, newobj, newmarker)
4969 else:
4970 self._adj_v_insert(index, newobj, newmarker)
4972 # Move following items in parent.
4973 if self._parent:
4974 self._parent.new_size_for(self)
4976 # Update data and marker lists.
4977 if self.w_.fluid_ref(insert_l3_tree = True):
4978 self._l3tree.insert_child_rec(index,
4979 newobj._l3tree,
4980 self.w_.state_.storage)
4981 if self.w_.fluid_ref(trace_gui_events = False):
4982 print "insertion tree"
4983 self._dlist.insert(index, newobj)
4984 self._marker_lst.insert(index, newmarker)
4986 self._mv_spacer()
4988 # inform obj of marker
4989 newobj.setup_marker(newmarker, self)
4990 pass
4991 l3aList.insert = insert
4993 def _adj_v_insert(self, index, newobj, newmarker):
4994 # Put object under marker.
4995 sl, st, sr, sb = marker_get_bounds(self.w_, newmarker).pad_tb()
4996 ol, ot, _, ob = newobj.get_bounds()
4997 newobj.move(sl - ol, sb - ot)
4999 # Move following markers, labels and objects.
5000 shift_y = (sb - st) + (ob - ot) ## + self.w_.cp_.marker_padding
5002 for itm in self._dlist[index : None]:
5003 itm.move(0, shift_y)
5005 for itm in self._marker_lst[index : None]:
5006 itm.move(0, shift_y)
5007 l3aList._adj_v_insert = _adj_v_insert
5009 def _adj_h_insert(self, index, newobj, newmarker):
5010 # Put object under marker.
5011 sl, st, sr, sb = marker_get_bounds(self.w_, newmarker).pad_lr()
5012 ol, ot, or_, ob = newobj.get_bounds()
5013 newobj.move(sr - ol, st - ot)
5015 # Move following markers, labels and objects.
5016 shift_x = (sr - sl) + (or_ - ol) ## + self.w_.cp_.marker_padding
5018 for itm in self._dlist[index : None]:
5019 itm.move(shift_x, 0)
5021 for itm in self._marker_lst[index : None]:
5022 itm.move(shift_x, 0)
5023 l3aList._adj_h_insert = _adj_h_insert
5026 def insert(self, marker, newobj):
5027 assert isinstance(newobj, l3Base)
5029 if marker != self._d_cond_marker:
5030 raise DisplayError("Insertion from wrong marker.")
5032 # Avoid special cases:
5033 if newobj.contains_recursive(self):
5034 self.w_.ten_second_message("Cannot insert if into itself.")
5035 return
5037 if newobj._canvas != self._canvas:
5038 self.w_.ten_second_message("Cannot move objects across displays. "
5039 "Copy instead.")
5040 return
5042 # Validate slot.
5043 old_entry = self._l3tree.deref(0)
5044 slot_available(self.w_, old_entry)
5046 # Update reference.
5047 self._d_cond = d_cond = newobj
5049 # Reparent object.
5050 newobj.reparent(self)
5052 # Position COND.
5053 flush_events()
5054 _, y, x, _ = self._d_if.get_bounds()
5055 u, v, _, _ = self._d_cond.get_bounds()
5056 d_cond.move(x + self.w_.cp_.loop_cond_sep - u, y - v)
5058 # Reposition rest.
5059 self._cond_shift()
5061 # Refresh decoration.
5062 self.refresh_deco()
5064 # Move following items in parent.
5065 if self._parent:
5066 self._parent.new_size_for(self)
5068 # Update tree data.
5069 if self.w_.fluid_ref(insert_l3_tree = True):
5070 self._l3tree.replace_child(old_entry._id, newobj._l3tree)
5072 # Inform obj of marker.
5073 newobj.setup_marker(self._d_cond_marker, self)
5074 pass
5075 l3If.insert = insert
5078 def insert(self, marker, newobj):
5080 Insert new condition.
5082 assert isinstance(newobj, l3Base)
5084 if marker != self._d_cond_marker:
5085 raise DisplayError("Insertion from wrong marker.")
5087 # Avoid special cases:
5088 if newobj.contains_recursive(self):
5089 self.w_.ten_second_message("Cannot insert if into itself.")
5090 return
5092 if newobj._canvas != self._canvas:
5093 self.w_.ten_second_message("Cannot move objects across displays. "
5094 "Copy instead.")
5095 return
5097 # Validate slot.
5098 if len(self._matcher['COND-PARENT']) != 0:
5099 raise DisplayError("Insertion in occupied slot.")
5101 # Update reference.
5102 self._d_cond = d_cond = newobj
5104 # Reparent object.
5105 newobj.reparent(self)
5107 # Position COND.
5108 flush_events()
5109 _, y, x, _ = self._d_loop.get_bounds()
5110 u, v, _, _ = self._d_cond.get_bounds()
5111 d_cond.move(x + self.w_.cp_.loop_cond_sep - u, y - v)
5113 # Reposition rest.
5114 self._cond_shift()
5116 # Refresh decoration.
5117 self.refresh_deco()
5119 # Move following items in parent.
5120 if self._parent:
5121 self._parent.new_size_for(self)
5123 # Update tree data.
5124 if self.w_.fluid_ref(insert_l3_tree = True):
5125 self._matcher['COND-PARENT'].insert_child(
5126 self._matcher['COND-IDX'], newobj._l3tree)
5127 ## from: self._l3tree.replace_child(old_entry._id, newobj._l3tree)
5129 # Inform obj of marker.
5130 newobj.setup_marker(self._d_cond_marker, self)
5131 pass
5132 l3IfWhile.insert = insert
5135 def insert(self, marker, newobj):
5136 assert isinstance(newobj, l3Base)
5138 if marker != self._d_func_marker:
5139 raise DisplayError("Insertion from wrong marker.")
5141 # Avoid special cases:
5142 if newobj.contains_recursive(self):
5143 self.w_.ten_second_message("Cannot insert if into itself.")
5144 return
5146 if newobj._canvas != self._canvas:
5147 self.w_.ten_second_message("Cannot move objects across displays. "
5148 "Copy instead.")
5149 return
5151 # Validate slot.
5152 old_entry = self._l3tree.deref(0)
5153 slot_available(self.w_, old_entry)
5155 # Update reference.
5156 self._d_func = d_func = newobj
5158 # Reparent object.
5159 newobj.reparent(self)
5161 # Position FUNC.
5162 flush_events()
5163 x, y, _, _ = self._d_func_marker.get_bounds()
5164 u, v, _, _ = self._d_func.get_bounds()
5165 d_func.move(x - u - self.w_.cp_.exp_marker_hoff,
5166 y - v - self.w_.cp_.exp_marker_voff)
5168 # Reposition rest.
5169 self._func_shift()
5171 # Update tree data.
5172 if self.w_.fluid_ref(insert_l3_tree = True):
5173 self._l3tree.replace_child(old_entry._id, newobj._l3tree)
5175 # Refresh decoration.
5176 self.refresh_deco()
5178 # Move following items in parent.
5179 if self._parent:
5180 self._parent.new_size_for(self)
5182 # Inform obj of marker.
5183 newobj.setup_marker(self._d_func_marker, self)
5184 pass
5185 l3Call.insert = insert
5188 def insert(self, marker, newobj):
5189 assert isinstance(newobj, l3Base)
5191 # Avoid special cases:
5192 if newobj.contains_recursive(self):
5193 self.w_.ten_second_message("Cannot insert if into itself.")
5194 return
5196 if newobj._canvas != self._canvas:
5197 self.w_.ten_second_message("Cannot move objects across displays. "
5198 "Copy instead.")
5199 return
5201 if marker == self._d_lhs_marker:
5202 # Validate slot.
5203 old_entry = self._l3tree.deref(0)
5204 slot_available(self.w_, old_entry)
5206 # Check and update reference.
5207 self._d_lhs = newobj
5209 # Reparent object.
5210 newobj.reparent(self)
5212 # Position lhs w.r.t. marker
5213 flush_events()
5214 x, y, _, _ = self._d_lhs_marker.get_bounds()
5215 u, v, _, _ = newobj.get_bounds()
5216 newobj.move(x - u - self.w_.cp_.exp_marker_hoff,
5217 y - v - self.w_.cp_.exp_marker_voff)
5219 # Reposition.
5220 self._align_to_lhs()
5222 # Update tree data.
5223 if self.w_.fluid_ref(insert_l3_tree = True):
5224 self._l3tree.replace_child(old_entry._id, newobj._l3tree)
5226 # Hide markers.
5227 self._d_lhs_marker.hide()
5229 elif marker == self._d_rhs_marker:
5230 # Validate slot.
5231 old_entry = self._l3tree.deref(1)
5232 slot_available(self.w_, old_entry)
5234 # Check and update reference.
5235 self._d_rhs = newobj
5237 # Reparent object.
5238 newobj.reparent(self)
5240 # Position rhs.
5241 # see also l3Set._align_to_lhs
5242 flush_events()
5243 x, _, _, y = self._d_rhs_marker.get_bounds()
5244 u, v, _, _ = newobj.get_bounds()
5245 newobj.move(x - u - self.w_.cp_.exp_marker_hoff,
5246 y - v - self._canvas.font_ascent_units() -
5247 self._canvas.bbox_offset_units()
5250 # Reposition.
5252 # Update tree data.
5253 if self.w_.fluid_ref(insert_l3_tree = True):
5254 self._l3tree.replace_child(old_entry._id, newobj._l3tree)
5256 # Hide markers.
5257 self._d_rhs_marker.hide()
5259 else:
5260 raise DisplayError("Insertion from invalid marker.")
5262 # Refresh decoration.
5263 self.refresh_deco()
5265 # Move following items in parent.
5266 if self._parent:
5267 self._parent.new_size_for(self)
5269 # Inform obj of marker.
5270 newobj.setup_marker(marker, self)
5272 pass
5273 l3Set.insert = insert
5277 #* movement
5278 def move(self, dx, dy):
5279 self._root_group.move(dx, dy)
5280 Label.move = move
5281 Image.move = move
5282 Widget.move = move
5283 uWidget.move = move
5285 def move(self, dx, dy):
5286 self._root_group.move(dx, dy)
5287 if self._outline:
5288 self._outline.move(dx, dy)
5289 l3Base.move = move
5291 def move(self, dx, dy):
5292 self._root_group.move(dx, dy)
5293 l3aList.move = move
5296 #* bounding boxes
5297 #** world
5298 def get_bounds_world(self):
5299 i2w = self._root_group.get_property("parent").i2w
5300 x1, y1, x2, y2 = self.get_bounds()
5301 u1, v1 = i2w(x1, y1)
5302 u2, v2 = i2w(x2, y2)
5303 return u1, v1, u2, v2
5304 l3Base.get_bounds_world = get_bounds_world
5305 Label.get_bounds_world = get_bounds_world
5306 Image.get_bounds_world = get_bounds_world
5307 Widget.get_bounds_world = get_bounds_world
5308 uWidget.get_bounds_world = get_bounds_world
5311 #** local
5312 def get_bounds(self):
5313 raise DisplayError("interface only")
5314 l3Base.get_bounds = get_bounds
5316 def get_bounds(self):
5317 ### magic bound offset adjustment hack
5318 x, y, u, v = self._root_group.get_bounds()
5320 # No adjustment when path is absent (like inserting or removing)
5321 if self._deco == None:
5322 # Good for "a = 2" (CanvasText at extremes), too much
5323 # for "<ph> = 2" (CanvasRect on left)
5324 return x + 0.5, y + 0.5, u - 0.5, v - 0.5
5325 # return x + 0.5, y + 0.5, u, v
5326 else:
5327 # With surrounding path decoration.
5328 cp_ = self.w_.cp_
5329 adj = (cp_.deco.outline_width / 2.0) * self._canvas._pixpu
5330 return x + adj, y + adj, u - adj, v - adj
5331 # with header
5332 # return x + 0.5, y + 0.5, u, v
5333 # return x + 0.5, y + 0.5, u - 0.5, v - 0.5
5334 # return x, y, u, v
5335 l3Nested.get_bounds = get_bounds
5337 def get_bounds(self):
5338 (ll, tt, rr, bb) = l3Nested.get_bounds(self)
5339 pad = self.w_.cp_.marker_padding
5340 return (ll, tt, rr + pad, bb + pad)
5341 l3Native.get_bounds = get_bounds
5343 # def get_bounds(self):
5344 # return self._root_group.get_bounds()
5345 # Placeholder.get_bounds = get_bounds
5347 def get_bounds(self):
5348 ### magic bound offset adjustment hack
5349 le, to, ri, bo = self._root_group.get_bounds()
5350 return le, to, ri, bo
5351 # return x + 0.5, y + 0.5, u - 0.5, v - 0.5
5352 l3aList.get_bounds = get_bounds
5354 def get_bounds(self):
5355 x, y, u, v = self._root_group.get_bounds()
5356 # No adjustment when path is absent.
5357 if self._deco == None:
5358 ### magic bound offset adjustment hack
5359 return x + 0.5, y + 0.5, u - 0.5, v - 0.5
5360 else:
5361 # With surrounding path decoration.
5362 cp_ = self.w_.cp_
5363 adj = (cp_.deco.outline_width / 2.0) * self._canvas._pixpu
5364 return x + adj, y + adj, u - adj, v - adj
5365 # with header
5366 l3Rawtext.get_bounds = get_bounds
5368 def get_bounds(self):
5369 ### magic bound offset adjustment hack
5370 x, y, u, v = self._root_group.get_bounds()
5371 return x + 0.5, y + 0.5, u - 0.5, v - 0.5
5372 Label.get_bounds = get_bounds
5373 uWidget.get_bounds = get_bounds
5376 # libart's bounding boxes are a serious problem.
5378 # A rotated rectangle changes bounding box size, which is not
5379 # desirable here. So use an ellipse as background.
5380 # This didn't help, because the triangle is a path, for which the
5381 # bounding box is wrong anyway.
5383 # For this purpose, using symbol glyphs is a better approach anyway,
5384 # but fonts are not handled very well either...
5386 # Time to switch drawing/canvas engine.
5388 def get_bounds(self):
5389 ### magic bound offset adjustment hack
5390 x, y, u, v = self._root_group.get_bounds()
5391 return x + 0.5, y + 0.5, u - 0.5, v - 0.5
5392 uVisibleSymbol.get_bounds = get_bounds
5393 uPartiallyVisible.get_bounds = get_bounds
5394 uInvisibleSymbol.get_bounds = get_bounds
5396 def get_bounds(self):
5397 x, y, u, v = self._root_group.get_bounds()
5398 return x, y, u, v
5399 Widget.get_bounds = get_bounds
5400 Image.get_bounds = get_bounds
5403 def get_bounds(self):
5404 return self._root_group.get_bounds()
5405 l3IfLoopContinue.get_bounds = get_bounds
5406 l3IfLoopBreak.get_bounds = get_bounds
5409 #* Draw insertion markers
5410 def draw_marker_shared(self):
5411 cp = self.w_.cp_
5412 points = [0 , 0,
5413 cp.exp_marker_width , 0,
5414 cp.exp_marker_width , cp.exp_marker_height,
5415 0 , cp.exp_marker_height,
5416 0 , 0,
5418 _marker = self._root_group.add(
5419 canvas.CanvasPolygon,
5420 fill_color = "black",
5421 points = points,
5422 width_units = 0.2,
5423 cap_style = gtk.gdk.CAP_ROUND,
5424 join_style = gtk.gdk.JOIN_ROUND,
5426 self._marker_points[_marker] = points
5427 return _marker
5428 l3Set.draw_marker = draw_marker_shared
5429 l3If.draw_marker = draw_marker_shared
5430 l3IfWhile.draw_marker = draw_marker_shared
5431 l3Call.draw_marker = draw_marker_shared
5432 l3Inline.draw_marker = draw_marker_shared
5434 #* Comments & headers
5436 # Fully movable text, but without any effect on execution.
5437 # These need most of the l3Rawtext functionality; a Label is grossly
5438 # insufficient.
5440 class l3Comment(l3Rawtext):
5441 pass
5443 # # def __init__(self, *args, **kwds):
5444 # # l3Rawtext.__init__(self, *args, **kwds)
5445 # # l3Comment.__init__ = __init__
5447 def init_params(self):
5448 self._font_desc = self.w_.cp_.comment_font
5449 self._font_size = self.w_.cp_.font_size_comments * 1.0 * pango.SCALE
5450 self._fill_color = self.w_.cp_.label_fill_color
5451 self._outline_color = self.w_.cp_.label_outline_color
5452 l3Comment.init_params = init_params
5455 #* Text editing support
5456 # Functions are in calling order.
5458 #** l3Rawtext
5459 def rebuild_tree(self, text):
5460 # Form new tree.
5461 ## print 'text_finished: parent: ', self._l3tree._parent
5462 if self._l3tree._parent:
5463 parent = self.w_.state_.storage.load( self._l3tree._parent )
5464 else:
5465 parent = empty_parent()
5467 ### trap parse errors here
5468 tree = reader.parse(text).body()
5469 tree.setup(parent, self.w_.state_.def_env, self.w_.state_.storage)
5470 tree.set_outl_edges(self.w_, None)
5472 # Reparent.
5473 if self._l3tree._parent:
5474 self._l3tree.deep_replace(tree, self.w_.state_.storage)
5476 self._l3tree = tree
5477 l3Rawtext.rebuild_tree = rebuild_tree
5479 def ed_new_text(self, text):
5480 # Reset text.
5481 self._text_orig = text
5482 text_and_outline(self)
5484 # Resize from root node.
5486 # Adjust highlighting.
5487 if self._outline:
5488 # Avoid repositioning mess.
5489 self.plain_view()
5490 self.highlight()
5491 self._under_destruction = False
5492 l3Rawtext.ed_new_text = ed_new_text
5494 def ed_restore_text(self):
5495 self.ed_new_text(self._text_orig)
5496 self._under_destruction = False
5497 l3Rawtext.ed_restore_text = ed_restore_text
5499 def finish_edit(self):
5500 # Remove decoration.
5502 # Refresh decoration.
5503 self.refresh_deco()
5504 # Move following items in parent.
5505 if self._parent:
5506 self._parent.new_size_for(self)
5507 l3Rawtext.finish_edit = finish_edit
5510 #** l3Comment
5511 def rebuild_tree(self, text):
5512 # Form new tree.
5513 tree = ast.Comment(text)
5515 # Attach (logically) to node.
5516 if self._parent:
5517 self.w_.deco_table_[ self._parent._l3tree._id ] = tree
5519 tree.setup(empty_parent(), self.w_.state_.def_env, self.w_.state_.storage)
5520 tree.set_outl_edges(self.w_, None)
5522 self._l3tree = tree
5523 l3Comment.rebuild_tree = rebuild_tree
5526 def ed_new_text(self, text):
5527 # Reset text.
5528 self._text_orig = text
5529 # # text_and_outline(self)
5531 # Resize from root node.
5533 # Adjust highlighting.
5534 if self._outline:
5535 # Avoid repositioning mess.
5536 self.plain_view()
5537 self.highlight()
5538 l3Comment.ed_new_text = ed_new_text
5541 def finish_edit(self):
5542 # Refresh decoration.
5543 self.refresh_deco()
5545 # Update geometry.
5546 if self._parent:
5547 p1 = self._parent
5548 p1.refresh_header()
5549 p1.refresh_deco()
5550 if p1._parent:
5551 p1._parent.new_size_for(p1)
5552 l3Comment.finish_edit = finish_edit
5555 #** l3ViewList / l3Program
5556 def ed_restore_text(self):
5557 pass
5558 l3ViewList.ed_restore_text = ed_restore_text
5559 l3Program.ed_restore_text = ed_restore_text
5562 def rebuild_tree(self, text):
5563 def body():
5564 self._l3tree.set_label(text)
5565 self.start_replace_visible()
5566 self.w_.with_fluids(body,
5567 position_tree = False)
5568 l3ViewList.rebuild_tree = rebuild_tree
5569 l3Program.rebuild_tree = rebuild_tree
5572 def ed_new_text(self, text):
5573 # Reset text.
5574 self._text_orig = text
5576 # Adjust highlighting.
5577 if self._outline:
5578 # Avoid repositioning mess.
5579 self.plain_view()
5580 self.highlight()
5581 l3ViewList.ed_new_text = ed_new_text
5582 l3Program.ed_new_text = ed_new_text
5585 def finish_edit(self):
5586 # Refresh decoration.
5587 self.refresh_deco()
5589 # Update geometry.
5590 if self._parent:
5591 self._parent.new_size_for(self)
5593 l3ViewList.finish_edit = finish_edit
5594 l3Program.finish_edit = finish_edit
5597 #** misc.
5598 def prep_deco_text(self):
5599 deco_t = self.w_.deco_table_
5601 # Clear up prior temporaries.
5602 if deco_t.has_key( self._l3tree._id ):
5603 if hasattr(deco_t[ self._l3tree._id ], '_placeholder_temporary'):
5604 del deco_t[ self._l3tree._id ]
5606 # Avoid empty display.
5607 # 1. No default text
5608 deco_text = ""
5610 # 2. Override with explicit deco text.
5611 if deco_t.has_key(self._l3tree._id):
5612 deco_text = deco_t[self._l3tree._id]
5614 # 3. Get automatic deco text for placeholder use, when no "good"
5615 # text is available.
5616 if deco_text == "" and isinstance(self, Placeholder):
5617 deco_text = self._l3tree.deco_title_text()
5618 if deco_text == "":
5619 deco_text = "unlabeled code"
5620 # Update deco info.
5621 # Set as default for this node.
5622 comm = deco_t[self._l3tree._id] = ast.Comment(deco_text)
5623 comm.setup(ast.empty_parent(),
5624 self.w_.state_.def_env,
5625 self.w_.state_.storage)
5626 # Mark as temporary.
5627 comm._placeholder_temporary = 1
5629 return deco_text
5630 l3Nested.prep_deco_text = prep_deco_text
5631 l3Rawtext.prep_deco_text = prep_deco_text
5634 # Draw the header before the current contents of the _root_group.
5635 # Additions are made to the same _root_group.
5637 # Used after subclass __init__, before init_deco().
5639 def init_header(self, rentab):
5640 deco_text = self.prep_deco_text()
5641 # Maybe add header.
5642 if deco_text != "":
5643 # Create header and align H[eader] with S[elf].
5644 self._header = self._canvas.add_header_of(self._l3tree, rentab)
5645 self._header.reparent(self) # Only reparent the canvas group.
5646 flush_events()
5647 ls, ts, rs, bs = self.padded_bounds_world()
5648 lh, th, rh, bh = self._header.get_bounds_world()
5650 # Vertical alignment:
5651 if isinstance(self, l3List):
5652 # Header on top of label (hide the label)
5653 ll, tl, rl, bl = self._head.get_bounds_world()
5654 self._header.move(ll - lh, bl - bh)
5655 else:
5656 # H[eader]
5657 # S[elf]
5658 self._header.move(ls - lh, ts - bh)
5659 else:
5660 # No header.
5661 self._header = None
5663 pass
5664 l3Nested.init_header = init_header
5665 l3Rawtext.init_header = init_header
5668 def init_header(self, rentab):
5669 deco_text = self.prep_deco_text()
5670 if deco_text == "":
5671 self._header = None
5672 return
5674 vl_type = self._l3tree.get_outline_type()
5676 if vl_type == 'flat':
5677 # Display only _head (label), ignore _header (comment).
5678 self._header = None
5679 return
5681 # Create header and align H[eader] with S[elf].
5682 self._header = self._canvas.add_header_of(self._l3tree, rentab)
5683 self._header.reparent(self) # Only reparent the canvas group.
5684 flush_events()
5685 # # ls, ts, rs, bs = self.padded_bounds_world()
5686 lh, th, rh, bh = self._header.get_bounds()
5687 le, te, re, be = self._expander.get_bounds()
5688 ll, tl, rl, bl = self._head.get_bounds()
5689 la, ta, ra, ba = self._alist.get_bounds()
5691 # Vertical alignment:
5692 if vl_type in ['nested', 'subtree']:
5693 # Layout:
5694 # E L
5698 # where:
5699 # _head (label, l)
5700 # _header (comment, h)
5701 # body (alist, a)
5703 # Keep existing horizontal positioning for E, L, A; only H is
5704 # new.
5705 self._header.move(la - lh, ta - bh) # H-A
5706 self._head.move(0, # L-A
5707 (ta - (bh - th)) - bl)
5708 self._expander.move(0, # E-A
5709 (ta - (bh - th)) - be)
5711 else:
5712 raise Exception("Invalid outline type. Internal error.")
5713 l3ViewList.init_header = init_header
5714 l3Program.init_header = init_header
5717 def refresh_header(self):
5718 from l3gui.l3canvas import RenderTable
5719 # Delete header visuals (and reinsert with current settings).
5720 _safed(self._header)
5721 self._header = None
5723 # todo: preserve rentab info?
5724 self.init_header(RenderTable())
5725 flush_events()
5726 l3Nested.refresh_header = refresh_header
5727 l3Rawtext.refresh_header = refresh_header
5730 #* event handling
5732 #** drag / selection
5733 def drag_event(self, item, event):
5734 if event.type == gtk.gdk.BUTTON_PRESS:
5735 if event.button == 1:
5736 if event.state & gtk.gdk.CONTROL_MASK:
5737 self.w_.selector.toggle_selection(self)
5738 else:
5739 self.w_.selector.set_selection(self)
5741 self._canvas.grab_focus() # Ensure keystrokes go here.
5743 # sm: move group start
5744 self.remember_x = event.x
5745 self.remember_y = event.y
5746 return True
5748 elif event.type == gtk.gdk.ENTER_NOTIFY:
5749 # Alt + Shift + motion adds self to selection.
5750 if ((event.state & gtk.gdk.SHIFT_MASK) and
5751 (event.state & gtk.gdk.MOD1_MASK)):
5752 self.w_.selector.add_to_selection(self)
5753 return True
5755 elif event.type == gtk.gdk.LEAVE_NOTIFY:
5756 self.w_.cp_.drag_state = (None, None)
5758 elif event.type == gtk.gdk.MOTION_NOTIFY:
5759 if event.state & gtk.gdk.BUTTON1_MASK:
5760 # Do not drag nested text, or the text of Inline.
5761 if isinstance(self._parent, (l3Rawtext, l3Inline)):
5762 (d_state, _) = self.w_.cp_.drag_state
5763 if d_state == "ds_dragging":
5764 return False
5765 else:
5766 self.w_.selector.remove(self)
5767 self.w_.cp_.drag_state = ("ds_start", (event.x, event.y))
5768 return False
5770 # Get the new position and move by the difference.
5772 # sm: move group
5773 # The button_press event above may not have occurred, so
5774 # ensure a valid prior position.
5775 if self.remember_x is None:
5776 self.remember_x = event.x
5777 self.remember_y = event.y
5779 new_x = event.x
5780 new_y = event.y
5781 dx = new_x - self.remember_x
5782 dy = new_y - self.remember_y
5783 self.move(dx, dy)
5784 self.remember_x = new_x
5785 self.remember_y = new_y
5787 # sm: detach item if needed.
5788 if self._marker:
5789 x, y = self.get_anchor()
5790 u, v = marker_get_anchor(self.w_, self._marker)
5791 if self.w_.fluid_ref(trace_gui_events = False):
5792 print "distance: ", abs(u - x) + abs(v - y)
5793 sys.stdout.flush()
5794 if (abs(u - x) + abs(v - y) >
5795 self.w_.cp_.item_detach_distance):
5796 self._marker = None
5797 self._container.detach(self )
5798 self._container = None
5800 return True
5801 return False
5802 l3Rawtext.drag_event = drag_event
5804 def drag_event(self, item, event):
5805 return False
5806 l3Comment.drag_event = drag_event
5808 def drag_event(self, item, event):
5809 if event.type == gtk.gdk.BUTTON_PRESS:
5810 if event.button == 1:
5811 if event.state & gtk.gdk.CONTROL_MASK:
5812 self.w_.selector.toggle_selection(self)
5813 else:
5814 self.w_.selector.set_selection(self)
5816 self._canvas.grab_focus() # Ensure keystrokes go here.
5818 # sm: move group start
5819 self.remember_x = event.x
5820 self.remember_y = event.y
5821 return True
5823 elif event.type == gtk.gdk.ENTER_NOTIFY:
5824 # Alt + Shift + motion adds self to selection.
5825 if ((event.state & gtk.gdk.SHIFT_MASK) and
5826 (event.state & gtk.gdk.MOD1_MASK)):
5827 self.w_.selector.add_to_selection(self)
5828 return True
5830 elif event.type == gtk.gdk.MOTION_NOTIFY:
5831 if event.state & gtk.gdk.BUTTON1_MASK:
5833 # Get the new position and move by the difference.
5835 # sm: move group
5836 # The button_press event above may not have occurred, so
5837 # ensure a valid prior position.
5838 (d_state, d_val) = self.w_.cp_.drag_state
5839 if d_state == "ds_start":
5840 (self.remember_x, self.remember_y) = d_val
5841 self.w_.cp_.drag_state = ("ds_dragging", d_val)
5843 if self.remember_x is None:
5844 self.remember_x = event.x
5845 self.remember_y = event.y
5847 new_x = event.x
5848 new_y = event.y
5849 dx = new_x - self.remember_x
5850 dy = new_y - self.remember_y
5851 self.move(dx, dy)
5852 self.remember_x = new_x
5853 self.remember_y = new_y
5855 if self._marker:
5856 # Detach from parent once self is far enough from marker.
5857 x, y = self.get_anchor()
5858 u, v = marker_get_anchor(self.w_, self._marker)
5859 if self.w_.fluid_ref(trace_gui_events = False):
5860 print "distance: ", abs(u - x) + abs(v - y)
5861 sys.stdout.flush()
5862 if (abs(u - x) + abs(v - y)) > self.w_.cp_.item_detach_distance:
5863 def _do():
5864 self._container.detach(self)
5865 self._marker = None
5866 self._container = None
5867 misc.single_event(_do)
5868 return True
5870 return False
5871 l3Nested.drag_event = drag_event
5874 def drag_event(self, item, event):
5875 if event.type == gtk.gdk.BUTTON_PRESS:
5876 if event.button == 1:
5877 if event.state & gtk.gdk.CONTROL_MASK:
5878 self.w_.selector.toggle_selection(self)
5879 else:
5880 self.w_.selector.set_selection(self)
5882 self._canvas.grab_focus() # Ensure keystrokes go here.
5884 # sm: move group start
5885 self.remember_x = event.x
5886 self.remember_y = event.y
5887 return True
5889 elif event.type == gtk.gdk.MOTION_NOTIFY:
5890 if event.state & gtk.gdk.BUTTON1_MASK:
5892 # Get the new position and move by the difference.
5894 # sm: move group
5895 new_x = event.x
5896 new_y = event.y
5897 dx = new_x - self.remember_x
5898 dy = new_y - self.remember_y
5899 self.move(dx, dy)
5900 self.remember_x = new_x
5901 self.remember_y = new_y
5902 return True
5904 return False
5905 l3aList.drag_event = drag_event
5908 #** deletion
5909 def destroy_event(self, item, event):
5910 if event.type == gtk.gdk._2BUTTON_PRESS:
5911 if event.button == 3:
5912 self._canvas.remove_node_edge_all(self)
5913 self.destroy()
5914 return False
5915 l3Rawtext.destroy_event = destroy_event
5918 #** edit start
5919 def head_event(self, item, event):
5920 # doubleclick-button-1 to edit.
5921 if event.type == gtk.gdk._2BUTTON_PRESS:
5922 if event.button == 1:
5923 self._editor = TextEdit(self.w_,
5924 self,
5925 self._l3tree.deco_title_text(),
5926 self._canvas._common_tag_table,
5927 self._canvas,
5929 self._editor._view.show()
5930 self._editor._view.grab_focus()
5931 return True
5932 return False
5933 l3ViewList.head_event = head_event
5934 l3Program.head_event = head_event
5938 #* Decoration
5940 #** l3Deco: Decoration around contents.
5941 # This includes the outlines, buttons (if
5942 # any) and the titlebar (if present)
5943 class l3Deco:
5944 pass
5946 def __init__(self, node):
5947 # For full initialization example, see add_deco_to.
5949 # # not practical: self._node_id = node._l3tree._id
5950 # # cyclic: self._node = node
5951 self.w_ = node.w_
5953 self._deco_path = None # art_path
5954 self._deco_path_item = None # canvas.CanvasBpath (holds _deco_path)
5955 self._draw_queue = [] # See add_item()
5957 l3Deco.__init__ = __init__
5959 def add_item(self, cnvsitem):
5960 assert hasattr(cnvsitem, "destroy")
5961 assert hasattr(cnvsitem, "refresh")
5962 self._draw_queue.append(cnvsitem)
5963 l3Deco.add_item = add_item
5965 def destroy(self):
5966 _safed(self._deco_path_item)
5967 for ii in self._draw_queue:
5968 _safed(ii)
5970 self._draw_queue = []
5971 self._deco_path = None
5972 self._deco_path_item = None
5973 l3Deco.destroy = destroy
5976 def magnify_event(self, item, event):
5977 if event.type == gtk.gdk.ENTER_NOTIFY:
5978 if self.w_.fluid_ref(trace_gui_events = False):
5979 print "deco_magnify_event"
5980 item.set_property("width_units",
5981 self.w_.cp_.deco.outline_width *
5982 self.w_.cp_.hover_magnification)
5983 return False
5985 elif event.type == gtk.gdk.LEAVE_NOTIFY:
5986 if self.w_.fluid_ref(trace_gui_events = False):
5987 print "deco_magnify_event"
5988 item.set_property("width_units", self.w_.cp_.deco.outline_width)
5989 return False
5990 l3Deco.magnify_event = magnify_event
5993 #** attach (add) deco to node
5994 def add_deco_to(node, callbacks = [], emphasis_for = None):
5996 Produce and return the l3Deco for `node`.
5997 This routine puts an outline around `node`.
5999 `node` interface requirements:
6000 .get_bounds_world()
6002 ._root_group
6004 Every ballback cb must (1) be callable
6005 (2) return a canvasitem or l3Nested instance OB
6006 The returned Ob must satisfy the interface specified in add_item().
6009 the_deco = l3Deco(node)
6010 # Self-drawing items.
6011 for cb in callbacks:
6012 the_deco.add_item(cb())
6014 # Bounds for the node body.
6015 ll1, tt1, rr1, bb1 = node.get_bounds_world()
6016 path_width = node.w_.cp_.deco.outline_width
6017 if isinstance(node, (l3Nested, Label)):
6018 padding = 2 * path_width
6019 # Pad the content.
6020 ll1 -= padding
6021 tt1 -= padding
6022 rr1 += padding
6023 bb1 += padding
6024 else:
6025 padding = 0
6027 # Check for content, and inflate bounds to get a valid vector path.
6028 if rr1 - ll1 < 1: rr1 = ll1 + 1.0
6029 if bb1 - tt1 < 1: bb1 = tt1 + 1.0
6032 # Draw boundary path.
6034 the_deco._deco_path = path_rectangle_rounded(ll1, rr1, tt1, bb1,
6035 0.5, 0.5, ### params
6036 0.3, 0.3,
6038 # Draw path.
6039 bdry_path = canvas.path_def_new(the_deco._deco_path)
6040 if emphasis_for:
6041 the_deco._deco_path_item = bdry_item = node._root_group.add(
6042 canvas.CanvasBpath,
6043 width_units = path_width,
6044 outline_color_rgba = node.w_.cp_.deco.emph_color[emphasis_for],
6045 fill_color = "green",
6046 cap_style = gtk.gdk.CAP_ROUND,
6047 join_style = gtk.gdk.JOIN_ROUND,
6049 else:
6050 the_deco._deco_path_item = bdry_item = node._root_group.add(
6051 canvas.CanvasBpath,
6052 width_units = path_width,
6053 outline_color_rgba = node.w_.cp_.deco.outline_color,
6054 fill_color = "green",
6055 cap_style = gtk.gdk.CAP_ROUND,
6056 join_style = gtk.gdk.JOIN_ROUND,
6058 bdry_item.set_bpath(bdry_path)
6059 # Adjust coordinate offsets.
6060 l_bi, t_bi, r_bi, b_bi_ = canvas_item_get_bounds_world(bdry_item)
6061 bdry_item.move(ll1 - l_bi - path_width, tt1 - t_bi - path_width)
6063 # Background coloring.
6064 the_deco._background = None
6065 if emphasis_for:
6066 # Draw background.
6067 bck = the_deco._background = node._root_group.add(
6068 canvas.CanvasRect,
6069 x1 = ll1, y1 = tt1,
6070 x2 = rr1, y2 = bb1,
6071 # fill_color_rgba = node.w_.cp_.deco.outline_color,
6072 fill_color = 'white',
6073 width_pixels = node.w_.cp_.outline_width_normal,
6075 # Move background to bottom.
6076 bck.lower_to_bottom()
6079 # Event handling.
6081 the_deco._deco_path_item.connect(
6082 "event", lambda *a: the_deco.magnify_event(*a))
6084 return the_deco
6088 #** init_deco
6089 def padded_bounds_world(self):
6090 # Bounds for the node body.
6091 ll1, tt1, rr1, bb1 = self.get_bounds_world()
6092 path_width = self.w_.cp_.deco.outline_width
6093 if isinstance(self, l3Nested):
6094 padding = 2 * path_width
6095 # Pad the content.
6096 ll1 -= padding
6097 tt1 -= padding
6098 rr1 += padding
6099 bb1 += padding
6100 else:
6101 padding = 0
6103 # Check for content, and inflate bounds to get a valid vector path.
6104 if rr1 - ll1 < 1: rr1 = ll1 + 1.0
6105 if bb1 - tt1 < 1: bb1 = tt1 + 1.0
6107 # Headerless state.
6108 nrr1 = rr1
6109 nbb1 = bb1
6111 return ll1, tt1, rr1, bb1
6112 l3Nested.padded_bounds_world = padded_bounds_world
6113 l3Rawtext.padded_bounds_world = padded_bounds_world
6117 # Draw the border and other decorations around the current
6118 # contents of the _root_group.
6119 # Additions are made to the same _root_group.
6121 # Used *after* init_header().
6124 def init_deco(self, rentab):
6125 # Decorate body.
6126 self._deco = add_deco_to(self)
6127 l3Function.init_deco = init_deco
6129 def init_deco(self, rentab):
6130 # Decorate body.
6131 ## self._deco = add_deco_to(self, callbacks = self._deco_cb_buffer)
6132 self._deco = None
6133 l3IfLoop.init_deco = init_deco
6134 l3IfWhile.init_deco = init_deco
6137 def init_deco(self, rentab):
6138 # Decorate body.
6139 self._deco = None
6140 # Move the placeholder to the right of the header string, into
6141 # the header path.
6142 if self._header != None:
6143 l1, t1, r1, b1 = self._header.get_bounds()
6144 l2, t2, r2, b2 = self._blank.get_bounds()
6145 self._blank.move(l1 - r2 - self.w_.cp_.placeholder.padding,
6146 t1 - t2)
6147 else:
6148 # init_header must always provide a header for Placeholder.
6149 raise Exception("Invalid state in l3Nested.init_deco. "
6150 "No header found. Internal error")
6151 # Emphasize body / add outline.
6152 emph = self._l3tree.get_emphasis()
6153 if emph:
6154 self._deco = add_deco_to(self, emphasis_for = emph)
6155 else:
6156 self._deco = None
6158 Placeholder.init_deco = init_deco
6161 def init_deco(self, rentab):
6162 # Emphasize body / add outline.
6163 emph = self._l3tree.get_emphasis()
6164 if emph:
6165 self._deco = add_deco_to(self, emphasis_for = emph)
6166 else:
6167 self._deco = None
6168 l3Rawtext.init_deco = init_deco
6169 l3Nested.init_deco = init_deco
6173 #** refresh
6174 def refresh_deco(self):
6175 from l3gui.l3canvas import RenderTable
6176 # Remove decoration.
6177 _safed(self._deco)
6179 # Bodyless state
6180 self._deco = None
6182 # todo: preserve rentab info?
6183 self.init_deco(RenderTable())
6184 flush_events()
6185 l3Nested.refresh_deco = refresh_deco
6186 l3Rawtext.refresh_deco = refresh_deco
6189 #** destroy
6190 def destroy_deco(self):
6191 _safed(self._deco)
6192 self._deco = None
6193 _safed(self._header)
6194 self._header = None
6195 # ???
6196 _safed(self._root_group)
6197 del self._root_group
6199 return True
6200 l3Nested.destroy_deco = destroy_deco
6203 #** interactive modification
6204 def deco_change_label(self, label = None):
6205 # The following also works in "evaluate locally"
6206 if not label:
6207 print "enter new label:"
6208 sys.stdout.flush()
6209 label = sys.stdin.readline().rstrip()
6211 assert isinstance(label, StringType), "Label must be a string."
6213 # Update deco info.
6214 comm = self.w_.deco_table_[self._l3tree._id] = ast.Comment(label)
6215 comm.setup(ast.empty_parent(),
6216 self.w_.state_.def_env,
6217 self.w_.state_.storage)
6219 # Refresh decoration.
6220 self.refresh_header()
6221 self.refresh_deco()
6223 # Propagate.
6224 if self._parent:
6225 self._parent.new_size_for(self)
6226 l3Base.deco_change_label = deco_change_label
6228 def deco_add_comment(self):
6229 # Update deco info.
6231 comm = self.w_.deco_table_[self._l3tree._id] = \
6232 ast.Comment("double-click to edit")
6233 comm.setup(ast.empty_parent(),
6234 self.w_.state_.def_env,
6235 self.w_.state_.storage)
6237 # Refresh decoration.
6238 self.refresh_header()
6239 self.refresh_deco()
6241 # Propagate.
6242 if self._parent:
6243 self._parent.new_size_for(self)
6244 l3Base.deco_add_comment = deco_add_comment
6247 #* Resizing / new_size_for
6248 #** alist
6249 def new_size_for(self, child):
6250 assert isinstance(child, (l3Base, l3aList))
6251 try:
6252 index = self._dlist.index(child)
6253 except ValueError:
6254 raise DisplayError("nonexistent child.")
6256 # Reposition child.
6257 if self._l3tree.getthe('layout') == 'horizontal':
6258 ml, mt, mr, mb = marker_get_bounds(
6259 self.w_, self._marker_lst[index]).pad_lr()
6260 cl, ct, cr, cb = child.get_bounds()
6262 # Horizontal alignment:
6263 # M C
6264 child.move(mr - cl, mt - ct)
6265 # Move following markers, labels, and objects
6266 # Find shift.
6267 ml, mt, mr, mb = marker_get_bounds(
6268 self.w_, self._marker_lst[index + 1]).pad_lr()
6269 cl, ct, cr, cb = child.get_bounds()
6270 shift_x = cr - ml
6271 for itm in self._dlist[index + 1 : None]:
6272 itm.move(shift_x, 0)
6273 for itm in self._marker_lst[index + 1 : None]:
6274 itm.move(shift_x, 0)
6276 else:
6277 ml, mt, mr, mb = marker_get_bounds(
6278 self.w_, self._marker_lst[index]).pad_tb()
6279 cl, ct, cr, cb = child.get_bounds()
6282 # Vertical alignment:
6285 child.move(ml - cl, mb - ct)
6286 # Move following markers, labels, and objects
6287 # Find shift.
6288 x, y, _, _ = marker_get_bounds(
6289 self.w_, self._marker_lst[index + 1]).pad_tb()
6290 _, _, u, v = child.get_bounds()
6291 shift_y = v - y
6292 for itm in self._dlist[index + 1 : None]:
6293 itm.move(0, shift_y)
6294 for itm in self._marker_lst[index + 1 : None]:
6295 itm.move(0, shift_y)
6297 # Propagate.
6298 if self._parent:
6299 self._parent.new_size_for(self)
6300 return
6301 l3aList.new_size_for = new_size_for
6304 #** nested / native
6305 def new_size_for(self, child):
6306 # Contained alist is updated; only forward request.
6307 if self._parent:
6308 self._parent.new_size_for(self)
6309 l3Nested.new_size_for = new_size_for
6311 def new_size_for(self, child):
6312 # Contained value updated; only forward request.
6313 if self._parent:
6314 self._parent.new_size_for(self)
6315 l3Native.new_size_for = new_size_for
6318 #*** if / set / call / function
6319 def new_size_for(self, child):
6321 # Shift contents vertically as needed.
6323 assert isinstance(child, (l3Base, l3aList))
6325 # Find and apply shift.
6326 if child == self._d_cond:
6327 _, _, _, y = self._d_cond.get_bounds()
6328 _, v, _, _ = self._d_yes.get_bounds()
6329 dy = y - v
6330 self._d_yes.move(0, dy)
6331 self._d_else.move(0, dy)
6332 self._d_no.move(0, dy)
6334 elif child == self._d_yes:
6335 _, _, _, y = self._d_yes.get_bounds()
6336 _, v, _, _ = self._d_else.get_bounds()
6337 dy = y - v
6338 self._d_else.move(0, dy)
6339 self._d_no.move(0, dy)
6341 elif child == self._d_no:
6342 pass
6344 else:
6345 raise DisplayError("nonexistent child.")
6347 # Refresh decoration.
6348 self.refresh_deco()
6350 # Propagate.
6351 if self._parent:
6352 self._parent.new_size_for(self)
6353 return
6354 l3If.new_size_for = new_size_for
6357 def new_size_for(self, child):
6359 # Shift contents vertically as needed.
6361 assert isinstance(child, (l3Base, l3aList))
6363 # Find and apply shift.
6364 if child is self._d_cond:
6365 _, _, _, bco = self._d_cond.get_bounds()
6366 _, tbo, _, _ = self._d_body.get_bounds()
6367 self._d_body.move(0, bco - tbo)
6369 # Refresh decoration.
6370 self.refresh_deco()
6372 # Propagate.
6373 if self._parent:
6374 self._parent.new_size_for(self)
6375 return
6376 l3IfWhile.new_size_for = new_size_for
6379 def new_size_for(self, child):
6381 # Shift contents vertically as needed.
6383 assert isinstance(child, (l3Base, l3aList))
6385 # Find and apply shift.
6387 # Refresh decoration.
6388 self.refresh_deco()
6390 # Propagate.
6391 if self._parent:
6392 self._parent.new_size_for(self)
6393 return
6394 l3IfLoop.new_size_for = new_size_for
6395 l3IfLoopBreak.new_size_for = new_size_for
6396 l3IfLoopContinue.new_size_for = new_size_for
6399 def new_size_for(self, child):
6401 # Shift contents as needed.
6403 assert isinstance(child, (l3Base, l3aList))
6405 # Find and apply shift.
6406 if child == self._d_lhs:
6407 self._align_to_lhs()
6409 elif child == self._d_rhs:
6410 pass
6412 else:
6413 raise DisplayError("nonexistent child.")
6415 # Refresh decoration.
6416 self.refresh_deco()
6418 # Propagate.
6419 if self._parent:
6420 self._parent.new_size_for(self)
6421 l3Set.new_size_for = new_size_for
6424 def new_size_for(self, child):
6426 # Shift contents as needed.
6428 assert isinstance(child, (l3Base, l3aList))
6430 # Find and apply shift.
6431 if child == self._d_func:
6432 self._func_shift()
6434 elif child == self._d_args:
6435 pass
6437 else:
6438 raise DisplayError("nonexistent child.")
6440 # Refresh decoration.
6441 self.refresh_deco()
6443 # Propagate.
6444 if self._parent:
6445 self._parent.new_size_for(self)
6446 l3Call.new_size_for = new_size_for
6449 def new_size_for(self, child):
6451 # Shift contents vertically as needed.
6453 assert isinstance(child, (l3Base, l3aList))
6455 # Find and apply shift.
6456 if child == self._d_args:
6457 _, _, _, y = self._d_args.get_bounds()
6458 _, v, _, _ = self._d_body.get_bounds()
6459 dy = y - v
6460 self._d_body.move(0, dy)
6462 elif child == self._d_body:
6463 pass
6465 else:
6466 raise DisplayError("nonexistent child.")
6468 # Refresh decoration.
6469 self.refresh_deco()
6471 # Propagate.
6472 if self._parent:
6473 self._parent.new_size_for(self)
6474 return
6475 l3Function.new_size_for = new_size_for
6478 #*** list / map / program
6479 def new_size_for(self, child):
6481 # Shift contents vertically as needed.
6483 assert isinstance(child, (l3aList,))
6485 # Find and apply shift.
6487 # Refresh decoration.
6488 self.refresh_deco()
6490 # Propagate.
6491 if self._parent:
6492 self._parent.new_size_for(self)
6493 return
6494 l3Map.new_size_for = new_size_for
6495 l3List.new_size_for = new_size_for
6496 l3Program.new_size_for = new_size_for
6499 #* Introspection
6500 #** internal functions
6501 def marker_eval_local(self, args):
6502 # args: (menuitem, index)
6503 # For console use:
6504 if 0:
6505 from code import InteractiveConsole
6506 ic_dict = locals()
6507 ic = InteractiveConsole(ic_dict)
6508 utils.print_("==== marker index is %d ====" % args[1] )
6509 ic.interact("==== local console; exit with end-of-file (ctrl-d) ====")
6510 utils.print_( "==== local console exit ====")
6511 l3aList.marker_eval_local = marker_eval_local
6513 def eval_local(self, args):
6514 # args: (menuitem, index)
6515 from l3gui.cruft.pylab import Console
6516 name_s = "Local Python Console\nfor %d" % self._l3tree._id
6517 console = Console(
6518 self.w_, locals(),
6519 quit_handler =
6520 lambda *args: (self.w_.notebk_consoles.destroy_page(name_s),
6521 self.w_.stdinouterr.pop()
6523 self.w_.notebk_consoles.add_page(console, name_s)
6524 self.w_.stdinouterr.push()
6525 console.std_to_widget()
6526 console.write("==== Local console; exit with end-of-file (ctrl-d) ====\n"
6527 "==== The name `self` is the instance. ====\n",
6528 style='error')
6529 console.banner()
6530 misc.show_consoles(self.w_)
6531 l3Base.selection_eval_local = eval_local
6532 l3Base.eval_local = eval_local
6533 l3aList.eval_local = eval_local
6536 def eval_local_console(self, args):
6537 # args: (menuitem)
6538 # For use from calling console; this version stops the gui event
6539 # loop.
6540 from code import InteractiveConsole
6541 ic_dict = locals()
6542 ic = InteractiveConsole(ic_dict)
6544 # Simple combination of TxtConsole._raw_input and new_termconsole().
6545 # This is a good overview of the use of those parts.
6546 def _raw_input(prompt=""):
6547 import thread
6548 from threading import Lock
6549 import time
6550 lck = Lock()
6551 value = []
6552 def _do():
6553 try:
6554 value.append(raw_input(prompt))
6555 except EOFError:
6556 value.append(EOFError())
6557 lck.release()
6558 lck.acquire()
6559 thread.start_new_thread(_do, ())
6560 # Manual main loop until input is ready.
6561 while not lck.acquire(False):
6562 flush_events()
6563 time.sleep(0.01)
6564 if isinstance(value[-1], Exception):
6565 raise value[-1]
6566 return value[-1]
6568 ic.raw_input = _raw_input
6569 ic.interact("==== local console; exit with end-of-file (ctrl-d) ====")
6570 utils.print_( "==== local console exit ====")
6571 l3Base.eval_local_console = eval_local_console
6575 #** User functions.
6576 def get_values_list(self):
6577 ''' return ((id, value) list)'''
6578 return self._l3tree.get_values_list(self.w_)
6579 l3Base.get_values_list = get_values_list
6580 l3aList.get_values_list = get_values_list
6582 # # def dump_values(self):
6583 # # st = self.w_.state_.storage
6584 # # tw = ast.TreeWork(self.w_.state_.storage)
6586 # # # Toplevel id (static).
6587 # # c_id = self._l3tree._id
6588 # # G.logger.info("value of %d is %s" % (
6589 # # c_id, st.get_attribute(c_id, 'interp_result')))
6591 # # # Dynamic id(s).
6592 # # clone_l = st.get_attribute(c_id, "interp_clone")
6593 # # if clone_l:
6594 # # G.logger.info("%d has %d clones.\nValues:" % (c_id, len(clone_l)))
6596 # # for id in clone_l:
6597 # # G.logger.info("%d %s\n" %
6598 # # (id, st.get_attribute(id, 'interp_result')))
6599 # # else:
6600 # # G.logger.info("No clones of %d" % c_id)
6602 # # def dump_values(self):
6603 # # ind = " "
6604 # # st = self.w_.state_.storage
6605 # # tw = ast.TreeWork(self.w_.state_.storage)
6607 # # # Toplevel id (static).
6608 # # c_id = self._l3tree._id
6609 # # print "----------------------"
6611 # # # Dynamic id(s).
6612 # # def _dynamic(c_id, lev):
6613 # # clone_l = st.get_attribute(c_id, "interp_clone")
6614 # # if clone_l:
6615 # # print ind*lev, "%d has %d clone(s):" % (c_id, len(clone_l))
6617 # # for id in clone_l:
6618 # # _dynamic(id, lev+1)
6619 # # else:
6620 # # print ind*lev, c_id, "has no clones;",\
6621 # # "value: ", st.get_attribute(c_id, 'interp_result')
6622 # # _dynamic(c_id, 0)
6625 def dump_values(self):
6626 """Print a (potentially) nested tree of values for self and all clones.
6628 ind = " "
6629 st = self.w_.state_.storage
6630 tw = ast.TreeWork(self.w_.state_.storage)
6632 # Toplevel id (static).
6633 c_id = self._l3tree._id
6634 print "----------------------"
6636 # Dynamic id(s).
6637 def _dynamic(c_id, lev):
6638 clone_l = st.get_attribute(c_id, "interp_clone")
6639 if clone_l:
6640 print ind*lev, "%d repeats %d time(s):" % (c_id, len(clone_l))
6642 for id in clone_l:
6643 _dynamic(id, lev+1)
6644 else:
6645 print ind*lev, c_id, "does not repeat;",\
6646 "value: ", st.get_attribute(c_id, 'interp_result')
6647 _dynamic(c_id, 0)
6648 l3Base.dump_values = dump_values
6649 l3aList.dump_values = dump_values
6651 def get_value(self, id):
6652 """Return the value corresponding to the numeric `id`.
6654 st = self.w_.state_.storage
6655 return st.get_attribute(id, 'interp_result')
6656 l3Base.get_value = get_value
6657 l3aList.get_value = get_value
6660 def string_export(self):
6661 ind = " "
6662 st = self.w_.state_.storage
6663 tw = ast.TreeWork(self.w_.state_.storage)
6665 # Toplevel id (static).
6666 c_id = self._l3tree._id
6667 print '---------------------------------'
6669 # Dynamic id(s).
6670 def _dynamic(c_id, lev):
6671 clone_l = st.get_attribute(c_id, "interp_clone")
6672 if clone_l:
6673 # print ind*lev, "%d repeats %d time(s):" % (c_id, len(clone_l))
6674 for id in clone_l:
6675 _dynamic(id, lev+1)
6676 else:
6677 # value in str() form
6678 val = st.get_attribute(c_id, 'interp_result')
6679 if val != None:
6680 print val
6681 _dynamic(c_id, 0)
6682 print '---------------------------------'
6683 l3Base.string_export = string_export
6684 l3aList.string_export = string_export
6687 #* Subtree visibility
6689 #** marking
6690 def mark_as(self, visibility):
6691 # Mark in table.
6692 self._canvas._vis_tab[self._l3tree._id] = visibility
6694 # Background visibility indicator.
6695 def _draw(color):
6696 if self._vis_indic:
6697 self._vis_indic.destroy()
6698 self._vis_indic = None
6700 flush_events()
6701 x1, y1, x2, y2 = self.get_bounds()
6702 pad = self.w_.cp_.highlight_padding
6703 x1 -= pad
6704 y1 -= pad
6705 x2 += pad
6706 y2 += pad
6708 # What depth to use?
6709 # Subtrees are in their own group; within that, the box is
6710 # always at the bottom.
6712 # Tree
6713 # subtree1
6714 # subtree2
6715 # box2
6716 # box1
6717 # Box
6719 # The parent's bounding box should not change.
6720 if self._parent:
6721 add = self._parent._root_group.add
6722 else:
6723 add = self._canvas.root().add
6724 self._vis_indic = add(
6725 canvas.CanvasRect,
6726 x1 = x1, y1 = y1,
6727 x2 = x2, y2 = y2,
6728 fill_color = color,
6729 outline_color = 'black',
6730 width_pixels = self.w_.cp_.outline_width_normal,
6732 self._vis_indic.lower_to_bottom()
6734 if visibility == "vis_hide":
6735 _draw("grey80")
6737 elif visibility == "vis_show":
6738 _draw("white")
6740 elif visibility == "vis_neutral":
6741 if self._vis_indic:
6742 self._vis_indic.destroy()
6743 self._vis_indic = None
6745 else:
6746 raise DisplayError("Invalid visibility flag.")
6747 l3Base.mark_as = mark_as
6750 #** subtree swapping
6751 def start_replace_visible(self):
6752 def body():
6753 if self._parent:
6754 self._parent.replace_visible(self)
6755 else:
6756 self._canvas.replace_visible(self)
6757 self.w_.with_fluids(body,
6758 detach_l3_tree = False,
6759 insert_l3_tree = False,
6761 l3Base.start_replace_visible = start_replace_visible
6763 def replace_visible(self, child):
6764 # Collect info.
6765 marker = child._marker
6766 l3tree = child._l3tree
6768 # Remove existing display.
6769 child.destroy()
6771 # Draw (visible) subtree.
6772 tree_disp = self._canvas.start_add_l3tree(l3tree)
6774 # Attach / move / reparent
6775 flush_events()
6776 self.insert(marker, tree_disp)
6778 # Resize parent.
6779 pass
6780 l3Nested.replace_visible = replace_visible
6782 def replace_visible(self, child):
6783 # Collect info.
6784 index = self._dlist.index(child)
6785 l3tree = child._l3tree
6786 # # if isinstance(l3tree, ast.viewList):
6787 # # l3tree = l3tree._real_tree
6788 # # assert l3tree
6790 # Remove existing display.
6791 child.destroy()
6793 # Draw (visible) subtree.
6794 ### self._canvas._vis_tab.get_render_table(l3tree)
6795 tree_disp = self._canvas.start_add_l3tree(l3tree)
6797 # Attach it.
6798 flush_events()
6799 self.insert(index, tree_disp)
6801 # Resize parent.
6802 if self._parent:
6803 self._parent.new_size_for(self)
6804 l3aList.replace_visible = replace_visible
6807 #** Convenience combinations.
6808 def show_nested_lhs(self):
6809 # Combination.
6810 vista = self._canvas._vis_tab
6812 # Clear existing markings
6813 vista.clear_tree( self._l3tree )
6815 # Mark subtree.
6816 self.mark_as("vis_show")
6818 ma = ast.Matcher()
6819 N = 3 # maximum visible sublevel
6820 for nd, dent in self._l3tree.top_down_indented():
6821 if dent > N:
6822 vista[nd._id] = "vis_hide"
6823 else:
6824 if ma.match(nd, Set(Marker('lhs'), Marker('rhs')) ):
6825 vista[ma['lhs']._id] = "vis_show"
6826 vista[ma['rhs']._id] = "vis_hide"
6828 # Redisplay.
6829 self.start_replace_visible()
6830 l3Base.show_nested_lhs = show_nested_lhs
6832 def show_one_subtree(self):
6833 # Combination.
6834 self.mark_as("vis_show")
6835 self._canvas._vis_tab.hide_at(self._l3tree, 2) ### choose level
6836 self.start_replace_visible()
6837 l3Base.show_one_subtree = show_one_subtree
6839 def show_subtree(self):
6840 # Combination.
6841 def body():
6842 self.mark_as("vis_show")
6843 self.start_replace_visible()
6844 self.w_.with_fluids(body, position_tree = False)
6845 l3Base.show_subtree = show_subtree
6847 def show_full_subtree(self):
6848 self._canvas._vis_tab.clear_tree( self._l3tree )
6849 # # self.mark_as("vis_show")
6850 self.start_replace_visible()
6851 l3Base.show_full_subtree = show_full_subtree
6853 def hide_subtree(self):
6854 # Combination.
6855 self.mark_as("vis_hide")
6856 self.start_replace_visible()
6857 l3Base.hide_subtree = hide_subtree
6860 def hide_subtree(self):
6861 # Redirect the hide request to the tree this comment is attached to.
6862 # Find key matching value -- deco_table_ maps (tree id -> comment)
6863 for tree_id, comment in self.w_.deco_table_.iteritems():
6864 if comment == self._l3tree: break
6865 l3tree = self._canvas._nodes[tree_id]
6866 l3tree.mark_as("vis_hide")
6867 l3tree.start_replace_visible()
6868 l3Comment.hide_subtree = hide_subtree
6871 #* detach header
6872 def detach_header(self, header):
6873 # Allow for multiple headers in the future.
6874 if header != self._header:
6875 raise Exception("detaching unknown header")
6877 # Reparent graphical parts.
6878 header.reparent_to_root()
6879 self._header = None
6881 # Update table data.
6882 # # if self.w_.deco_table_.has_key(self._l3tree._id):
6883 # # del self.w_.deco_table_[self._l3tree._id]
6884 # # else:
6885 # # print "WARNING: disconnected header was not known." \
6886 # # " Internal inconsistency."
6888 # Decoration and header must be separate when destroying
6889 # placeholder constructs (and others?).
6890 ## self.refresh_deco()
6892 # Move following items in parent.
6893 if self._parent:
6894 self._parent.new_size_for(self)
6895 l3Base.detach_header = detach_header
6898 #* detach subtree
6899 #** hooks
6900 def add_reparent_to_root_hook(self, func):
6901 self._reparent_to_root_hook.append(func)
6902 l3Base.add_reparent_to_root_hook = add_reparent_to_root_hook
6903 l3aList.add_reparent_to_root_hook = add_reparent_to_root_hook
6904 Label.add_reparent_to_root_hook = add_reparent_to_root_hook
6905 Widget.add_reparent_to_root_hook = add_reparent_to_root_hook
6906 Image.add_reparent_to_root_hook = add_reparent_to_root_hook
6907 uWidget.add_reparent_to_root_hook = add_reparent_to_root_hook
6909 def _run_reparent_to_root_hook(self):
6910 for hook in self._reparent_to_root_hook:
6911 hook()
6912 self._reparent_to_root_hook = []
6913 l3Base._run_reparent_to_root_hook = _run_reparent_to_root_hook
6914 l3aList._run_reparent_to_root_hook = _run_reparent_to_root_hook
6915 Label._run_reparent_to_root_hook = _run_reparent_to_root_hook
6916 Widget._run_reparent_to_root_hook = _run_reparent_to_root_hook
6917 Image._run_reparent_to_root_hook = _run_reparent_to_root_hook
6918 uWidget._run_reparent_to_root_hook = _run_reparent_to_root_hook
6921 #** editing and deletion
6922 #*** l3aList
6923 def detach(self, child):
6924 # When drag distance from marker to item exceeds item_detach_distance,
6925 # detach the item. This criterion is implemented in
6926 # l3Rawtext.drag_event.
6928 index = self._dlist.index(child)
6929 marker = self._marker_lst[index]
6931 # Reparent graphical parts.
6932 child.reparent_to_root()
6934 # Shift following marker over to-be-detached marker. Also shift
6935 # following objects.
6936 sl, st, _, _ = marker_get_bounds(self.w_, marker)
6937 ol, ot, _, _ = marker_get_bounds(self.w_, self._marker_lst[index + 1])
6938 ## child.get_bounds() is off after the child was dragged.
6939 ## shift_y = (sy2 - st) + (oy2 - ot)
6940 if self._l3tree.getthe('layout') == 'horizontal':
6941 shift_x = - (ol - sl)
6942 for itm in self._dlist[index + 1 : None]:
6943 itm.move(shift_x, 0)
6944 for itm in self._marker_lst[index + 1 : None]:
6945 itm.move(shift_x, 0)
6946 del itm
6948 else:
6949 shift_y = - (ot - st)
6950 for itm in self._dlist[index + 1 : None]:
6951 itm.move(0, shift_y)
6952 for itm in self._marker_lst[index + 1 : None]:
6953 itm.move(0, shift_y)
6954 del itm
6956 # Move following items in parent.
6957 if self._parent:
6958 self._parent.new_size_for(self)
6960 # Update data and marker lists.
6961 if self.w_.fluid_ref(detach_l3_tree = True):
6962 self._l3tree[index].detach_from_parent(self.w_.state_.storage)
6963 del self._dlist[index]
6964 del self._marker_lst[index]
6966 # Delete detached marker.
6967 destroy_if_last(marker)
6968 pass
6969 l3aList.detach = detach
6973 #*** l3Set / l3If / l3Call
6974 def detach(self, child):
6975 assert(child is self._d_body)
6977 # Reparent graphical parts.
6978 child.reparent_to_root()
6980 # Update reference.
6981 self._d_body = None
6983 # Shift body parts.
6985 # Refresh decoration.
6986 self.refresh_deco()
6988 # Move following items in parent.
6989 if self._parent:
6990 self._parent.new_size_for(self)
6992 # Update tree data.
6993 if self.w_.fluid_ref(detach_l3_tree = True):
6994 fail()
6995 # _matcher['LBODY'] is fixed at match time -- manual editing
6996 # invalidates it.
6997 self._matcher['LBODY'].detach_from_parent(self.w_.state_.storage)
6999 pass
7000 l3IfLoop.detach = detach
7002 def detach(self, child):
7003 # When drag distance from marker to item exceeds item_detach_distance,
7004 # detach the item. This criterion is implemented in
7005 # l3Rawtext.drag_event.
7007 if child != self._d_cond:
7008 self.detach_only(child)
7009 return
7011 # Reparent graphical parts.
7012 child.reparent_to_root()
7014 # Update reference.
7015 self._d_cond = None
7017 # Shift body parts.
7018 self._cond_shift()
7020 # Refresh decoration.
7021 self.refresh_deco()
7023 # Move following items in parent.
7024 if self._parent:
7025 self._parent.new_size_for(self)
7027 # Update tree data.
7028 if self.w_.fluid_ref(detach_l3_tree = True):
7029 self._l3tree[0].detach_from_parent(self.w_.state_.storage)
7031 pass
7032 l3If.detach = detach
7035 def detach(self, child):
7037 Detach the condition.
7039 # When drag distance from marker to item exceeds item_detach_distance,
7040 # detach the item. This criterion is implemented in
7041 # l3Rawtext.drag_event.
7043 if child != self._d_cond:
7044 self.detach_only(child)
7045 return
7047 # Reparent graphical parts.
7048 child.reparent_to_root()
7050 # Update reference.
7051 self._d_cond = None
7053 # Shift body parts.
7054 self._cond_shift()
7056 # Refresh decoration.
7057 self.refresh_deco()
7059 # Move following items in parent.
7060 if self._parent:
7061 self._parent.new_size_for(self)
7063 # Update tree data.
7064 if self.w_.fluid_ref(detach_l3_tree = True):
7065 self._matcher.get_cond().detach_from_parent(self.w_.state_.storage)
7066 pass
7067 l3IfWhile.detach = detach
7070 def detach(self, child):
7071 assert(child == self._pystr)
7073 # Reparent graphical parts.
7074 child.reparent_to_root()
7076 # Update reference.
7077 self._pystr = None
7079 # Shift body parts.
7081 # Refresh decoration.
7082 self.refresh_deco()
7084 # Move following items in parent.
7085 if self._parent:
7086 self._parent.new_size_for(self)
7088 # Update tree data.
7089 if self.w_.fluid_ref(detach_l3_tree = True):
7090 self._l3tree[0].detach_from_parent(self.w_.state_.storage)
7092 pass
7093 l3Inline.detach = detach
7095 def detach(self, child):
7096 if child == self._d_func:
7097 ### print "detach: ", self
7099 # Reparent graphical parts.
7100 child.reparent_to_root()
7102 # Update reference.
7103 self._d_func = None
7105 # Shift body parts.
7106 self._func_shift()
7108 # Refresh decoration.
7109 self.refresh_deco()
7111 # Move following items in parent.
7112 if self._parent:
7113 self._parent.new_size_for(self)
7115 # Update tree data.
7116 if self.w_.fluid_ref(detach_l3_tree = True):
7117 self._l3tree[0].detach_from_parent(self.w_.state_.storage)
7119 elif child == self._d_args:
7120 # aList children are only detached on deletion -- no shifting
7121 # or resizing
7123 # Reparent graphical parts.
7124 child.reparent_to_root()
7126 # Update reference.
7127 self._d_args = None
7128 self._update_refs()
7130 # Shift body parts.
7131 pass
7133 # Refresh decoration.
7134 pass
7136 # Move following items in parent.
7138 # Update tree data.
7139 if self.w_.fluid_ref(detach_l3_tree = True):
7140 self._l3tree[1].detach_from_parent(self.w_.state_.storage)
7142 pass
7143 l3Call.detach = detach
7145 def detach(self, child):
7146 if child == self._d_lhs:
7147 # Reparent graphical parts.
7148 child.reparent_to_root()
7150 # Update reference.
7151 self._d_lhs = None
7153 # Shift body parts.
7155 # Update tree data.
7156 if self.w_.fluid_ref(detach_l3_tree = True):
7157 self._l3tree[0].detach_from_parent(self.w_.state_.storage)
7159 # Show markers.
7160 self._d_lhs_marker.show()
7162 elif child == self._d_rhs:
7163 # Reparent graphical parts.
7164 child.reparent_to_root()
7166 # Update reference.
7167 self._d_rhs = None
7169 # Shift body parts.
7171 # Update tree data.
7172 if self.w_.fluid_ref(detach_l3_tree = True):
7173 self._l3tree[1].detach_from_parent(self.w_.state_.storage)
7175 # Show markers.
7176 self._d_rhs_marker.show()
7178 else:
7179 raise DisplayError("Detaching invalid child.")
7181 ### print "detach: ", self
7183 # Refresh decoration.
7184 self.refresh_deco()
7186 # Move following items in parent.
7187 if self._parent:
7188 self._parent.new_size_for(self)
7189 pass
7190 l3Set.detach = detach
7192 #** internal use only
7194 # These types' children are never detached in editing; currently,
7195 # these functions are only called for object destruction.
7197 #*** l3List
7198 def detach(self, child):
7199 # This function is for internal use, not editing -- yet.
7201 if child != self._alist:
7202 raise DisplayError("Detaching invalid child.")
7204 ### print "detach: ", self
7206 # Reparent graphical parts.
7207 child.reparent_to_root()
7209 # Update reference.
7210 self._alist = None
7212 # Shift body parts.
7214 # Refresh decoration.
7215 pass
7217 # Move following items in parent.
7218 if self._parent:
7219 self._parent.new_size_for(self)
7221 # Update tree data.
7222 if self.w_.fluid_ref(detach_l3_tree = True):
7223 self._l3tree[0].detach_from_parent(self.w_.state_.storage)
7225 pass
7226 l3List.detach = detach
7227 l3Map.detach = detach
7228 l3Program.detach = detach
7230 def detach(self, child):
7231 raise Exception("Native types cannot be detached. Internal error.")
7232 l3Native.detach = detach
7236 #*** l3If
7237 def detach_only(self, child):
7238 # Detach any child from self, without shifting, resizing, or
7239 # updating insertion markers.
7240 try:
7241 idx = self._d_elements.index(child)
7242 except ValueError:
7243 raise DisplayError("Detaching invalid child.")
7245 ### print "detach: ", self
7247 # Reparent graphical parts.
7248 child.reparent_to_root()
7250 # Update reference.
7251 self._d_elements[idx] = None
7252 self._update_refs()
7254 # Shift body parts.
7256 # Refresh decoration.
7257 self.refresh_deco()
7259 # Move following items in parent.
7261 # Update tree data.
7262 if self.w_.fluid_ref(detach_l3_tree = True):
7263 self._l3tree[idx].detach_from_parent(self.w_.state_.storage)
7264 l3If.detach_only = detach_only
7265 l3IfWhile.detach_only = detach_only
7269 #*** l3Function
7270 def detach(self, child):
7271 if child == self._d_args:
7272 # Reparent graphical parts.
7273 child.reparent_to_root()
7275 # Update reference.
7276 self._d_args = None
7278 # Shift body parts.
7280 # Update tree data.
7281 if self.w_.fluid_ref(detach_l3_tree = True):
7282 self._l3tree[0].detach_from_parent(self.w_.state_.storage)
7284 elif child == self._d_body:
7285 # Reparent graphical parts.
7286 child.reparent_to_root()
7288 # Update reference.
7289 self._d_body = None
7291 # Shift body parts.
7293 # Update tree data.
7294 if self.w_.fluid_ref(detach_l3_tree = True):
7295 self._l3tree[1].detach_from_parent(self.w_.state_.storage)
7297 else:
7298 raise DisplayError("Detaching invalid child.")
7300 ### print "detach: ", self
7302 # Refresh decoration.
7303 pass
7305 # Move following items in parent.
7306 if self._parent:
7307 self._parent.new_size_for(self)
7308 pass
7309 l3Function.detach = detach
7312 #*** l3Loop
7313 def detach(self, child):
7314 # Skeleton detach method, for compatibility only.
7315 # Because Loop is a macro, none of the toplevel children can be
7316 # edited; but detach is also needed for destruction.
7318 # If detach is called for any child, it should be called for
7319 # all children.
7321 # Reparent graphical parts.
7322 child.reparent_to_root()
7324 # Update reference.
7326 # Shift body parts.
7328 # Refresh decoration.
7330 # Move following items in parent.
7332 # Update tree data.
7333 pass
7334 l3Loop.detach = detach
7338 #* destroy etc: l3 "widget" destruction functions.
7339 from copy import deepcopy, copy
7340 from l3gui.misc import _safed
7342 #** destroy subtree and display
7343 # .destroy() is recursive
7344 #*** common hooks
7345 def add_destroy_hook(self, func):
7346 self._destroy_hook.append(func)
7347 l3Base.add_destroy_hook = add_destroy_hook
7348 l3aList.add_destroy_hook = add_destroy_hook
7349 Label.add_destroy_hook = add_destroy_hook
7350 Widget.add_destroy_hook = add_destroy_hook
7351 Image.add_destroy_hook = add_destroy_hook
7352 uWidget.add_destroy_hook = add_destroy_hook
7354 def _run_destroy_hook(self):
7355 for hook in self._destroy_hook:
7356 hook()
7357 del self._destroy_hook
7358 l3Base._run_destroy_hook = _run_destroy_hook
7359 l3aList._run_destroy_hook = _run_destroy_hook
7360 Label._run_destroy_hook = _run_destroy_hook
7361 Widget._run_destroy_hook = _run_destroy_hook
7362 Image._run_destroy_hook = _run_destroy_hook
7363 uWidget._run_destroy_hook = _run_destroy_hook
7366 #*** Base
7367 def destroy(self):
7368 self._root_group.hide() # Much faster w/o display update.
7369 if self._vis_indic:
7370 self._vis_indic.destroy()
7371 self._vis_indic = None
7373 self._run_destroy_hook()
7374 l3Base.destroy = destroy
7377 #**** Nested
7378 def destroy(self):
7379 l3Base.destroy(self)
7381 if self._container != self._parent:
7382 raise DisplayError("internal _container / _parent inconsistency.")
7384 if self._parent:
7385 self._parent.detach(self)
7386 self._parent = None
7388 # Headerless state.
7389 if self._header:
7390 _safed(self._header)
7391 self._header = None
7393 if self.w_.selector.get_selection() == self:
7394 self.w_.selector.unselect()
7396 self._marker = None
7397 self._container = None
7399 return True
7400 l3Nested.destroy = destroy
7404 #***** derived
7405 def destroy(self):
7406 l3Nested.destroy(self)
7407 _safed(self._d_loop)
7408 _safed(self._d_body)
7409 l3Nested.destroy_deco(self)
7410 l3IfLoop.destroy = destroy
7412 def destroy(self):
7413 l3Nested.destroy(self)
7414 _safed(self._d_label)
7415 l3Nested.destroy_deco(self)
7416 l3IfLoopBreak.destroy = destroy
7417 l3IfLoopContinue.destroy = destroy
7419 def destroy(self):
7420 l3Nested.destroy(self)
7422 _safed(self._d_if)
7423 _safed(self._d_cond)
7424 _safed(self._d_yes)
7425 _safed(self._d_else)
7426 _safed(self._d_no)
7427 ### self._l3tree.delete(self.w_.state_.storage)
7428 l3Nested.destroy_deco(self)
7429 l3If.destroy = destroy
7431 def destroy(self):
7432 l3Nested.destroy(self)
7433 _safed(self._d_loop)
7434 _safed(self._d_cond)
7435 _safed(self._d_body)
7436 l3Nested.destroy_deco(self)
7437 l3IfWhile.destroy = destroy
7439 def destroy(self):
7440 l3Nested.destroy(self)
7442 _safed(self._blank)
7443 l3Nested.destroy_deco(self)
7444 Placeholder.destroy = destroy
7446 def destroy(self):
7447 l3Nested.destroy(self)
7449 _safed(self._d_lhs)
7450 _safed(self._d_equal)
7451 _safed(self._d_rhs)
7452 ### self._l3tree.delete(self.w_.state_.storage)
7453 l3Nested.destroy_deco(self)
7454 l3Set.destroy = destroy
7456 def destroy(self):
7457 l3Nested.destroy(self)
7459 _safed(self._d_func)
7460 _safed(self._d_args)
7461 ### self._l3tree.delete(self.w_.state_.storage)
7462 l3Nested.destroy_deco(self)
7463 l3Call.destroy = destroy
7465 def destroy(self):
7466 l3Nested.destroy(self)
7468 _safed(self._d_function)
7469 _safed(self._d_args)
7470 _safed(self._d_body)
7472 ### self._l3tree.delete(self.w_.state_.storage)
7474 l3Nested.destroy_deco(self)
7475 l3Function.destroy = destroy
7477 def destroy(self):
7478 l3Nested.destroy(self)
7479 _safed(self._head)
7480 _safed(self._alist)
7481 ### self._l3tree.delete(self.w_.state_.storage)
7482 l3Nested.destroy_deco(self)
7483 l3List.destroy = destroy
7484 l3Map.destroy = destroy
7485 l3Program.destroy = destroy
7487 def destroy(self):
7488 l3Nested.destroy(self)
7489 _safed(self._head)
7490 _safed(self._pystr)
7491 l3Nested.destroy_deco(self)
7492 l3Inline.destroy = destroy
7493 l3Native.destroy = destroy
7496 #*** independent
7497 def destroy(self):
7498 self._root_group.hide() # Much faster w/o display update.
7499 if self._parent:
7500 self._parent.detach(self)
7501 self._parent = None
7502 self._marker = None
7503 self._container = None
7505 if self.w_.selector.get_selection() == self:
7506 self.w_.selector.unselect()
7508 # Remove other's _marker references first.
7509 for entry in copy(self._dlist): # _dlist changes during deletion
7510 _safed(entry)
7512 for mark in copy(self._marker_lst):
7513 _safed(mark)
7515 ### self._l3tree.delete(self.w_.state_.storage)
7517 _safed(self._root_group)
7518 del self._root_group
7520 self._run_destroy_hook()
7521 pass
7522 l3aList.destroy = destroy
7525 def destroy(self):
7526 # .destroy order checks.
7527 self._under_destruction = True
7529 self._canvas.remove_zoom_text(self)
7531 l3Base.destroy(self)
7533 # For structures, _marker and _container are set.
7534 # For nested text fragments (only editable via text editing),
7535 # _container and _marker are not set.
7536 inside_text = isinstance(self._parent, l3Rawtext)
7537 if not inside_text:
7538 if self._container != self._parent:
7539 raise DisplayError("internal _parent / _marker inconsistency.")
7541 if self._parent:
7542 if not inside_text:
7543 self._parent.detach(self)
7544 self._parent = None
7545 self._marker = None
7546 self._container = None
7548 # Headerless state.
7549 if self._header:
7550 _safed(self._header)
7551 self._header = None
7553 # if self._marker:
7554 # self._container.detach(self)
7555 # self._marker = None
7556 # self._container = None
7558 if self.w_.selector.get_selection() == self:
7559 self.w_.selector.unselect()
7561 # Destroy children first.
7562 for ch in copy(self._subtrees):
7563 _safed(ch)
7565 ### self._l3tree.delete(self.w_.state_.storage)
7567 # Destroy the display elements.
7568 _safed(self._ltext)
7569 _safed(self._loutline)
7570 _safed(self._root_group)
7571 del self._root_group
7572 del self._ltext
7573 del self._loutline
7574 l3Rawtext.destroy = destroy
7576 def destroy(self):
7577 # .destroy order checks.
7578 self._under_destruction = True
7580 self._canvas.remove_zoom_text(self)
7582 l3Base.destroy(self)
7584 # Diff.
7585 if self._container != None:
7586 raise DisplayError("internal marker inconsistency.")
7588 # Diff.
7589 if self._parent:
7590 self._parent.detach_header(self)
7591 self._parent = None
7592 self._marker = None
7593 self._container = None
7595 if self.w_.selector.get_selection() == self:
7596 self.w_.selector.unselect()
7598 _safed(self._ltext)
7599 _safed(self._loutline)
7600 _safed(self._root_group)
7601 del self._root_group
7602 del self._ltext
7603 del self._loutline
7604 l3Comment.destroy = destroy
7606 def destroy(self):
7607 self._canvas.remove_zoom_text(self)
7608 _safed(self._loutline)
7609 _safed(self._ltext)
7610 _safed(self._root_group)
7612 self._run_destroy_hook()
7613 Label.destroy = destroy
7615 def destroy(self):
7616 _safed(self._ltext)
7617 _safed(self._root_group)
7618 self._run_destroy_hook()
7619 Widget.destroy = destroy
7620 Image.destroy = destroy
7621 uWidget.destroy = destroy
7624 #* Saving and loading of state.
7625 # The main entry points are `save_state` and `load_state`.
7626 from types import IntType, LongType
7628 from l3gui.misc import _get_props, _set_props
7631 #** saving
7632 # The Canvas* instances are not extensible; either subclass them here for
7633 # smooth persistence -- or use a dispatch table.
7635 # Some of the more useful properties (width-units, char* version of
7636 # fill-color) are write-only. Subclasses could intercept and store
7637 # them.
7638 # Generally, mirroring properties in a derived class (or elsewhere)
7639 # is not practical; many properties are changed via commands other
7640 # than .set() and .set_property(), and those changes need to be
7641 # observed.
7643 # So, rather than mirroring much of the gtk object tree, use only
7644 # attributes that are Read/Write, AND can be pickled if possible.
7646 # When absolutely necessary, provide special access functions for
7647 # specific properties.
7650 #*** gtk widgets
7651 def st_TextBuffer(item, prop_list):
7652 ## item.get_text(*item.get_bounds())
7653 return _get_props(
7654 item, prop_list,
7655 ## "tag-table", # GtkTextTagTable : Read / Write / Construct Only
7656 # Not in pygtk
7657 ## "text", # gchararray : Read / Write
7660 def st_TextTag(item, prop_list):
7661 return _get_props(
7662 item, prop_list,
7663 ## "background", # gchararray : Write
7664 "background-full-height-set", # gboolean : Read / Write
7665 "background-full-height", # gboolean : Read / Write
7666 ## "background-gdk", # GdkColor : Read / Write
7667 ## "background-set", # gboolean : Read / Write
7668 ## "background-stipple", # GdkPixmap : Read / Write
7669 ## "background-stipple-set", # gboolean : Read / Write
7670 ## "direction", # GtkTextDirection : Read / Write
7671 "editable-set", # gboolean : Read / Write
7672 "editable", # gboolean : Read / Write
7673 "family-set", # gboolean : Read / Write
7674 "family", # gchararray : Read / Write
7675 "font", # gchararray : Read / Write
7676 ## "font-desc", # PangoFontDescription : Read / Write
7677 ## "foreground", # gchararray : Write
7678 ## "foreground-gdk", # GdkColor : Read / Write
7679 ## "foreground-set", # gboolean : Read / Write
7680 ## "foreground-stipple", # GdkPixmap : Read / Write
7681 ## "foreground-stipple-set", # gboolean : Read / Write
7682 ## "indent", # gint : Read / Write
7683 ## "indent-set", # gboolean : Read / Write
7684 ## "invisible", # gboolean : Read / Write
7685 ## "invisible-set", # gboolean : Read / Write
7686 ## "justification", # GtkJustification : Read / Write
7687 ## "justification-set", # gboolean : Read / Write
7688 ## "language", # gchararray : Read / Write
7689 ## "language-set", # gboolean : Read / Write
7690 ## "left-margin", # gint : Read / Write
7691 ## "left-margin-set", # gboolean : Read / Write
7692 ## "name", # gchararray : Read / Write / Construct Only
7693 ## "paragraph-background", # gchararray : Write
7694 ## "paragraph-background-gdk", # GdkColor : Read / Write
7695 ## "paragraph-background-set", # gboolean : Read / Write
7696 ## "pixels-above-lines", # gint : Read / Write
7697 ## "pixels-above-lines-set", # gboolean : Read / Write
7698 ## "pixels-below-lines", # gint : Read / Write
7699 ## "pixels-below-lines-set", # gboolean : Read / Write
7700 ## "pixels-inside-wrap", # gint : Read / Write
7701 ## "pixels-inside-wrap-set", # gboolean : Read / Write
7702 ## "right-margin", # gint : Read / Write
7703 ## "right-margin-set", # gboolean : Read / Write
7704 ## "rise", # gint : Read / Write
7705 ## "rise-set", # gboolean : Read / Write
7706 ## "scale", # gdouble : Read / Write
7707 ## "scale-set", # gboolean : Read / Write
7708 ## "size", # gint : Read / Write
7709 "size-set", # gboolean : Read / Write
7710 "size-points", # gdouble : Read / Write
7711 ## "stretch", # PangoStretch : Read / Write
7712 ## "stretch-set", # gboolean : Read / Write
7713 ## "strikethrough", # gboolean : Read / Write
7714 ## "strikethrough-set", # gboolean : Read / Write
7715 ## "style", # PangoStyle : Read / Write
7716 ## "style-set", # gboolean : Read / Write
7717 ## "tabs", # PangoTabArray : Read / Write
7718 ## "tabs-set", # gboolean : Read / Write
7719 ## "underline", # PangoUnderline : Read / Write
7720 ## "underline-set", # gboolean : Read / Write
7721 ## "variant", # PangoVariant : Read / Write
7722 ## "variant-set", # gboolean : Read / Write
7723 ## "weight", # gint : Read / Write
7724 ## "weight-set", # gboolean : Read / Write
7725 ## "wrap-mode", # GtkWrapMode : Read / Write
7726 ## "wrap-mode-set", # gboolean : Read / Write
7729 def st_TextView(item, prop_list):
7730 return _get_props(
7731 item, prop_list,
7732 "accepts-tab", # gboolean : Read / Write
7733 ## "buffer", # GtkTextBuffer : Read / Write
7734 "cursor-visible", # gboolean : Read / Write
7735 "editable", # gboolean : Read / Write
7736 "indent", # gint : Read / Write
7737 ## "justification", # GtkJustification : Read / Write
7738 "left-margin", # gint : Read / Write
7739 "overwrite", # gboolean : Read / Write
7740 "pixels-above-lines", # gint : Read / Write
7741 "pixels-below-lines", # gint : Read / Write
7742 "pixels-inside-wrap", # gint : Read / Write
7743 "right-margin", # gint : Read / Write
7744 ## "tabs", # PangoTabArray : Read / Write
7745 ## "wrap-mode", # GtkWrapMode : Read / Write
7749 #*** canvas items
7750 # List of readable & writeable display properties taken from the
7751 # documentation.
7755 #**** CanvasShape
7756 def st_CanvasShape(item, prop_list):
7757 # The fill-color-gdk is a C struct;
7758 # fill-color-rgba is a (platform-dependent?) value
7759 # The width-units property is needed for scaling.
7760 return _get_props(
7761 item, prop_list,
7762 "cap-style", # GdkCapStyle : Read / Write
7763 "dash", # gpointer : Read / Write
7765 ## "fill-color", # gchararray : Write
7766 ## "fill-color-gdk", # GdkColor : Read / Write
7767 "fill-color-rgba", # guint : Read / Write
7769 "fill-stipple", # GdkDrawable : Read / Write
7770 "join-style", # GdkJoinStyle : Read / Write
7771 "miterlimit", # gdouble : Read / Write
7773 ## "outline-color", # gchararray : Write
7774 ## "outline-color-gdk", # GdkColor : Read / Write
7775 "outline-color-rgba", # guint : Read / Write
7777 "outline-stipple", # GdkDrawable : Read / Write
7778 "width-pixels", # guint : Read / Write
7779 # Do not use scaled units.
7780 ## "width-units", # gdouble : Write
7781 "wind", # guint : Read / Write
7785 #**** CanvasRE
7786 def st_CanvasRE(item, prop_list):
7787 return _get_props(item, prop_list,
7788 "x1", # gdouble : Read / Write
7789 "x2", # gdouble : Read / Write
7790 "y1", # gdouble : Read / Write
7791 "y2", # gdouble : Read / Write
7793 # # Ignore these renderer-specific properties.
7794 # # return st_CanvasShape(item, prop_list)
7797 #**** CanvasPolygon
7798 def st_CanvasPolygon(w_, item, prop_list, creator = None):
7799 if creator:
7800 prop_list.append( ("points", creator._marker_points[item]) )
7801 else:
7802 ### This fails because of a bug in
7803 ### gnome_canvas_polygon_get_property().
7804 raise DisplayError("st_CanvasPolygon(internal): "
7805 "Missing creator arg.")
7806 _get_props(item, prop_list,
7807 "points", # GnomeCanvasPoints : Read / Write
7810 if w_.fluid_ref(dump_ps = False):
7811 pts = creator._marker_points[item]
7812 i2w = item.i2w
7814 print "% st_CanvasPolygon"
7815 if len(pts) >=2 :
7816 print " %s %s gcmoveto" % i2w(pts[0], pts[1])
7817 for pt1, pt2 in zip(pts[2::2], pts[3::2]):
7818 print " %s %s gclineto" % i2w(pt1, pt2)
7819 print " stroke"
7821 return st_CanvasShape(item, prop_list)
7824 #**** CanvasRect
7825 def st_CanvasRect(w_, item, prop_list):
7826 return st_CanvasRE(item, prop_list)
7829 #**** CanvasGroup
7830 def st_CanvasGroup(item, prop_list):
7831 return _get_props(item, prop_list,
7832 "x", # gdouble : Read / Write
7833 "y", # gdouble : Read / Write
7836 #**** CanvasText
7837 def st_CanvasText(w_, item, prop_list):
7838 if w_.fluid_ref(dump_ps = False):
7839 get = item.get_property
7840 i2w = item.i2w
7841 print "% st_CanvasText"
7842 x1, y1, _, _ = item.get_bounds()
7843 print " %s %s gcmoveto" % i2w(x1, y1)
7844 print ' (%s) gcshow' % (get("text"))
7846 return _get_props(
7847 item, prop_list,
7848 "anchor", # GtkAnchorType : Read / Write
7849 ## None fails to restore.
7850 ## "attributes", # PangoAttrList : Read / Write
7851 "clip", # gboolean : Read / Write
7852 "clip-height", # gdouble : Read / Write
7853 "clip-width", # gdouble : Read / Write
7854 "family-set", # gboolean : Read / Write
7855 "family", # gchararray : Read / Write
7856 "fill-color", # gchararray : Read / Write
7857 ## "fill-color-gdk", # GdkColor : Read / Write
7858 "fill-color-rgba", # guint : Read / Write
7859 "fill-stipple", # GdkDrawable : Read / Write
7860 "font", # gchararray : Read / Write
7861 ## "font-desc", # PangoFontDescription : Read / Write
7862 "justification", # GtkJustification : Read / Write
7863 ## "markup", # gchararray : Write
7864 "rise-set", # gboolean : Read / Write
7865 "rise", # gint : Read / Write
7866 "scale-set", # gboolean : Read / Write
7867 "scale", # gdouble : Read / Write
7868 "size", # gint : Read / Write
7869 "size-set", # gboolean : Read / Write
7870 "size-points", # gdouble : Read / Write
7871 "stretch-set", # gboolean : Read / Write
7872 "stretch", # PangoStretch : Read / Write
7873 "strikethrough-set", # gboolean : Read / Write
7874 "strikethrough", # gboolean : Read / Write
7875 "style-set", # gboolean : Read / Write
7876 "style", # PangoStyle : Read / Write
7877 "text", # gchararray : Read / Write
7879 # property text-height is not writable text-height 2.28
7880 # property text-width is not writable text-width 1.68
7881 ## "text-height", # gdouble : Read / Write
7882 ## "text-width", # gdouble : Read / Write
7883 "underline-set", # gboolean : Read / Write
7884 "underline", # PangoUnderline : Read / Write
7885 "variant-set", # gboolean : Read / Write
7886 "variant", # PangoVariant : Read / Write
7887 "weight-set", # gboolean : Read / Write
7888 "weight", # gint : Read / Write
7889 "x", # gdouble : Read / Write
7890 "x-offset", # gdouble : Read / Write
7891 "y", # gdouble : Read / Write
7892 "y-offset", # gdouble : Read / Write
7896 #**** CanvasLine
7897 def st_CanvasLine(w_, item, prop_list):
7898 if w_.fluid_ref(dump_ps = False):
7899 pts = item.get_property("points")
7900 i2w = item.i2w
7902 print "% st_CanvasLine"
7903 if len(pts) >=2 :
7904 print " %s %s gcmoveto" % i2w(pts[0], pts[1])
7905 for pt1, pt2 in zip(pts[2::2], pts[3::2]):
7906 print " %s %s gclineto" % i2w(pt1, pt2)
7907 print " stroke"
7909 return _get_props(
7910 item, prop_list,
7911 "arrow-shape-a", # gdouble : Read / Write
7912 "arrow-shape-b", # gdouble : Read / Write
7913 "arrow-shape-c", # gdouble : Read / Write
7914 "cap-style", # GdkCapStyle : Read / Write
7915 "fill-color", # gchararray : Read / Write
7916 ## "fill-color-gdk", # GdkColor : Read / Write
7917 "fill-color-rgba", # guint : Read / Write
7918 "fill-stipple", # GdkDrawable : Read / Write
7919 "first-arrowhead", # gboolean : Read / Write
7920 "join-style", # GdkJoinStyle : Read / Write
7921 "last-arrowhead", # gboolean : Read / Write
7922 "line-style", # GdkLineStyle : Read / Write
7923 "points", # GnomeCanvasPoints : Read / Write
7924 "smooth", # gboolean : Read / Write
7925 "spline-steps", # guint : Read / Write
7926 ## "width-pixels", # guint : Read / Write
7927 "width-units", # gdouble : Read / Write
7931 #**** CanvasWidget
7932 def st_CanvasWidget(item, prop_list):
7933 return _get_props(
7934 item, prop_list,
7935 ## "anchor", # GtkAnchorType : Read / Write
7936 "height", # gdouble : Read / Write
7937 "size-pixels", # gboolean : Read / Write
7938 ## "widget", # GtkWidget : Read / Write
7939 "width", # gdouble : Read / Write
7940 "x", # gdouble : Read / Write
7941 "y", # gdouble : Read / Write
7945 #*** local classes
7947 # Originally, the full display state was saved via recursion of the
7948 # display tree. This turned out to be a bad idea for file size,
7949 # reliabilty, and made using old state files impossible (just like a
7950 # well-known word processor).
7952 # Now, only the position information of the topmost object is
7953 # collected.
7955 # The traversal used and the data structuring may be
7956 # useful in other contexts (e.g., export to PDF), so the code is
7957 # retained here. Some original notes:
7959 # Pickling highly intertwined (graph-structured) state is tricky. The
7960 # get_state() members are arranged to produce a spanning tree traversal of
7961 # all data and avoid circularities.
7963 # For trees, starting pickling in a subtree and (randomly) getting the
7964 # parents is bad for pickling and reconstruction.
7966 # Also, there is some special state, the global
7967 # w_ == w_
7968 # and multiple upward references like
7969 # self._canvas (circular -- parent)
7971 # This special state is saved only once, and the links restored in a
7972 # separate pass.
7974 def get_state(self):
7976 st = utils.Shared(
7977 _root_group = st_CanvasGroup(self._root_group, []),
7979 return st
7980 l3aList.get_state = get_state
7981 Label.get_state = get_state
7982 Widget.get_state = get_state
7983 Image.get_state = get_state
7984 uWidget.get_state = get_state
7986 def get_state(self):
7987 return utils.Shared(
7988 # # _l3tree = self._l3tree,
7989 _root_group = st_CanvasGroup(self._root_group, []),
7991 l3Base.get_state = get_state
7993 def get_state(self):
7994 return utils.Shared(
7995 l3base = l3Base.get_state(self),
7996 # # _root_group = st_CanvasGroup(self._root_group, []),
7998 l3Rawtext.get_state = get_state
8000 def get_state(self):
8001 return utils.Shared(
8002 l3base = l3Base.get_state(self),
8004 l3Nested.get_state = get_state
8007 def get_state(self):
8008 return None
8009 CommonPopup.get_state = get_state
8011 #*** table formation
8013 def start_set_tables(l3tree):
8015 Fill tables with values stored as attributes of an astType tree
8016 `l3tree`. Return a table of tables.
8018 from l3gui.l3canvas import RenderTable
8019 tables = utils.Shared(rentab = RenderTable())
8020 l3tree.set_tables(tables)
8021 return tables
8023 # These functions are intended to fully traverse the (display) widget
8024 # tree, and may be split into traversal iterators / main functions
8025 # later.
8027 def set_tables(self, tables):
8028 l3Base.set_tables(self, tables)
8029 tables.rentab.set_state(self._l3tree, self._render_mode)
8030 l3Nested.set_tables = set_tables
8033 def set_tables(self, tables):
8034 l3Base.set_tables(self, tables)
8035 tables.rentab.set_state(self._l3tree, self._render_mode)
8036 for st in self._subtrees:
8037 st.set_tables(tables)
8038 l3Rawtext.set_tables = set_tables
8040 def set_tables(self, tables):
8041 l3Rawtext.set_tables(self, tables)
8042 l3Comment.set_tables = set_tables
8044 def set_tables(self, tables):
8045 l3List.set_tables(self, tables)
8046 self._alist.set_tables(tables)
8047 l3ViewList.set_tables = set_tables
8050 def set_tables(self, tables):
8051 l3Nested.set_tables(self, tables)
8052 self._alist.set_tables(tables)
8053 l3List.set_tables = set_tables
8054 l3Map.set_tables = set_tables
8057 def set_tables(self, tables):
8058 l3Nested.set_tables(self, tables)
8059 (_d_cond,
8060 _d_yes ,
8061 _d_no ,
8065 ) = self._d_elements
8066 _d_cond.set_tables(tables)
8067 _d_yes .set_tables(tables)
8068 _d_no .set_tables(tables)
8069 l3If.set_tables = set_tables
8072 def set_tables(self, tables):
8073 l3Nested.set_tables(self, tables)
8074 ( d_loop,
8075 d_body, ) = self._d_elements
8076 d_loop.set_tables(tables)
8077 d_body.set_tables(tables)
8078 l3IfOutline.set_tables = set_tables
8079 l3IfLoop.set_tables = set_tables
8082 def set_tables(self, tables):
8083 l3Nested.set_tables(self, tables)
8084 (d_cond,
8085 d_body,
8088 ) = self._d_elements
8089 d_cond.set_tables(tables)
8090 d_body.set_tables(tables)
8091 l3IfWhile.set_tables = set_tables
8094 def set_tables(self, tables):
8095 l3Nested.set_tables(self, tables)
8096 ( _d_func,
8097 _d_args,
8100 ) = self._d_elements
8101 _d_func.set_tables(tables)
8102 _d_args.set_tables(tables)
8103 l3Call.set_tables = set_tables
8106 def set_tables(self, tables):
8107 l3Nested.set_tables(self, tables)
8108 self._alist.set_tables(tables)
8109 l3Program.set_tables = set_tables
8112 def set_tables(self, tables):
8113 l3Nested.set_tables(self, tables)
8114 self._pystr.set_tables(tables)
8115 l3Inline.set_tables = set_tables
8118 def set_tables(self, tables):
8119 l3Nested.set_tables(self, tables)
8120 self._d_function.set_tables(tables)
8121 self._d_args.set_tables(tables)
8122 self._d_body.set_tables(tables)
8123 l3Function.set_tables = set_tables
8126 def set_tables(self, tables):
8127 l3Nested.set_tables(self, tables)
8128 self._d_lhs.set_tables(tables)
8129 self._d_rhs.set_tables(tables)
8130 l3Set.set_tables = set_tables
8133 def set_tables(self, tables):
8134 l3Nested.set_tables(self, tables)
8135 Placeholder.set_tables = set_tables
8136 l3IfLoopBreak.set_tables = set_tables
8137 l3IfLoopContinue.set_tables = set_tables
8138 l3Native.set_tables = set_tables
8141 def set_tables(self, tables):
8142 pass
8143 Label.set_tables = set_tables
8144 Widget.set_tables = set_tables
8145 Image.set_tables = set_tables
8146 uWidget.set_tables = set_tables
8147 l3Base.set_tables = set_tables
8148 l3Deco.set_tables = set_tables
8151 def set_tables(self, tables):
8152 for itm in self._dlist:
8153 itm.set_tables(tables)
8154 l3aList.set_tables = set_tables
8158 #*** find_root
8159 def find_root(self):
8160 if self._parent != None:
8161 return self._parent.find_root()
8162 else:
8163 return self, self._l3tree._id
8164 l3Base.find_root = find_root
8165 l3aList.find_root = find_root
8167 #** loading
8168 # The reconstruction of the data structures is done in passes:
8169 # - form the spanning tree using loaded data
8170 # - reconnect graph edges.
8173 #*** l3aList
8174 def set_state(self, state):
8175 _set_props(self._root_group, state._root_group)
8176 l3Base.set_state = set_state
8177 l3aList.set_state = set_state
8178 Label.set_state = set_state
8179 Widget.set_state = set_state
8180 Image.set_state = set_state
8181 uWidget.set_state = set_state
8183 def set_state(self, state):
8184 l3Base.set_state(self, state.l3base)
8185 # # _set_props(self._root_group, state._root_group)
8186 l3Rawtext.set_state = set_state
8188 def set_state(self, state):
8189 l3Base.set_state(self, state.l3base)
8190 l3Nested.set_state = set_state
8193 #*** CommonPopup
8194 def set_state(self, state):
8195 pass
8196 CommonPopup.set_state = set_state
8199 #* l3IfFor members
8200 # An experiment of grouping the members with the class definition,
8201 # instead of functional grouping.
8202 # As expected, this class grouping makes function-based additions much
8203 # more difficult.
8205 def continue_stop_pt(self):
8206 # todo.
8207 lc, tc, rc, bc = self._d_loop.get_bounds_world()
8208 return lc + (rc - lc)/10, bc ## parameters
8209 l3IfFor.continue_stop_pt = continue_stop_pt
8211 def break_stop_pt(self):
8212 # todo.
8213 lc, tc, rc, bc = self.get_bounds_world()
8214 return rc + 4, bc ## parameters
8215 l3IfFor.break_stop_pt = break_stop_pt
8218 def __init__(self, w_, cnvs, tree_id, rentab, matcher):
8219 # l3 tree.
8220 l3tree = w_.state_.storage.load(tree_id)
8221 assert isinstance(l3tree, ast.If)
8222 l3Nested.__init__(self, w_, cnvs, l3tree, rentab)
8224 # Bindings to keep.
8225 self.w_ = w_
8226 self._canvas = cnvs
8227 self._marker_points = {} # marker -> point list
8228 self._matcher = ma = matcher
8229 self._l3tree = l3tree
8230 self._deco_cb_buffer = [] # callback list for add_item()
8233 # Display elements.
8234 # -- for
8235 d_loop_for = Label(w_, cnvs, self._root_group, "for")
8237 # -- V
8238 d_cond_V = cnvs.add_l3tree(ma.get_current_V(), rentab)
8239 d_cond_V.reparent(self)
8241 # -- in
8242 d_loop_in = Label(w_, cnvs, self._root_group, "in")
8244 # -- SEQ
8245 d_cond_SEQ = cnvs.add_l3tree(ma.get_current_SEQ(), rentab)
8246 d_cond_SEQ.reparent(self)
8248 # -- B
8249 d_body = l3aList(w_, cnvs,
8250 ma.get_B(),
8251 rentab,
8252 root_group = self._root_group,
8253 draggable = False,
8254 parent = self,
8257 # Marker(s).
8258 d_cond_V_marker = self.draw_marker()
8259 d_cond_V_marker.lower_to_bottom()
8261 d_cond_SEQ_marker = self.draw_marker()
8262 d_cond_SEQ_marker.lower_to_bottom()
8264 # Inform obj of marker.
8265 d_cond_V.setup_marker(d_cond_V_marker, self)
8266 d_cond_SEQ.setup_marker(d_cond_SEQ_marker, self)
8268 # Bindings to keep.
8269 # Indexed access. Match l3tree indexing where applicable.
8270 self._d_elements = [
8271 # l3 ast.
8272 d_cond_V,
8273 d_cond_SEQ,
8274 d_body,
8276 # Rest
8277 d_loop_for,
8278 d_loop_in,
8279 d_cond_V_marker,
8280 d_cond_SEQ_marker,
8282 self._update_refs()
8285 # Align display elements, starting from d_loop_for
8287 self._align_display()
8289 # Highlighting.
8290 self._outline = None
8292 # Item cross-referencing.
8293 self._marker = None
8294 self._container = None
8296 # Event handling.
8297 d_cond_V_marker.connect("event", lambda *a: self.insert_event(*a))
8298 d_cond_SEQ_marker.connect("event", lambda *a: self.insert_event(*a))
8300 # Borders etc.
8301 self.init_header(rentab)
8302 self.init_deco(rentab)
8304 pass
8305 l3IfFor.__init__ = __init__
8307 def _update_refs(self):
8309 self._d_cond_V,
8310 self._d_cond_SEQ,
8311 self._d_body,
8313 self._d_loop_for,
8314 self._d_loop_in,
8315 self._d_cond_V_marker,
8316 self._d_cond_SEQ_marker,
8317 ) = self._d_elements
8318 l3IfFor._update_refs = _update_refs
8321 def _align_display(self):
8322 flush_events()
8324 # Align w.r.t. 'for'
8327 d_cond_V,
8328 d_cond_SEQ,
8329 d_body,
8331 d_loop_for,
8332 d_loop_in,
8333 d_cond_V_marker,
8334 d_cond_SEQ_marker,
8335 ) = self._d_elements
8337 w_ = self.w_
8338 # -- V
8339 l_for, t_for, r_for, b_for = d_loop_for.get_bounds()
8340 l_v, t_v, r_v, b_v = d_cond_V.get_bounds()
8341 d_cond_V.move(r_for + w_.cp_.loop_cond_sep - l_v,
8342 t_for - t_v)
8343 # marker for child replacement.
8344 l_v, t_v, r_v, b_v = d_cond_V.get_bounds()
8345 lma, tma, _, _ = d_cond_V_marker.get_bounds()
8346 d_cond_V_marker.move(l_v - lma + w_.cp_.exp_marker_hoff,
8347 t_v - tma + w_.cp_.exp_marker_voff)
8349 # -- in
8350 l_v, t_v, r_v, b_v = d_cond_V.get_bounds()
8351 l_in, t_in, r_in, b_in = d_loop_in.get_bounds()
8352 d_loop_in.move(r_v + w_.cp_.loop_cond_sep - l_in,
8353 t_v - t_in)
8355 # -- SEQ
8356 l_seq, t_seq, r_seq, b_seq = d_cond_SEQ.get_bounds()
8357 l_in, t_in, r_in, b_in = d_loop_in.get_bounds()
8358 d_cond_SEQ.move(r_in + w_.cp_.loop_cond_sep - l_seq,
8359 t_in - t_seq)
8360 # marker
8361 l_seq, t_seq, r_seq, b_seq = d_cond_SEQ.get_bounds()
8362 lma, tma, _, _ = d_cond_SEQ_marker.get_bounds()
8363 d_cond_SEQ_marker.move(l_seq - lma + w_.cp_.exp_marker_hoff,
8364 t_seq - tma + w_.cp_.exp_marker_voff)
8366 # -- B
8367 l_v, t_v, r_v, b_v = d_cond_V.get_bounds()
8368 l_seq, t_seq, r_seq, b_seq = d_cond_SEQ.get_bounds()
8369 l_b, t_b, r_b, b_b = d_body.get_bounds()
8370 d_body.move(l_for - l_b,
8371 max(b_v, b_seq) - t_b)
8372 l3IfFor._align_display = _align_display
8375 def hide(self):
8376 self._root_group.hide()
8377 self._d_loop_for.hide()
8378 self._d_loop_in.hide()
8379 self._d_cond_V.hide()
8380 self._d_cond_SEQ.hide()
8381 self._d_body.hide()
8382 l3IfFor.hide = hide
8385 def insert(self, marker, newobj):
8387 # Insert new value or sequence
8389 assert isinstance(newobj, l3Base)
8391 # Avoid special cases:
8392 if newobj.contains_recursive(self):
8393 self.w_.ten_second_message("Cannot insert if into itself.")
8394 return
8396 if newobj._canvas != self._canvas:
8397 self.w_.ten_second_message("Cannot move objects across displays. "
8398 "Copy instead.")
8399 return
8401 # Which marker?
8402 if marker == self._d_cond_V_marker:
8403 # Validate slot.
8404 if not self._matcher.get_current_V().eql(ast.aNone()):
8405 raise DisplayError("Insertion in occupied slot.")
8407 # Update reference.
8408 self._d_cond_V = d_cond_V = newobj
8410 # Reparent object.
8411 newobj.reparent(self)
8413 # Position COND.
8414 flush_events()
8416 # Reposition rest.
8417 self._align_display()
8419 # Refresh decoration.
8420 self.refresh_deco()
8422 # Move following items in parent.
8423 if self._parent:
8424 self._parent.new_size_for(self)
8426 # Update tree data.
8427 if self.w_.fluid_ref(insert_l3_tree = True):
8428 self._matcher.set_V(newobj._l3tree)
8430 # Inform obj of marker.
8431 newobj.setup_marker(self._d_cond_V_marker, self)
8433 elif marker == self._d_cond_SEQ_marker:
8434 # Validate slot.
8435 if not self._matcher.get_current_SEQ().eql(ast.aNone()):
8436 raise DisplayError("Insertion in occupied slot.")
8438 # Update reference.
8439 self._d_cond_SEQ = d_cond_SEQ = newobj
8441 # Reparent object.
8442 newobj.reparent(self)
8444 # Position COND.
8445 flush_events()
8447 # Reposition rest.
8448 self._align_display()
8450 # Refresh decoration.
8451 self.refresh_deco()
8453 # Move following items in parent.
8454 if self._parent:
8455 self._parent.new_size_for(self)
8457 # Update tree data.
8458 if self.w_.fluid_ref(insert_l3_tree = True):
8459 self._matcher.set_SEQ(newobj._l3tree)
8461 # Inform obj of marker.
8462 newobj.setup_marker(self._d_cond_SEQ_marker, self)
8464 else:
8465 raise DisplayError("Insertion from wrong marker.")
8466 l3IfFor.insert = insert
8469 def init_deco(self, rentab):
8470 # Decorate body.
8471 self._deco = None ## add_deco_to(self, callbacks = self._deco_cb_buffer)
8472 l3IfFor.init_deco = init_deco
8475 def new_size_for(self, child):
8477 # Shift contents vertically as needed.
8479 assert isinstance(child, (l3Base, l3aList))
8481 # Find and apply shift.
8482 if child in [self._d_cond_V, self._d_cond_SEQ]:
8483 self._align_display()
8485 # Refresh decoration.
8486 self.refresh_deco()
8488 # Propagate.
8489 if self._parent:
8490 self._parent.new_size_for(self)
8491 return
8492 l3IfFor.new_size_for = new_size_for
8495 def detach(self, child):
8496 # When drag distance from marker to item exceeds item_detach_distance,
8497 # detach the item. This criterion is implemented in
8498 # l3Rawtext.drag_event.
8500 if child == self._d_cond_V:
8501 # Update reference.
8502 self._d_cond_V = None
8503 the_child = self._matcher.get_current_V()
8505 elif child == self._d_cond_SEQ:
8506 # Update reference.
8507 self._d_cond_SEQ = None
8508 the_child = self._matcher.get_current_SEQ()
8510 else:
8511 self.detach_only(child)
8512 return
8514 # Reparent graphical parts.
8515 child.reparent_to_root()
8517 # Shift body parts.
8518 # self._align_display()
8520 # Refresh decoration.
8521 self.refresh_deco()
8523 # Move following items in parent.
8524 if self._parent:
8525 self._parent.new_size_for(self)
8527 # Update tree data.
8528 if self.w_.fluid_ref(detach_l3_tree = True):
8529 the_child.detach_from_parent(self.w_.state_.storage)
8530 pass
8531 l3IfFor.detach = detach
8534 def destroy(self):
8535 l3Nested.destroy(self)
8536 _safed(self._d_loop_for)
8537 _safed(self._d_loop_in)
8538 _safed(self._d_cond_SEQ)
8539 _safed(self._d_cond_V)
8540 _safed(self._d_body)
8541 l3Nested.destroy_deco(self)
8542 l3IfFor.destroy = destroy
8545 def set_tables(self, tables):
8546 l3Nested.set_tables(self, tables)
8548 d_cond_V,
8549 d_cond_SEQ,
8550 d_body,
8555 ) = self._d_elements
8556 d_cond_V.set_tables(tables)
8557 d_cond_SEQ.set_tables(tables)
8558 d_body.set_tables(tables)
8559 l3IfFor.set_tables = set_tables
8562 l3IfFor.detach_only = detach_only
8563 l3IfFor.draw_marker = draw_marker_shared
8564 l3IfFor.insert_event = insert_event_shared
8565 l3IfFor.detach_only = detach_only