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,358 @@
1
+ # Wx::SF - layout algorithm classes
2
+ # Copyright (c) M.J.N. Corino, The Netherlands
3
+
4
+ module Wx::SF
5
+
6
+ # Base class for all layouting algorithms.
7
+ class LayoutAlgorithm
8
+
9
+ # Function performing the layout change. All derived classes must implement it.
10
+ # @param [Array<Shape>] shapes List of shapes which should be layout-ed
11
+ def do_layout(shapes)
12
+ ::Kernel.raise NotImplementedError
13
+ end
14
+
15
+ protected
16
+
17
+ # Calculate bounding box surrounding given shapes.
18
+ # @param [Array<Shape>] shapes List of shapes
19
+ # @return [Wx::Rect] Bounding box
20
+ def get_bounding_box(shapes)
21
+ shapes.inject(nil) do |rct_bb, shape|
22
+ rct_bb ? rct_bb.union!(shape.get_bounding_box) : shape.get_bounding_box
23
+ end
24
+ end
25
+
26
+ # Get overall extent of all given shapes calculated as a sum of their width and height.
27
+ # @param [Array<Shape>] shapes List of shapes
28
+ # @return [Wx::Size] Overall shapes extent
29
+ def get_shapes_extent(shapes)
30
+ shapes.inject(Wx::Size.new) do |ext_sz, shape|
31
+ sh_bb = shape.get_bounding_box
32
+ ext_sz.width += sh_bb.width
33
+ ext_sz.height += sh_bb.height
34
+ ext_sz
35
+ end
36
+ end
37
+
38
+ # Get center point of given shapes.
39
+ # @param [Array<Shape>] shapes List of shapes
40
+ # @return [Wx::RealPoint] Center point
41
+ def get_shapes_center(shapes)
42
+ center = shapes.inject(Wx::RealPoint.new) do |centre, shape|
43
+ sh_pos = shape.get_absolute_position
44
+ centre.x += sh_pos.x
45
+ centre.y += sh_pos.y
46
+ centre
47
+ end
48
+ center.x /= shapes.size
49
+ center.y /= shapes.size
50
+ center
51
+ end
52
+
53
+ # Get top-left point of bounding box surrounding given shapes.
54
+ # @param [Array<Shape>] shapes List of shapes
55
+ # @return [Wx::RealPoint] Top-left point of bounding box surrounding given shapes
56
+ def get_top_left(shapes)
57
+ shapes.inject(Wx::RealPoint.new(::Float::MAX, ::Float::MAX)) do |pos, shape|
58
+ sh_pos = shape.get_absolute_position
59
+ pos.x = sh_pos.x if sh_pos.x < pos.x
60
+ pos.y = sh_pos.y if sh_pos.y < pos.y
61
+ pos
62
+ end
63
+ end
64
+
65
+ end
66
+
67
+
68
+ # pre-defined algorithms ###########################
69
+
70
+ # Class encapsulating algorithm which layouts all top-most shapes into circle registered under "Circle" name.
71
+ # The algorithm doesn't optimize connection lines crossing.
72
+ class LayoutCircle < LayoutAlgorithm
73
+
74
+ # Constructor.
75
+ def initialize
76
+ @distance_ratio = 1.0
77
+ end
78
+
79
+ # Get or set ratio in which calculated distance between shapes will be reduced. Values less than
80
+ # 1 means that the distance will be smaller, values bigger than 1 means that the distance will be
81
+ # bigger.
82
+ attr_accessor :distance_ratio
83
+
84
+ # Function performing the layout change.
85
+ # @param [Array<Shape>] shapes List of shapes which should be layout-ed
86
+ def do_layout(shapes)
87
+ size_shapes = get_shapes_extent(shapes)
88
+ center = get_shapes_center(shapes)
89
+
90
+ # double x, y
91
+ step = 360.0 / shapes.size
92
+ degree = 0
93
+ rx = (size_shapes.x / 2) * @distance_ratio
94
+ ry = (size_shapes.y / 2) * @distance_ratio
95
+
96
+ shapes.each do |shape|
97
+ x = center.x + Math.cos(degree * Math::PI / 180 ) * rx
98
+ y = center.y + Math.sin( degree * Math::PI / 180 ) * ry
99
+ degree += step
100
+ shape.move_to(x, y)
101
+ end
102
+ end
103
+
104
+ end
105
+
106
+ # Class encapsulating algorithm which layouts all top-most shapes into vertical tree registered under "Vertical Tree" name.
107
+ class LayoutVerticalTree < LayoutAlgorithm
108
+
109
+ # Constructor.
110
+ def initialize
111
+ @h_space = @v_space = 30.0
112
+ end
113
+
114
+ # Get or set horizontal space between shapes.
115
+ attr_accessor :h_space
116
+
117
+ # Get or set vertical space between shapes.
118
+ attr_accessor :v_space
119
+
120
+ # Function performing the layout change.
121
+ # @param [Array<Shape>] shapes List of shapes which should be layout-ed
122
+ def do_layout(shapes)
123
+ start = get_top_left(shapes)
124
+ min_x = start.x
125
+ # find root items
126
+ shapes.each do |shape|
127
+ lst_connections = shape.get_assigned_connections(LineShape, Shape::CONNECTMODE::ENDING)
128
+
129
+ min_x, _ = process_node(shape, start.y, min_x, 0) if lst_connections.empty?
130
+ end
131
+ end
132
+
133
+ protected
134
+
135
+ # Process single shape.
136
+ # @param [Wx::Shape] node processed shape.
137
+ # @param [Float] y Vertical position of the shape.
138
+ # @param [Float] min_x
139
+ # @param [Integer] curr_max_width
140
+ # @return [Array(Float, Integer)]
141
+ def process_node(node, y, min_x, curr_max_width)
142
+ if node
143
+ node.move_to(min_x, y)
144
+
145
+ rct_bb = node.get_bounding_box
146
+ curr_max_width = rct_bb.width if rct_bb.width > curr_max_width
147
+
148
+ lst_neighbours = node.get_neighbours(Shape, Shape::CONNECTMODE::STARTING)
149
+
150
+ if lst_neighbours.empty?
151
+ min_x += curr_max_width + @h_space
152
+ else
153
+ lst_neighbours.each do |nbs|
154
+ unless nbs.get_parent_shape
155
+ min_x, curr_max_width = process_node(nbs, y + rct_bb.height + @v_space, min_x, curr_max_width)
156
+ end
157
+ end
158
+ end
159
+ end
160
+ [min_x, curr_max_width]
161
+ end
162
+
163
+ end
164
+
165
+ # Class encapsulating algorithm which layouts all top-most shapes into horizontal tree registered under "Horizontal Tree" name.
166
+ class LayoutHorizontalTree < LayoutAlgorithm
167
+
168
+ # Constructor.
169
+ def initialize
170
+ @h_space = @v_space = 30.0
171
+ end
172
+
173
+ # Get or set horizontal space between shapes.
174
+ attr_accessor :h_space
175
+
176
+ # Get or set vertical space between shapes.
177
+ attr_accessor :v_space
178
+
179
+ # Function performing the layout change.
180
+ # @param [Array<Shape>] shapes List of shapes which should be layout-ed
181
+ def do_layout(shapes)
182
+ start = get_top_left(shapes)
183
+ min_y = start.y
184
+ # find root items
185
+ shapes.each do |shape|
186
+ lst_connections = shape.get_assigned_connections(LineShape, Shape::CONNECTMODE::ENDING)
187
+
188
+ min_y, _ = process_node(shape, start.x, min_y, 0) if lst_connections.empty?
189
+ end
190
+ end
191
+
192
+ protected
193
+
194
+ # Process single shape.
195
+ # @param [Wx::Shape] node processed shape.
196
+ # @param [Float] x Vertical position of the shape.
197
+ # @param [Float] min_y
198
+ # @param [Integer] curr_max_height
199
+ # @return [Array(Float, Integer)]
200
+ def process_node(node, x, min_y, curr_max_height)
201
+ if node
202
+ node.move_to(x, min_y)
203
+
204
+ rct_bb = node.get_bounding_box
205
+ curr_max_height = rct_bb.height if rct_bb.height > curr_max_height
206
+
207
+ lst_neighbours = node.get_neighbours(Shape, Shape::CONNECTMODE::STARTING)
208
+
209
+ if lst_neighbours.empty?
210
+ min_y += curr_max_height + @v_space
211
+ else
212
+ lst_neighbours.each do |nbs|
213
+ unless nbs.get_parent_shape
214
+ min_y, curr_max_height = process_node(nbs, x + rct_bb.width + @h_space, min_y, curr_max_height)
215
+ end
216
+ end
217
+ end
218
+ end
219
+ [min_y, curr_max_height]
220
+ end
221
+
222
+ end
223
+
224
+ # Class encapsulating algorithm which layouts all top-most shapes into mesh registered under "Mesh" name.
225
+ # The algorithm doesn't optimize connection lines crossing.
226
+ class LayoutMesh < LayoutAlgorithm
227
+
228
+ # Constructor.
229
+ def initialize
230
+ @h_space = @v_space = 30.0
231
+ end
232
+
233
+ # Get or set horizontal space between shapes.
234
+ attr_accessor :h_space
235
+
236
+ # Get or set vertical space between shapes.
237
+ attr_accessor :v_space
238
+
239
+ # Function performing the layout change.
240
+ # @param [Array<Shape>] shapes List of shapes which should be layout-ed
241
+ def do_layout(shapes)
242
+ cols = Math.sqrt(shapes.size).floor
243
+ max_h = -@h_space
244
+ roffset = coffset = 0
245
+
246
+ start = get_top_left(shapes)
247
+
248
+ shapes.each_with_index do |shape, i|
249
+ if (i % cols) == 0
250
+ coffset = 0
251
+ roffset += max_h + @h_space
252
+ max_h = 0
253
+ end
254
+
255
+ shape.move_to(start.x + coffset, start.y + roffset)
256
+
257
+ rct_bb = shape.get_bounding_box
258
+ coffset += rct_bb.width + @v_space
259
+
260
+ max_h = rct_bb.height if rct_bb.height > max_h
261
+ end
262
+ end
263
+
264
+ end
265
+
266
+ # Module implements automatic diagram layout. The module allows to automatically layout shapes
267
+ # included in diagram manager/shape canvas/list of shapes by using several pre-defined layouting
268
+ # algorithms. The module should be used as it is.
269
+ module AutoLayout
270
+
271
+ class << self
272
+
273
+ def layout_algorithms_table
274
+ @layout_algorithms ||= {}
275
+ end
276
+ private :layout_algorithms_table
277
+
278
+ def register_layout_algorithm(name, klass)
279
+ layout_algorithms_table[name.to_s] = klass
280
+ end
281
+
282
+ def get_layout_algorithm(name)
283
+ layout_algorithms_table.has_key?(name.to_s) ? layout_algorithms_table[name.to_s].new : nil
284
+ end
285
+
286
+ def layout_algorithms
287
+ layout_algorithms_table.keys
288
+ end
289
+
290
+ def each_layout_algorithm(&block)
291
+ if block
292
+ layout_algorithms_table.each_value { |klass| block.call(klass.new) }
293
+ else
294
+ ::Enumerator.new { |y| layout_algorithms_table.each_value { |klass| y << klass.new } }
295
+ end
296
+ end
297
+
298
+ # @overload layout(shapes, algname)
299
+ # Layout shapes included in given list.
300
+ # @param [Array<Shape>] shapes List of shapes
301
+ # @param [String] algname Algorithm name
302
+ # @return [Boolean] true if layout algorithm was found and executed, false otherwise
303
+ # @overload layout(diagram, algname)
304
+ # Layout shapes included in given diagram.
305
+ # @param [Diagram] diagram Reference to diagram
306
+ # @param [String] algname Algorithm name
307
+ # @return [Boolean] true if layout algorithm was found and executed, false otherwise
308
+ # @overload layout(canvas, algname)
309
+ # Layout shapes included in given shape canvas.
310
+ # @param [ShapeCanvas] canvas Reference to shape canvas
311
+ # @param [String] algname Algorithm name
312
+ # @return [Boolean] true if layout algorithm was found and executed, false otherwise
313
+ def layout(shapes, algname)
314
+ if shapes.is_a?(::Array)
315
+ alg = get_layout_algorithm(algname)
316
+ if alg
317
+ shapes.first.get_diagram.set_modified unless shapes.empty? || shapes.first.get_diagram.nil?
318
+ alg.do_layout(shapes)
319
+ return true
320
+ end
321
+ elsif shapes.is_a?(Diagram) || shapes.is_a?(ShapeCanvas)
322
+ alg = get_layout_algorithm(algname)
323
+ if alg
324
+ diagram = shapes.is_a?(Diagram) ? shapes : shapes.get_diagram
325
+ # layout all top level shapes excluding the line shapes
326
+ alg.do_layout(diagram.get_top_shapes.reject { |shp| shp.is_a?(LineShape) })
327
+ diagram.move_shapes_from_negatives
328
+ diagram.set_modified
329
+ update_canvas(diagram.get_shape_canvas) if diagram.get_shape_canvas
330
+ return true
331
+ end
332
+ else
333
+ ::Kernel.raise ArgumentError, 'Expected array, diagram or canvas'
334
+ end
335
+ false
336
+ end
337
+
338
+ protected
339
+
340
+ # Update given shape canvas.
341
+ # @param [ShapeCanvas] canvas
342
+ def update_canvas(canvas)
343
+ canvas.center_shapes
344
+ canvas.update_virtual_size
345
+ canvas.update_multiedit_size
346
+ canvas.refresh(false)
347
+ end
348
+
349
+ end
350
+
351
+ register_layout_algorithm('Circle', LayoutCircle)
352
+ register_layout_algorithm('Mesh', LayoutMesh)
353
+ register_layout_algorithm('Horizontal Tree', LayoutHorizontalTree)
354
+ register_layout_algorithm('Vertical Tree', LayoutVerticalTree)
355
+
356
+ end
357
+
358
+ end
@@ -0,0 +1,33 @@
1
+ # Wx::SF base definitions
2
+ # Copyright (c) M.J.N. Corino, The Netherlands
3
+
4
+ module Wx::SF
5
+
6
+ class SFException < ::Exception; end
7
+
8
+ RECURSIVE = true
9
+ NORECURSIVE = false
10
+ DIRECT = true
11
+ INDIRECT = false
12
+ WITHCHILDREN = true
13
+ WITHOUTCHILDREN = false
14
+ ANY = nil
15
+ DELAYED = true
16
+ ACCEPT_ALL = :*
17
+
18
+ INCLUDE_PARENTS = true
19
+ WITHOUT_PARENTS = false
20
+ INITIALIZE = true
21
+ DONT_INITIALIZE = false
22
+
23
+ DEFAULT_ME_OFFSET = 5
24
+ SAVE_STATE = true
25
+ DONT_SAVE_STATE = false
26
+ FROM_PAINT = true
27
+ NOT_FROM_PAINT = false
28
+ PROMPT = true
29
+ NO_PROMPT = false
30
+ WITH_BACKGROUND = true
31
+ WITHOUT_BACKGROUND = false
32
+
33
+ end
@@ -0,0 +1,84 @@
1
+ # Wx::SF::CanvasHistory - canvas history class
2
+ # Copyright (c) M.J.N. Corino, The Netherlands
3
+
4
+ module Wx::SF
5
+
6
+ class CanvasHistory
7
+
8
+ module DEFAULT
9
+ MAX_CANVAS_STATES = 25
10
+ end
11
+
12
+ # Constructor.
13
+ def initialize
14
+ @canvas_states = []
15
+ @current_state = nil
16
+ @current_state_index = nil
17
+ @max_states = DEFAULT::MAX_CANVAS_STATES
18
+ end
19
+
20
+ attr_accessor :max_states
21
+
22
+ # Save current canvas state.
23
+ # @param [String] state serialized diagram state
24
+ def save_canvas_state(state)
25
+ # delete all states newer than the current state
26
+ if @current_state
27
+ @canvas_states.slice!(@current_state_index+1, @canvas_states.size)
28
+ end
29
+
30
+ # append new canvas state
31
+ @current_state_index = @canvas_states.size
32
+ @canvas_states << (@current_state = state)
33
+
34
+ # check the history bounds
35
+ if @canvas_states.size > @max_states
36
+ @canvas_states.shift
37
+ @current_state_index -= 1
38
+ end
39
+ end
40
+
41
+ # Perform the 'Undo' operation.
42
+ # @return [String] state to undo
43
+ def restore_older_state
44
+ return nil unless @current_state && @current_state_index>0
45
+
46
+ # move to previous canvas state and restore
47
+ @current_state_index -= 1
48
+ @current_state = @canvas_states[@current_state_index]
49
+ end
50
+
51
+ # Perform the 'Redo' operation.
52
+ # @return [String] state to redo
53
+ def restore_newer_state
54
+ return nil unless @current_state && @current_state_index<(@canvas_states.size-1)
55
+
56
+ # move to next canvas state and restore
57
+ @current_state_index += 1
58
+ @current_state = @canvas_states[@current_state_index]
59
+ end
60
+
61
+ # Clear all canvas history.
62
+ def clear
63
+ @canvas_states.clear
64
+ @current_state = nil
65
+ @current_state_index = nil
66
+ end
67
+
68
+ # The function gives information whether the 'Undo' operation is available
69
+ # (exists any stored canvas state older than the current one.
70
+ # @return [Boolean] true if the 'undo' operation can be performed, otherwise false
71
+ def can_undo
72
+ @current_state && @current_state_index>0
73
+ end
74
+
75
+ # The function gives information whether the 'redo' operation is available
76
+ # (exists any stored canvas state newer than the current one.
77
+ # @return true if the 'redo' operation can be performed, otherwise false
78
+ def can_redo
79
+ @current_state && @current_state_index < (@canvas_states.size - 1)
80
+ end
81
+
82
+ end
83
+
84
+ end
@@ -0,0 +1,238 @@
1
+ # Wx::SF::ConnectionPoint - shape connection point class
2
+ # Copyright (c) M.J.N. Corino, The Netherlands
3
+
4
+ require 'wx/shapes/serializable'
5
+
6
+ module Wx::SF
7
+
8
+ class ConnectionPoint
9
+
10
+ include Wx::SF::Serializable
11
+
12
+ property :type
13
+ property :ortho_direction
14
+ property :relative_position
15
+
16
+ RADIUS = 3
17
+
18
+ # Connection point type
19
+ class CPTYPE < Wx::Enum
20
+ UNDEF = self.new(0)
21
+ TOPLEFT = self.new(1)
22
+ TOPMIDDLE = self.new(2)
23
+ TOPRIGHT = self.new(3)
24
+ CENTERLEFT = self.new(4)
25
+ CENTERMIDDLE = self.new(5)
26
+ CENTERRIGHT = self.new(6)
27
+ BOTTOMLEFT = self.new(7)
28
+ BOTTOMMIDDLE = self.new(8)
29
+ BOTTOMRIGHT = self.new(9)
30
+ CUSTOM = self.new(10)
31
+ end
32
+
33
+ # Direction of orthogonal connection
34
+ class CPORTHODIR < Wx::Enum
35
+ UNDEF = self.new(1)
36
+ HORIZONTAL = self.new(2)
37
+ VERTICAL = self.new(3)
38
+ end
39
+
40
+ module DEFAULT
41
+ # Default value of Wx::SF::ConnectionPoint @rel_position data member
42
+ RELPOS = Wx::RealPoint.new(0, 0)
43
+ # Default value of Wx::SF::ConnectionPoint @ortho_dir data member
44
+ ORTHODIR = CPORTHODIR::UNDEF
45
+ end
46
+
47
+ # Constructor
48
+ # @overload initialize()
49
+ # default ctor for deserialize
50
+ # @overload initialize(parent, pos, id=nil)
51
+ # @param [Wx::SF::Shape] parent parent shape
52
+ # @param [Wx::RealPoint,Array(Float, Float)] pos relative position in percentages
53
+ # @param [Integer] id point id
54
+ # @overload initialize(parent, type)
55
+ # @param [Wx::SF::Shape] parent parent shape
56
+ # @param [Wx::SF::ConnectionPoint::CPTYPE] type connection point type
57
+ def initialize(*args)
58
+ @parent_shape, type_or_pos, id = *args
59
+ if type_or_pos # allow parent to be nil (mainly for testing purposes)
60
+ if CPTYPE === type_or_pos
61
+ @type = type_or_pos
62
+ @rel_position = DEFAULT::RELPOS
63
+ @id = nil
64
+ elsif Wx::RealPoint === type_or_pos || Array === type_or_pos
65
+ @type = CPTYPE::CUSTOM
66
+ @rel_position = Wx::RealPoint === type_or_pos ? type_or_pos : Wx::RealPoint.new(*type_or_pos)
67
+ @id = id
68
+ else
69
+ ::Kernel.raise ArgumentError, 'Invalid arguments'
70
+ end
71
+ @ortho_dir = DEFAULT::ORTHODIR
72
+ @mouse_over = false
73
+ end
74
+ end
75
+
76
+ # Get connection point type.
77
+ # @return [CPTYPE] Connection point type
78
+ def get_type
79
+ @type
80
+ end
81
+ alias :type :get_type
82
+
83
+ # Set direction of orthogonal line's connection.
84
+ # @param [CPORTHODIR] dir Required direction
85
+ # @see CPORTHODIR
86
+ def set_ortho_direction(dir)
87
+ @ortho_dir = dir
88
+ end
89
+ alias :ortho_direction= :set_ortho_direction
90
+
91
+ # Get direction of orthogonal line's connection.
92
+ # @return [CPORTHODIR] Current direction
93
+ # @see CPORTHODIR
94
+ def get_ortho_direction
95
+ @ortho_dir
96
+ end
97
+ alias :ortho_direction :get_ortho_direction
98
+
99
+ # Set parent shape.
100
+ # @param [Wx::SF::Shape] parent parent shape
101
+ def set_parent_shape(parent)
102
+ @parent_shape = parent
103
+ end
104
+ alias :parent_shape= :set_parent_shape
105
+
106
+ # Get parent shape.
107
+ # @return [Wx::SF::Shape] parent shape
108
+ def get_parent_shape
109
+ @parent_shape
110
+ end
111
+ alias :parent_shape :get_parent_shape
112
+
113
+ # Set relative position of custom connection point.
114
+ # @param [Wx::RealPoint,Array(Float, Float)] pos Relative position in percentages
115
+ def set_relative_position(pos)
116
+ @rel_position = Wx::RealPoint === pos ? pos : Wx::RealPoint.new(*pos)
117
+ end
118
+ alias :relative_position= :set_relative_position
119
+
120
+ # Get relative position of custom connection point.
121
+ # @return [Wx::RealPoint] Relative position in percentages
122
+ def get_relative_position
123
+ @rel_position
124
+ end
125
+ alias :relative_position :get_relative_position
126
+
127
+ # Get absolute position of the connection point.
128
+ # @return [Wx::RealPoint] Absolute position of the connection point
129
+ def get_connection_point
130
+ if @parent_shape
131
+ rctParent = @parent_shape.get_bounding_box
132
+
133
+ case @type
134
+ when CPTYPE::TOPLEFT
135
+ return rctParent.top_left.to_real
136
+
137
+ when CPTYPE::TOPMIDDLE
138
+ return Wx::RealPoint.new((rctParent.left + rctParent.width/2).to_f, rctParent.top.to_f)
139
+
140
+ when CPTYPE::TOPRIGHT
141
+ return rctParent.top_right.to_real
142
+
143
+ when CPTYPE::CENTERLEFT
144
+ return Wx::RealPoint.new(rctParent.left.to_f, (rctParent.top + rctParent.height/2).to_f)
145
+
146
+ when CPTYPE::CENTERMIDDLE
147
+ return Wx::RealPoint.new((rctParent.left + rctParent.width/2).to_f, (rctParent.top + rctParent.height/2).to_f)
148
+
149
+ when CPTYPE::CENTERRIGHT
150
+ return Wx::RealPoint.new(rctParent.right.to_f, (rctParent.top + rctParent.height/2).to_f)
151
+
152
+ when CPTYPE::BOTTOMLEFT
153
+ return rctParent.bottom_left.to_real
154
+
155
+ when CPTYPE::BOTTOMMIDDLE
156
+ return Wx::RealPoint.new((rctParent.left + rctParent.width/2).to_f, rctParent.bottom.to_f)
157
+
158
+ when CPTYPE::BOTTOMRIGHT
159
+ return rctParent.bottom_right.to_real
160
+
161
+ when CPTYPE::CUSTOM
162
+ return Wx::RealPoint.new(rctParent.left + rctParent.width * @rel_position.x/100, rctParent.top + rctParent.height * @rel_position.y/100)
163
+ end
164
+ end
165
+ Wx::RealPoint.new
166
+ end
167
+ alias :connection_point :get_connection_point
168
+
169
+ # Find out whether given point is inside the connection point.
170
+ # @param [Wx::Point] pos Examined point
171
+ # @return [Boolean] true if the point is inside the handle, otherwise false
172
+ def contains(pos)
173
+ # HINT: overload it for custom actions...
174
+ connection_point.distance_to(pos.to_real_point) < (3 * RADIUS)
175
+ end
176
+ alias :contains? :contains
177
+
178
+ # Draw connection point.
179
+ # @param [Wx::DC] dc Device context where the handle will be drawn
180
+ def draw(dc)
181
+ if @mouse_over
182
+ draw_hover(dc)
183
+ else
184
+ draw_normal(dc)
185
+ end
186
+ end
187
+
188
+ # Refresh (repaint) the dock point
189
+ def refresh
190
+ @parent_shape.refresh(DELAYED)
191
+ end
192
+
193
+ protected
194
+
195
+ # Draw the connection point in the normal way. The function can be overridden if necessary.
196
+ # @param [Wx::DC] dc Reference to device context where the shape will be drawn to
197
+ def draw_normal(dc)
198
+ # HINT: overload it for custom actions...
199
+ end
200
+
201
+ # Draw the connection point in the hover mode (the mouse cursor is above the shape).
202
+ # The function can be overridden if necessary.
203
+ # @param [Wx::DC] dc Reference to device context where the shape will be drawn to
204
+ def draw_hover(dc)
205
+ # HINT: overload it for custom actions...
206
+ dc.with_pen(Wx::BLACK_PEN) do
207
+ dc.with_brush(Wx::RED_BRUSH) do
208
+ dc.draw_circle(connection_point.to_point, RADIUS)
209
+ end
210
+ end
211
+ end
212
+
213
+ private
214
+
215
+ # For deserialization
216
+ def set_type(type)
217
+ @type = type
218
+ end
219
+
220
+ # Event handler called when the mouse pointer is moving above shape canvas.
221
+ # @param [Wx::Point] pos Current mouse position
222
+ def _on_mouse_move(pos)
223
+ if contains?(pos)
224
+ unless @mouse_over
225
+ @mouse_over = true
226
+ refresh
227
+ end
228
+ else
229
+ if @mouse_over
230
+ @mouse_over = false
231
+ refresh
232
+ end
233
+ end
234
+ end
235
+
236
+ end
237
+
238
+ end