wxruby3-shapes 0.9.0.pre.beta.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +12 -0
  3. data/CREDITS.md +18 -0
  4. data/INSTALL.md +39 -0
  5. data/LICENSE +21 -0
  6. data/README.md +118 -0
  7. data/assets/screenshot.png +0 -0
  8. data/bin/wx-shapes +9 -0
  9. data/lib/wx/shapes/arrow_base.rb +86 -0
  10. data/lib/wx/shapes/arrows/circle_arrow.rb +39 -0
  11. data/lib/wx/shapes/arrows/diamond_arrow.rb +33 -0
  12. data/lib/wx/shapes/arrows/open_arrow.rb +56 -0
  13. data/lib/wx/shapes/arrows/solid_arrow.rb +69 -0
  14. data/lib/wx/shapes/art/shape_canvas/page.xpm +73 -0
  15. data/lib/wx/shapes/auto_layout.rb +358 -0
  16. data/lib/wx/shapes/base.rb +33 -0
  17. data/lib/wx/shapes/canvas_history.rb +84 -0
  18. data/lib/wx/shapes/connection_point.rb +238 -0
  19. data/lib/wx/shapes/core.rb +19 -0
  20. data/lib/wx/shapes/diagram.rb +659 -0
  21. data/lib/wx/shapes/events.rb +389 -0
  22. data/lib/wx/shapes/printout.rb +136 -0
  23. data/lib/wx/shapes/serializable.rb +440 -0
  24. data/lib/wx/shapes/serialize/core.rb +40 -0
  25. data/lib/wx/shapes/serialize/id.rb +82 -0
  26. data/lib/wx/shapes/serialize/wx.rb +104 -0
  27. data/lib/wx/shapes/serializer/json.rb +258 -0
  28. data/lib/wx/shapes/serializer/yaml.rb +125 -0
  29. data/lib/wx/shapes/shape.rb +2129 -0
  30. data/lib/wx/shapes/shape_canvas.rb +3285 -0
  31. data/lib/wx/shapes/shape_data_object.rb +43 -0
  32. data/lib/wx/shapes/shape_handle.rb +287 -0
  33. data/lib/wx/shapes/shape_list.rb +161 -0
  34. data/lib/wx/shapes/shapes/bitmap_shape.rb +257 -0
  35. data/lib/wx/shapes/shapes/circle_shape.rb +136 -0
  36. data/lib/wx/shapes/shapes/control_shape.rb +483 -0
  37. data/lib/wx/shapes/shapes/curve_shape.rb +231 -0
  38. data/lib/wx/shapes/shapes/diamond_shape.rb +62 -0
  39. data/lib/wx/shapes/shapes/edit_text_shape.rb +317 -0
  40. data/lib/wx/shapes/shapes/ellipse_shape.rb +106 -0
  41. data/lib/wx/shapes/shapes/flex_grid_shape.rb +78 -0
  42. data/lib/wx/shapes/shapes/grid_shape.rb +404 -0
  43. data/lib/wx/shapes/shapes/line_shape.rb +907 -0
  44. data/lib/wx/shapes/shapes/multi_sel_rect.rb +214 -0
  45. data/lib/wx/shapes/shapes/ortho_shape.rb +357 -0
  46. data/lib/wx/shapes/shapes/polygon_shape.rb +294 -0
  47. data/lib/wx/shapes/shapes/rect_shape.rb +378 -0
  48. data/lib/wx/shapes/shapes/round_ortho_shape.rb +131 -0
  49. data/lib/wx/shapes/shapes/round_rect_shape.rb +142 -0
  50. data/lib/wx/shapes/shapes/square_shape.rb +119 -0
  51. data/lib/wx/shapes/shapes/text_shape.rb +324 -0
  52. data/lib/wx/shapes/thumbnail.rb +234 -0
  53. data/lib/wx/shapes/version.rb +12 -0
  54. data/lib/wx/shapes/wx.rb +29 -0
  55. data/lib/wx/shapes.rb +18 -0
  56. data/lib/wx/wx-shapes/base.rb +87 -0
  57. data/lib/wx/wx-shapes/cmd/sampler.rb +58 -0
  58. data/lib/wx/wx-shapes/cmd/test.rb +27 -0
  59. data/rakelib/yard/templates/default/fulldoc/html/css/wxruby3.css +7 -0
  60. data/rakelib/yard/templates/default/layout/html/setup.rb +5 -0
  61. data/rakelib/yard/yard/relative_markdown_links/version.rb +8 -0
  62. data/rakelib/yard/yard/relative_markdown_links.rb +39 -0
  63. data/rakelib/yard/yard-custom-templates.rb +2 -0
  64. data/rakelib/yard/yard-relative_markdown_links.rb +4 -0
  65. data/samples/demo/art/AlignBottom.xpm +35 -0
  66. data/samples/demo/art/AlignCenter.xpm +35 -0
  67. data/samples/demo/art/AlignLeft.xpm +35 -0
  68. data/samples/demo/art/AlignMiddle.xpm +35 -0
  69. data/samples/demo/art/AlignRight.xpm +35 -0
  70. data/samples/demo/art/AlignTop.xpm +35 -0
  71. data/samples/demo/art/Bitmap.xpm +25 -0
  72. data/samples/demo/art/Circle.xpm +22 -0
  73. data/samples/demo/art/Curve.xpm +21 -0
  74. data/samples/demo/art/Diamond.xpm +22 -0
  75. data/samples/demo/art/EditText.xpm +21 -0
  76. data/samples/demo/art/Ellipse.xpm +22 -0
  77. data/samples/demo/art/FixedRect.xpm +22 -0
  78. data/samples/demo/art/FlexGrid.xpm +22 -0
  79. data/samples/demo/art/GC.xpm +23 -0
  80. data/samples/demo/art/Grid.xpm +22 -0
  81. data/samples/demo/art/Line.xpm +21 -0
  82. data/samples/demo/art/NoSource.xpm +69 -0
  83. data/samples/demo/art/OrthoLine.xpm +21 -0
  84. data/samples/demo/art/Rect.xpm +22 -0
  85. data/samples/demo/art/RoundOrthoLine.xpm +21 -0
  86. data/samples/demo/art/RoundRect.xpm +22 -0
  87. data/samples/demo/art/Shadow.xpm +23 -0
  88. data/samples/demo/art/StandAloneLine.xpm +22 -0
  89. data/samples/demo/art/Text.xpm +21 -0
  90. data/samples/demo/art/Tool.xpm +23 -0
  91. data/samples/demo/art/sample.xpm +251 -0
  92. data/samples/demo/demo.rb +658 -0
  93. data/samples/demo/frame_canvas.rb +422 -0
  94. data/samples/demo/images/motyl.bmp +0 -0
  95. data/samples/demo/images/motyl2.bmp +0 -0
  96. data/samples/sample1/art/sample.xpm +251 -0
  97. data/samples/sample1/sample.rb +263 -0
  98. data/samples/sample2/art/sample.xpm +251 -0
  99. data/samples/sample2/sample.rb +133 -0
  100. data/samples/sample2/sample_canvas.rb +35 -0
  101. data/samples/sample2/sample_shape.rb +108 -0
  102. data/samples/sample3/art/sample.xpm +251 -0
  103. data/samples/sample3/sample.rb +281 -0
  104. data/samples/sample4/art/sample.xpm +251 -0
  105. data/samples/sample4/sample.rb +180 -0
  106. data/tests/art/motyl.bmp +0 -0
  107. data/tests/lib/wxapp_runner.rb +64 -0
  108. data/tests/serializer_tests.rb +521 -0
  109. data/tests/test_grid_shapes.rb +42 -0
  110. data/tests/test_serialize.rb +7 -0
  111. data/tests/test_serialize_yaml.rb +17 -0
  112. metadata +242 -0
