wxruby3-shapes 0.9.0.pre.beta.3 → 0.9.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/INSTALL.md +5 -7
  3. data/README.md +38 -6
  4. data/assets/logo.svg +339 -0
  5. data/assets/logo.xpm +60 -0
  6. data/assets/screenshot.png +0 -0
  7. data/assets/social.png +0 -0
  8. data/bin/wx-shapes +1 -1
  9. data/lib/wx/shapes/arrow_base.rb +4 -11
  10. data/lib/wx/shapes/arrows/circle_arrow.rb +22 -11
  11. data/lib/wx/shapes/arrows/circle_prong_arrow.rb +48 -0
  12. data/lib/wx/shapes/arrows/cross_bar_arrow.rb +57 -0
  13. data/lib/wx/shapes/arrows/cross_bar_circle_arrow.rb +56 -0
  14. data/lib/wx/shapes/arrows/cross_bar_prong_arrow.rb +49 -0
  15. data/lib/wx/shapes/arrows/crossed_circle.rb +46 -0
  16. data/lib/wx/shapes/arrows/cup_arrow.rb +65 -0
  17. data/lib/wx/shapes/arrows/diamond_arrow.rb +8 -13
  18. data/lib/wx/shapes/arrows/double_cross_bar_arrow.rb +27 -0
  19. data/lib/wx/shapes/arrows/filled_arrow.rb +60 -0
  20. data/lib/wx/shapes/arrows/line_arrow.rb +67 -0
  21. data/lib/wx/shapes/arrows/open_arrow.rb +22 -23
  22. data/lib/wx/shapes/arrows/prong_arrow.rb +42 -0
  23. data/lib/wx/shapes/arrows/solid_arrow.rb +21 -35
  24. data/lib/wx/shapes/arrows/square_arrow.rb +37 -0
  25. data/lib/wx/shapes/auto_layout.rb +2 -2
  26. data/lib/wx/shapes/base.rb +1 -1
  27. data/lib/wx/shapes/canvas_history.rb +20 -0
  28. data/lib/wx/shapes/connection_point.rb +10 -6
  29. data/lib/wx/shapes/diagram.rb +98 -78
  30. data/lib/wx/shapes/events.rb +8 -8
  31. data/lib/wx/shapes/printout.rb +3 -16
  32. data/lib/wx/shapes/serializable.rb +2 -436
  33. data/lib/wx/shapes/serialize/wx.rb +30 -18
  34. data/lib/wx/shapes/shape.rb +211 -168
  35. data/lib/wx/shapes/shape_canvas.rb +728 -267
  36. data/lib/wx/shapes/shape_data_object.rb +99 -18
  37. data/lib/wx/shapes/shape_handle.rb +18 -11
  38. data/lib/wx/shapes/shape_list.rb +34 -67
  39. data/lib/wx/shapes/shapes/bitmap_shape.rb +23 -24
  40. data/lib/wx/shapes/shapes/box_shape.rb +389 -0
  41. data/lib/wx/shapes/shapes/circle_shape.rb +19 -22
  42. data/lib/wx/shapes/shapes/control_shape.rb +77 -41
  43. data/lib/wx/shapes/shapes/curve_shape.rb +38 -31
  44. data/lib/wx/shapes/shapes/diamond_shape.rb +7 -17
  45. data/lib/wx/shapes/shapes/edit_text_shape.rb +6 -9
  46. data/lib/wx/shapes/shapes/ellipse_shape.rb +12 -15
  47. data/lib/wx/shapes/shapes/flex_grid_shape.rb +58 -33
  48. data/lib/wx/shapes/shapes/grid_shape.rb +259 -161
  49. data/lib/wx/shapes/shapes/line_shape.rb +155 -161
  50. data/lib/wx/shapes/shapes/manager_shape.rb +77 -0
  51. data/lib/wx/shapes/shapes/multi_sel_rect.rb +8 -8
  52. data/lib/wx/shapes/shapes/ortho_shape.rb +31 -36
  53. data/lib/wx/shapes/shapes/polygon_shape.rb +23 -29
  54. data/lib/wx/shapes/shapes/rect_shape.rb +95 -53
  55. data/lib/wx/shapes/shapes/round_ortho_shape.rb +6 -8
  56. data/lib/wx/shapes/shapes/round_rect_shape.rb +20 -24
  57. data/lib/wx/shapes/shapes/square_shape.rb +14 -17
  58. data/lib/wx/shapes/shapes/text_shape.rb +95 -53
  59. data/lib/wx/shapes/version.rb +1 -1
  60. data/lib/wx/shapes/wx.rb +16 -7
  61. data/lib/wx/wx-shapes/cmd/test.rb +1 -1
  62. data/samples/demo/arrows.json +1 -0
  63. data/samples/demo/arrows.yaml +793 -0
  64. data/samples/demo/art/HBox.xpm +22 -0
  65. data/samples/demo/art/VBox.xpm +22 -0
  66. data/samples/demo/art/logo.xpm +60 -0
  67. data/samples/demo/class.json +1 -0
  68. data/samples/demo/class.yaml +5631 -0
  69. data/samples/demo/demo.rb +301 -91
  70. data/samples/demo/dialogs.rb +1405 -0
  71. data/samples/demo/erd.json +1 -0
  72. data/samples/demo/erd.yaml +4072 -0
  73. data/samples/demo/frame_canvas.rb +409 -33
  74. data/samples/sample1/art/logo.xpm +60 -0
  75. data/samples/sample1/sample.rb +11 -11
  76. data/samples/sample2/art/logo.xpm +60 -0
  77. data/samples/sample2/sample.rb +2 -2
  78. data/samples/sample2/sample_shape.rb +15 -15
  79. data/samples/sample3/art/logo.xpm +60 -0
  80. data/samples/sample3/sample.rb +3 -3
  81. data/samples/sample4/art/logo.xpm +60 -0
  82. data/samples/sample4/sample.rb +2 -2
  83. data/tests/lib/wxapp_runner.rb +4 -0
  84. data/tests/serializer_tests.rb +8 -441
  85. data/tests/test_grid_shapes.rb +2 -2
  86. data/tests/test_serialize_xml.rb +17 -0
  87. data/tests/test_serialize_yaml.rb +2 -2
  88. metadata +78 -28
  89. data/lib/wx/shapes/serialize/core.rb +0 -40
  90. data/lib/wx/shapes/serialize/id.rb +0 -82
  91. data/lib/wx/shapes/serializer/json.rb +0 -258
  92. data/lib/wx/shapes/serializer/yaml.rb +0 -125
  93. data/samples/demo/art/sample.xpm +0 -251
  94. data/samples/sample1/art/sample.xpm +0 -251
  95. data/samples/sample2/art/sample.xpm +0 -251
  96. data/samples/sample3/art/sample.xpm +0 -251
  97. data/samples/sample4/art/sample.xpm +0 -251
@@ -2,6 +2,7 @@
2
2
  # Copyright (c) M.J.N. Corino, The Netherlands
3
3
 
4
4
  require 'wx/shapes/serializable'
5
+ require 'wx/shapes/shapes/manager_shape'
5
6
 
6
7
  require 'set'
7
8
 
@@ -29,15 +30,16 @@ module Wx::SF
29
30
  # mostly triggered by a parent shape canvas.
30
31
  class Shape
31
32
 
32
- include Serializable
33
+ include FIRM::Serializable
33
34
 
34
- property :id, :active, :visibility, :style,
35
+ property :active, :visibility, :style,
35
36
  :accepted_children, :accepted_connections,
36
37
  :accepted_src_neighbours, :accepted_trg_neighbours,
