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