@@ -0,0 +1,2129 @@
1
+ # Wx::SF::Shape - base shape class
2
+ # Copyright (c) M.J.N. Corino, The Netherlands
3
+
4
+ require 'wx/shapes/serializable'
5
+
6
+ require 'set'
7
+
8
+ module Wx::SF
9
+
10
+ class ERRCODE < Wx::Enum
11
+ OK = self.new(0)
12
+ NOT_CREATED = self.new(1)
13
+ NOT_ACCEPTED = self.new(2)
14
+ INVALID_INPUT = self.new(3)
15
+ end
16
+
17
+ # Base class for all shapes providing fundamental functionality and publishing set
18
+ # of virtual functions which must be defined by the user in derived shapes. This class
19
+ # shouldn't be used as it is.
20
+ #
21
+ # Shape objects derived from this class use hierarchical approach. It means that every
22
+ # shape must have defined parent shape (can be NULL for topmost shapes). An absolute
23
+ # shape position is then calculated as a summation of all relative positions of all parent
24
+ # shapes. Also the size of the parent shape can be limited be a bounding box of all
25
+ # children shapes.
26
+ #
27
+ # This class also declares set of virtual functions used as event handlers for various
28
+ # events (moving, sizing, drawing, mouse events, serialization and deserialization requests, ...)
29
+ # mostly triggered by a parent shape canvas.
30
+ class Shape
31
+
32
+ include Serializable
33
+
34
+ property :id, :active, :visibility, :style,
35
+ :accepted_children, :accepted_connections,
36
+ :accepted_src_neighbours, :accepted_trg_neighbours,
37
+ :hover_colour, :relative_position,
38
+ :h_align, :v_align, :h_border, :v_border,
39
+ :custom_dock_point, :connection_points,
40
+ :user_data
41
+ property child_shapes: :serialize_child_shapes
42
+
43
+ class SEARCHMODE < Wx::Enum
44
+ # Depth-First-Search algorithm
45
+ DFS = self.new(0)
46
+ # Breadth-First-Search algorithm
47
+ BFS = self.new(1)
48
+ end
49
+
50
+ # Bit flags for Wx::SF::Shape get_complete_bounding_box function
51
+ class BBMODE < Wx::Enum
52
+ SELF = self.new(1)
53
+ CHILDREN = self.new(2)
54
+ CONNECTIONS = self.new(4)
55
+ SHADOW = self.new(8)
56
+ ALL = self.new(15)
57
+ end
58
+
59
+ # Search mode flags for get_assigned_connections function
60
+ class CONNECTMODE < Wx::Enum
61
+ # Search for connection starting in examined shape
62
+ STARTING = self.new(0)
63
+ # Search for connection ending in examined shape
64
+ ENDING = self.new(1)
65
+ # Search for both starting and ending connections
66
+ BOTH = self.new(2)
67
+ end
68
+
69
+ # Flags for set_v_align function
70
+ class VALIGN < Wx::Enum
71
+ NONE = self.new(0)
72
+ TOP = self.new(1)
73
+ MIDDLE = self.new(2)
74
+ BOTTOM = self.new(3)
75
+ EXPAND = self.new(4)
76
+ LINE_START = self.new(5)
77
+ LINE_END = self.new(6)
78
+ end
79
+
80
+ # Flags for set_h_align function
81
+ class HALIGN < Wx::Enum
82
+ NONE = self.new(0)
83
+ LEFT = self.new(1)
84
+ CENTER = self.new(2)
85
+ RIGHT = self.new(3)
86
+ EXPAND = self.new(4)
87
+ LINE_START = self.new(5)
88
+ LINE_END = self.new(6)
89
+ end
90
+
91
+ # Basic shape's styles used with set_style function
92
+ class STYLE < Wx::Enum
93
+ # Interactive parent change is allowed
94
+ PARENT_CHANGE = self.new(1)
95
+ # Interactive position change is allowed
96
+ POSITION_CHANGE = self.new(2)
97
+ # Interactive size change is allowed
98
+ SIZE_CHANGE = self.new(4)
99
+ # Shape is highlighted at mouse hovering
100
+ HOVERING = self.new(8)
101
+ # Shape is highlighted at shape dragging
102
+ HIGHLIGHTING = self.new(16)
103
+ # Shape is always inside its parent
104
+ ALWAYS_INSIDE = self.new(32)
105
+ # User data is destroyed at the shape deletion
106
+ DELETE_USER_DATA = self.new(64)
107
+ # The DEL key is processed by the shape (not by the shape canvas)
108
+ PROCESS_DEL = self.new(128)
109
+ # Show handles if the shape is selected
110
+ SHOW_HANDLES = self.new(256)
111
+ # Show shadow under the shape
112
+ SHOW_SHADOW = self.new(512)
113
+ # Lock children relative position if the parent is resized
114
+ LOCK_CHILDREN = self.new(1024)
115
+ # Emit events (catchable in shape canvas)
116
+ EMIT_EVENTS = self.new(2048)
117
+ # Propagate mouse dragging event to parent shape
118
+ PROPAGATE_DRAGGING = self.new(4096)
119
+ # Propagate selection to parent shape (it means this shape cannot be selected because its focus is redirected to its parent shape)
120
+ PROPAGATE_SELECTION = self.new(8192)
121
+ # Propagate interactive connection request to parent shape (it means this shape cannot be connected interactively because this feature is redirected to its parent shape)
122
+ PROPAGATE_INTERACTIVE_CONNECTION = self.new(16384)
123
+ # Do no resize the shape to fit its children automatically
124
+ NO_FIT_TO_CHILDREN = self.new(32768)
125
+ # Propagate hovering to parent.
126
+ PROPAGATE_HOVERING = self.new(65536)
127
+ # Propagate hovering to parent.
128
+ PROPAGATE_HIGHLIGHTING = self.new(131072)
129
+ # Default shape style
130
+ DEFAULT_SHAPE_STYLE = PARENT_CHANGE | POSITION_CHANGE | SIZE_CHANGE | HOVERING | HIGHLIGHTING | SHOW_HANDLES | ALWAYS_INSIDE | DELETE_USER_DATA
131
+ end
132
+
133
+ # Default values
134
+ module DEFAULT
135
+ # Default value of Wx::SF::Shape @visible data member
136
+ VISIBILITY = true
137
+ # Default value of Wx::SF::Shape @active data member
138
+ 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
+ # Default value of Wx::SF::Shape @relativePosition data member
143
+ POSITION = Wx::RealPoint.new(0, 0)
144
+ # Default value of Wx::SF::Shape @vAlign data member
145
+ VALIGN = VALIGN::NONE
146
+ # Default value of Wx::SF::Shape @hAlign data member
147
+ HALIGN = HALIGN::NONE
148
+ # Default value of Wx::SF::Shape @vBorder data member
149
+ VBORDER = 0.0
150
+ # Default value of Wx::SF::Shape @hBorder data member
151
+ HBORDER = 0.0
152
+ # Default value of Wx::SF::Shape @style data member
153
+ DEFAULT_STYLE = STYLE::DEFAULT_SHAPE_STYLE
154
+ # Default value of Wx::SF::Shape @customDockPoint data member
155
+ DOCK_POINT = -3
156
+ end
157
+
158
+ # Provide Shape and derivatives with component set container
159
+ class << self
160
+ def component_shapes
161
+ @component_shapes ||= ::Set.new
162
+ end
163
+ end
164
+
165
+ # Declare a component shape property for the shape class.
166
+ # @overload component(*comp_id)
167
+ # Specifies one or more serialized component properties.
168
+ # The serialization framework will determine the availability of setter and getter methods
169
+ # automatically by looking for methods <code>"#{comp_id}=(v)"</code>, <code>"set_#{comp_id}(v)"</code> or <code>"#{comp_id}(v)"</code>
170
+ # for setters and <code>"#{comp_id}()"</code> or <code>"get_#{comp_id}"</code> for getters.
171
+ # @param [String,Symbol] comp_id id of component property
172
+ # @overload component(hash)
173
+ # Specifies one or more serialized component properties with associated setter/getter method ids/procs/lambda-s.
174
+ # @example
175
+ # property(
176
+ # prop_a: ->(obj, *val) {
177
+ # obj.my_prop_a_setter(val.first) unless val.empty?
178
+ # obj.my_prop_a_getter
179
+ # },
180
+ # prop_b: Proc.new { |obj, *val|
181
+ # obj.my_prop_b_setter(val.first) unless val.empty?
182
+ # obj.my_prop_b_getter
183
+ # },
184
+ # prop_c: :serialization_method)
185
+ # Procs with setter support MUST accept 1 or 2 arguments (1 for getter, 2 for setter).
186
+ # @note Use `*val` to specify the optional value argument for setter requests instead of `val=nil`
187
+ # to be able to support setting explicit nil values.
188
+ # @param [Hash] hash a hash of pairs of property ids and getter/setter procs
189
+ def self.component(*args)
190
+ args.flatten.each do |arg|
191
+ if arg.is_a?(::Hash)
192
+ arg.each_pair do |pn, pp|
193
+ # define serialized property for component (checks for duplicates)
194
+ property({pn => pp}, force: true)
195
+ # get the property definition and register as component
196
+ component_shapes << self.serializer_properties.last
197
+ end
198
+ else
199
+ # define serialized property for component (checks for duplicates)
200
+ property(arg, force: true)
201
+ # get the property definition and register as component
202
+ component_shapes << self.serializer_properties.last
203
+ end
204
+ end
205
+ # check if the current class already has the appropriate support
206
+ unless self.const_defined?(:ComponentSerializerMethods)
207
+ class << self
208
+ def disable_component_serialize(obj)
209
+ component_shapes.each { |pd| pd.get(obj).disable_serialize }
210
+ superclass.disable_component_serialize(obj) if superclass.respond_to?(:disable_component_serialize)
211
+ end
212
+
213
+ # override the #new method
214
+ def new(*)
215
+ instance = super
216
+ disable_component_serialize(instance)
217
+ instance
218
+ end
219
+ end
220
+ self.class_eval <<~__CODE
221
+ module ComponentSerializerMethods
222
+ def from_serialized(hash)
223
+ super(hash)
224
+ #{self.name}.component_shapes.each { |pd| pd.get(self).set_parent_shape(self) }
225
+ self
226
+ end
227
+ protected :from_serialized
228
+ end
229
+ include ComponentSerializerMethods
230
+ __CODE
231
+ end
232
+ end
233
+
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
241
+ ::Kernel.raise ArgumentError, "Invalid arguments pos: #{pos}, diagram: #{diagram}" unless
242
+ args.empty? || (Wx::RealPoint === pos && (diagram.nil? || Wx::SF::Diagram === diagram))
243
+
244
+ @id = Serializable::ID.new
245
+ @diagram = diagram
246
+ @parent_shape = nil
247
+ @child_shapes = ShapeList.new
248
+ @components = ::Set.new
249
+
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
259
+
260
+ @selected = false
261
+ @mouse_over = false
262
+ @first_move = false
263
+ @highlight_parent = false
264
+ @user_data = nil
265
+
266
+ # archived properties
267
+ @visible = DEFAULT::VISIBILITY
268
+ @active = DEFAULT::ACTIVITY
269
+ @style = DEFAULT::DEFAULT_STYLE
270
+ @v_align = DEFAULT::VALIGN
271
+ @h_align = DEFAULT::HALIGN
272
+ @v_border = DEFAULT::VBORDER
273
+ @h_border = DEFAULT::HBORDER
274
+ @custom_dock_point = DEFAULT::DOCK_POINT
275
+
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
281
+
282
+ @handles = []
283
+ @connection_pts = []
284
+
285
+ @accepted_children = ::Set.new
286
+ @accepted_connections = ::Set.new
287
+ @accepted_src_neighbours = ::Set.new
288
+ @accepted_trg_neighbours = ::Set.new
289
+ end
290
+
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
+ # Set managing diagram
306
+ # @param [Wx::SF::Diagram] diagram
307
+ def set_diagram(diagram)
308
+ if @diagram != diagram
309
+ @diagram = diagram
310
+ @child_shapes.each { |child| child.set_diagram(diagram) }
311
+ end
312
+ self
313
+ end
314
+
315
+ # Get managing diagram
316
+ # @return [Wx::SF::Diagram]
317
+ def get_diagram
318
+ @diagram
319
+ end
320
+ alias :diagram :get_diagram
321
+
322
+ # Get the shape canvas of the parent diagram
323
+ # @return [Wx::SF::ShapeCanvas,nil]
324
+ def get_parent_canvas
325
+ @diagram ? @diagram.get_shape_canvas : nil
326
+ end
327
+
328
+ # Add a child shape
329
+ # @param [Wx::SF::Shape] shape
330
+ def add_child(shape)
331
+ @child_shapes << shape if shape
332
+ end
333
+ private :add_child
334
+
335
+ # Remove a child shape
336
+ # @param [Wx::SF::Shape] shape
337
+ def remove_child(shape)
338
+ @child_shapes.delete(shape) if shape
339
+ end
340
+ private :remove_child
341
+
342
+ # Adds child shape is accepted. Removes the child shape as a toplevel diagram shape if appropriate.
343
+ # @param [Wx::SF::Shape] child child shape to add
344
+ # @return [Wx::SF::Shape,nil] added child shape or nil if not accepted
345
+ def add_child_shape(child)
346
+ if is_child_accepted(child.class)
347
+ if child.get_diagram
348
+ child.get_diagram.reparent_shape(child, shape)
349
+ else
350
+ child.set_parent_shape(self)
351
+ end
352
+ child.update
353
+ return child
354
+ end
355
+ nil
356
+ end
357
+
358
+ # Set parent shape object.
359
+ # @param [Wx::SF::Shape] parent
360
+ # @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
+ # @note Note that this does not add (if parent == nil) or remove (if parent != nil) the shape from the diagram's
362
+ # toplevel shapes. Use Diagram#reparent_shape when that is needed.
363
+ def set_parent_shape(parent)
364
+ @parent_shape.send(:remove_child, self) if @parent_shape
365
+ parent.send(:add_child, self) if parent
366
+ set_diagram(parent.get_diagram) if parent
367
+ @parent_shape = parent
368
+ end
369
+ alias :parent_shape= :set_parent_shape
370
+
371
+ # Get parent shape
372
+ # @return [Wx::SF::Shape,nil] parent shape
373
+ def get_parent_shape
374
+ @parent_shape
375
+ end
376
+ alias :parent_shape :get_parent_shape
377
+
378
+ # Get pointer to the topmost parent shape
379
+ # @return [Wx::SF::Shape] topmost parent shape
380
+ def get_grand_parent_shape
381
+ @parent_shape ? @parent_shape.get_grand_parent_shape : self
382
+ end
383
+ alias :grand_parent_shape :get_grand_parent_shape
384
+
385
+ # Refresh (redraw) the shape
386
+ # @param [Boolean] delayed If true then the shape canvas will be rather invalidated than refreshed.
387
+ # @see ShapeCanvas#invalidate_rect
388
+ # @see ShapeCanvas#refresh_invalidated_rect
389
+ def refresh(delayed = false)
390
+ refresh_rect(get_bounding_box, delayed)
391
+ end
392
+
393
+ # Draw shape. Default implementation tests basic shape visual states
394
+ # (normal/ready, mouse is over the shape, dragged shape can be accepted) and
395
+ # call appropriate virtual functions (DrawNormal, DrawHover, DrawHighlighted)
396
+ # for its visualisation. The function can be overridden if necessary.
397
+ # @param [Wx::DC] dc Reference to a device context where the shape will be drawn to
398
+ # @param [Boolean] children true if the shape's children should be drawn as well
399
+ def draw(dc, children = WITHCHILDREN)
400
+ return unless @diagram && @diagram.shape_canvas
401
+ return unless @visible
402
+
403
+ # draw the shape shadow if required
404
+ draw_shadow(dc) if !@selected && has_style?(STYLE::SHOW_SHADOW)
405
+
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
411
+ else
412
+ draw_hover(dc)
413
+ end
414
+ else
415
+ draw_normal(dc)
416
+ end
417
+
418
+ draw_selected(dc) if @selected
419
+
420
+ # ... then draw connection points ...
421
+ @connection_pts.each { |cpt| cpt.draw(dc) }
422
+
423
+ # ... then draw child shapes
424
+ if children
425
+ @child_shapes.each { |child| child.draw(dc) }
426
+ end
427
+ end
428
+
429
+ # Test whether the given point is inside the shape. The function
430
+ # can be overridden if necessary.
431
+ # @param [Wx::Point] pos Examined point
432
+ # @return [Boolean] true if the point is inside the shape area, otherwise false
433
+ def contains?(pos)
434
+ # HINT: overload it for custom actions...
435
+
436
+ get_bounding_box.contains?(pos)
437
+ end
438
+
439
+ # Test whether the shape is completely inside given rectangle. The function
440
+ # can be overridden if necessary.
441
+ # @param [Wx::Rect] rct Examined rectangle
442
+ # @return [Boolean] true if the shape is completely inside given rectangle, otherwise false
443
+ def inside?(rct)
444
+ # HINT: overload it for custom actions...
445
+
446
+ rct.contains?(get_bounding_box)
447
+ end
448
+
449
+ # Test whether the given rectangle intersects the shape.
450
+ # @param [Wx::Rect] rct Examined rectangle
451
+ # @return [Boolean] true if the examined rectangle intersects the shape, otherwise false
452
+ def intersects?(rct)
453
+ # HINT: overload it for custom actions...
454
+
455
+ rct.intersects(get_bounding_box)
456
+ end
457
+
458
+ # Get the shape's absolute position in the canvas (calculated as a summation
459
+ # of all relative positions in the shapes' hierarchy. The function can be overridden if necessary.
460
+ # @return [Wx::RealPoint] Shape's position
461
+ def get_absolute_position
462
+ # HINT: overload it for custom actions...
463
+ parent_shape = get_parent_shape
464
+ if parent_shape
465
+ @relative_position + get_parent_absolute_position
466
+ else
467
+ @relative_position.dup
468
+ end
469
+ end
470
+
471
+ # Get intersection point of the shape border and a line leading from
472
+ # 'start' point to 'finish' point. Default implementation does nothing. The function can be overridden if necessary.
473
+ # @param [Wx::RealPoint] _start Starting point of the virtual intersection line
474
+ # @param [Wx::RealPoint] _finish Ending point of the virtual intersection line
475
+ # @return [Wx::RealPoint] Intersection point
476
+ def get_border_point(_start, _finish)
477
+ # HINT: overload it for custom actions...
478
+ Wx::RealPoint.new
479
+ end
480
+
481
+ # Get shape's center. Default implementation does nothing. The function can be overridden if necessary.
482
+ # @return [Wx::RealPoint] Center point
483
+ def get_center
484
+ # HINT: overload it for custom actions...
485
+
486
+ bb = get_bounding_box
487
+ Wx::RealPoint.new(bb.left + bb.width/2, bb.top + bb.height/2)
488
+ end
489
+
490
+ # Function called by the framework responsible for creation of shape handles
491
+ # at the creation time. Default implementation does nothing. The function can be overridden if necessary.
492
+ def create_handles
493
+ # HINT: overload it for custom actions...
494
+ end
495
+
496
+ # Show/hide shape handles. Hidden handles are inactive.
497
+ # @param [Boolean] show true for showing, false for hiding
498
+ def show_handles(show)
499
+ @handles.each { |h| h.show(show) }
500
+ end
501
+
502
+ # Set shape's style.
503
+ #
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
505
+ # @param [Integer] style Combination of the shape's styles
506
+ # @see STYLE
507
+ def set_style(style)
508
+ @style = style
509
+ end
510
+ alias :style= :set_style
511
+
512
+ # Get current shape style.
513
+ # @return [Integer] shape style
514
+ def get_style
515
+ @style
516
+ end
517
+ alias :style :get_style
518
+
519
+ def add_style(style)
520
+ @style |= style
521
+ end
522
+ def remove_style(style)
523
+ @style &= ~style
524
+ end
525
+ def contains_style(style)
526
+ (@style & style) != 0
527
+ end
528
+ alias :contains_style? :contains_style
529
+ alias :has_style? :contains_style
530
+
531
+ # Find out whether this shape has some children.
532
+ # @return [Boolean] true if the parent shape has children, otherwise false
533
+ def has_children
534
+ !@child_shapes.empty?
535
+ end
536
+ alias :has_children? :has_children
537
+
538
+ # Get children of given type.
539
+ # @param [Class,nil] type Child shape type (if nil then all children are returned)
540
+ # @param [Array<Wx::SF::Shape>] list list where all found child shapes will be appended
541
+ # @return [Array<Wx::SF::Shape>] list with appended child shapes
542
+ def get_children(type, list)
543
+ @child_shapes.each_with_object(list) { |child, lst| lst << child if type.nil? || type === child }
544
+ end
545
+
546
+ # Get all children of given type recursively (i.e. children of children of .... ).
547
+ # @param [Class,nil] type Child shape type (if nil then all children are returned)
548
+ # @param [Array<Wx::SF::Shape>] list list where all found child shapes will be appended
549
+ # @param [SEARCHMODE] mode Search mode. User can choose Depth-First-Search or Breadth-First-Search algorithm (BFS is default)
550
+ # @see SEARCHMODE
551
+ def get_children_recursively(type, mode = SEARCHMODE::BFS, list = [])
552
+ @child_shapes.each do |child|
553
+ list << child if type.nil? || type === child
554
+ child.get_children_recursively(type, mode, list) if mode == SEARCHMODE::DFS
555
+ end
556
+ if mode == SEARCHMODE::BFS
557
+ @child_shapes.each { |child| child.get_children_recursively(type, mode, list) }
558
+ end
559
+ list
560
+ end
561
+
562
+ # Get child shapes associated with this (parent) shape.
563
+ # @param [Class,nil] type Type of searched child shapes (nil for any type)
564
+ # @param [Boolean] recursive Set this flag true if also children of children of ... should be found (also RECURSIVE or NORECURSIVE constants can be used).
565
+ # @param [SEARCHMODE] mode Search mode (has sense only for recursive search)
566
+ # @param [Array<Wx::SF::Shape>] list of child shapes to fill
567
+ # @return [Array<Wx::SF::Shape>] list of child shapes filled
568
+ def get_child_shapes(type, recursive = NORECURSIVE, mode = SEARCHMODE::BFS, list = [])
569
+ if recursive
570
+ get_children_recursively(type, mode, list)
571
+ else
572
+ get_children(type, list)
573
+ end
574
+ end
575
+
576
+ # Get neighbour shapes connected to this shape.
577
+ # @param [Class,nil] shape_info Line object type
578
+ # @param [CONNECTMODE] condir Connection direction
579
+ # @param [Boolean] direct Set this flag to true if only closest shapes should be found, otherwise also shapes connected by forked lines will be found (also constants DIRECT and INDIRECT can be used)
580
+ # @param [Array<Wx::SF::Shape>] neighbours List of neighbour shapes
581
+ # @return [Array<Wx::SF::Shape>] list of neighbour shapes filled
582
+ # @see CONNECTMODE
583
+ def get_neighbours(shape_info, condir, direct = DIRECT, neighbours = [])
584
+ unless Wx::SF::LineShape === self
585
+ _get_neighbours(shape_info, condir, direct, neighbours)
586
+ # delete starting object if necessary (can be added in a case of complex connection network)
587
+ neighbours.delete(self)
588
+ end
589
+ neighbours
590
+ end
591
+
592
+ # Get list of connections assigned to this shape.
593
+ # @note For proper functionality the shape must be managed by a diagram manager.
594
+ # @param [Class] shape_info Line object type
595
+ # @param [CONNECTMODE] mode Search mode
596
+ # @param [Array<Wx::SF::Shape>] lines shape list where all found connections will be stored
597
+ # @return [Array<Wx::SF::Shape>] list of connection shapes filled
598
+ # @see CONNECTMODE
599
+ def get_assigned_connections(shape_info, mode, lines = [])
600
+ @diagram.get_assigned_connections(self, shape_info, mode, lines) if @diagram
601
+ lines
602
+ end
603
+
604
+ # Get shape's bounding box. The function can be overridden if necessary.
605
+ # @return [Wx::Rect] Bounding rectangle
606
+ def get_bounding_box
607
+ # HINT: overload it for custom actions...
608
+
609
+ Wx::Rect.new
610
+ end
611
+
612
+ # Get shape's bounding box which includes also associated child shapes and connections.
613
+ # @param [Wx::Rect] rct bounding rectangle
614
+ # @param [BBMODE] mask Bit mask of object types which should be included into calculation
615
+ # @return [Wx::Rect] returned bounding box
616
+ # @see BBMODE
617
+ def get_complete_bounding_box(rct, mask = BBMODE::ALL)
618
+ _get_complete_bounding_box(rct, mask)
619
+ end
620
+
621
+ # Scale the shape size in both directions. The function can be overridden if necessary
622
+ # (new implementation should call default one or scale shape's children manually if necessary).
623
+ # @overload scale(x,y, children: WITHCHILDREN)
624
+ # @param [Float] x Horizontal scale factor
625
+ # @param [Float] y Vertical scale factor
626
+ # @param [Boolean] children true if the shape's children should be scaled as well, otherwise the shape will be updated after scaling via #update function.
627
+ # @overload scale(scale, children: WITHCHILDREN)
628
+ # @param [Wx::RealPoint] scale scale factors
629
+ # @param [Boolean] children true if the shape's children should be scaled as well, otherwise the shape will be updated after scaling via #update function.
630
+ def scale(*args, children: WITHCHILDREN)
631
+ # HINT: overload it for custom actions...
632
+
633
+ x, y = (args.size == 1 ? args.first : args)
634
+ scale_children(x, y) if children
635
+
636
+ @diagram.set_modified(true) if @diagram
637
+ # self.update
638
+ end
639
+
640
+ # Scale shape's children
641
+ # @param [Float] x Horizontal scale factor
642
+ # @param [Float] y Vertical scale factor
643
+ # @see Scale
644
+ def scale_children(x, y)
645
+ lst_children = get_child_shapes(ANY, RECURSIVE)
646
+
647
+ lst_children.each do |shape|
648
+ if shape.has_style?(STYLE::SIZE_CHANGE) && shape.is_a?(Wx::SF::TextShape)
649
+ shape.scale(x, y, children: WITHOUTCHILDREN)
650
+ end
651
+
652
+ if shape.has_style?(STYLE::POSITION_CHANGE) && (shape.get_v_align == VALIGN::NONE || shape.get_h_align == HALIGN::NONE)
653
+ shape.set_relative_position(shape.get_relative_position.x*x, shape.get_relative_position.y*y)
654
+ end
655
+
656
+ # re-align shapes which have set any alignment mode
657
+ shape.do_alignment
658
+ end
659
+ end
660
+
661
+ # Move the shape to the given absolute position. The function can be overridden if necessary.
662
+ # @overload move_to(x,y)
663
+ # @param [Float] x X coordinate
664
+ # @param [Float] y Y coordinate
665
+ # @overload move_to(pos)
666
+ # @param [Wx::RealPoint] pos New absolute position
667
+ def move_to(*args)
668
+ # HINT: overload it for custom actions...
669
+
670
+ pos = (args.size == 1 ? args.first.to_real_point : Wx::RealPoint.new(*args))
671
+ @relative_position = pos - get_parent_absolute_position
672
+
673
+ @diagram.set_modified(true) if @diagram
674
+ end
675
+
676
+ # Move the shape by the given offset. The function can be overridden if necessary.
677
+ # @overload move_by(x,y)
678
+ # @param [Float] x X offset
679
+ # @param [Float] y Y offset
680
+ # @overload move_by(delta)
681
+ # @param [Wx::RealPoint] delta Offset
682
+ def move_by(*args)
683
+ # HINT: overload it for custom actions...
684
+
685
+ x, y = (args.size == 1 ? args.first : args)
686
+ @relative_position.x += x
687
+ @relative_position.y += y
688
+
689
+ @diagram.set_modified(true) if @diagram
690
+ end
691
+
692
+ # Update the shape's position in order to its alignment
693
+ def do_alignment
694
+ parent = get_parent_shape
695
+
696
+ if parent && !parent.is_a?(Wx::SF::GridShape)
697
+
698
+ if parent.is_a?(Wx::SF::LineShape)
699
+ line_pos = get_parent_absolute_position
700
+ parent_bb = Wx::Rect.new(line_pos.x.to_i, line_pos.y.to_i, 1, 1)
701
+ else
702
+ parent_bb = parent.get_bounding_box
703
+ end
704
+
705
+ shape_bb = get_bounding_box
706
+
707
+ # do vertical alignment
708
+ case @v_align
709
+ when VALIGN::TOP
710
+ @relative_position.y = @v_border
711
+
712
+ when VALIGN::MIDDLE
713
+ @relative_position.y = parent_bb.height/2 - shape_bb.height/2
714
+
715
+ when VALIGN::BOTTOM
716
+ @relative_position.y = parent_bb.height - shape_bb.height - @v_border
717
+
718
+ when VALIGN::EXPAND
719
+ if has_style?(STYLE::SIZE_CHANGE)
720
+ @relative_position.y = @v_border
721
+ scale(1.0, ((parent_bb.height - 2*@v_border)/shape_bb.height).to_f)
722
+ end
723
+
724
+ when VALIGN::LINE_START
725
+ if parent.is_a?(Wx::SF::LineShape)
726
+ line_start, line_end = parent.get_line_segment(0)
727
+
728
+ if line_end.y >= line_start.y
729
+ @relative_position.y = line_start.y - line_pos.y + @v_border
730
+ else
731
+ @relative_position.y = line_start.y - line_pos.y - shape_bb.height - @v_border
732
+ end
733
+ end
734
+
735
+ 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)
738
+
739
+ if line_end.y >= line_start.y
740
+ @relative_position.y = line_end.y - line_pos.y - shape_bb.height - @v_border
741
+ else
742
+ @relative_position.y = line_end.y - line_pos.y + @v_border
743
+ end
744
+ end
745
+ end
746
+
747
+ # do horizontal alignment
748
+ case @h_align
749
+ when HALIGN::LEFT
750
+ @relative_position.x = @h_border
751
+
752
+ when HALIGN::CENTER
753
+ @relative_position.x = parent_bb.width/2 - shape_bb.width/2
754
+
755
+ when HALIGN::RIGHT
756
+ @relative_position.x = parent_bb.width - shape_bb.width - @h_border
757
+
758
+ when HALIGN::EXPAND
759
+ if has_style?(STYLE::SIZE_CHANGE)
760
+ @relative_position.x = @h_border
761
+ scale(((parent_bb.width - 2*@h_border)/shape_bb.width).to_f, 1.0)
762
+ end
763
+
764
+ when HALIGN::LINE_START
765
+ if parent.is_a?(Wx::SF::LineShape)
766
+ line_start, line_end = parent.get_line_segment(0)
767
+
768
+ if line_end.x >= line_start.x
769
+
770
+ @relative_position.x = line_start.x - line_pos.x + @h_border
771
+ else
772
+ @relative_position.x = line_start.x - line_pos.x - shape_bb.width - @h_border
773
+ end
774
+ end
775
+
776
+ 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)
779
+
780
+ if line_end.x >= line_start.x
781
+ @relative_position.x = line_end.x - line_pos.x - shape_bb.width - @h_border
782
+ else
783
+ @relative_position.x = line_end.x - line_pos.x + @h_border
784
+ end
785
+ end
786
+ end
787
+ end
788
+ end
789
+
790
+ # Update shape (align all child shapes and resize it to fit them)
791
+ def update
792
+ # do self-alignment
793
+ do_alignment
794
+
795
+ # do alignment of shape's children (if required)
796
+ @child_shapes.each { |child| child.do_alignment }
797
+
798
+ # fit the shape to its children
799
+ fit_to_children unless has_style?(STYLE::NO_FIT_TO_CHILDREN)
800
+
801
+ # do it recursively on all parent shapes
802
+ if (parent = get_parent_shape)
803
+ parent.update
804
+ end
805
+ end
806
+
807
+ # Resize the shape to bound all child shapes. The function can be overridden if necessary.
808
+ def fit_to_children
809
+ # HINT: overload it for custom actions...
810
+ end
811
+
812
+ # Function returns true if the shape is selected, otherwise returns false
813
+ def selected?
814
+ @selected
815
+ end
816
+
817
+ # Set the shape as a selected/deselected one
818
+ # @param [Boolean] state Selection state (true is selected, false is deselected)
819
+ def select(state)
820
+ @selected = state
821
+ show_handles(state && (@style & STYLE::SHOW_HANDLES) != 0)
822
+ end
823
+
824
+ # Set shape's relative position. Absolute shape's position is then calculated
825
+ # as a summation of the relative positions of this shape and all parent shapes in the shape's
826
+ # hierarchy.
827
+ # @overload set_relative_position(pos)
828
+ # @param [Wx::RealPoint] pos New relative position
829
+ # @overload set_relative_position(x,y)
830
+ # @param [Float] x Horizontal coordinate of new relative position
831
+ # @param [Float] y Vertical coordinate of new relative position
832
+ # @see #move_to
833
+ def set_relative_position(*arg)
834
+ x, y = (arg.size == 1 ? arg.first.to_real_point : arg)
835
+ @relative_position.x = x
836
+ @relative_position.y = y
837
+ end
838
+
839
+ # Get shape's relative position.
840
+ # @return [Wx::RealPoint] Current relative position
841
+ # @see #get_absolute_position
842
+ def get_relative_position
843
+ @relative_position.dup
844
+ end
845
+
846
+ # Set vertical alignment of this shape inside its parent
847
+ # @param [VALIGN] val Alignment type
848
+ # @see VALIGN
849
+ def set_v_align(val)
850
+ @v_align = val
851
+ end
852
+ alias :v_align= :set_v_align
853
+
854
+ # Get vertical alignment of this shape inside its parent
855
+ # @return [VALIGN] Alignment type
856
+ # @see VALIGN
857
+ def get_v_align
858
+ @v_align
859
+ end
860
+ alias :v_align :get_v_align
861
+
862
+ # Set horizontal alignment of this shape inside its parent
863
+ # @param [HALIGN] val Horizontal type
864
+ # @see HALIGN
865
+ def set_h_align(val)
866
+ @h_align = val
867
+ end
868
+ alias :h_align= :set_h_align
869
+
870
+ # Get horizontal alignment of this shape inside its parent
871
+ # @return [HALIGN] Alignment type
872
+ # @see HALIGN
873
+ def get_h_align
874
+ @h_align
875
+ end
876
+ alias :h_align :get_h_align
877
+
878
+ # Set vertical border between this shape and its parent (if vertical
879
+ # alignment is set).
880
+ # @param [Float] border Vertical border
881
+ # @see #set_v_align
882
+ def set_v_border(border)
883
+ @v_border = border
884
+ end
885
+ alias :v_border= :set_v_border
886
+
887
+ # Get vertical border between this shape and its parent (if vertical
888
+ # alignment is set).
889
+ # @return [Float] Vertical border
890
+ # @see #set_v_align
891
+ def get_v_border
892
+ @v_border
893
+ end
894
+ alias :v_border :get_v_border
895
+
896
+ # Set horizontal border between this shape and its parent (if horizontal
897
+ # alignment is set).
898
+ # @param [Float] border Horizontal border
899
+ # @see #set_h_align
900
+ def set_h_border(border)
901
+ @h_border = border
902
+ end
903
+ alias :h_border= :set_h_border
904
+
905
+ # Get horizontal border between this shape and its parent (if horizontal
906
+ # alignment is set).
907
+ # @return [Float] Vertical border
908
+ # @see #set_h_align
909
+ def get_h_border
910
+ @h_border
911
+ end
912
+ alias :h_border :get_h_border
913
+
914
+ # Set custom dock point used if the shape is child shape of a line shape.
915
+ # @param [Integer] dp Custom dock point
916
+ def set_custom_dock_point(dp)
917
+ @custom_dock_point = dp
918
+ end
919
+ alias :custom_dock_point= :set_custom_dock_point
920
+
921
+ # Get custom dock point used if the shape is child shape of a line shape.
922
+ # @return [Integer] Custom dock point
923
+ def get_custom_dock_point
924
+ @custom_dock_point
925
+ end
926
+ alias :custom_dock_point :get_custom_dock_point
927
+
928
+ # Determine whether this shape is ancestor of given child shape.
929
+ # @param [Wx::SF::Shape] child child shape.
930
+ # @return true if this shape is parent of given child shape, otherwise false
931
+ def ancestor?(child)
932
+ @child_shapes.include?(child) || @child_shapes.any? { |c| c.ancestor?(child) }
933
+ end
934
+
935
+ # Determine whether this shape is descendant of given parent shape.
936
+ # @param [Wx::SF::Shape] parent parent shape
937
+ # @return true if this shape is a child of given parent shape, otherwise false
938
+ def descendant?(parent)
939
+ parent && parent.ancestor?(self)
940
+ end
941
+
942
+ # Associate user data with the shape.
943
+ # If the data object is properly set then its marked properties will be serialized
944
+ # together with the parent shape. This means the user data must either be a serializable
945
+ # core type or a Wx::SF::Serializable.
946
+ # @param [Object] data user data
947
+ def set_user_data(data)
948
+ @user_data = data
949
+ end
950
+ alias :user_data= :set_user_data
951
+
952
+ # Get associated user data.
953
+ # @return [Object,nil] user data
954
+ def get_user_data
955
+ @user_data
956
+ end
957
+ alias :user_data :get_user_data
958
+
959
+ # Get shape's diagram canvas
960
+ # @return [Wx::SF::ShapeCanvas,nil] shape canvas if assigned via diagram, otherwise nil
961
+ # @see Wx::SF::Diagram
962
+ def get_shape_canvas
963
+ return nil unless @diagram
964
+
965
+ @diagram.shape_canvas
966
+ end
967
+ alias :shape_canvas :get_shape_canvas
968
+
969
+ # Get the shape's visibility status
970
+ # @return [Boolean] true if the shape is visible, otherwise false
971
+ def visible?
972
+ @visible
973
+ end
974
+ alias :visibility :visible?
975
+
976
+ # Show/hide shape
977
+ # @param [Boolean] show Set the parameter to true if the shape should be visible, otherwise use false
978
+ def show(show)
979
+ @visible = show
980
+ end
981
+ alias :set_visibility :show
982
+
983
+ # Set shape's hover color
984
+ # @param [Wx::Colour,String,Symbol] col Hover color
985
+ def set_hover_colour(col)
986
+ @hover_color = Wx::Colour === col ? col : Wx::Colour.new(col)
987
+ end
988
+ alias :hover_colour= :set_hover_colour
989
+
990
+ # Get shape's hover color
991
+ # @return [Wx::Colour] Current hover color
992
+ def get_hover_colour
993
+ @hover_color
994
+ end
995
+ alias :hover_colour :get_hover_colour
996
+
997
+ # Function returns value of a shape's activation flag.
998
+ # Non-active shapes are visible, but don't receive (process) any events.
999
+ # @return [Boolean] true if the shape is active, otherwise false
1000
+ def active?
1001
+ @active
1002
+ end
1003
+ alias :active :active?
1004
+
1005
+ # Shape's activation/deactivation
1006
+ # Deactivated shapes are visible, but don't receive (process) any events.
1007
+ # @param [Boolean] active true for activation, false for deactivation
1008
+ # @see #show
1009
+ def activate(active)
1010
+ @active = active
1011
+ end
1012
+ alias :set_active :activate
1013
+
1014
+ # Tells whether the given shape type is accepted by this shape (it means
1015
+ # whether this shape can be its parent).
1016
+ #
1017
+ # The function is typically used by the framework for determination whether a dropped
1018
+ # shape can be assigned to an underlying shape as its child.
1019
+ # @param [Class] type Class of examined shape object
1020
+ # @return [Boolean] true if the shape type is accepted, otherwise false.
1021
+ def is_child_accepted(type)
1022
+ @accepted_children.include?(type) || @accepted_children.include?(ACCEPT_ALL)
1023
+ end
1024
+ alias :child_accepted? :is_child_accepted
1025
+
1026
+ # Returns true if *all* currently dragged shapes can be accepted
1027
+ # as children of this shape.
1028
+ # @return [Boolean]
1029
+ # @see #is_shape_accepted
1030
+ def accept_currently_dragged_shapes
1031
+ return false unless get_shape_canvas
1032
+
1033
+ unless is_child_accepted(ACCEPT_ALL)
1034
+ lst_selection = get_shape_canvas.get_selected_shapes
1035
+
1036
+ return false if lst_selection.any? { |shape| !@accepted_children.include?(shape.class.name) }
1037
+ end
1038
+ true
1039
+ end
1040
+
1041
+ # Add given shape type to an acceptance list. The acceptance list contains class
1042
+ # names of the shapes which can be accepted as children of this shape.
1043
+ # Note: Constant value {Wx::SF::ACCEPT_ALL} behaves like any class.
1044
+ # @param [Class] type Class of accepted shape object
1045
+ # @see #is_child_accepted
1046
+ def accept_child(type)
1047
+ ::Kernel.raise ArgumentError, 'Class or ACCEPT_ALL expected' unless type.is_a?(::Class) || type == ACCEPT_ALL
1048
+ @accepted_children << type
1049
+ end
1050
+
1051
+ # Get shape types acceptance list.
1052
+ # @return [Set<String>] String set with class names of accepted shape types.
1053
+ # @see #is_child_accepted
1054
+ def get_accepted_children
1055
+ @accepted_children
1056
+ end
1057
+ alias :accepted_children :get_accepted_children
1058
+
1059
+ # Tells whether the given connection type is accepted by this shape (it means
1060
+ # whether this shape can be connected to another one by a connection of given type).
1061
+ #
1062
+ # The function is typically used by the framework during interactive connection creation.
1063
+ # @param [Class] type Class of examined connection object
1064
+ # @return true if the connection type is accepted, otherwise false.
1065
+ def is_connection_accepted(type)
1066
+ @accepted_connections.include?(type) || @accepted_connections.include?(ACCEPT_ALL)
1067
+ end
1068
+ alias :connection_accepted? :is_connection_accepted
1069
+
1070
+ # Add given connection type to an acceptance list. The acceptance list contains class
1071
+ # names of the connection which can be accepted by this shape.
1072
+ # Note: Constant value {Wx::SF::ACCEPT_ALL} behaves like any class.
1073
+ # @param [Class] type Class of accepted connection object
1074
+ # @see #is_connection_accepted
1075
+ def accept_connection(type)
1076
+ ::Kernel.raise ArgumentError, 'Class or ACCEPT_ALL expected' unless type.is_a?(::Class) || type == ACCEPT_ALL
1077
+ @accepted_connections << type
1078
+ end
1079
+
1080
+ # Get connection types acceptance list.
1081
+ # @return [Set<String>] String set with class names of accepted connection types.
1082
+ # @see #is_connection_accepted
1083
+ def get_accepted_connections
1084
+ @accepted_connections
1085
+ end
1086
+ alias :accepted_connections :get_accepted_connections
1087
+
1088
+ # Tells whether the given shape type is accepted by this shape as its source neighbour(it means
1089
+ # whether this shape can be connected from another one of given type).
1090
+ #
1091
+ # The function is typically used by the framework during interactive connection creation.
1092
+ # @param [Class] type Class of examined connection object
1093
+ # @return true if the shape type is accepted, otherwise false.
1094
+ def is_src_neighbour_accepted(type)
1095
+ @accepted_src_neighbours.include?(type) || @accepted_src_neighbours.include?(ACCEPT_ALL)
1096
+ end
1097
+ alias :src_neighbour_accepted? :is_src_neighbour_accepted
1098
+
1099
+ # Add given shape type to an source neighbours' acceptance list. The acceptance list contains class
1100
+ # names of the shape types which can be accepted by this shape as its source neighbour.
1101
+ # Note: Constant value {Wx::SF::ACCEPT_ALL} behaves like any class.
1102
+ # @param [Class] type Class of accepted connection object
1103
+ # @see #is_src_neighbour_accepted
1104
+ def accept_src_neighbour(type)
1105
+ ::Kernel.raise ArgumentError, 'Class or ACCEPT_ALL expected' unless type.is_a?(::Class) || type == ACCEPT_ALL
1106
+ @accepted_src_neighbours << type
1107
+ end
1108
+
1109
+ # Get source neighbour types acceptance list.
1110
+ # @return [Set<String>] String set with class names of accepted source neighbours types.
1111
+ # @see #is_src_neighbour_accepted
1112
+ def get_accepted_src_neighbours
1113
+ @accepted_src_neighbours
1114
+ end
1115
+ alias :accepted_src_neighbours :get_accepted_src_neighbours
1116
+
1117
+ # Tells whether the given shape type is accepted by this shape as its target neighbour(it means
1118
+ # whether this shape can be connected to another one of given type).
1119
+ #
1120
+ # The function is typically used by the framework during interactive connection creation.
1121
+ # @param [Class] type Class of examined connection object
1122
+ # @return [Boolean] true if the shape type is accepted, otherwise false.
1123
+ def is_trg_neighbour_accepted(type)
1124
+ @accepted_trg_neighbours.include?(type) || @accepted_trg_neighbours.include?(ACCEPT_ALL)
1125
+ end
1126
+ alias :trg_neighbour_accepted? :is_trg_neighbour_accepted
1127
+
1128
+ # Add given shape type to an target neighbours' acceptance list. The acceptance list contains class
1129
+ # names of the shape types which can be accepted by this shape as its target neighbour.
1130
+ # Note: Constant value {Wx::SF::ACCEPT_ALL} behaves like any class.
1131
+ # @param [Class] type Class of accepted connection object
1132
+ # @see #is_trg_neighbour_accepted
1133
+ def accept_trg_neighbour(type)
1134
+ ::Kernel.raise ArgumentError, 'Class or ACCEPT_ALL expected' unless type.is_a?(::Class) || type == ACCEPT_ALL
1135
+ @accepted_trg_neighbours << type
1136
+ end
1137
+
1138
+ # Get target neighbour types acceptance list.
1139
+ # @return [Set<String>] String set with class names of accepted target neighbours types.
1140
+ # @see #is_trg_neighbour_accepted
1141
+ def get_accepted_trg_neighbours
1142
+ @accepted_trg_neighbours
1143
+ end
1144
+ alias :accepted_trg_neighbours :get_accepted_trg_neighbours
1145
+
1146
+ # Clear shape object acceptance list
1147
+ # @see #accept_child
1148
+ def clear_accepted_childs
1149
+ @accepted_children.clear
1150
+ end
1151
+
1152
+ # Clear connection object acceptance list
1153
+ # @see #accept_connection
1154
+ def clear_accepted_connections
1155
+ @accepted_connections.clear
1156
+ end
1157
+
1158
+ # Clear source neighbour objects acceptance list
1159
+ # @see #accept_src_neighbour
1160
+ def clear_accepted_src_neighbours
1161
+ @accepted_src_neighbours.clear
1162
+ end
1163
+
1164
+ # Clear target neighbour objects acceptance list
1165
+ # @see #accept_trg_neighbour
1166
+ def clear_accepted_trg_neighbours
1167
+ @accepted_trg_neighbours.clear
1168
+ end
1169
+
1170
+ # Get list of currently assigned shape handles.
1171
+ # @return [Array<Wx::SF::Shape::Handle>] handle list
1172
+ def get_handles
1173
+ @handles
1174
+ end
1175
+ alias :handles :get_handles
1176
+
1177
+ # Get shape handle.
1178
+ # @param [Wx::SF::Shape::Handle::TYPE] type Handle type
1179
+ # @param [Integer] id Handle ID (useful only for line control points)
1180
+ # @return [Wx::SF::Shape::Handle,nil] shape handle object if exist
1181
+ # @see Wx::SF::Shape::Handle
1182
+ def get_handle(type, id = -1)
1183
+ @handles.find { |h| h.type == type && (id == -1 || h.id == id) }
1184
+ end
1185
+ alias :handle :get_handle
1186
+
1187
+ # Add new handle to the shape.
1188
+ #
1189
+ # The function creates new instance of shape handle (if it doesn't exist yet)
1190
+ # and inserts it into handle list.
1191
+ # @param [Wx::SF::Shape::Handle::TYPE] type Handle type
1192
+ # @param [Integer] id Handle ID (useful only for line control points)
1193
+ # @see Wx::SF::Shape::Handle
1194
+ def add_handle(type, id = -1)
1195
+ unless get_handle(type, id)
1196
+ @handles << Handle.new(self, type, id)
1197
+ end
1198
+ end
1199
+
1200
+ # Remove given shape handle (if exists).
1201
+ # @param [Wx::SF::Shape::Handle::TYPE] type Handle type
1202
+ # @param [Integer] id Handle ID (useful only for line control points)
1203
+ # @see Wx::SF::Shape::Handle
1204
+ def remove_handle(type, id = -1)
1205
+ @handles.delete_if { |h| h.type == type && (id == -1 || h.id == id) }
1206
+ end
1207
+
1208
+ # Get reference to connection points list.
1209
+ # @return [Array<Wx::SF::ConnectionPoint>] connection points list
1210
+ def get_connection_points
1211
+ @connection_pts
1212
+ end
1213
+ alias :connection_points :get_connection_points
1214
+
1215
+ # Get connection point of given type assigned to the shape.
1216
+ # @param [Wx::SF::ConnectionPoint::CPTYPE] type Connection point type
1217
+ # @param [Integer] id Optional connection point ID
1218
+ # @return [Wx::SF::ConnectionPoint,nil] connection point if exists, otherwise nil
1219
+ # @see Wx::SF::ConnectionPoint::CPTYPE
1220
+ def get_connection_point(type, id = -1)
1221
+ @connection_pts.find { |cp| cp.type == type && cp.id == id }
1222
+ end
1223
+ alias :connection_point :get_connection_point
1224
+
1225
+ # Get connection point closest to the given position.
1226
+ # @param [Wx::RealPoint] pos Position
1227
+ # @return [Wx::SF::ConnectionPoint,nil] closest connection point if exists, otherwise nil
1228
+ def get_nearest_connection_point(pos)
1229
+ pos = pos.to_real_point
1230
+ min_dist = Float::MAX
1231
+ @connection_pts.inject(nil) do |nearest, cp|
1232
+ if (curr_dist = pos.distance_to(cp.get_connection_point)) < min_dist
1233
+ min_dist = curr_dist
1234
+ nearest = cp
1235
+ end
1236
+ nearest
1237
+ end
1238
+ end
1239
+ alias :nearest_connection_point :get_nearest_connection_point
1240
+
1241
+ # Assign connection point of given type to the shape.
1242
+ # @overload add_connection_point(type, persistent: true)
1243
+ # @param [Wx::SF::ConnectionPoint::CPTYPE] type Connection point type
1244
+ # @param [Boolean] persistent true if the connection point should be serialized
1245
+ # @return [Wx::SF::ConnectionPoint] new connection point
1246
+ # @overload add_connection_point(relpos, id=-1, persistent: true)
1247
+ # @param [Wx::RealPoint] relpos Relative position in percentages
1248
+ # @param [Integer] id connection point ID
1249
+ # @param [Boolean] persistent true if the connection point should be serialized
1250
+ # @return [Wx::SF::ConnectionPoint] new connection point
1251
+ # @overload add_connection_point(cp, persistent: true)
1252
+ # @param [Wx::SF::ConnectionPoint] cp connection point (shape will take the ownership)
1253
+ # @param [Boolean] persistent true if the connection point should be serialized
1254
+ # @return [Wx::SF::ConnectionPoint] added connection point
1255
+ # @see Wx::SF::ConnectionPoint::CPTYPE
1256
+ def add_connection_point(arg, *rest, persistent: true)
1257
+ cp = nil
1258
+ case arg
1259
+ when ConnectionPoint::CPTYPE
1260
+ unless get_connection_point(arg)
1261
+ cp = ConnectionPoint.new(self, arg)
1262
+ cp.disable_serialize unless persistent
1263
+ end
1264
+ when Wx::RealPoint, ::Array
1265
+ cp = ConnectionPoint.new(self, arg.to_real_point, *rest)
1266
+ cp.disable_serialize unless persistent
1267
+ when ConnectionPoint
1268
+ cp = arg
1269
+ cp.disable_serialize unless persistent
1270
+ else
1271
+ ::Kernel.raise ArgumentError, "Invalid arguments: arg: #{arg}, rest: #{rest}"
1272
+ end
1273
+ @connection_pts << cp if cp
1274
+ cp
1275
+ end
1276
+
1277
+ # Remove connection point of given type from the shape (if present).
1278
+ # @param [Wx::SF::ConnectionPoint::CPTYPE] type Connection point type
1279
+ # @see Wx::SF::ConnectionPoint::CPTYPE
1280
+ def remove_connection_point(type)
1281
+ @connection_pts.delete_if { |cp| cp.type == type }
1282
+ end
1283
+
1284
+ # Event handler called when the shape is clicked by
1285
+ # the left mouse button. The function can be overridden if necessary.
1286
+ #
1287
+ # The function is called by the framework (by the shape canvas).
1288
+ # Default implementation emits Wx::SF::EVT_SF_SHAPE_LEFT_DOWN event.
1289
+ # @param [Wx::Point] pos Current mouse position
1290
+ # @see Wx::SF::ShapeCanvas
1291
+ def on_left_click(pos)
1292
+ # HINT: overload it for custom actions...
1293
+
1294
+ if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1295
+ evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_LEFT_DOWN, self.id)
1296
+ evt.set_shape(self)
1297
+ evt.set_mouse_position(pos)
1298
+ get_shape_canvas.get_event_handler.process_event(evt)
1299
+ end
1300
+ end
1301
+
1302
+ # Event handler called when the shape is clicked by
1303
+ # the right mouse button. The function can be overridden if necessary.
1304
+ #
1305
+ # The function is called by the framework (by the shape canvas).
1306
+ # Default implementation emits Wx::SF::EVT_SF_SHAPE_RIGHT_DOWN event.
1307
+ # @param [Wx::Point] pos Current mouse position
1308
+ # @see Wx::SF::ShapeCanvas
1309
+ def on_right_click(pos)
1310
+ # HINT: overload it for custom actions...
1311
+
1312
+ if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1313
+ evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_RIGHT_DOWN, self.id)
1314
+ evt.set_shape(self)
1315
+ evt.set_mouse_position(pos)
1316
+ get_shape_canvas.get_event_handler.process_event(evt)
1317
+ end
1318
+ end
1319
+
1320
+ # Event handler called when the shape is double-clicked by
1321
+ # the left mouse button. The function can be overridden if necessary.
1322
+ #
1323
+ # The function is called by the framework (by the shape canvas).
1324
+ # Default implementation emits Wx::SF::EVT_SF_SHAPE_LEFT_DCLICK event.
1325
+ # @param [Wx::Point] pos Current mouse position
1326
+ # @see Wx::SF::ShapeCanvas
1327
+ def on_left_double_click(pos)
1328
+ # HINT: overload it for custom actions...
1329
+
1330
+ if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1331
+ evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_LEFT_DCLICK, self.id)
1332
+ evt.set_shape(self)
1333
+ evt.set_mouse_position(pos)
1334
+ get_shape_canvas.get_event_handler.process_event(evt)
1335
+ end
1336
+ end
1337
+
1338
+ # Event handler called when the shape is double-clicked by
1339
+ # the right mouse button. The function can be overridden if necessary.
1340
+ #
1341
+ # The function is called by the framework (by the shape canvas).
1342
+ # Default implementation emits Wx::SF::EVT_SF_SHAPE_RIGHT_DCLICK event.
1343
+ # @param [Wx::Point] pos Current mouse position
1344
+ # @see Wx::SF::ShapeCanvas
1345
+ def on_right_double_click(pos)
1346
+ # HINT: overload it for custom actions...
1347
+
1348
+ if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1349
+ evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_RIGHT_DCLICK, self.id)
1350
+ evt.set_shape(self)
1351
+ evt.set_mouse_position(pos)
1352
+ get_shape_canvas.get_event_handler.process_event(evt)
1353
+ end
1354
+ end
1355
+
1356
+ # Event handler called at the beginning of the shape dragging process.
1357
+ # The function can be overridden if necessary.
1358
+ #
1359
+ # The function is called by the framework (by the shape canvas).
1360
+ # Default implementation emits Wx::SF::EVT_SF_SHAPE_DRAG_BEGIN event.
1361
+ # @param [Wx::Point] pos Current mouse position
1362
+ # @see Wx::SF::ShapeCanvas
1363
+ def on_begin_drag(pos)
1364
+ # HINT: overload it for custom actions...
1365
+
1366
+ if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1367
+ evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_DRAG_BEGIN, self.id)
1368
+ evt.set_shape(self)
1369
+ evt.set_mouse_position(pos)
1370
+ get_shape_canvas.get_event_handler.process_event(evt)
1371
+ end
1372
+ end
1373
+
1374
+ # Event handler called during the shape dragging process.
1375
+ # The function can be overridden if necessary.
1376
+ #
1377
+ # The function is called by the framework (by the shape canvas).
1378
+ # Default implementation emits Wx::SF::EVT_SF_SHAPE_DRAG event.
1379
+ # @param [Wx::Point] pos Current mouse position
1380
+ # @see Wx::SF::ShapeCanvas
1381
+ def on_dragging(pos)
1382
+ # HINT: overload it for custom actions...
1383
+
1384
+ if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1385
+ evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_DRAG, self.id)
1386
+ evt.set_shape(self)
1387
+ evt.set_mouse_position(pos)
1388
+ get_shape_canvas.get_event_handler.process_event(evt)
1389
+ end
1390
+ end
1391
+
1392
+ # Event handler called at the end of the shape dragging process.
1393
+ # The function can be overridden if necessary.
1394
+ #
1395
+ # The function is called by the framework (by the shape canvas).
1396
+ # Default implementation emits Wx::SF::EVT_SF_SHAPE_DRAG_END event.
1397
+ # @param [Wx::Point] pos Current mouse position
1398
+ # @see Wx::SF::ShapeCanvas
1399
+ def on_end_drag(pos)
1400
+ # HINT: overload it for custom actions...
1401
+
1402
+ if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1403
+ evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_DRAG_END, self.id)
1404
+ evt.set_shape(self)
1405
+ evt.set_mouse_position(pos)
1406
+ get_shape_canvas.get_event_handler.process_event(evt)
1407
+ end
1408
+ end
1409
+
1410
+ # Event handler called when the user started to drag the shape handle.
1411
+ # The function can be overridden if necessary.
1412
+ #
1413
+ # The function is called by the framework (by the shape canvas).
1414
+ # Default implementation emits Wx::SF::EVT_SF_SHAPE_HANDLE_BEGIN event.
1415
+ # @param [Wx::SF::Shape::Handle] handle dragged handle
1416
+ def on_begin_handle(handle)
1417
+ # HINT: overload it for custom actions...
1418
+
1419
+ if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1420
+ evt = Wx::SF::ShapeHandleEvent.new(Wx::SF::EVT_SF_SHAPE_HANDLE_BEGIN, self.id)
1421
+ evt.set_shape(self)
1422
+ evt.set_handle(handle)
1423
+ get_shape_canvas.get_event_handler.process_event(evt)
1424
+ end
1425
+ end
1426
+
1427
+ # Event handler called during dragging of the shape handle.
1428
+ # The function can be overridden if necessary.
1429
+ #
1430
+ # The function is called by the framework (by the shape canvas).
1431
+ # Default implementation emits Wx::SF::EVT_SF_SHAPE_HANDLE event.
1432
+ # @param [Wx::SF::Shape::Handle] handle dragged handle
1433
+ def on_handle(handle)
1434
+ # HINT: overload it for custom actions...
1435
+
1436
+ if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1437
+ evt = Wx::SF::ShapeHandleEvent.new(Wx::SF::EVT_SF_SHAPE_HANDLE, self.id)
1438
+ evt.set_shape(self)
1439
+ evt.set_handle(handle)
1440
+ get_shape_canvas.get_event_handler.process_event(evt)
1441
+ end
1442
+ end
1443
+
1444
+ # Event handler called when the user finished dragging of the shape handle.
1445
+ # The function can be overridden if necessary.
1446
+ #
1447
+ # The function is called by the framework (by the shape canvas).
1448
+ # Default implementation emits Wx::SF::EVT_SF_SHAPE_HANDLE_END event.
1449
+ # @param [Wx::SF::Shape::Handle] handle dragged handle
1450
+ def on_end_handle(handle)
1451
+ # HINT: overload it for custom actions...
1452
+
1453
+ if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1454
+ evt = Wx::SF::ShapeHandleEvent.new(Wx::SF::EVT_SF_SHAPE_HANDLE_END, self.id)
1455
+ evt.set_shape(self)
1456
+ evt.set_handle(handle)
1457
+ get_shape_canvas.get_event_handler.process_event(evt)
1458
+ end
1459
+ end
1460
+
1461
+ # Event handler called when a mouse pointer enters the shape area.
1462
+ # The function can be overridden if necessary.
1463
+ #
1464
+ # The function is called by the framework (by the shape canvas).
1465
+ # Default implementation emits Wx::SF::EVT_SF_SHAPE_MOUSE_ENTER event.
1466
+ # @param [Wx::Point] pos Current mouse position
1467
+ def on_mouse_enter(pos)
1468
+ # HINT: overload it for custom actions...
1469
+
1470
+ if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1471
+ evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_MOUSE_ENTER, self.id)
1472
+ evt.set_shape(self)
1473
+ evt.set_mouse_position(pos)
1474
+ get_shape_canvas.get_event_handler.process_event(evt)
1475
+ end
1476
+ end
1477
+
1478
+ # Event handler called when a mouse pointer moves above the shape area.
1479
+ # The function can be overridden if necessary.
1480
+ #
1481
+ # The function is called by the framework (by the shape canvas).
1482
+ # Default implementation emits Wx::SF::EVT_SF_SHAPE_MOUSE_OVER event.
1483
+ # @param [Wx::Point] pos Current mouse position
1484
+ def on_mouse_over(pos)
1485
+ # HINT: overload it for custom actions...
1486
+
1487
+ if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1488
+ evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_MOUSE_OVER, self.id)
1489
+ evt.set_shape(self)
1490
+ evt.set_mouse_position(pos)
1491
+ get_shape_canvas.get_event_handler.process_event(evt)
1492
+ end
1493
+ end
1494
+
1495
+ # Event handler called when a mouse pointer leaves the shape area.
1496
+ # The function can be overridden if necessary.
1497
+ #
1498
+ # The function is called by the framework (by the shape canvas).
1499
+ # Default implementation emits Wx::SF::EVT_SF_SHAPE_MOUSE_LEAVE event.
1500
+ # @param [Wx::Point] pos Current mouse position
1501
+ def on_mouse_leave(pos)
1502
+ # HINT: overload it for custom actions...
1503
+
1504
+ if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1505
+ evt = Wx::SF::ShapeMouseEvent.new(Wx::SF::EVT_SF_SHAPE_MOUSE_LEAVE, self.id)
1506
+ evt.set_shape(self)
1507
+ evt.set_mouse_position(pos)
1508
+ get_shape_canvas.get_event_handler.process_event(evt)
1509
+ end
1510
+ end
1511
+
1512
+ # Event handler called when any key is pressed (in the shape canvas).
1513
+ # The function can be overridden if necessary.
1514
+ #
1515
+ # The function is called by the framework (by the shape canvas).
1516
+ # Default implementation emits Wx::SF::EVT_SF_SHAPE_KEYDOWN event.
1517
+ # @param [Integer] key The key code
1518
+ # @return The function must return true if the default event routine should be called
1519
+ # as well, otherwise false
1520
+ # @see Wx::SF::Shape::_on_key
1521
+ def on_key(key)
1522
+ # HINT: overload it for custom actions...
1523
+
1524
+ if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1525
+ evt = Wx::SF::ShapeKeyEvent.new(Wx::SF::EVT_SF_SHAPE_KEYDOWN, self.id)
1526
+ evt.set_shape(self)
1527
+ evt.set_key_code(key)
1528
+ get_shape_canvas.get_event_handler.process_event(evt)
1529
+ end
1530
+
1531
+ true
1532
+ end
1533
+
1534
+ # Event handler called when any shape is dropped above this shape (and the dropped
1535
+ # shape is accepted as a child of this shape). The function can be overridden if necessary.
1536
+ #
1537
+ # The function is called by the framework (by the shape canvas).
1538
+ # Default implementation emits Wx::SF::EVT_SF_SHAPE_CHILD_DROP event.
1539
+ # @param [Wx::RealPoint] _pos Relative position of dropped shape
1540
+ # @param [Wx::SF::Shape] child dropped shape
1541
+ def on_child_dropped(_pos, child)
1542
+ # HINT: overload it for custom actions...
1543
+
1544
+ if has_style?(STYLE::EMIT_EVENTS) && get_shape_canvas
1545
+ evt = Wx::SF::ShapeChildDropEvent.new(Wx::SF::EVT_SF_SHAPE_CHILD_DROP, self.id)
1546
+ evt.set_shape(self)
1547
+ evt.set_child_shape(child)
1548
+ get_shape_canvas.get_event_handler.process_event(evt)
1549
+ end
1550
+ end
1551
+
1552
+ def to_s
1553
+ "#<#{self.class}:#{id.to_i}#{parent_shape ? " parent=#{parent_shape.id.to_i}" : ''}>"
1554
+ end
1555
+
1556
+ def inspect
1557
+ to_s
1558
+ end
1559
+
1560
+ protected
1561
+
1562
+ # Draw the shape in the normal way. The function can be overridden if necessary.
1563
+ # @param [Wx::DC] _dc Reference to device context where the shape will be drawn to
1564
+ def draw_normal(_dc)
1565
+ # HINT: overload it for custom actions...
1566
+ end
1567
+
1568
+ # Draw the shape in the selected way. The function can be overridden if necessary.
1569
+ # @param [Wx::DC] dc Reference to device context where the shape will be drawn to
1570
+ def draw_selected(dc)
1571
+ # HINT: overload it for custom actions...
1572
+
1573
+ if has_style?(STYLE::SHOW_HANDLES)
1574
+ @handles.each { |h| h.send(:draw, dc) }
1575
+ end
1576
+ end
1577
+
1578
+ # Draw the shape in the hover mode (the mouse cursor is above the shape).
1579
+ # The function can be overridden if necessary.
1580
+ # @param [Wx::DC] _dc Reference to device context where the shape will be drawn to
1581
+ def draw_hover(_dc)
1582
+ # HINT: overload it for custom actions...
1583
+ end
1584
+
1585
+ # Draw the shape in the highlighted mode (another shape is dragged over this
1586
+ # shape and this shape will accept the dragged one if it will be dropped on it).
1587
+ # The function can be overridden if necessary.
1588
+ # @param [Wx::DC] _dc Reference to device context where the shape will be drawn to
1589
+ def draw_highlighted(_dc)
1590
+ # HINT: overload it for custom actions...
1591
+ end
1592
+
1593
+ # Draw shadow under the shape. The function can be overridden if necessary.
1594
+ # @param [Wx::DC] _dc Reference to device context where the shadow will be drawn to
1595
+ def draw_shadow(_dc)
1596
+ # HINT: overload it for custom actions...
1597
+ end
1598
+
1599
+ # Repaint the shape
1600
+ # @param [Wx::Rect] rct Canvas portion that should be updated
1601
+ # @param [Boolean] delayed If true then the shape canvas will be rather invalidated than refreshed.
1602
+ # @see Wx::SF::ShapeCanvas#invalidate_rect
1603
+ # @see Wx::SF::ShapeCanvas#refresh_invalidated_rect
1604
+ def refresh_rect(rct, delayed = false)
1605
+ if get_shape_canvas
1606
+ if delayed
1607
+ get_shape_canvas.invalidate_rect(rct)
1608
+ else
1609
+ get_shape_canvas.refresh_canvas(false, rct)
1610
+ end
1611
+ end
1612
+ end
1613
+
1614
+ # Get absolute position of the shape parent.
1615
+ # @return [Wx::RealPoint] Absolute position of the shape parent if exists, otherwise 0,0
1616
+ 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)
1620
+ else
1621
+ return parent.get_absolute_position
1622
+ end
1623
+ end
1624
+
1625
+ Wx::RealPoint.new(0, 0)
1626
+ end
1627
+
1628
+ private
1629
+
1630
+ # Auxiliary function called by GetNeighbours function.
1631
+ # @param [Class,nil] shapeInfo Line object type
1632
+ # @param [CONNECTMODE] condir Connection direction
1633
+ # @param [Boolean] direct Set this flag to TRUE if only closest shapes should be found,
1634
+ # otherwise also shapes connected by forked lines will be found (also
1635
+ # constants DIRECT and INDIRECT can be used)
1636
+ # @param [Array<Wx::SF::Shape] neighbours List to add neighbour shapes to
1637
+ # @param [Set<Wx::SF::Shape] processed set to keep track of processed shapes
1638
+ # @return [Array<Wx::SF::Shape] List of neighbour shapes
1639
+ # @see #get_neighbours
1640
+ def _get_neighbours(shape_info, condir, direct, neighbours, processed = ::Set.new)
1641
+ if @diagram
1642
+ return if processed.include?(self)
1643
+
1644
+ opposite = nil
1645
+
1646
+ lst_connections = get_assigned_connections(shape_info, condir)
1647
+
1648
+ # find opposite shapes in direct branches
1649
+ lst_connections.each do |line|
1650
+ case condir
1651
+ when CONNECTMODE::STARTING
1652
+ opposite = @diagram.find_shape(line.get_trg_shape_id)
1653
+
1654
+ when CONNECTMODE::ENDING
1655
+ opposite = @diagram.find_shape(line.get_src_shape_id)
1656
+
1657
+ when CONNECTMODE::BOTH
1658
+ if @id == line.get_src_shape_id
1659
+ opposite = @diagram.find_shape(line.get_trg_shape_id)
1660
+ else
1661
+ opposite = @diagram.find_shape(line.get_src_shape_id)
1662
+ end
1663
+ end
1664
+
1665
+ # add opposite shape to the list (if applicable)
1666
+ neighbours << opposite if opposite && !opposite.is_a?(Wx::SF::LineShape) && !neighbours.include?(opposite)
1667
+
1668
+ # find next shapes
1669
+ if !direct && opposite
1670
+ # in the case of indirect branches we must differentiate between connections
1671
+ # and ordinary shapes
1672
+ processed << self
1673
+
1674
+ if opposite.is_a?(Wx::SF::LineShape)
1675
+ case condir
1676
+ when CONNECTMODE::STARTING
1677
+ opposite = @diagram.find_shape(opposite.get_src_shape_id)
1678
+
1679
+ if opposite.is_a?(Wx::SF::LineShape)
1680
+ opposite.__send__(:_get_neighbours, shape_info, condir, direct, neighbours, processed)
1681
+ elsif !neighbours.include?(opposite)
1682
+ neighbours << opposite
1683
+ end
1684
+
1685
+ when CONNECTMODE::ENDING
1686
+ opposite = @diagram.find_shape(opposite.get_trg_shape_id)
1687
+
1688
+ if opposite.is_a?(Wx::SF::LineShape)
1689
+ opposite.__send__(:_get_neighbours, shape_info, condir, direct, neighbours, processed)
1690
+ elsif !neighbours.include?(opposite)
1691
+ neighbours << opposite
1692
+ end
1693
+
1694
+ when CONNECTMODE::BOTH
1695
+ opposite = @diagram.find_shape(opposite.get_src_shape_id)
1696
+ if opposite.is_a?(Wx::SF::LineShape)
1697
+ opposite.__send__(:_get_neighbours, shape_info, condir, direct, neighbours, processed)
1698
+ elsif !neighbours.include?(opposite)
1699
+ neighbours << opposite
1700
+ end
1701
+
1702
+ opposite = @diagram.find_shape(opposite.get_trg_shape_id)
1703
+ if opposite.is_a?(Wx::SF::LineShape)
1704
+ opposite.__send__(:_get_neighbours, shape_info, condir, direct, neighbours, processed)
1705
+ elsif !neighbours.include?(opposite)
1706
+ neighbours << opposite
1707
+ end
1708
+ end
1709
+ else
1710
+ opposite.__send__(:_get_neighbours, shape_info, condir, direct, neighbours, processed)
1711
+ end
1712
+ end
1713
+ end
1714
+ end
1715
+ end
1716
+
1717
+ # Auxiliary function called by GetCompleteBoundingBox function.
1718
+ # @param [Wx::Rect] rct bounding rectangle to update
1719
+ # @param [BBMODE] mask Bit mask of object types which should be included into calculation
1720
+ # @param [Set<Wx::SF::Shape] processed set to keep track of processed shapes
1721
+ # @return [Wx::Rect] bounding rectangle
1722
+ # @see BBMODE
1723
+ def _get_complete_bounding_box(rct, mask = BBMODE::ALL, processed = ::Set.new)
1724
+ return rct unless @diagram
1725
+ return rct if processed.include?(self)
1726
+
1727
+ processed << self
1728
+
1729
+ # 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))
1733
+ else
1734
+ rct.union!(get_bounding_box.inflate!(@h_border.abs.to_i, @v_border.abs.to_i))
1735
+
1736
+ # add also shadow offset if necessary
1737
+ if (mask & BBMODE::SHADOW) != 0 && has_style?(STYLE::SHOW_SHADOW) && get_parent_canvas
1738
+ n_offset = get_parent_canvas.get_shadow_offset
1739
+
1740
+ if n_offset.x < 0
1741
+ rct.set_x(rct.x + n_offset.x.to_i)
1742
+ rct.set_width(rct.width - n_offset.x.to_i)
1743
+ else
1744
+ rct.set_width(rct.width + n_offset.x.to_i)
1745
+ end
1746
+
1747
+ if n_offset.y < 0
1748
+ rct.set_y(rct.y + n_offset.y.to_i)
1749
+ rct.set_height(rct.height - n_offset.y.to_i)
1750
+ else
1751
+ rct.set_height(rct.height + n_offset.y.to_i)
1752
+ end
1753
+ end
1754
+ end
1755
+ else
1756
+ mask |= BBMODE::SELF
1757
+ end
1758
+
1759
+ # get list of all connection lines assigned to the shape and find their child shapes
1760
+ lst_children = []
1761
+ if (mask & BBMODE::CONNECTIONS) != 0
1762
+ lst_lines = get_assigned_connections(Wx::SF::LineShape, CONNECTMODE::BOTH)
1763
+
1764
+ lst_lines.each do |line|
1765
+ # rct.union!(line.get_bounding_box)
1766
+ lst_children << line
1767
+
1768
+ # get children of the connections
1769
+ line.get_child_shapes(ANY, NORECURSIVE, SEARCHMODE::BFS, lst_children)
1770
+ end
1771
+ end
1772
+
1773
+ # get children of this shape
1774
+ if (mask & BBMODE::CHILDREN) != 0
1775
+ get_child_shapes(ANY, NORECURSIVE, SEARCHMODE::BFS, lst_children)
1776
+
1777
+ # now, call this function for all children recursively...
1778
+ lst_children.each do |child|
1779
+ child.send(:_get_complete_bounding_box, rct, mask, processed)
1780
+ end
1781
+ end
1782
+ rct
1783
+ end
1784
+
1785
+ # Original protected event handler called when the mouse pointer is moving around the shape canvas.
1786
+ # The function is called by the framework (by the shape canvas). After processing the event
1787
+ # relevant overridable event handlers are called.
1788
+ # @param [Wx::Point] pos Current mouse position
1789
+ # @see Wx::SF::Shape#on_mouse_enter
1790
+ # @see Wx::SF::Shape#on_mouse_over
1791
+ # @see Wx::SF::Shape#on_mouse_leave
1792
+ def _on_mouse_move(pos)
1793
+ return unless @diagram
1794
+
1795
+ if @visible && @active
1796
+ f_update_shape = false
1797
+ canvas = get_shape_canvas
1798
+
1799
+ # send the event to the shape handles too...
1800
+ @handles.each { |h| h.__send__(:_on_mouse_move, pos) }
1801
+
1802
+ # send the event to the connection points too...
1803
+ @connection_pts.each { |cp| cp.__send__(:_on_mouse_move, pos) }
1804
+
1805
+ # determine, whether the shape should be highlighted for any reason
1806
+ if canvas
1807
+ case canvas.get_mode
1808
+ when Wx::SF::ShapeCanvas::MODE::SHAPEMOVE
1809
+ if has_style?(STYLE::HIGHLIGHTING) && canvas.has_style?(Wx::SF::ShapeCanvas::STYLE::HIGHLIGHTING)
1810
+ shape_under_cursor = canvas.get_shape_under_cursor(Wx::SF::ShapeCanvas::SEARCHMODE::UNSELECTED)
1811
+ while shape_under_cursor
1812
+ break unless shape_under_cursor.has_style?(STYLE::PROPAGATE_HIGHLIGHTING)
1813
+ shape_under_cursor = shape_under_cursor.get_parent_shape
1814
+ end
1815
+ if shape_under_cursor == self
1816
+ f_update_shape = @highlight_parent = accept_currently_dragged_shapes
1817
+ end
1818
+ end
1819
+
1820
+ when Wx::SF::ShapeCanvas::MODE::HANDLEMOVE
1821
+ if has_style?(STYLE::HOVERING) && canvas.has_style?(Wx::SF::ShapeCanvas::STYLE::HOVERING)
1822
+ shape_under_cursor = canvas.get_shape_under_cursor(Wx::SF::ShapeCanvas::SEARCHMODE::UNSELECTED)
1823
+ while shape_under_cursor
1824
+ break unless shape_under_cursor.has_style?(STYLE::PROPAGATE_HOVERING)
1825
+ shape_under_cursor = shape_under_cursor.get_parent_shape
1826
+ end
1827
+
1828
+ f_update_shape = true if shape_under_cursor == self
1829
+ @highlight_parent = false
1830
+ end
1831
+
1832
+ else
1833
+ if has_style?(STYLE::HOVERING) && canvas.has_style?(Wx::SF::ShapeCanvas::STYLE::HOVERING)
1834
+ shape_under_cursor = canvas.get_shape_under_cursor
1835
+ while shape_under_cursor
1836
+ break unless shape_under_cursor.has_style?(STYLE::PROPAGATE_HOVERING)
1837
+ shape_under_cursor = shape_under_cursor.get_parent_shape
1838
+ end
1839
+
1840
+ f_update_shape = true if shape_under_cursor == self
1841
+ @highlight_parent = false
1842
+ end
1843
+ end
1844
+ end
1845
+
1846
+ if contains?(pos) && f_update_shape
1847
+ if !@mouse_over
1848
+ @mouse_over = true
1849
+ on_mouse_enter(pos)
1850
+ refresh(DELAYED)
1851
+ else
1852
+ on_mouse_over(pos)
1853
+ end
1854
+ else
1855
+ if @mouse_over
1856
+ @mouse_over = false
1857
+ on_mouse_leave(pos)
1858
+ refresh(DELAYED)
1859
+ end
1860
+ end
1861
+ end
1862
+ end
1863
+
1864
+ # Original protected event handler called at the beginning of dragging process.
1865
+ # The function is called by the framework (by the shape canvas). After processing the event
1866
+ # an overridable event handler is called.
1867
+ # @param [Wx::Point] pos Current mouse position
1868
+ # @see Wx::SF::Shape#on_begin_drag
1869
+ def _on_begin_drag(pos)
1870
+ return unless @active
1871
+
1872
+ @first_move = true
1873
+ on_begin_drag(pos)
1874
+
1875
+ if get_parent_shape && has_style?(STYLE::PROPAGATE_DRAGGING)
1876
+ get_parent_shape.__send__(:_on_begin_drag, pos)
1877
+ end
1878
+ end
1879
+
1880
+ # Original protected event handler called during a dragging process.
1881
+ # The function is called by the framework (by the shape canvas). After processing the event
1882
+ # an overridable event handler is called.
1883
+ # @param [Wx::Point] pos Current mouse position
1884
+ # @see Wx::SF::Shape#on_dragging
1885
+ def _on_dragging(pos)
1886
+ return unless @diagram
1887
+
1888
+ if @visible && @active && has_style?(STYLE::POSITION_CHANGE)
1889
+ if @first_move
1890
+ @mouse_offset = Wx::RealPoint.new(pos.x, pos.y) - get_absolute_position
1891
+ end
1892
+
1893
+ # 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)
1895
+
1896
+ move_to(pos.x - @mouse_offset.x, pos.y - @mouse_offset.y)
1897
+ on_dragging(pos)
1898
+
1899
+ # GUI controls in child control shapes must be updated explicitly
1900
+ lst_child_ctrls = get_child_shapes(Wx::SF::ControlShape, RECURSIVE)
1901
+ lst_child_ctrls.each { |ctrl| ctrl.update_control }
1902
+
1903
+ # 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)
1905
+
1906
+ # update canvas
1907
+ refresh_rect(prev_bb.union!(curr_bb), DELAYED)
1908
+
1909
+ @first_move = false
1910
+ end
1911
+
1912
+ if get_parent_shape && has_style?(STYLE::PROPAGATE_DRAGGING)
1913
+ get_parent_shape.__send__(:_on_dragging, pos)
1914
+ end
1915
+ end
1916
+
1917
+ # Original protected event handler called at the end of dragging process.
1918
+ # The function is called by the framework (by the shape canvas). After processing the event
1919
+ # an overridable event handler is called.
1920
+ # @param [Wx::Point] pos Current mouse position
1921
+ # @see Wx::SF::Shape#on_end_drag
1922
+ def _on_end_drag(pos)
1923
+ return unless @active
1924
+
1925
+ on_end_drag(pos)
1926
+
1927
+ if get_parent_shape && has_style?(STYLE::PROPAGATE_DRAGGING)
1928
+ get_parent_shape.__send__(:_on_end_drag, pos)
1929
+ end
1930
+ end
1931
+
1932
+ # Original protected event handler called when any key is pressed (in the shape canvas).
1933
+ # The function is called by the framework (by the shape canvas).
1934
+ # Default implementation performs operations necessary for proper shape's
1935
+ # moving and repainting.
1936
+ # @param [Integer] key The key code
1937
+ # @see Wx::SF::Shape#on_key
1938
+ def _on_key(key)
1939
+ canvas = get_shape_canvas
1940
+
1941
+ return unless canvas
1942
+
1943
+ if @visible && @active
1944
+ dx = 1.0
1945
+ dy = 1.0
1946
+ f_refresh_all = false
1947
+
1948
+ if canvas.has_style?(Wx::SF::ShapeCanvas::STYLE::GRID_USE)
1949
+ dx = canvas.get_grid_size.x
1950
+ dy = canvas.get_grid_size.y
1951
+ end
1952
+
1953
+ lst_selection = canvas.get_selected_shapes
1954
+ if (lst_selection.size > 1) && lst_selection.include?(self)
1955
+ f_refresh_all = true
1956
+ end
1957
+
1958
+ prev_bb = Wx::Rect.new
1959
+ if !f_refresh_all
1960
+ prev_bb = get_complete_bounding_box(prev_bb, BBMODE::SELF | BBMODE::CONNECTIONS | BBMODE::CHILDREN | BBMODE::SHADOW)
1961
+ end
1962
+
1963
+ if on_key(key)
1964
+ case key
1965
+ when Wx::K_LEFT
1966
+ move_by(-dx, 0) if has_style?(STYLE::POSITION_CHANGE)
1967
+
1968
+ when Wx::K_RIGHT
1969
+ move_by(dx, 0) if has_style?(STYLE::POSITION_CHANGE)
1970
+
1971
+ when Wx::K_UP
1972
+ move_by(0, -dy) if has_style?(STYLE::POSITION_CHANGE)
1973
+
1974
+ when Wx::K_DOWN
1975
+ move_by(0, dy) if has_style?(STYLE::POSITION_CHANGE)
1976
+ end
1977
+ end
1978
+
1979
+ if !f_refresh_all
1980
+ curr_bb = get_complete_bounding_box(Wx::Rect.new, BBMODE::SELF | BBMODE::CONNECTIONS | BBMODE::CHILDREN | BBMODE::SHADOW)
1981
+
1982
+ prev_bb.union!(curr_bb)
1983
+ refresh_rect(prev_bb, DELAYED)
1984
+ else
1985
+ canvas.refresh(false)
1986
+ end
1987
+ end
1988
+ end
1989
+
1990
+ # Original protected event handler called during dragging of the shape handle.
1991
+ # The function is called by the framework (by the shape canvas).
1992
+ # Default implementation manages the child shapes' alignment (if set).
1993
+ # @param [Wx::SF::Shape::Handle] handle dragged handle
1994
+ def _on_handle(handle)
1995
+ return unless @diagram
1996
+
1997
+ if @parent_shape
1998
+ prev_bb = get_grand_parent_shape.get_complete_bounding_box(Wx::Rect.new)
1999
+ else
2000
+ prev_bb = get_complete_bounding_box(Wx::Rect.new)
2001
+ end
2002
+
2003
+ # call appropriate user-defined handler
2004
+ on_handle(handle)
2005
+
2006
+ # align children
2007
+ @child_shapes.each do |child|
2008
+ if child.get_v_align != VALIGN::NONE || child.get_h_align != HALIGN::NONE
2009
+ child.do_alignment
2010
+ end
2011
+ end
2012
+
2013
+ # update shape
2014
+ update
2015
+
2016
+ if @parent_shape
2017
+ curr_bb = get_grand_parent_shape.get_complete_bounding_box(Wx::Rect.new)
2018
+ else
2019
+ curr_bb = get_complete_bounding_box(Wx::Rect.new)
2020
+ end
2021
+
2022
+ # refresh shape
2023
+ refresh_rect(curr_bb.union!(prev_bb), DELAYED)
2024
+ end
2025
+
2026
+ # Event handler called by ShapeCanvas to request,report canvas changes.
2027
+ # Default implementation does nothing.
2028
+ # @param [ShapeCanvas::CHANGE] _change change type indicator
2029
+ # @param [Array] _args any additional arguments
2030
+ # @return [Boolean]
2031
+ def _on_canvas(_change, *_args)
2032
+ # overridden in some derived shapes
2033
+ true
2034
+ end
2035
+
2036
+ # Sets accepted children. Exclusively for deserialization.
2037
+ def set_accepted_children(set)
2038
+ @accepted_children.replace(set.collect { |e| e.is_a?(::String) ? ::Object.const_get(e) : e })
2039
+ end
2040
+
2041
+ # Sets accepted connection. Exclusively for deserialization.
2042
+ def set_accepted_connections(set)
2043
+ @accepted_connections.replace(set.collect { |e| e.is_a?(::String) ? ::Object.const_get(e) : e })
2044
+ end
2045
+
2046
+ # Sets accepted src neighbours. Exclusively for deserialization.
2047
+ def set_accepted_src_neighbours(set)
2048
+ @accepted_src_neighbours.replace(set.collect { |e| e.is_a?(::String) ? ::Object.const_get(e) : e })
2049
+ end
2050
+
2051
+ # Sets accepted trg neighbours. Exclusively for deserialization.
2052
+ def set_accepted_trg_neighbours(set)
2053
+ @accepted_trg_neighbours.replace(set.collect { |e| e.is_a?(::String) ? ::Object.const_get(e) : e })
2054
+ end
2055
+
2056
+ # Sets connection points. Exclusively for deserialization.
2057
+ def set_connection_points(list)
2058
+ @connection_pts.replace(list)
2059
+ @connection_pts.each { |cp| cp.parent_shape = self }
2060
+ end
2061
+
2062
+ def update_child_parents
2063
+ @child_shapes.each do |shape|
2064
+ shape.instance_variable_set(:@parent_shape, self)
2065
+ shape.send(:update_child_parents)
2066
+ end
2067
+ end
2068
+
2069
+ # (de-)serialize child shapes. Exclusively for deserialization.
2070
+ def serialize_child_shapes(*val)
2071
+ unless val.empty?
2072
+ @child_shapes = val.first
2073
+ update_child_parents
2074
+ end
2075
+ @child_shapes
2076
+ end
2077
+
2078
+ public
2079
+
2080
+ # Returns intersection point of two lines (if any)
2081
+ # @param [Wx::RealPoint] from1
2082
+ # @param [Wx::RealPoint] to1
2083
+ # @param [Wx::RealPoint] from2
2084
+ # @param [Wx::RealPoint] to2
2085
+ # @return [Wx::RealPoint,nil] intersection point or nil
2086
+ def self.lines_intersection(from1, to1, from2, to2)
2087
+ # create line 1 info
2088
+ a1 = to1.y - from1.y
2089
+ b1 = from1.x - to1.x
2090
+ c1 = -a1*from1.x - b1*from1.y
2091
+
2092
+ # create line 2 info
2093
+ a2 = to2.y - from2.y
2094
+ b2 = from2.x - to2.x
2095
+ c2 = -a2*from2.x - b2*from2.y
2096
+
2097
+ # check, whether the lines are parallel...
2098
+ ka = a1 / a2
2099
+ kb = b1 / b2
2100
+
2101
+ return nil if ka == kb
2102
+
2103
+ xi = (b1*c2 - c1*b2) / (a1*b2 - a2*b1)
2104
+ yi = -(a1*c2 - a2*c1) / (a1*b2 - a2*b1)
2105
+
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) )
2110
+ return Wx::RealPoint.new(xi, yi)
2111
+ end
2112
+
2113
+ nil
2114
+ end
2115
+
2116
+ # Allow shapes to call class method as instance method.
2117
+ def lines_intersection(*args)
2118
+ Shape.lines_intersection(*args)
2119
+ end
2120
+
2121
+ end # class Shape
2122
+
2123
+ end # module Wx::SF
2124
+
2125
+ require 'wx/shapes/shape_handle'
2126
+
2127
+ Dir[File.join(__dir__, 'shapes', '*.rb')].each do |f|
2128
+ require "wx/shapes/shapes/#{File.basename(f, '.rb')}"
2129
+ end