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

Sign up to get free protection for your applications and to get access to all the features.
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