37
- :hover_colour, :relative_position,
38
+ :relative_position,
38
39
  :h_align, :v_align, :h_border, :v_border,
39
40
  :custom_dock_point, :connection_points,
40
41
  :user_data
42
+ property({ hover_colour: :serialize_hover_colour }, optional: true)
41
43
  property child_shapes: :serialize_child_shapes
42
44
 
43
45
  class SEARCHMODE < Wx::Enum
@@ -102,8 +104,10 @@ module Wx::SF
102
104
  HIGHLIGHTING = self.new(16)
103
105
  # Shape is always inside its parent
104
106
  ALWAYS_INSIDE = self.new(32)
105
- # User data is destroyed at the shape deletion
106
- DELETE_USER_DATA = self.new(64)
107
+ # Shape is not drawn. Does not apply to children.
108
+ # Should be combined with PROPAGATE_DRAGGING | PROPAGATE_SELECTION | PROPAGATE_INTERACTIVE_CONNECTION | PROPAGATE_HOVERING | PROPAGATE_HIGHLIGHTING | PROPAGATE_DROPPING
109
+ # in most cases.
110
+ NOT_DRAWN = self.new(64)
107
111
  # The DEL key is processed by the shape (not by the shape canvas)
108
112
  PROCESS_DEL = self.new(128)
109
113
  # Show handles if the shape is selected
@@ -124,21 +128,26 @@ module Wx::SF
124
128
  NO_FIT_TO_CHILDREN = self.new(32768)
125
129
  # Propagate hovering to parent.
126
130
  PROPAGATE_HOVERING = self.new(65536)
127
- # Propagate hovering to parent.
131
+ # Propagate highlighting to parent.
128
132
  PROPAGATE_HIGHLIGHTING = self.new(131072)
133
+ # Propagate dropping to parent.
134
+ PROPAGATE_DROPPING = self.new(262144)
129
135
  # Default shape style
130
- DEFAULT_SHAPE_STYLE = PARENT_CHANGE | POSITION_CHANGE | SIZE_CHANGE | HOVERING | HIGHLIGHTING | SHOW_HANDLES | ALWAYS_INSIDE | DELETE_USER_DATA
136
+ DEFAULT_SHAPE_STYLE = PARENT_CHANGE | POSITION_CHANGE | SIZE_CHANGE | HOVERING | HIGHLIGHTING | SHOW_HANDLES | ALWAYS_INSIDE
137
+ # Shortcut for all propagation options
138
+ PROPAGATE_ALL = PROPAGATE_DRAGGING | PROPAGATE_SELECTION | PROPAGATE_INTERACTIVE_CONNECTION | PROPAGATE_HOVERING | PROPAGATE_HIGHLIGHTING | PROPAGATE_DROPPING
131
139
  end
132
140
 
133
141
  # Default values
134
142
  module DEFAULT
143
+ class << self
144
+ # Default value of Wx::SF::Shape @hoverColor data member
145
+ def hover_colour; Wx::Colour.new(120, 120, 255); end
146
+ end
135
147
  # Default value of Wx::SF::Shape @visible data member
136
148
  VISIBILITY = true
137
149
  # Default value of Wx::SF::Shape @active data member
138
150
  ACTIVITY = true
139
- # Default value of Wx::SF::Shape @hoverColor data member
140
- HOVERCOLOUR = Wx::Colour.new(120, 120, 255) if Wx::App.is_main_loop_running
141
- Wx.add_delayed_constant(self, :HOVERCOLOUR) { Wx::Colour.new(120, 120, 255) }
142
151
  # Default value of Wx::SF::Shape @relativePosition data member
143
152
  POSITION = Wx::RealPoint.new(0, 0)
144
153
  # Default value of Wx::SF::Shape @vAlign data member
@@ -172,7 +181,7 @@ module Wx::SF
172
181
  # @overload component(hash)
173
182
  # Specifies one or more serialized component properties with associated setter/getter method ids/procs/lambda-s.
174
183
  # @example
175
- # property(
184
+ # component(
176
185
  # prop_a: ->(obj, *val) {
177
186
  # obj.my_prop_a_setter(val.first) unless val.empty?
178
187
  # obj.my_prop_a_getter
@@ -227,35 +236,24 @@ module Wx::SF
227
236
  protected :from_serialized
228
237
  end
229
238
  include ComponentSerializerMethods
230
- __CODE
239
+ __CODE
231
240
  end
232
241
  end
233
242
 
234
- # @overload initialize()
235
- # default constructor
236
- # @overload initialize(pos, manager)
237
- # @param [Wx::RealPoint] pos Initial relative position
238
- # @param [Diagram] diagram containing diagram
239
- def initialize(*args)
240
- pos, diagram = args
243
+ # Constructor
244
+ # @param [Wx::RealPoint, Wx::Point] pos Initial relative position
245
+ # @param [Diagram] diagram containing diagram
246
+ def initialize(pos = DEFAULT::POSITION, diagram: nil)
241
247
  ::Kernel.raise ArgumentError, "Invalid arguments pos: #{pos}, diagram: #{diagram}" unless
242
- args.empty? || (Wx::RealPoint === pos && (diagram.nil? || Wx::SF::Diagram === diagram))
248
+ Wx::RealPoint === pos && (diagram.nil? || Wx::SF::Diagram === diagram)
243
249
 
244
- @id = Serializable::ID.new
245
250
  @diagram = diagram
246
251
  @parent_shape = nil
247
252
  @child_shapes = ShapeList.new
248
253
  @components = ::Set.new
249
254
 
250
- if @diagram
251
- if @diagram.shape_canvas
252
- @hover_color = @diagram.shape_canvas.hover_colour
253
- else
254
- @hover_color = DEFAULT::HOVERCOLOUR;
255
- end
256
- else
257
- @hover_color = DEFAULT::HOVERCOLOUR;
258
- end
255
+ # by default the common canvas hover colour will be used
256
+ @hover_color = nil
259
257
 
260
258
  @selected = false
261
259
  @mouse_over = false
@@ -273,11 +271,7 @@ module Wx::SF
273
271
  @h_border = DEFAULT::HBORDER
274
272
  @custom_dock_point = DEFAULT::DOCK_POINT
275
273
 
276
- if pos && get_parent_shape
277
- @relative_position = pos.to_real_point - get_parent_absolute_position
278
- else
279
- @relative_position = DEFAULT::POSITION.dup
280
- end
274
+ @relative_position = Wx::RealPoint === pos ? pos.dup : pos.to_real_point
281
275
 
282
276
  @handles = []
283
277
  @connection_pts = []
@@ -288,20 +282,6 @@ module Wx::SF
288
282
  @accepted_trg_neighbours = ::Set.new
289
283
  end
290
284
 
291
- # Get the shape's id
292
- # @return [Wx::SF::Serializable::ID]
293
- def get_id
294
- @id
295
- end
296
- alias :id :get_id
297
-
298
- # Set the shape's id. Deserialization only.
299
- # @param [Wx::SF::Serializable::ID] id
300
- def set_id(id)
301
- @id = id
302
- end
303
- private :set_id
304
-
305
285
  # Set managing diagram
306
286
  # @param [Wx::SF::Diagram] diagram
307
287
  def set_diagram(diagram)
@@ -343,9 +323,10 @@ module Wx::SF
343
323
  # @param [Wx::SF::Shape] child child shape to add
344
324
  # @return [Wx::SF::Shape,nil] added child shape or nil if not accepted
345
325
  def add_child_shape(child)
