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,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