wxruby3-shapes 0.9.0.pre.beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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,19 @@
1
+ # Core class extensions
2
+ # Copyright (c) M.J.N. Corino, The Netherlands
3
+
4
+ class ::Array
5
+
6
+ def resize(sz, obj=nil, &block)
7
+ if sz > self.size
8
+ if block
9
+ self.fill(sz-1, 0, &block)
10
+ else
11
+ self.fill(obj, sz-1, 0)
12
+ end
13
+ elsif sz < self.size
14
+ self.slice!(0, sz)
15
+ end
16
+ self
17
+ end
18
+
19
+ end
@@ -0,0 +1,659 @@
1
+ # Wx::SF::Diagram - diagram class
2
+ # Copyright (c) M.J.N. Corino, The Netherlands
3
+
4
+ require 'set'
5
+
6
+ require 'wx/shapes/serializable'
7
+ require 'wx/shapes/shape'
8
+ require 'wx/shapes/shape_canvas'
9
+
10
+ module Wx::SF
11
+
12
+ class Diagram
13
+
14
+ include Serializable
15
+
16
+ property shapes: :serialize_shapes
17
+ property :accepted_shapes, :accepted_top_shapes
18
+
19
+ # Search mode flags for get_shape_at_position method
20
+ SEARCHMODE = ShapeCanvas::SEARCHMODE
21
+
22
+ def initialize
23
+ @shapes = ShapeList.new
24
+ @shape_canvas = nil
25
+ @is_modified = false
26
+ @accepted_shapes = ::Set.new([ACCEPT_ALL])
27
+ @accepted_top_shapes = ::Set.new([ACCEPT_ALL])
28
+ end
29
+
30
+ # Returns the shape canvas.
31
+ # @return [Wx::SF::ShapeCanvas]
32
+ def get_shape_canvas
33
+ @shape_canvas
34
+ end
35
+ alias :shape_canvas :get_shape_canvas
36
+
37
+ # Set the shape canvas.
38
+ # @param [Wx::SF::ShapeCanvas] canvas
39
+ def set_shape_canvas(canvas)
40
+ @shape_canvas = canvas
41
+ end
42
+ alias :shape_canvas= :set_shape_canvas
43
+
44
+ # Get information about managed diagram's modification.
45
+ #
46
+ # The function returns TRUE if the diagram has been modified and its content
47
+ # should be saved. The modification flag is cleared when the content is saved.
48
+ # @return [Boolean] true if managed diagram is modified, otherwise false.
49
+ def is_modified
50
+ @is_modified
51
+ end
52
+ alias :modified? :is_modified
53
+
54
+ # Set diagram's modification flag manually.
55
+ # @param [Boolean] state State of diagram's modification flag.
56
+ def set_modified(state = true)
57
+ @is_modified = state
58
+ end
59
+ alias :modified= :set_modified
60
+
61
+ # Create new direct connection between two shapes.
62
+ #
63
+ # This function creates new simple connection line (without arrows) between given
64
+ # shapes.
65
+ # @overload create_connection(src_id, trg_id, save_state = true)
66
+ # @param [Wx::Serializable::ID] src_id id of a source shape
67
+ # @param [Wx::Serializable::ID] trg_id id of target shape
68
+ # @param [Boolean] save_state set the parameter true if you wish to save canvas state after the operation
69
+ # @return [Array(Wx::SF::ERRCODE, Wx::SF::Shape)] operation result and new connection object. the object is added to the shape canvas automatically.
70
+ # @overload create_connection(src_id, trg_id, line_info, save_state = true)
71
+ # @param [Wx::Serializable::ID] src_id id of a source shape
72
+ # @param [Wx::Serializable::ID] trg_id id of target shape
73
+ # @param [Class] line_info Connection type (any class inherited from Wx::SF::LineShape)
74
+ # @param [Boolean] save_state set the parameter true if you wish to save canvas state after the operation
75
+ # @return [Array(Wx::SF::ERRCODE, Wx::SF::Shape)] operation result and new connection object. the object is added to the shape canvas automatically.
76
+ # @overload create_connection(src_id, trg_id, line, save_state = true)
77
+ # @param [Wx::Serializable::ID] src_id id of a source shape
78
+ # @param [Wx::Serializable::ID] trg_id id of target shape
79
+ # @param [Wx::SF::LineShape] line the line shape
80
+ # @param [Boolean] save_state set the parameter true if you wish to save canvas state after the operation
81
+ # @return [Array(Wx::SF::ERRCODE, Wx::SF::Shape)] operation result and new connection object. the object is added to the shape canvas automatically.
82
+ # @see start_interactive_connection
83
+ def create_connection(src_id, trg_id, *rest)
84
+ err = shape = nil
85
+ if rest.first.is_a?(LineShape)
86
+ line = rest.shift
87
+ save_state = rest.empty? ? true : rest.shift
88
+ err = add_shape(line, nil, Wx::DEFAULT_POSITION, INITIALIZE, DONT_SAVE_STATE)
89
+ shape = line if err == ERRCODE::OK
90
+ else
91
+ line_type = (rest.empty? || !rest.first.is_a?(::Class)) ? LineShape : rest.shift
92
+ save_state = rest.empty? ? true : rest.shift
93
+ err, shape = create_shape(line_type, DONT_SAVE_STATE)
94
+ end
95
+ if shape
96
+ shape.set_src_shape_id(src_id)
97
+ shape.set_trg_shape_id(trg_id)
98
+
99
+ if @shape_canvas
100
+ @shape_canvas.save_canvas_state if save_state
101
+ shape.refresh
102
+ end
103
+ end
104
+ [err, shape]
105
+ end
106
+
107
+ # Create new shape and add it to the shape canvas.
108
+ # @overload create_shape(shape_info, save_state = true)
109
+ # @param [Class] shape_info Shape type
110
+ # @param [Boolean] save_state Set the parameter true if you wish to save canvas state after the operation
111
+ # @return [Array(Wx::SF::ERRCODE, Wx::SF::Shape)] operation result and new shape. the object is added to the shape canvas automatically.
112
+ # @overload create_shape(shape_info, pos, save_state = true)
113
+ # @param [Class] shape_info Shape type
114
+ # @param [Wx::Point] pos shape position
115
+ # @param [Boolean] save_state Set the parameter true if you wish to save canvas state after the operation
116
+ # @return [Array(Wx::SF::ERRCODE, Wx::SF::Shape)] operation result and new shape. the object is added to the shape canvas automatically.
117
+ def create_shape(shape_info, *rest)
118
+ pos = if rest.first.respond_to?(:to_point)
119
+ rest.shift.to_point
120
+ elsif @shape_canvas
121
+ clt_rect = @shape_canvas.get_client_rect
122
+ Wx::Point.new((clt_rect.right - clt_rect.left)/2,
123
+ (clt_rect.bottom - clt_rect.top)/2)
124
+ else
125
+ Wx::Point.new
126
+ end
127
+ save_state = rest.empty? ? true : rest.shift
128
+ if shape_info && is_shape_accepted(shape_info)
129
+ # create shape object from class info
130
+ shape = shape_info.new
131
+
132
+ parent_shape = nil
133
+ # update given position
134
+ lpos = pos;
135
+ lpos = @shape_canvas.fit_position_to_grid(@shape_canvas.dp2lp(pos)) if @shape_canvas
136
+ # line shapes can be assigned to root only
137
+ parent_shape = get_shape_at_position(lpos) unless shape.is_a?(LineShape)
138
+
139
+ if parent_shape && parent_shape.is_child_accepted(shape_info)
140
+ err = add_shape(shape, parent_shape, pos - parent_shape.get_absolute_position.to_point, INITIALIZE, save_state)
141
+ else
142
+ err = add_shape(shape, nil, pos, INITIALIZE, save_state)
143
+ end
144
+
145
+ parent_shape.update if parent_shape
146
+
147
+ [err, shape]
148
+ else
149
+ [ERRCODE::NOT_ACCEPTED, nil]
150
+ end
151
+ end
152
+
153
+ # Add an existing shape to the canvas.
154
+ # @param [Wx::SF::Shape] shape new shape
155
+ # @param [Wx::SF::Shape] parent the parent shape
156
+ # @param [Wx::Point] pos shape position
157
+ # @param [Boolean] initialize true if the shape should be reinitialized, otherwise false
158
+ # @param [Boolean] save_state true if the canvas state should be saved
159
+ # @return [Wx::SF::ERRCODE] operation result
160
+ def add_shape(shape, parent, pos, initialize, save_state = true)
161
+ if shape
162
+ if shape.is_a?(Shape) && is_shape_accepted(shape.class)
163
+ pos = pos.to_point
164
+ if @shape_canvas
165
+ new_pos = @shape_canvas.fit_position_to_grid(@shape_canvas.dp2lp(pos))
166
+ shape.set_relative_position(new_pos.to_real)
167
+ else
168
+ shape.set_relative_position(pos.to_real)
169
+ end
170
+
171
+ # add shape
172
+ if parent
173
+ shape.set_parent_shape(parent)
174
+ else
175
+ if is_top_shape_accepted(shape.class)
176
+ @shapes << shape
177
+ shape.set_diagram(self)
178
+ else
179
+ return ERRCODE::NOT_ACCEPTED
180
+ end
181
+ end
182
+
183
+ # initialize added shape
184
+ if initialize
185
+ shape.create_handles
186
+
187
+ shape.set_hover_colour(@shape_canvas.get_hover_colour) if @shape_canvas
188
+
189
+ if has_children(shape)
190
+ # get shape's children (if exist)
191
+ lst_children = shape.get_child_shapes(ANY, RECURSIVE)
192
+ # initialize shape's children
193
+ lst_children.each do |child|
194
+ child.create_handles
195
+ child.update
196
+
197
+ child.set_hover_colour(@shape_canvas.get_hover_colour) if @shape_canvas
198
+ end
199
+ end
200
+ end
201
+
202
+ # reset scale of assigned shape canvas (if exists and it is necessary...)
203
+ if @shape_canvas && shape.is_a?(ControlShape)
204
+ @shape_canvas.set_scale(1.0)
205
+ end
206
+
207
+ @shape_canvas.save_canvas_state if @shape_canvas && save_state
208
+
209
+ @is_modified = true
210
+
211
+ ERRCODE::OK
212
+ else
213
+ ERRCODE::NOT_ACCEPTED
214
+ end
215
+ else
216
+ ERRCODE::INVALID_INPUT
217
+ end
218
+ end
219
+
220
+ # Remove given shape from the shape canvas.
221
+ # @param [Wx::SF::Shape] shape shape object that should be deleted
222
+ # @param [Boolean] refresh Set the parameter to true if you wish to repaint the canvas
223
+ def remove_shape(shape, refresh = true)
224
+ return unless shape
225
+
226
+ parent = shape.get_parent_shape
227
+
228
+ # get all shape's children
229
+ lst_children = shape.get_child_shapes(ANY, RECURSIVE)
230
+ lst_children << shape # and shape itself
231
+
232
+ # retrieve all assigned lines
233
+ lst_connections = []
234
+ lst_children.each do |child|
235
+ get_assigned_connections(child, LineShape, Shape::CONNECTMODE::BOTH, lst_connections)
236
+ end
237
+
238
+ # remove all assigned lines
239
+ lst_removed_connections = []
240
+ lst_connections.each do |line|
241
+ # one connection may be used by the parent and also by his child
242
+ unless lst_removed_connections.include?(line)
243
+ lst_removed_connections << line
244
+ remove_shape(line,false)
245
+ end
246
+ end
247
+
248
+ # remove the shape and it's children from canvas cache and shape index list
249
+ lst_children.each do |child|
250
+ @shape_canvas.send(:remove_from_temporaries, shape) if @shape_canvas
251
+ end
252
+
253
+ # remove the shape
254
+ shape.set_parent_shape(nil) # also removes shape from parent if it had a parent
255
+ shape.set_diagram(nil)
256
+ @shapes.delete(shape)
257
+
258
+ @is_modified = true
259
+
260
+ parent.update if parent
261
+
262
+ @shape_canvas.refresh(false) if refresh && @shape_canvas
263
+ end
264
+
265
+ # Remove shapes from the shape canvas
266
+ # @param [Array<Wx::SF::Shape>] selection List of shapes which should be removed from the canvas
267
+ def remove_shapes(selection)
268
+ selection.each { |shape| remove_shape(shape, false) if contains?(shape) }
269
+ end
270
+
271
+ # Change shape's parent (possibly making it unparented i.e. toplevel)
272
+ # @param [Wx::SF::Shape] shape shape to reparent
273
+ # @param [Wx::SF::Shape,nil] parent new parent or nil
274
+ # @return [Wx::SF::Shape] re-parented shape
275
+ def reparent_shape(shape, parent)
276
+ prev_parent = shape.get_parent_shape
277
+ if prev_parent.nil? && parent
278
+ @shapes.delete(shape) # remove from top level list if the shape will become parented
279
+ elsif prev_parent && parent.nil?
280
+ @shapes << shape # add to toplevel shapes if the shape will become unparented
281
+ shape.set_diagram(self) # make sure the right diagram is set
282
+ end
283
+ shape.set_parent_shape(parent)
284
+ shape
285
+ end
286
+
287
+ # Returns true if the given shape is part of the diagram, false otherwise
288
+ # @param [Wx::SF::Shape] shape
289
+ # @return [Boolean]
290
+ def contains_shape(shape)
291
+ @shapes.include?(shape.id,true)
292
+ end
293
+ alias :contains_shape? :contains_shape
294
+ alias :contains? :contains_shape
295
+
296
+ # Remove all shapes from canvas
297
+ def clear
298
+ @shapes.clear
299
+ if @shape_canvas
300
+ @shape_canvas.get_multiselection_box.show(false)
301
+ @shape_canvas.update_virtual_size
302
+ end
303
+ end
304
+
305
+ # Move given shape to the end of the shapes list
306
+ # @param [Wx::SF::Shape] shape
307
+ def move_to_end(shape)
308
+ if (a_shape = @shapes.delete(shape))
309
+ @shapes << a_shape
310
+ end
311
+ end
312
+
313
+ # Move all shapes so none of it will be located in negative position
314
+ def move_shapes_from_negatives
315
+ min_x = min_y = 0.0
316
+
317
+ # find the maximal negative position value
318
+ shapes = get_shapes
319
+
320
+ shapes.each_with_index do |shape, ix|
321
+ shape_pos = shape.get_absolute_position
322
+ if ix == 0
323
+ min_x = shape_pos.x
324
+ min_y = shape_pos.y
325
+ else
326
+ min_x = shape_pos.x if shape_pos.x < min_x
327
+ min_y = shape_pos.y if shape_pos.y < min_y
328
+ end
329
+ end
330
+
331
+ # move all parents shape so they (and their children) will be located in the positive values only
332
+ if min_x < 0.0 || min_y < 0.0
333
+ shapes.each do |shape|
334
+ unless shape.get_parent_shape
335
+ shape.move_by(min_x.to_i.abs, 0) if min_x < 0.0
336
+ shape.move_by(0, min_y.to_i.abs) if min_y < 0.0
337
+ end
338
+ end
339
+ end
340
+ end
341
+
342
+ # Update all shapes in the diagram manager
343
+ def update_all
344
+ get_shapes.each { |shape| shape.update unless shape.has_children? }
345
+ end
346
+
347
+ # Add given shape type to an acceptance list. The acceptance list contains class
348
+ # names of the shapes which can be inserted into this instance of shapes canvas.
349
+ # Note: Constant value {Wx::SF::ACCEPT_ALL} behaves like any class.
350
+ # @param [Class] type Class of accepted shape object
351
+ # @see is_shape_accepted
352
+ def accept_shape(type)
353
+ ::Kernel.raise ArgumentError, 'Class or ACCEPT_ALL expected' unless type.is_a?(::Class) || type == ACCEPT_ALL
354
+ @accepted_shapes << type
355
+ end
356
+
357
+ # Tells whether the given shape type is accepted by this canvas instance (it means
358
+ # whether this shape can be inserted into it).
359
+ #
360
+ # The function is typically used by the framework for determination whether class type supplied
361
+ # by add_shape or create_shape function can be inserted into shape canvas.
362
+ # @param [Class] type Class of examined shape object
363
+ # @return [Boolean] true if the shape type is accepted, otherwise false.
364
+ def is_shape_accepted(type)
365
+ @accepted_shapes.include?(type) || @accepted_shapes.include?(ACCEPT_ALL)
366
+ end
367
+ alias :shape_accepted? :is_shape_accepted
368
+
369
+ # Clear shape object acceptance list
370
+ # @see accept_shape
371
+ def clear_accepted_shapes
372
+ @accepted_shapes.clear
373
+ end
374
+
375
+ # Get reference to shape acceptance list
376
+ # @return [Set<String>]
377
+ def get_accepted_shapes
378
+ @accepted_shapes
379
+ end
380
+ alias :accepted_shapes :get_accepted_shapes
381
+
382
+ # Add given shape type to list of accepted top shapes. The acceptance list contains class
383
+ # names of the shapes which can be inserted into this instance of shapes canvas as a shape without
384
+ # any parent (i.e. shape placed directly onto the canvas).
385
+ # Note: Constant value {Wx::SF::ACCEPT_ALL} behaves like any class.
386
+ # @param [Class] type Class of accepted shape object
387
+ # @see is_top_shape_accepted
388
+ def accept_top_shape(type)
389
+ ::Kernel.raise ArgumentError, 'Class or ACCEPT_ALL expected' unless type.is_a?(::Class) || type == ACCEPT_ALL
390
+ @accepted_top_shapes << type
391
+ end
392
+
393
+ # Tells whether the given shape type is accepted by this canvas instance as a top shape
394
+ # (it means whether this shape can be inserted directly into it without any parent).
395
+ #
396
+ # The function is typically used by the framework for determination whether class type supplied
397
+ # by add_shape or create_shape function can be inserted directly onto shape canvas.
398
+ # @param [Class] type Class of examined shape object
399
+ # @return [Boolean] true if the shape type is accepted, otherwise false.
400
+ def is_top_shape_accepted(type)
401
+ @accepted_top_shapes.include?(type) || @accepted_top_shapes.include?(ACCEPT_ALL)
402
+ end
403
+ alias :top_shape_accepted? :is_top_shape_accepted
404
+
405
+ # Clear top shapes acceptance list
406
+ # @see accept_shape
407
+ def clear_accepted_top_shapes
408
+ @accepted_top_shapes.clear
409
+ end
410
+
411
+ # Get reference to top shapes acceptance list
412
+ # @return [Set<String>]
413
+ def get_accepted_top_shapes
414
+ @accepted_top_shapes
415
+ end
416
+ alias :accepted_top_shapes :get_accepted_top_shapes
417
+
418
+ # Find shape with given ID.
419
+ # @param [Wx::SF::Serializable::ID] id Shape's ID
420
+ # @return [Wx::SF::Shape] shape if exists, otherwise nil
421
+ def find_shape(id)
422
+ @shapes.get(id, true)
423
+ end
424
+
425
+ # Get list of connections assigned to given parent shape.
426
+ # @param [Wx::SF::Shape] parent parent shape
427
+ # @param [Class] shape_info Line object type
428
+ # @param [Wx::SF::Shape::CONNECTMODE] mode Search mode
429
+ # @param [Array<Wx::SF::Shape>] lines shape list where all found connections will be stored
430
+ # @return [Array<Wx::SF::Shape>] shape list
431
+ # @see Wx::SF::Shape::CONNECTMODE
432
+ def get_assigned_connections(parent, shape_info, mode, lines = [])
433
+ return lines unless parent && parent.get_id
434
+
435
+ # lines are all toplevel so we do not have to search recursively...
436
+ lst_lines = @shapes.select { |shape| shape.is_a?(shape_info) }
437
+
438
+ lst_lines.each do |line|
439
+ case mode
440
+ when Shape::CONNECTMODE::STARTING
441
+ lines << line if line.get_src_shape_id == parent.get_id
442
+ when Shape::CONNECTMODE::ENDING
443
+ lines << line if line.get_trg_shape_id == parent.get_id
444
+ when Shape::CONNECTMODE::BOTH
445
+ lines << line if line.get_src_shape_id == parent.get_id || line.get_trg_shape_id == parent.get_id
446
+ end
447
+ end
448
+ lines
449
+ end
450
+
451
+ # Returns the list of top level shapes
452
+ def get_top_shapes
453
+ @shapes
454
+ end
455
+
456
+ def get_all_shapes
457
+ @shapes.all
458
+ end
459
+
460
+ # Get list of shapes of given type.
461
+ # @param [Class] shape_info Line object type
462
+ # @param [Shape::SEARCHMODE] mode Search algorithm
463
+ # @param [Array<Wx::SF::Shape>] shapes shape list where all found shapes will be stored
464
+ # @return [Array<Wx::SF::Shape>] shape list
465
+ # @see Shape::SEARCHMODE
466
+ def get_shapes(shape_info = Wx::SF::Shape, mode = Shape::SEARCHMODE::BFS, shapes = [])
467
+ @shapes.each do |shape|
468
+ shapes << shape if shape.is_a?(shape_info)
469
+ shape.get_children_recursively(shape_info, mode, shapes) if mode == Shape::SEARCHMODE::DFS
470
+ end
471
+ if mode == Shape::SEARCHMODE::BFS
472
+ @shapes.each { |shape| shape.get_children_recursively(shape_info, mode, shapes) }
473
+ end
474
+ shapes
475
+ end
476
+
477
+ # Get shape at given logical position
478
+ # @param [Wx::Point] pos Logical position
479
+ # @param [Integer] zorder Z-order of searched shape (useful if several shapes are located at the given position)
480
+ # @param [SEARCHMODE] mode Search mode
481
+ # @return [Wx::SF::Shape] shape if found, otherwise nil
482
+ # @see SEARCHMODE
483
+ # @see Wx::SF::ShapeCanvas::dp2lp
484
+ # @see Wx::SF::ShapeCanvas#get_shape_under_cursor
485
+ def get_shape_at_position(pos, zorder = 1, mode = SEARCHMODE::BOTH)
486
+ # sort shapes list in the way that the line shapes will be at the top of the list
487
+ # and all non-line shapes get listed in reversed order as returned from get_shapes (for z order)
488
+ ins_pos = 0
489
+ pos = pos.to_point
490
+ shapes = get_shapes.inject([]) do |list, shape|
491
+ if shape.is_a?(LineShape)
492
+ list.prepend(shape)
493
+ ins_pos += 1
494
+ else
495
+ list.insert(ins_pos, shape)
496
+ end
497
+ list
498
+ end
499
+ # find the topmost shape according to the given rules
500
+ counter = 1
501
+ shapes.each do |shape|
502
+ if shape.visible? && shape.active? && shape.contains?(pos)
503
+ case mode
504
+ when SEARCHMODE::SELECTED
505
+ if shape.selected?
506
+ return shape if counter == zorder
507
+ counter += 1
508
+ end
509
+ when SEARCHMODE::UNSELECTED
510
+ unless shape.selected?
511
+ return shape if counter == zorder
512
+ counter += 1
513
+ end
514
+ when SEARCHMODE::BOTH
515
+ return shape if counter == zorder
516
+ counter += 1
517
+ end
518
+ end
519
+ end
520
+
521
+ nil
522
+ end
523
+
524
+ # Get list of all shapes located at given position
525
+ # @param [Wx::Point] pos Logical position
526
+ # @param [Array<Wx::SF::Shape>] shapes shape list where all found shapes will be stored
527
+ # @return [Array<Wx::SF::Shape>] shape list
528
+ # @see Wx::SF::ShapeCanvas::dp2lp
529
+ def get_shapes_at_position(pos, shapes = [])
530
+ pos = pos.to_point
531
+ get_shapes.each do |shape|
532
+ shapes << shape if shape.visible? && shape.active? && shape.contains?(pos)
533
+ end
534
+ end
535
+
536
+ # Get list of shapes located inside given rectangle
537
+ # @param [Wx::Rect] rct Examined rectangle
538
+ # @param [Array<Wx::SF::Shape>] shapes shape list where all found shapes will be stored
539
+ # @return [Array<Wx::SF::Shape>] shape list
540
+ def get_shapes_inside(rct, shapes = [])
541
+ get_shapes.each do |shape|
542
+ shapes << shape if shape.visible? && shape.active? && shape.intersects?(rct)
543
+ end
544
+ end
545
+
546
+ # Determines whether the diagram contains some shapes.
547
+ # @return true if there are no shapes in the diagram, otherwise false
548
+ def is_empty
549
+ @shapes.empty?
550
+ end
551
+ alias :empty? :is_empty
552
+
553
+ # Function finds out whether given shape has some children.
554
+ # @param [Wx::SF::Shape] parent potential parent shape
555
+ # @return [Boolean] true if the parent shape has children, otherwise false
556
+ def has_children(parent)
557
+ parent.has_children?
558
+ end
559
+ alias :has_children? :has_children
560
+
561
+ # Get neighbour shapes connected to given parent shape.
562
+ # @param [Wx::SF::Shape] parent parent shape (can be nil for all topmost shapes)
563
+ # @param [Class] shape_info Line object type
564
+ # @param [Wx::SF::Shape::CONNECTMODE] condir Connection direction
565
+ # @param [Boolean] direct set this flag to true if only closest shapes should be found
566
+ # otherwise also shapes connected by forked lines will be found (also
567
+ # constants DIRECT and INDIRECT can be used)
568
+ # @param [Array<Wx::SF::Shape>] neighbours List to add neighbour shapes to
569
+ # @return [Array<Wx::SF::Shape>] shape list
570
+ # @see Wx::SF::Shape::CONNECTMODE
571
+ def get_neighbours(parent, shape_info, condir, direct = true, neighbours = [])
572
+ if parent
573
+ parent.get_neighbours(shape_info, condir, direct, neighbours)
574
+ else
575
+ @shapes.each do |shape|
576
+ shape.get_neighbours(shape_info, condir, direct, neighbours)
577
+ end
578
+ end
579
+ end
580
+
581
+ private
582
+
583
+ # Update connection shapes after importing/dropping of new shapes
584
+ def check_new_shapes(new_shapes)
585
+ # deserializing will create unique ids synchronized across all deserialized shapes
586
+ # lines and both connected shapes should have matching ids
587
+ # we will remove any lines for which one or both connected shapes are missing (not copied)
588
+ new_shapes.select! do |shape|
589
+ if shape.is_a?(LineShape)
590
+ # so that lines with both connected shapes will have matching ids
591
+ # we will remove any lines for which one or both connected shapes are missing (not copied)
592
+ if @shapes.include?(shape.get_src_shape_id) && @shapes.include?(shape.get_trg_shape_id)
593
+ shape.create_handles
594
+ true # keep
595
+ else
596
+ # remove from diagram
597
+ @shapes.delete(shape)
598
+ false # remove from new_shapes
599
+ end
600
+ else
601
+ true # keep
602
+ end
603
+ end
604
+ # deserializing will create unique ids synchronized across all deserialized shapes
605
+ # so that grids and shapes linked to it's cells should have matching ids
606
+ # we will clear any cells for which shapes are missing (not copied)
607
+ update_grids(new_shapes) unless new_shapes.empty?
608
+ end
609
+
610
+ # Update grid shapes after importing/dropping of new shapes
611
+ def update_grids(new_shapes)
612
+ # deserializing will create unique ids synchronized across all deserialized shapes
613
+ # so that grids and shapes linked to it's cells will have matching ids
614
+ # we will clear any cells for which shapes are missing (not copied)
615
+ new_shapes.each do |shape|
616
+ if shape.is_a?(GridShape)
617
+ grid.each_cell do |row, col, id|
618
+ grid.clear_cell(row, col) unless id.nil? || @shapes.include?(id)
619
+ end
620
+ elsif shape.has_children?
621
+ shape.get_children_recursively(nil, Shape::SEARCHMODE::DFS).each do |child|
622
+ if shape.is_a?(GridShape)
623
+ grid.each_cell do |row, col, id|
624
+ grid.clear_cell(row, col) unless id.nil? || @shapes.include?(id)
625
+ end
626
+ end
627
+ end
628
+ end
629
+ end
630
+ end
631
+
632
+ # Shape lis (de-)serialization
633
+ def serialize_shapes(*arg)
634
+ unless arg.empty?
635
+ @shapes = arg.shift
636
+ @shapes.each { |shape| shape.set_diagram(self); shape.create_handles }
637
+ end
638
+ @shapes
639
+ end
640
+
641
+ # Set accepted shapes. Deserialization only.
642
+ # @param [Array<String>] shp_names
643
+ def set_accepted_shapes(shp_names)
644
+ @accepted_shapes.merge(shp_names.collect { |e| e.is_a?(::String) ? ::Object.const_get(e) : e })
645
+ end
646
+
647
+ # Set accepted top shapes. Deserialization only.
648
+ # @param [Array<String>] shp_names
649
+ def set_accepted_top_shapes(shp_names)
650
+ @accepted_top_shapes.merge(shp_names.collect { |e| e.is_a?(::String) ? ::Object.const_get(e) : e })
651
+ end
652
+
653
+ public def inspect
654
+ "#<Wx::SF::Diagram:#{object_id}>"
655
+ end
656
+
657
+ end
658
+
659
+ end