326
+ raise SFException, 'Illegal attempt to add self as Shape child' if child == self
346
327
  if is_child_accepted(child.class)
347
328
  if child.get_diagram
348
- child.get_diagram.reparent_shape(child, shape)
329
+ child.get_diagram.reparent_shape(child, self)
349
330
  else
350
331
  child.set_parent_shape(self)
351
332
  end
@@ -355,12 +336,24 @@ module Wx::SF
355
336
  nil
356
337
  end
357
338
 
339
+
340
+ # Returns true if the given shape is included in the child shapes list.
341
+ # Performs a recursive search in case :recursive is true.
342
+ # @param [shape] shape shape to match
343
+ # @param [Boolean] recursive pass true to search recursively, false for non-recursive
344
+ # @return [Boolean] true if included, otherwise false
345
+ def include_child_shape?(shape, recursive = false)
346
+ @child_shapes.include?(shape, recursive)
347
+ end
348
+
358
349
  # Set parent shape object.
359
350
  # @param [Wx::SF::Shape] parent
360
351
  # @note Note that this does not check this shape against the acceptance list of the parent. Use #add_child_shape if that is required.
361
352
  # @note Note that this does not add (if parent == nil) or remove (if parent != nil) the shape from the diagram's
362
353
  # toplevel shapes. Use Diagram#reparent_shape when that is needed.
363
354
  def set_parent_shape(parent)
355
+ raise SFException, 'Illegal to set Shape parent to self' if parent == self
356
+ raise SFException, 'Illegal to set Shape parent to (grand-)child of self' if parent && include_child_shape?(parent, true)
364
357
  @parent_shape.send(:remove_child, self) if @parent_shape
365
358
  parent.send(:add_child, self) if parent
366
359
  set_diagram(parent.get_diagram) if parent
@@ -383,7 +376,7 @@ module Wx::SF
383
376
  alias :grand_parent_shape :get_grand_parent_shape
384
377
 
385
378
  # Refresh (redraw) the shape
386
- # @param [Boolean] delayed If true then the shape canvas will be rather invalidated than refreshed.
379
+ # @param [Boolean] delayed If true then the shape canvas will be invalidated rather than refreshed.
387
380
  # @see ShapeCanvas#invalidate_rect
388
381
  # @see ShapeCanvas#refresh_invalidated_rect
389
382
  def refresh(delayed = false)
@@ -400,25 +393,29 @@ module Wx::SF
400
393
  return unless @diagram && @diagram.shape_canvas
401
394
  return unless @visible
402
395
 
403
- # draw the shape shadow if required
404
- draw_shadow(dc) if !@selected && has_style?(STYLE::SHOW_SHADOW)
396
+ unless has_style?(STYLE::NOT_DRAWN)
397
+
398
+ # draw the shape shadow if required
399
+ draw_shadow(dc) if !@selected && has_style?(STYLE::SHOW_SHADOW)
405
400
 
406
- # first, draw itself
407
- if @mouse_over && (@highlight_parent || has_style?(STYLE::HOVERING))
408
- if @highlight_parent
409
- draw_highlighted(dc)
410
- @highlight_parent = false
401
+ # first, draw itself
402
+ if @mouse_over && (@highlight_parent || has_style?(STYLE::HOVERING))
403
+ if @highlight_parent
404
+ draw_highlighted(dc)
405
+ @highlight_parent = false
406
+ else
407
+ draw_hover(dc)
408
+ end
411
409
  else
412
- draw_hover(dc)
410
+ draw_normal(dc)
413
411
  end
414
- else
415
- draw_normal(dc)
416
- end
417
412
 
418
- draw_selected(dc) if @selected
413
+ draw_selected(dc) if @selected
419
414
 
420
- # ... then draw connection points ...
421
- @connection_pts.each { |cpt| cpt.draw(dc) }
415
+ # ... then draw connection points ...
416
+ @connection_pts.each { |cpt| cpt.draw(dc) } unless has_style?(STYLE::PROPAGATE_INTERACTIVE_CONNECTION)
417
+
418
+ end
422
419
 
423
420
  # ... then draw child shapes
424
421
  if children
@@ -460,11 +457,10 @@ module Wx::SF
460
457
  # @return [Wx::RealPoint] Shape's position
461
458
  def get_absolute_position
462
459
  # HINT: overload it for custom actions...
463
- parent_shape = get_parent_shape
464
- if parent_shape
460
+ if @parent_shape
465
461
  @relative_position + get_parent_absolute_position
466
462
  else
467
- @relative_position.dup
463
+ @relative_position
468
464
  end
469
465
  end
470
466
 
@@ -501,7 +497,7 @@ module Wx::SF
501
497
 
502
498
  # Set shape's style.
503
499
  #
504
- # Default value is STYLE::PARENT_CHANGE | STYLE::POSITION_CHANGE | STYLE::SIZE_CHANGE | STYLE::HOVERING | STYLE::HIGHLIGHTING | STYLE::SHOW_HANDLES | STYLE::ALWAYS_INSIDE | STYLE::DELETE_USER_DATA
500
+ # Default value is STYLE::PARENT_CHANGE | STYLE::POSITION_CHANGE | STYLE::SIZE_CHANGE | STYLE::HOVERING | STYLE::HIGHLIGHTING | STYLE::SHOW_HANDLES | STYLE::ALWAYS_INSIDE
505
501
  # @param [Integer] style Combination of the shape's styles
506
502
  # @see STYLE
507
503
  def set_style(style)
@@ -523,7 +519,7 @@ module Wx::SF
523
519
  @style &= ~style
524
520
  end
525
521
  def contains_style(style)
526
- (@style & style) != 0
522
+ @style.allbits?(style)
527
523
  end
528
524
  alias :contains_style? :contains_style
529
525
  alias :has_style? :contains_style
@@ -610,7 +606,7 @@ module Wx::SF
610
606
  end
611
607
 
612
608
  # Get shape's bounding box which includes also associated child shapes and connections.
613
- # @param [Wx::Rect] rct bounding rectangle
609
+ # @param [Wx::Rect, nil] rct bounding rectangle
614
610
  # @param [BBMODE] mask Bit mask of object types which should be included into calculation
615
611
  # @return [Wx::Rect] returned bounding box
616
612
  # @see BBMODE
@@ -689,17 +685,31 @@ module Wx::SF
689
685
  @diagram.set_modified(true) if @diagram
690
686
  end
691
687
 
692
- # Update the shape's position in order to its alignment
693
- def do_alignment
694
- parent = get_parent_shape
688
+ # Returns true if this shape manages (size/position/alignment) of it's child shapes.
689
+ # Returns false by default.
690
+ # @return [Boolean]
691
+ def is_manager
692
+ false
693
+ end
694
+ alias :manager? :is_manager
695
+
696
+ # Returns true if this shape is managed (size/position/alignment) by it's parent shape.
697
+ # @return [Boolean]
698
+ def is_managed
699
+ !!@parent_shape&.is_manager
700
+ end
701
+ alias :managed? :is_managed
695
702
 
696
- if parent && !parent.is_a?(Wx::SF::GridShape)
697
703
 
698
- if parent.is_a?(Wx::SF::LineShape)
704
+ # Update the shape's position in order to its alignment
705
+ def do_alignment
706
+ # align to parent unless parent is manager
707
+ unless @parent_shape.nil? || managed?
708
+ if @parent_shape.is_a?(Wx::SF::LineShape)
699
709
  line_pos = get_parent_absolute_position
700
710
  parent_bb = Wx::Rect.new(line_pos.x.to_i, line_pos.y.to_i, 1, 1)
701
711
  else
702
- parent_bb = parent.get_bounding_box
712
+ parent_bb = @parent_shape.get_bounding_box
703
713
  end
704
714
 
705
715
  shape_bb = get_bounding_box
@@ -722,8 +732,8 @@ module Wx::SF
722
732
  end
723
733
 
724
734
  when VALIGN::LINE_START
725
- if parent.is_a?(Wx::SF::LineShape)
726
- line_start, line_end = parent.get_line_segment(0)
735
+ if @parent_shape.is_a?(Wx::SF::LineShape)
736
+ line_start, line_end = @parent_shape.get_line_segment(0)
727
737
 
728
738
  if line_end.y >= line_start.y
729
739
  @relative_position.y = line_start.y - line_pos.y + @v_border
@@ -733,8 +743,8 @@ module Wx::SF
733
743
  end
734
744
 
735
745
  when VALIGN::LINE_END
736
- if parent.is_a?(Wx::SF::LineShape)
737
- line_start, line_end = parent.get_line_segment(parent.get_control_points.get_count)
746
+ if @parent_shape.is_a?(Wx::SF::LineShape)
747
+ line_start, line_end = @parent_shape.get_line_segment(parent.get_control_points.get_count)
738
748
 
739
749
  if line_end.y >= line_start.y
740
750
  @relative_position.y = line_end.y - line_pos.y - shape_bb.height - @v_border
@@ -762,8 +772,8 @@ module Wx::SF
762
772
  end
763
773
 
764
774
  when HALIGN::LINE_START
765
- if parent.is_a?(Wx::SF::LineShape)
766
- line_start, line_end = parent.get_line_segment(0)
775
+ if @parent_shape.is_a?(Wx::SF::LineShape)
776
+ line_start, line_end = @parent_shape.get_line_segment(0)
767
777
 
768
778
  if line_end.x >= line_start.x
769
779
 
@@ -774,8 +784,8 @@ module Wx::SF
774
784
  end
775
785
 
776
786
  when HALIGN::LINE_END
777
- if parent.is_a?(Wx::SF::LineShape)
778
- line_start, line_end = parent.get_line_segment(parent.get_control_points.get_count)
787
+ if @parent_shape.is_a?(Wx::SF::LineShape)
788
+ line_start, line_end = @parent_shape.get_line_segment(@parent_shape.get_control_points.get_count)
779
789
 
780
790
  if line_end.x >= line_start.x
781
791
  @relative_position.x = line_end.x - line_pos.x - shape_bb.width - @h_border
@@ -788,7 +798,7 @@ module Wx::SF
788
798
  end
789
799
 
790
800
  # Update shape (align all child shapes and resize it to fit them)
791
- def update
801
+ def update(recurse = true)
792
802
  # do self-alignment
793
803
  do_alignment
794
804
 
@@ -799,8 +809,8 @@ module Wx::SF
799
809
  fit_to_children unless has_style?(STYLE::NO_FIT_TO_CHILDREN)
800
810
 
801
811
  # do it recursively on all parent shapes
802
- if (parent = get_parent_shape)
803
- parent.update
812
+ if recurse && (parent = get_parent_shape)
813
+ parent.update(recurse)
804
814
  end
805
815
  end
806
816
 
@@ -814,11 +824,17 @@ module Wx::SF
814
824
  @selected
815
825
  end
816
826
 
827
+ # Returns true if any (grand-)parent is selected?
828
+ def has_selected_parent?
829
+ @parent_shape&.selected? || @parent_shape&.has_selected_parent?
830
+ end
831
+ alias :selected_parent? :has_selected_parent?
832
+
817
833
  # Set the shape as a selected/deselected one
818
834
  # @param [Boolean] state Selection state (true is selected, false is deselected)
819
835
  def select(state)
820
836
  @selected = state
821
- show_handles(state && (@style & STYLE::SHOW_HANDLES) != 0)
837
+ show_handles(state && has_style?(STYLE::SHOW_HANDLES))
822
838
  end
823
839
 
824
840
  # Set shape's relative position. Absolute shape's position is then calculated
@@ -840,7 +856,7 @@ module Wx::SF
840
856
  # @return [Wx::RealPoint] Current relative position
841
857
  # @see #get_absolute_position
842
858
  def get_relative_position
843
- @relative_position.dup
859
+ @relative_position
844
860
  end
845
861
 
846
862
  # Set vertical alignment of this shape inside its parent
@@ -942,7 +958,7 @@ module Wx::SF
942
958
  # Associate user data with the shape.
943
959
  # If the data object is properly set then its marked properties will be serialized
944
960
  # together with the parent shape. This means the user data must either be a serializable
945
- # core type or a Wx::SF::Serializable.
961
+ # core type or a FIRM::Serializable.
946
962
  # @param [Object] data user data
947
963
  def set_user_data(data)
948
964
  @user_data = data
@@ -990,7 +1006,7 @@ module Wx::SF
990
1006
  # Get shape's hover color
991
1007
  # @return [Wx::Colour] Current hover color
992
1008
  def get_hover_colour
993
- @hover_color
1009
+ @hover_color || (@diagram&.shape_canvas ? @diagram.shape_canvas.hover_colour : DEFAULT.hover_colour)
994
1010
  end
995
1011
  alias :hover_colour :get_hover_colour
996
1012
 
@@ -1030,10 +1046,10 @@ module Wx::SF
1030
1046
  def accept_currently_dragged_shapes
1031
1047
  return false unless get_shape_canvas
1032
1048
 
1033
- unless is_child_accepted(ACCEPT_ALL)
1049
+ unless @accepted_children.include?(ACCEPT_ALL)
1034
1050
  lst_selection = get_shape_canvas.get_selected_shapes
1035
1051
 
1036
- return false if lst_selection.any? { |shape| !@accepted_children.include?(shape.class.name) }
1052
+ return false if lst_selection.any? { |shape| !@accepted_children.include?(shape.class) }
1037
1053
  end
1038
1054
  true
1039
1055
  end
@@ -1044,7 +1060,7 @@ module Wx::SF
1044
1060
  # @param [Class] type Class of accepted shape object
1045
1061
  # @see #is_child_accepted
1046
1062
  def accept_child(type)
1047
- ::Kernel.raise ArgumentError, 'Class or ACCEPT_ALL expected' unless type.is_a?(::Class) || type == ACCEPT_ALL
1063
+ ::Kernel.raise ArgumentError, 'Class or ACCEPT_ALL expected' unless type.is_a?(::Class)
1048
1064
  @accepted_children << type
1049
1065
  end
1050
1066
 
@@ -1056,6 +1072,14 @@ module Wx::SF
1056
1072
  end
1057
1073
  alias :accepted_children :get_accepted_children
1058
1074
 
1075
+ # Tells whether the shape does not accept ANY children
1076
+ # @return [Boolean] true if no children accepted, false otherwise
1077
+ def does_not_accept_children?
1078
+ @accepted_children.empty?
1079
+ end
1080
+ alias :no_children_accepted? :does_not_accept_children?
1081
+ alias :accepts_no_children? :does_not_accept_children?
1082
+
1059
1083
  # Tells whether the given connection type is accepted by this shape (it means
1060
1084
  # whether this shape can be connected to another one by a connection of given type).
1061
1085
  #
@@ -1073,7 +1097,7 @@ module Wx::SF
1073
1097
  # @param [Class] type Class of accepted connection object
1074
1098
  # @see #is_connection_accepted
1075
1099
  def accept_connection(type)
1076
- ::Kernel.raise ArgumentError, 'Class or ACCEPT_ALL expected' unless type.is_a?(::Class) || type == ACCEPT_ALL
1100
+ ::Kernel.raise ArgumentError, 'Class or ACCEPT_ALL expected' unless type.is_a?(::Class)
1077
1101
  @accepted_connections << type
1078
1102
  end
1079
1103
 
@@ -1102,7 +1126,7 @@ module Wx::SF
1102
1126
  # @param [Class] type Class of accepted connection object
1103
1127
  # @see #is_src_neighbour_accepted
1104
1128
  def accept_src_neighbour(type)
1105
- ::Kernel.raise ArgumentError, 'Class or ACCEPT_ALL expected' unless type.is_a?(::Class) || type == ACCEPT_ALL
1129
+ ::Kernel.raise ArgumentError, 'Class or ACCEPT_ALL expected' unless type.is_a?(::Class)
1106
1130
  @accepted_src_neighbours << type
1107
1131
  end
1108
1132
 
@@ -1131,7 +1155,7 @@ module Wx::SF
1131
1155
  # @param [Class] type Class of accepted connection object
1132
1156
  # @see #is_trg_neighbour_accepted
1133
1157
  def accept_trg_neighbour(type)
1134
- ::Kernel.raise ArgumentError, 'Class or ACCEPT_ALL expected' unless type.is_a?(::Class) || type == ACCEPT_ALL
1158
+ ::Kernel.raise ArgumentError, 'Class or ACCEPT_ALL expected' unless type.is_a?(::Class)
1135
1159
  @accepted_trg_neighbours << type
1136
1160
  end
1137
1161
 
@@ -1214,10 +1238,10 @@ module Wx::SF
1214
1238
 
1215
1239
  # Get connection point of given type assigned to the shape.
1216
1240
  # @param [Wx::SF::ConnectionPoint::CPTYPE] type Connection point type
1217
- # @param [Integer] id Optional connection point ID
1241
+ # @param [Integer, nil] id Optional connection point ID
1218
1242
  # @return [Wx::SF::ConnectionPoint,nil] connection point if exists, otherwise nil
1219
1243
  # @see Wx::SF::ConnectionPoint::CPTYPE
1220
- def get_connection_point(type, id = -1)
1244
+ def get_connection_point(type, id = nil)
1221
1245
  @connection_pts.find { |cp| cp.type == type && cp.id == id }
1222
1246
  end
1223
1247
  alias :connection_point :get_connection_point
@@ -1242,16 +1266,16 @@ module Wx::SF
1242
1266
  # @overload add_connection_point(type, persistent: true)
1243
1267
  # @param [Wx::SF::ConnectionPoint::CPTYPE] type Connection point type
1244
1268
  # @param [Boolean] persistent true if the connection point should be serialized
1245
- # @return [Wx::SF::ConnectionPoint] new connection point
1269
+ # @return [Wx::SF::ConnectionPoint, nil] new connection point if succeeded, otherwise nil
1246
1270
  # @overload add_connection_point(relpos, id=-1, persistent: true)
1247
1271
  # @param [Wx::RealPoint] relpos Relative position in percentages
1248
1272
  # @param [Integer] id connection point ID
1249
1273
  # @param [Boolean] persistent true if the connection point should be serialized
1250
- # @return [Wx::SF::ConnectionPoint] new connection point
1274
+ # @return [Wx::SF::ConnectionPoint, nil] new connection point if succeeded, otherwise nil
1251
1275
  # @overload add_connection_point(cp, persistent: true)
1252
1276
  # @param [Wx::SF::ConnectionPoint] cp connection point (shape will take the ownership)
1253
1277
  # @param [Boolean] persistent true if the connection point should be serialized
1254
- # @return [Wx::SF::ConnectionPoint] added connection point
1278
+ # @return [Wx::SF::ConnectionPoint, nil] added connection point if succeeded, otherwise nil
1255
1279
  # @see Wx::SF::ConnectionPoint::CPTYPE
1256
1280
  def add_connection_point(arg, *rest, persistent: true)
1257
1281
  cp = nil
@@ -1292,7 +1316,7 @@ module Wx::SF
1292
1316
  # HINT: overload it for custom actions...
1293
1317
 
1294
1318
  if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1295
- evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_LEFT_DOWN, self.id)
1319
+ evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_LEFT_DOWN, self.object_id)
1296
1320
  evt.set_shape(self)
1297
1321
  evt.set_mouse_position(pos)
1298
1322
  get_shape_canvas.get_event_handler.process_event(evt)
@@ -1310,7 +1334,7 @@ module Wx::SF
1310
1334
  # HINT: overload it for custom actions...
1311
1335
 
1312
1336
  if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1313
- evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_RIGHT_DOWN, self.id)
1337
+ evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_RIGHT_DOWN, self.object_id)
1314
1338
  evt.set_shape(self)
1315
1339
  evt.set_mouse_position(pos)
1316
1340
  get_shape_canvas.get_event_handler.process_event(evt)
@@ -1328,7 +1352,7 @@ module Wx::SF
1328
1352
  # HINT: overload it for custom actions...
1329
1353
 
1330
1354
  if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1331
- evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_LEFT_DCLICK, self.id)
1355
+ evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_LEFT_DCLICK, self.object_id)
1332
1356
  evt.set_shape(self)
1333
1357
  evt.set_mouse_position(pos)
1334
1358
  get_shape_canvas.get_event_handler.process_event(evt)
@@ -1346,7 +1370,7 @@ module Wx::SF
1346
1370
  # HINT: overload it for custom actions...
1347
1371
 
1348
1372
  if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1349
- evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_RIGHT_DCLICK, self.id)
1373
+ evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_RIGHT_DCLICK, self.object_id)
1350
1374
  evt.set_shape(self)
1351
1375
  evt.set_mouse_position(pos)
1352
1376
  get_shape_canvas.get_event_handler.process_event(evt)
@@ -1364,7 +1388,7 @@ module Wx::SF
1364
1388
  # HINT: overload it for custom actions...
1365
1389
 
1366
1390
  if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1367
- evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_DRAG_BEGIN, self.id)
1391
+ evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_DRAG_BEGIN, self.object_id)
1368
1392
  evt.set_shape(self)
1369
1393
  evt.set_mouse_position(pos)
1370
1394
  get_shape_canvas.get_event_handler.process_event(evt)
@@ -1382,7 +1406,7 @@ module Wx::SF
1382
1406
  # HINT: overload it for custom actions...
1383
1407
 
1384
1408
  if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1385
- evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_DRAG, self.id)
1409
+ evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_DRAG, self.object_id)
1386
1410
  evt.set_shape(self)
1387
1411
  evt.set_mouse_position(pos)
1388
1412
  get_shape_canvas.get_event_handler.process_event(evt)
@@ -1400,7 +1424,7 @@ module Wx::SF
1400
1424
  # HINT: overload it for custom actions...
1401
1425
 
1402
1426
  if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1403
- evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_DRAG_END, self.id)
1427
+ evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_DRAG_END, self.object_id)
1404
1428
  evt.set_shape(self)
1405
1429
  evt.set_mouse_position(pos)
1406
1430
  get_shape_canvas.get_event_handler.process_event(evt)
@@ -1417,7 +1441,7 @@ module Wx::SF
1417
1441
  # HINT: overload it for custom actions...
1418
1442
 
1419
1443
  if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1420
- evt = Wx::SF::ShapeHandleEvent.new(Wx::SF::EVT_SF_SHAPE_HANDLE_BEGIN, self.id)
1444
+ evt = Wx::SF::ShapeHandleEvent.new(Wx::SF::EVT_SF_SHAPE_HANDLE_BEGIN, self.object_id)
1421
1445
  evt.set_shape(self)
1422
1446
  evt.set_handle(handle)
1423
1447
  get_shape_canvas.get_event_handler.process_event(evt)
@@ -1434,7 +1458,7 @@ module Wx::SF
1434
1458
  # HINT: overload it for custom actions...
1435
1459
 
1436
1460
  if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1437
- evt = Wx::SF::ShapeHandleEvent.new(Wx::SF::EVT_SF_SHAPE_HANDLE, self.id)
1461
+ evt = Wx::SF::ShapeHandleEvent.new(Wx::SF::EVT_SF_SHAPE_HANDLE, self.object_id)
1438
1462
  evt.set_shape(self)
1439
1463
  evt.set_handle(handle)
1440
1464
  get_shape_canvas.get_event_handler.process_event(evt)
@@ -1451,7 +1475,7 @@ module Wx::SF
1451
1475
  # HINT: overload it for custom actions...
1452
1476
 
1453
1477
  if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1454
- evt = Wx::SF::ShapeHandleEvent.new(Wx::SF::EVT_SF_SHAPE_HANDLE_END, self.id)
1478
+ evt = Wx::SF::ShapeHandleEvent.new(Wx::SF::EVT_SF_SHAPE_HANDLE_END, self.object_id)
1455
1479
  evt.set_shape(self)
1456
1480
  evt.set_handle(handle)
1457
1481
  get_shape_canvas.get_event_handler.process_event(evt)
@@ -1468,7 +1492,7 @@ module Wx::SF
1468
1492
  # HINT: overload it for custom actions...
1469
1493
 
1470
1494
  if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1471
- evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_MOUSE_ENTER, self.id)
1495
+ evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_MOUSE_ENTER, self.object_id)
1472
1496
  evt.set_shape(self)
1473
1497
  evt.set_mouse_position(pos)
1474
1498
  get_shape_canvas.get_event_handler.process_event(evt)
@@ -1485,7 +1509,7 @@ module Wx::SF
1485
1509
  # HINT: overload it for custom actions...
1486
1510
 
1487
1511
  if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1488
- evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_MOUSE_OVER, self.id)
1512
+ evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_MOUSE_OVER, self.object_id)
1489
1513
  evt.set_shape(self)
1490
1514
  evt.set_mouse_position(pos)
1491
1515
  get_shape_canvas.get_event_handler.process_event(evt)
@@ -1502,7 +1526,7 @@ module Wx::SF
1502
1526
  # HINT: overload it for custom actions...
1503
1527
 
1504
1528
  if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1505
- evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_MOUSE_LEAVE, self.id)
1529
+ evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_MOUSE_LEAVE, self.object_id)
1506
1530
  evt.set_shape(self)
1507
1531
  evt.set_mouse_position(pos)
1508
1532
  get_shape_canvas.get_event_handler.process_event(evt)
@@ -1522,7 +1546,7 @@ module Wx::SF
1522
1546
  # HINT: overload it for custom actions...
1523
1547
 
1524
1548
  if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1525
- evt = Wx::SF::ShapeKeyEvent.new(Wx::SF::EVT_SF_SHAPE_KEYDOWN, self.id)
1549
+ evt = Wx::SF::ShapeKeyEvent.new(Wx::SF::EVT_SF_SHAPE_KEYDOWN, self.object_id)
1526
1550
  evt.set_shape(self)
1527
1551
  evt.set_key_code(key)
1528
1552
  get_shape_canvas.get_event_handler.process_event(evt)
@@ -1542,7 +1566,7 @@ module Wx::SF
1542
1566
  # HINT: overload it for custom actions...
1543
1567
 
1544
1568
  if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1545
- evt = Wx::SF::ShapeChildDropEvent.new(Wx::SF::EVT_SF_SHAPE_CHILD_DROP, self.id)
1569
+ evt = Wx::SF::ShapeChildDropEvent.new(Wx::SF::EVT_SF_SHAPE_CHILD_DROP, self.object_id)
1546
1570
  evt.set_shape(self)
1547
1571
  evt.set_child_shape(child)
1548
1572
  get_shape_canvas.get_event_handler.process_event(evt)
@@ -1550,7 +1574,7 @@ module Wx::SF
1550
1574
  end
1551
1575
 
1552
1576
  def to_s
1553
- "#<#{self.class}:#{id.to_i}#{parent_shape ? " parent=#{parent_shape.id.to_i}" : ''}>"
1577
+ "#<#{self.class}:#{self.object_id}#{@parent_shape ? " parent=#{@parent_shape.object_id}" : ''}>"
1554
1578
  end
1555
1579
 
1556
1580
  def inspect
@@ -1559,6 +1583,13 @@ module Wx::SF
1559
1583
 
1560
1584
  protected
1561
1585
 
1586
+ # called after the shape has been newly imported/pasted/dropped
1587
+ # allows for checking stale links
1588
+ # by default does nothing
1589
+ def on_import
1590
+ # nothing
1591
+ end
1592
+
1562
1593
  # Draw the shape in the normal way. The function can be overridden if necessary.
1563
1594
  # @param [Wx::DC] _dc Reference to device context where the shape will be drawn to
1564
1595
  def draw_normal(_dc)
@@ -1614,11 +1645,11 @@ module Wx::SF
1614
1645
  # Get absolute position of the shape parent.
1615
1646
  # @return [Wx::RealPoint] Absolute position of the shape parent if exists, otherwise 0,0
1616
1647
  def get_parent_absolute_position
1617
- if parent = get_parent_shape
1618
- if parent.is_a?(Wx::SF::LineShape) && @custom_dock_point != DEFAULT::DOCK_POINT
1619
- return parent.get_dock_point_position(@custom_dock_point)
1648
+ if @parent_shape
1649
+ if @parent_shape.is_a?(Wx::SF::LineShape) && @custom_dock_point != DEFAULT::DOCK_POINT
1650
+ return @parent_shape.get_dock_point_position(@custom_dock_point)
1620
1651
  else
1621
- return parent.get_absolute_position
1652
+ return @parent_shape.get_absolute_position
1622
1653
  end
1623
1654
  end
1624
1655
 
@@ -1628,7 +1659,7 @@ module Wx::SF
1628
1659
  private
1629
1660
 
1630
1661
  # Auxiliary function called by GetNeighbours function.
1631
- # @param [Class,nil] shapeInfo Line object type
1662
+ # @param [Class,nil] shape_info Line object type
1632
1663
  # @param [CONNECTMODE] condir Connection direction
1633
1664
  # @param [Boolean] direct Set this flag to TRUE if only closest shapes should be found,
1634
1665
  # otherwise also shapes connected by forked lines will be found (also
@@ -1649,16 +1680,16 @@ module Wx::SF
1649
1680
  lst_connections.each do |line|
1650
1681
  case condir
1651
1682
  when CONNECTMODE::STARTING
1652
- opposite = @diagram.find_shape(line.get_trg_shape_id)
1683
+ opposite = line.get_trg_shape
1653
1684
 
1654
1685
  when CONNECTMODE::ENDING
1655
- opposite = @diagram.find_shape(line.get_src_shape_id)
1686
+ opposite = line.get_src_shape
1656
1687
 
1657
1688
  when CONNECTMODE::BOTH
1658
- if @id == line.get_src_shape_id
1659
- opposite = @diagram.find_shape(line.get_trg_shape_id)
1689
+ if self == line.get_src_shape
1690
+ opposite = line.get_trg_shape
1660
1691
  else
1661
- opposite = @diagram.find_shape(line.get_src_shape_id)
1692
+ opposite = line.get_src_shape
1662
1693
  end
1663
1694
  end
1664
1695
 
@@ -1674,7 +1705,7 @@ module Wx::SF
1674
1705
  if opposite.is_a?(Wx::SF::LineShape)
1675
1706
  case condir
1676
1707
  when CONNECTMODE::STARTING
1677
- opposite = @diagram.find_shape(opposite.get_src_shape_id)
1708
+ opposite = opposite.get_src_shape
1678
1709
 
1679
1710
  if opposite.is_a?(Wx::SF::LineShape)
1680
1711
  opposite.__send__(:_get_neighbours, shape_info, condir, direct, neighbours, processed)
@@ -1683,7 +1714,7 @@ module Wx::SF
1683
1714
  end
1684
1715
 
1685
1716
  when CONNECTMODE::ENDING
1686
- opposite = @diagram.find_shape(opposite.get_trg_shape_id)
1717
+ opposite = opposite.get_trg_shape
1687
1718
 
1688
1719
  if opposite.is_a?(Wx::SF::LineShape)
1689
1720
  opposite.__send__(:_get_neighbours, shape_info, condir, direct, neighbours, processed)
@@ -1692,14 +1723,14 @@ module Wx::SF
1692
1723
  end
1693
1724
 
1694
1725
  when CONNECTMODE::BOTH
1695
- opposite = @diagram.find_shape(opposite.get_src_shape_id)
1726
+ opposite = opposite.get_src_shape
1696
1727
  if opposite.is_a?(Wx::SF::LineShape)
1697
1728
  opposite.__send__(:_get_neighbours, shape_info, condir, direct, neighbours, processed)
1698
1729
  elsif !neighbours.include?(opposite)
1699
1730
  neighbours << opposite
1700
1731
  end
1701
1732
 
1702
- opposite = @diagram.find_shape(opposite.get_trg_shape_id)
1733
+ opposite = opposite.get_trg_shape
1703
1734
  if opposite.is_a?(Wx::SF::LineShape)
1704
1735
  opposite.__send__(:_get_neighbours, shape_info, condir, direct, neighbours, processed)
1705
1736
  elsif !neighbours.include?(opposite)
@@ -1715,7 +1746,7 @@ module Wx::SF
1715
1746
  end
1716
1747
 
1717
1748
  # Auxiliary function called by GetCompleteBoundingBox function.
1718
- # @param [Wx::Rect] rct bounding rectangle to update
1749
+ # @param [Wx::Rect, nil] rct bounding rectangle to update
1719
1750
  # @param [BBMODE] mask Bit mask of object types which should be included into calculation
1720
1751
  # @param [Set<Wx::SF::Shape] processed set to keep track of processed shapes
1721
1752
  # @return [Wx::Rect] bounding rectangle
@@ -1727,14 +1758,18 @@ module Wx::SF
1727
1758
  processed << self
1728
1759
 
1729
1760
  # first, get bounding box of the current shape
1730
- if (mask & BBMODE::SELF) != 0
1731
- if rct.is_empty
1732
- rct.assign(get_bounding_box.inflate!(@h_border.abs.to_i, @v_border.abs.to_i))
1761
+ if mask.allbits?(BBMODE::SELF)
1762
+ if rct.nil?
1763
+ rct = get_bounding_box.inflate!(@h_border.abs.to_i, @v_border.abs.to_i)
1733
1764
  else
1734
- rct.union!(get_bounding_box.inflate!(@h_border.abs.to_i, @v_border.abs.to_i))
1765
+ if rct.empty?
1766
+ rct += get_bounding_box.inflate!(@h_border.abs.to_i, @v_border.abs.to_i)
1767
+ else
1768
+ rct.union!(get_bounding_box.inflate!(@h_border.abs.to_i, @v_border.abs.to_i))
1769
+ end
1735
1770
 
1736
1771
  # add also shadow offset if necessary
1737
- if (mask & BBMODE::SHADOW) != 0 && has_style?(STYLE::SHOW_SHADOW) && get_parent_canvas
1772
+ if mask.allbits?(BBMODE::SHADOW) && has_style?(STYLE::SHOW_SHADOW) && !has_style(STYLE::NOT_DRAWN) && get_parent_canvas
1738
1773
  n_offset = get_parent_canvas.get_shadow_offset
1739
1774
 
1740
1775
  if n_offset.x < 0
@@ -1758,7 +1793,7 @@ module Wx::SF
1758
1793
 
1759
1794
  # get list of all connection lines assigned to the shape and find their child shapes
1760
1795
  lst_children = []
1761
- if (mask & BBMODE::CONNECTIONS) != 0
1796
+ if mask.allbits?(BBMODE::CONNECTIONS)
1762
1797
  lst_lines = get_assigned_connections(Wx::SF::LineShape, CONNECTMODE::BOTH)
1763
1798
 
1764
1799
  lst_lines.each do |line|
@@ -1771,15 +1806,15 @@ module Wx::SF
1771
1806
  end
1772
1807
 
1773
1808
  # get children of this shape
1774
- if (mask & BBMODE::CHILDREN) != 0
1809
+ if mask.allbits?(BBMODE::CHILDREN)
1775
1810
  get_child_shapes(ANY, NORECURSIVE, SEARCHMODE::BFS, lst_children)
1776
1811
 
1777
1812
  # now, call this function for all children recursively...
1778
1813
  lst_children.each do |child|
1779
- child.send(:_get_complete_bounding_box, rct, mask, processed)
1814
+ rct = child.send(:_get_complete_bounding_box, rct, mask, processed)
1780
1815
  end
1781
1816
  end
1782
- rct
1817
+ rct || Wx::Rect.new
1783
1818
  end
1784
1819
 
1785
1820
  # Original protected event handler called when the mouse pointer is moving around the shape canvas.
@@ -1800,7 +1835,7 @@ module Wx::SF
1800
1835
  @handles.each { |h| h.__send__(:_on_mouse_move, pos) }
1801
1836
 
1802
1837
  # send the event to the connection points too...
1803
- @connection_pts.each { |cp| cp.__send__(:_on_mouse_move, pos) }
1838
+ @connection_pts.each { |cp| cp.__send__(:_on_mouse_move, pos) } unless has_style?(STYLE::PROPAGATE_INTERACTIVE_CONNECTION)
1804
1839
 
1805
1840
  # determine, whether the shape should be highlighted for any reason
1806
1841
  if canvas
@@ -1872,8 +1907,8 @@ module Wx::SF
1872
1907
  @first_move = true
1873
1908
  on_begin_drag(pos)
1874
1909
 
1875
- if get_parent_shape && has_style?(STYLE::PROPAGATE_DRAGGING)
1876
- get_parent_shape.__send__(:_on_begin_drag, pos)
1910
+ if @parent_shape && has_style?(STYLE::PROPAGATE_DRAGGING)
1911
+ @parent_shape.__send__(:_on_begin_drag, pos)
1877
1912
  end
1878
1913
  end
1879
1914
 
@@ -1891,7 +1926,7 @@ module Wx::SF
1891
1926
  end
1892
1927
 
1893
1928
  # get shape BB BEFORE movement and combine it with BB of assigned lines
1894
- prev_bb = get_complete_bounding_box(Wx::Rect.new, BBMODE::SELF | BBMODE::CONNECTIONS | BBMODE::CHILDREN | BBMODE::SHADOW)
1929
+ prev_bb = get_complete_bounding_box(nil, BBMODE::SELF | BBMODE::CONNECTIONS | BBMODE::CHILDREN | BBMODE::SHADOW)
1895
1930
 
1896
1931
  move_to(pos.x - @mouse_offset.x, pos.y - @mouse_offset.y)
1897
1932
  on_dragging(pos)
@@ -1901,7 +1936,7 @@ module Wx::SF
1901
1936
  lst_child_ctrls.each { |ctrl| ctrl.update_control }
1902
1937
 
1903
1938
  # get shape BB AFTER movement and combine it with BB of assigned lines
1904
- curr_bb = get_complete_bounding_box(Wx::Rect.new, BBMODE::SELF | BBMODE::CONNECTIONS | BBMODE::CHILDREN | BBMODE::SHADOW)
1939
+ curr_bb = get_complete_bounding_box(nil, BBMODE::SELF | BBMODE::CONNECTIONS | BBMODE::CHILDREN | BBMODE::SHADOW)
1905
1940
 
1906
1941
  # update canvas
1907
1942
  refresh_rect(prev_bb.union!(curr_bb), DELAYED)
@@ -1909,8 +1944,8 @@ module Wx::SF
1909
1944
  @first_move = false
1910
1945
  end
1911
1946
 
1912
- if get_parent_shape && has_style?(STYLE::PROPAGATE_DRAGGING)
1913
- get_parent_shape.__send__(:_on_dragging, pos)
1947
+ if @parent_shape && has_style?(STYLE::PROPAGATE_DRAGGING)
1948
+ @parent_shape.__send__(:_on_dragging, pos)
1914
1949
  end
1915
1950
  end
1916
1951
 
@@ -1924,8 +1959,8 @@ module Wx::SF
1924
1959
 
1925
1960
  on_end_drag(pos)
1926
1961
 
1927
- if get_parent_shape && has_style?(STYLE::PROPAGATE_DRAGGING)
1928
- get_parent_shape.__send__(:_on_end_drag, pos)
1962
+ if @parent_shape && has_style?(STYLE::PROPAGATE_DRAGGING)
1963
+ @parent_shape.__send__(:_on_end_drag, pos)
1929
1964
  end
1930
1965
  end
1931
1966
 
@@ -1946,8 +1981,8 @@ module Wx::SF
1946
1981
  f_refresh_all = false
1947
1982
 
1948
1983
  if canvas.has_style?(Wx::SF::ShapeCanvas::STYLE::GRID_USE)
1949
- dx = canvas.get_grid_size.x
1950
- dy = canvas.get_grid_size.y
1984
+ dx = canvas.get_grid_size
1985
+ dy = canvas.get_grid_size
1951
1986
  end
1952
1987
 
1953
1988
  lst_selection = canvas.get_selected_shapes
@@ -1955,8 +1990,8 @@ module Wx::SF
1955
1990
  f_refresh_all = true
1956
1991
  end
1957
1992
 
1958
- prev_bb = Wx::Rect.new
1959
- if !f_refresh_all
1993
+ prev_bb = nil
1994
+ unless f_refresh_all
1960
1995
  prev_bb = get_complete_bounding_box(prev_bb, BBMODE::SELF | BBMODE::CONNECTIONS | BBMODE::CHILDREN | BBMODE::SHADOW)
1961
1996
  end
1962
1997
 
@@ -1977,10 +2012,9 @@ module Wx::SF
1977
2012
  end
1978
2013
 
1979
2014
  if !f_refresh_all
1980
- curr_bb = get_complete_bounding_box(Wx::Rect.new, BBMODE::SELF | BBMODE::CONNECTIONS | BBMODE::CHILDREN | BBMODE::SHADOW)
2015
+ curr_bb = get_complete_bounding_box(prev_bb, BBMODE::SELF | BBMODE::CONNECTIONS | BBMODE::CHILDREN | BBMODE::SHADOW)
1981
2016
 
1982
- prev_bb.union!(curr_bb)
1983
- refresh_rect(prev_bb, DELAYED)
2017
+ refresh_rect(curr_bb, DELAYED)
1984
2018
  else
1985
2019
  canvas.refresh(false)
1986
2020
  end
@@ -1995,9 +2029,9 @@ module Wx::SF
1995
2029
  return unless @diagram
1996
2030
 
1997
2031
  if @parent_shape
1998
- prev_bb = get_grand_parent_shape.get_complete_bounding_box(Wx::Rect.new)
2032
+ prev_bb = get_grand_parent_shape.get_complete_bounding_box(nil)
1999
2033
  else
2000
- prev_bb = get_complete_bounding_box(Wx::Rect.new)
2034
+ prev_bb = get_complete_bounding_box(nil)
2001
2035
  end
2002
2036
 
2003
2037
  # call appropriate user-defined handler
@@ -2014,9 +2048,9 @@ module Wx::SF
2014
2048
  update
2015
2049
 
2016
2050
  if @parent_shape
2017
- curr_bb = get_grand_parent_shape.get_complete_bounding_box(Wx::Rect.new)
2051
+ curr_bb = get_grand_parent_shape.get_complete_bounding_box(nil)
2018
2052
  else
2019
- curr_bb = get_complete_bounding_box(Wx::Rect.new)
2053
+ curr_bb = get_complete_bounding_box(nil)
2020
2054
  end
2021
2055
 
2022
2056
  # refresh shape
@@ -2062,7 +2096,6 @@ module Wx::SF
2062
2096
  def update_child_parents
2063
2097
  @child_shapes.each do |shape|
2064
2098
  shape.instance_variable_set(:@parent_shape, self)
2065
- shape.send(:update_child_parents)
2066
2099
  end
2067
2100
  end
2068
2101
 
@@ -2070,11 +2103,21 @@ module Wx::SF
2070
2103
  def serialize_child_shapes(*val)
2071
2104
  unless val.empty?
2072
2105
  @child_shapes = val.first
2106
+ # @parent_shape is not serialized, instead we rely on child shapes being (de-)serialized
2107
+ # by their parent (child shapes restored before restoring parent child list) and let
2108
+ # the parent reset the @parent_shape attributes of their children.
2109
+ # That way the links never get out of sync.
2073
2110
  update_child_parents
2074
2111
  end
2075
2112
  @child_shapes
2076
2113
  end
2077
2114
 
2115
+ # (de-)serialize hover colour; allows for nil values
2116
+ def serialize_hover_colour(*val)
2117
+ @hover_colour = val.first unless val.empty?
2118
+ @hover_colour
2119
+ end
2120
+
2078
2121
  public
2079
2122
 
2080
2123
  # Returns intersection point of two lines (if any)
@@ -2103,10 +2146,10 @@ module Wx::SF
2103
2146
  xi = (b1*c2 - c1*b2) / (a1*b2 - a2*b1)
2104
2147
  yi = -(a1*c2 - a2*c1) / (a1*b2 - a2*b1)
2105
2148
 
2106
- if( ((from1.x - xi)*(xi - to1.x) >= 0.0) &&
2107
- ((from2.x - xi)*(xi - to2.x) >= 0.0) &&
2108
- ((from1.y - yi)*(yi - to1.y) >= 0.0) &&
2109
- ((from2.y - yi)*(yi - to2.y) >= 0.0) )
2149
+ if ((from1.x - xi) * (xi - to1.x) >= 0.0) &&
2150
+ ((from2.x - xi) * (xi - to2.x) >= 0.0) &&
2151
+ ((from1.y - yi) * (yi - to1.y) >= 0.0) &&
2152
+ ((from2.y - yi) * (yi - to2.y) >= 0.0)
2110
2153
  return Wx::RealPoint.new(xi, yi)
2111
2154
  end
2112
2155