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,483 @@
1
+ # Wx::SF::ControlShape - control shape class
2
+ # Copyright (c) M.J.N. Corino, The Netherlands
3
+
4
+ require 'wx/shapes/shapes/rect_shape'
5
+
6
+ module Wx::SF
7
+
8
+ FIT_SHAPE_TO_CONTROL = true
9
+ FIT_CONTROL_TO_SHAPE = false
10
+
11
+ # Class encapsulates a special shape able to manage assigned GUI controls (widgets). The GUI control's
12
+ # position and size can by modified via parent control shape. User can also specify how events incoming from the
13
+ # managed GUI control are processed.
14
+ #
15
+ # Note that the managed controls use a shape canvas as their parent window so these shapes cannot be used
16
+ # without existing and properly initialized shape canvas. Moreover, managed GUI controls are not serialized in any
17
+ # way internally so it is completely up to the user to provide this functionality if needed.
18
+ class ControlShape < RectShape
19
+
20
+ # Auxiliary class used by Wx::SF::ControlShape. All events generated by a GUI control (widget)
21
+ # managed by parent control shape are redirected to this event sink which invokes a default event handler
22
+ # or send a copy of the event to shape canvas if requested.
23
+ class EventSink < Wx::EvtHandler
24
+
25
+ # @overload initialize()
26
+ # Default constructor.
27
+ # @overload initialize(parent)
28
+ # User constructor.
29
+ # @param [ControlShape] parent parent control shape
30
+ def initialize(parent = nil)
31
+ @parent_shape = parent
32
+ end
33
+
34
+ # Event handler used for delayed processing of a mouse button events.
35
+ # The handler creates new key event instance and sends it to a shape canvas for further processing.
36
+ # @param [Wx::MouseEvent] event Mouse event
37
+ def _on_mouse_button(event)
38
+ if (@parent_shape.get_event_processing & EVTPROCESSING::MOUSE2CANVAS) != 0
39
+ updated_event = event.clone
40
+
41
+ update_mouse_event(updated_event)
42
+ send_event(updated_event)
43
+ end
44
+
45
+ # process the event also by an original handler if requested
46
+ event.skip if (@parent_shape.get_event_processing & EVTPROCESSING::MOUSE2GUI) != 0
47
+ end
48
+
49
+ # Event handler used for delayed processing of a mouse event (mouse movement).
50
+ # The handler creates new key event instance and sends it to a shape canvas for further processing.
51
+ # @param [Wx::MouseEvent] event Mouse event
52
+ def _on_mouse_move(event)
53
+ if (@parent_shape.get_event_processing & EVTPROCESSING::MOUSE2CANVAS) != 0
54
+ updated_event = event.clone
55
+
56
+ update_mouse_event(updated_event)
57
+ send_event(updated_event)
58
+ end
59
+
60
+ # process the event also by an original handler if requested
61
+ event.skip if (@parent_shape.get_event_processing & EVTPROCESSING::MOUSE2GUI) != 0
62
+ end
63
+
64
+ # Event handler used for delayed processing of a key event.
65
+ # The handler creates new key event instance and sends it to a shape canvas for further processing.
66
+ # @param [Wx::KeyEvent] event Keyboard event
67
+ def _on_key_down(event)
68
+ send_event(event) if (@parent_shape.get_event_processing & EVTPROCESSING::KEY2CANVAS) !=0
69
+
70
+ # process the event also by an original handler if requested
71
+ event.skip if (@parent_shape.get_event_processing & EVTPROCESSING::KEY2GUI) != 0
72
+ end
73
+
74
+ # Event handler used for adjusting the parent shape's size in accordance to size of managed GUI control.
75
+ # @param [Wx::SizeEvent] event Size event
76
+ def _on_size(event)
77
+ event.skip
78
+
79
+ @parent_shape.update_shape
80
+ end
81
+
82
+ protected
83
+
84
+ # Send copy of incoming event to a shape canvas.
85
+ # @param [Wx::Event] event Event to be send
86
+ def send_event(event)
87
+ if @parent_shape && @parent_shape.get_diagram
88
+ canvas = @parent_shape.get_diagram.get_shape_canvas
89
+ # send copy of the event to the shape canvas
90
+ Wx.post_event(canvas, event) if canvas
91
+ end
92
+ end
93
+
94
+ # Modify given mouse event (recalculate the event's position in accordance to parent control
95
+ # shape's position.
96
+ # @param [Wx::MouseEvent] event Mouse event to be updated
97
+ def update_mouse_event(event)
98
+ abs_pos = @parent_shape.get_absolute_position
99
+
100
+ x, y = @parent_shape.get_parent_canvas.calc_unscrolled_position(0, 0)
101
+
102
+ event.x += (abs_pos.x.to_i + @parent_shape.get_control_offset - x)
103
+ event.y += (abs_pos.y.to_i + @parent_shape.get_control_offset - y)
104
+ end
105
+
106
+ end
107
+
108
+ # Way of processing of GUI control's events.
109
+ class EVTPROCESSING < Wx::Enum
110
+ # Event isn't processed.
111
+ NONE = self.new(0)
112
+ # Keyboard events are processed by the GUI control.
113
+ KEY2GUI = self.new(1)
114
+ # Keyboard events are send to a shape canvas.
115
+ KEY2CANVAS = self.new(2)
116
+ # Mouse events are processed by the GUI control.
117
+ MOUSE2GUI = self.new(4)
118
+ # Mouse events are send to a shape canvas.
119
+ MOUSE2CANVAS = self.new(8)
120
+ end
121
+
122
+ # Defaults
123
+ module DEFAULT
124
+ CONTROLOFFSET = 0
125
+ PROCESSEVENTS = EVTPROCESSING::KEY2CANVAS | EVTPROCESSING::MOUSE2CANVAS
126
+ MODFILL = Wx::Brush.new(Wx::BLUE, Wx::BrushStyle::BRUSHSTYLE_BDIAGONAL_HATCH) if Wx::App.is_main_loop_running
127
+ Wx.add_delayed_constant(self, :MODFILL) { Wx::Brush.new(Wx::BLUE, Wx::BrushStyle::BRUSHSTYLE_BDIAGONAL_HATCH) }
128
+ MODBORDER = Wx::Pen.new(Wx::BLUE, 1, Wx::PenStyle::PENSTYLE_SOLID) if Wx::App.is_main_loop_running
129
+ Wx.add_delayed_constant(self, :MODBORDER) { Wx::Pen.new(Wx::BLUE, 1, Wx::PenStyle::PENSTYLE_SOLID) }
130
+ end
131
+
132
+ property :event_processing, :control_offset, :mod_fill, :mod_border
133
+
134
+ # @overload initialize()
135
+ # Default constructor.
136
+ # @overload initialize(pos, size, diagram)
137
+ # User constructor.
138
+ # @param [Wx::Window] ctrl managed GUI control
139
+ # @param [Wx::RealPoint] pos Initial position
140
+ # @param [Wx::RealPoint] size Initial size
141
+ # @param [Wx::SF::Diagram] diagram parent diagram
142
+ def initialize(*args)
143
+ if args.empty?
144
+ super
145
+ @control = nil
146
+ else
147
+ ctrl = args.shift
148
+ super(*args)
149
+ set_control(ctrl)
150
+ end
151
+ add_style(Shape::STYLE::PROCESS_DEL)
152
+ @process_events = DEFAULT::PROCESSEVENTS
153
+ @mod_fill = DEFAULT::MODFILL
154
+ @mod_border = DEFAULT::MODBORDER
155
+ @control_offset = DEFAULT::CONTROLOFFSET
156
+
157
+ @event_sink = EventSink.new(self)
158
+
159
+ @prev_parent = nil
160
+ @prev_style = 0
161
+ @prev_fill = nil
162
+ @prev_border = nil
163
+
164
+ @fill = Wx::TRANSPARENT_BRUSH
165
+ @border = Wx::TRANSPARENT_PEN
166
+ end
167
+
168
+ # Set managed GUI control.
169
+ # @param [Wx::Window] ctrl existing manager GUI control
170
+ # @param [Boolean] fit true if the control shape should be resized in accordance to the given GUI control
171
+ def set_control(ctrl, fit = FIT_SHAPE_TO_CONTROL)
172
+ if @control
173
+ @control.disconnect(Wx::ID_ANY, Wx::ID_ANY, Wx::EVT_LEFT_DOWN)
174
+ @control.disconnect(Wx::ID_ANY, Wx::ID_ANY, Wx::EVT_RIGHT_DOWN)
175
+ @control.disconnect(Wx::ID_ANY, Wx::ID_ANY, Wx::EVT_LEFT_UP)
176
+ @control.disconnect(Wx::ID_ANY, Wx::ID_ANY, Wx::EVT_RIGHT_UP)
177
+ @control.disconnect(Wx::ID_ANY, Wx::ID_ANY, Wx::EVT_LEFT_DCLICK)
178
+ @control.disconnect(Wx::ID_ANY, Wx::ID_ANY, Wx::EVT_RIGHT_DCLICK)
179
+ @control.disconnect(Wx::ID_ANY, Wx::ID_ANY, Wx::EVT_MOTION)
180
+ @control.disconnect(Wx::ID_ANY, Wx::ID_ANY, Wx::EVT_KEY_DOWN)
181
+ @control.disconnect(Wx::ID_ANY, Wx::ID_ANY, Wx::EVT_SIZE)
182
+ @control.hide
183
+ @control.reparent(@prev_parent)
184
+ end
185
+
186
+ @control = ctrl
187
+
188
+ if @control
189
+ @prev_parent = ctrl.get_parent
190
+ if @diagram
191
+ canvas = @diagram.get_shape_canvas
192
+
193
+ # reparent GUI control if necessary
194
+ @control.reparent(canvas) if canvas && canvas != @prev_parent
195
+
196
+ # redirect mouse events to the event sink for their delayed processing
197
+ @control.evt_left_down { |evt| @event_sink._on_mouse_button(evt) }
198
+ @control.evt_right_down { |evt| @event_sink._on_mouse_button(evt) }
199
+ @control.evt_left_up { |evt| @event_sink._on_mouse_button(evt) }
200
+ @control.evt_right_up { |evt| @event_sink._on_mouse_button(evt) }
201
+ @control.evt_left_dclick { |evt| @event_sink._on_mouse_button(evt) }
202
+ @control.evt_right_dclick { |evt| @event_sink._on_mouse_button(evt) }
203
+ @control.evt_motion { |evt| @event_sink._on_mouse_move(evt) }
204
+ @control.evt_key_down { |evt| @event_sink._on_key_down(evt) }
205
+ @control.evt_size { |evt| @event_sink._on_size(evt) }
206
+ end
207
+
208
+ update_shape if fit
209
+
210
+ update_control
211
+ end
212
+ end
213
+
214
+ # Get managed GUI control.
215
+ # @return [Wx::Window] the GUI control
216
+ def get_control
217
+ @control
218
+ end
219
+
220
+ # Set the way how GUI control's events are processed.
221
+ # @param [EVTPROCESSING] mask Event processing
222
+ # @see EVTPROCESSING
223
+ def set_event_processing(mask)
224
+ @process_events = mask
225
+ end
226
+
227
+ # Get the way how GUI control's events are processed.
228
+ # @return [EVTPROCESSING] Combination of EVTPROCESSING flags
229
+ # @see EVTPROCESSING
230
+ def get_event_processing
231
+ @process_events
232
+ end
233
+
234
+ # Set control shape's background style used during its modification.
235
+ # @param [Wx::Brush] brush Reference to used brush
236
+ def set_mod_fill(brush)
237
+ @mod_fill = brush
238
+ end
239
+
240
+ # Get control shape's background style used during its modification.
241
+ # @return [Wx::Brush] Used brush
242
+ def get_mod_fill
243
+ @mod_fill
244
+ end
245
+
246
+ # Set control shape's border style used during its modification.
247
+ # @param [Wx::Pen] pen Reference to used pen
248
+ def set_mod_border(pen)
249
+ @mod_border = pen
250
+ end
251
+
252
+ # Get control shape's border style used during its modification.
253
+ # @return [Wx::Pen] Used pen
254
+ def get_mod_border
255
+ @mod_border
256
+ end
257
+
258
+ # Set control shape's offset (a gap between the shape's border and managed GUI control).
259
+ # @param [Integer] offset Offset size
260
+ def set_control_offset(offset)
261
+ @control_offset = offset
262
+ end
263
+
264
+ # Get control shape's offset (a gap between the shape's border and managed GUI control).
265
+ # @return [Integer] Offset size
266
+ def get_control_offset
267
+ @control_offset
268
+ end
269
+
270
+ # Update size and position of the managed control according to the parent shape.
271
+ def update_control
272
+ if @control
273
+ min_bb = @control.get_min_size
274
+ rct_bb = get_bounding_box.deflate!(@control_offset, @control_offset)
275
+
276
+ if rct_bb.width < min_bb.width
277
+ rct_bb.width = min_bb.width
278
+ @rect_size.x = min_bb.width + 2*@control_offset
279
+ end
280
+ if rct_bb.height < min_bb.height
281
+ rct_bb.height = min_bb.height
282
+ @rect_size.y = min_bb.height + 2*@control_offset
283
+ end
284
+
285
+ x, y = get_parent_canvas.calc_unscrolled_position(0, 0)
286
+
287
+ # set the control's dimensions and position according to the parent control shape
288
+ @control.set_size([rct_bb.width, rct_bb.height])
289
+ @control.move(rct_bb.left - x, rct_bb.top - y)
290
+ end
291
+ end
292
+
293
+ # Update size of the shape position according to the managed control.
294
+ def update_shape
295
+ if @control
296
+ ctrl_size = @control.size
297
+
298
+ @rect_size.x = ctrl_size.x + 2*@control_offset
299
+ @rect_size.y = ctrl_size.y + 2*@control_offset
300
+
301
+ get_parent_canvas.refresh(false)
302
+ end
303
+ end
304
+
305
+ # Set parent shape object
306
+ # @param [Wx::SF::Shape] parent
307
+ def set_parent_shape(parent)
308
+ super
309
+ update
310
+ end
311
+ alias :parent_shape= :set_parent_shape
312
+
313
+ # Scale the shape size by in both directions. The function can be overridden if necessary
314
+ # (new implementation should call default one ore scale shape's children manually if necessary).
315
+ # @param [Float] x Horizontal scale factor
316
+ # @param [Float] y Vertical scale factor
317
+ # @param children true if the shape's children should be scaled as well, otherwise the shape will be updated after scaling via update() function.
318
+ def scale(x, y, children = WITHCHILDREN)
319
+ super
320
+ update_control
321
+ end
322
+
323
+ # Move the shape to the given absolute position. The function can be overridden if necessary.
324
+ # @param [Float] x X coordinate
325
+ # @param [Float] y Y coordinate
326
+ def move_to(x, y)
327
+ super
328
+ update_control
329
+ end
330
+
331
+ # Move the shape by the given offset. The function can be overridden if necessary.
332
+ # @param [Float] x X offset
333
+ # @param [Float] y Y offset
334
+ def move_by(x, y)
335
+ super
336
+ update_control
337
+ end
338
+
339
+ # Update shape (align all child shapes an resize it to fit them)
340
+ def update
341
+ super
342
+ update_control
343
+ end
344
+
345
+ # Resize the shape to bound all child shapes. The function can be overridden if necessary.
346
+ def fit_to_children
347
+ bb_rct = get_bounding_box
348
+
349
+ ctrl_rct = if @control
350
+ Wx::Rect.new(@control.position, @control.size)
351
+ else
352
+ bb_rct
353
+ end
354
+
355
+ super
356
+
357
+ update_shape if bb_rct.intersects(ctrl_rct) && !bb_rct.contains(ctrl_rct)
358
+ end
359
+
360
+ # Event handler called at the beginning of the shape dragging process.
361
+ # The function can be overridden if necessary.
362
+ #
363
+ # The function is called by the framework (by the shape canvas).
364
+ # @param [Wx::Point] pos Current mouse position
365
+ # @see ShapeCanvas
366
+ def on_begin_drag(pos)
367
+ @prev_fill = @fill
368
+ @fill = @mod_fill
369
+ canvas = get_parent_canvas
370
+ if canvas
371
+ @prev_style = canvas.get_style
372
+ canvas.remove_style(ShapeCanvas::STYLE::DND)
373
+ end
374
+ if @control
375
+ @control.hide
376
+ @control.disconnect(Wx::ID_ANY, Wx::ID_ANY, Wx::EVT_SIZE)
377
+ end
378
+
379
+ super
380
+ end
381
+
382
+ # Event handler called at the end of the shape dragging process.
383
+ # The function can be overridden if necessary.
384
+ #
385
+ # The function is called by the framework (by the shape canvas).
386
+ # @param [Wx::Point] pos Current mouse position
387
+ # @see ShapeCanvas
388
+ def on_end_drag(pos)
389
+ @fill = @prev_fill
390
+ canvas = get_parent_canvas
391
+ canvas.set_style(@prev_style) if canvas
392
+ update_control
393
+ if @control
394
+ @control.evt_size { |evt| @event_sink._on_size(evt) }
395
+ @control.show
396
+ @control.set_focus
397
+ end
398
+
399
+ super
400
+ end
401
+
402
+ # Event handler called when the user started to drag the shape handle.
403
+ # The function can be overridden if necessary.
404
+ #
405
+ # The function is called by the framework (by the shape canvas).
406
+ # @param [Shape::Handle] handle Reference to dragged handle
407
+ def on_begin_handle(handle)
408
+ @prev_border = @border
409
+ @border = @mod_border
410
+ @prev_fill = @fill
411
+ @fill = @mod_fill
412
+
413
+ if @control
414
+ @control.hide
415
+ @control.disconnect(Wx::ID_ANY, Wx::ID_ANY, Wx::EVT_SIZE)
416
+ end
417
+
418
+ # call default handler
419
+ super
420
+ end
421
+
422
+ # Event handler called during dragging of the shape handle.
423
+ # The function can be overridden if necessary.
424
+ #
425
+ # The function is called by the framework (by the shape canvas).
426
+ # @param [Shape::Handle] handle Reference to dragged handle
427
+ def on_handle(handle)
428
+ super
429
+ update_control
430
+ end
431
+
432
+ # Event handler called when the user finished dragging of the shape handle.
433
+ # The function can be overridden if necessary.
434
+ #
435
+ # The function is called by the framework (by the shape canvas).
436
+ # @param [Shape::Handle] handle Reference to dragged handle
437
+ def on_end_handle(handle)
438
+ @border = @prev_border
439
+ @fill = @prev_fill
440
+
441
+ if @control
442
+ @control.show
443
+ @control.set_focus
444
+ @control.evt_size { |evt| @event_sink._on_size(evt) }
445
+ end
446
+
447
+ # call default handler
448
+ super
449
+ end
450
+
451
+ protected
452
+
453
+ # Event handler called by ShapeCanvas to request,report canvas changes.
454
+ # @param [ShapeCanvas::CHANGE] change change type indicator
455
+ # @param [Array] args any additional arguments
456
+ # @return [Boolean,nil]
457
+ def _on_canvas(change, *args)
458
+ case change
459
+ when ShapeCanvas::CHANGE::SET_SCALE
460
+ msg = args.shift
461
+ msg << 'rescaling control (GUI) shapes not supported'
462
+ return false
463
+ when ShapeCanvas::CHANGE::VIRTUAL_SIZE
464
+ self.update
465
+ end
466
+ super
467
+ end
468
+
469
+ private
470
+
471
+ def _on_key(key)
472
+ super
473
+ if key == Wx::K_DELETE
474
+ set_control(nil)
475
+ if @diagram
476
+ @diagram.remove_shape(self, false)
477
+ end
478
+ end
479
+ end
480
+
481
+ end
482
+
483
+ end
@@ -0,0 +1,231 @@
1
+ # Wx::SF::CurveShape - curved line shape class
2
+ # Copyright (c) M.J.N. Corino, The Netherlands
3
+
4
+ require 'wx/shapes/shapes/line_shape'
5
+
6
+ module Wx::SF
7
+
8
+ # Interpolation line shape. The class extends LineShape class and allows
9
+ # user to create curved connection line.
10
+ class CurveShape < LineShape
11
+
12
+ # @overload initialize()
13
+ # default constructor
14
+ # @overload initialize(src, trg, path, manager)
15
+ # @param [Wx::SF::Serializable::ID] src ID of the source shape
16
+ # @param [Wx::SF::Serializable::ID] trg ID of the target shape
17
+ # @param [Array<Wx::RealPoint>] path List of the line control points (can be empty)
18
+ # @param [Diagram] diagram containing diagram
19
+ # @overload initialize(src, trg, path, manager)
20
+ # @param [Wx::RealPoint] src starting line point
21
+ # @param [Wx::RealPoint] trg end line point
22
+ # @param [Array<Wx::RealPoint>,nil] path List of the line control points (can be empty or nil)
23
+ # @param [Diagram] diagram containing diagram
24
+ def initialize(*args)
25
+ super
26
+ end
27
+
28
+ # Get line's bounding box. The function can be overridden if necessary.
29
+ # @return [Wx::Rect] Bounding rectangle
30
+ def get_bounding_box
31
+ super.inflate(20, 20)
32
+ end
33
+
34
+ # Get a line point laying on the given line segment and shifted
35
+ # from the beginning of the segment by given offset.
36
+ # @param [Integer] segment Zero-based index of the line segment
37
+ # @param [Float] offset Real value in the range from 0 to 1 which determines
38
+ # the line-point offset inside the line segment
39
+ # @return [Wx::RealPoint] Line point
40
+ def get_point(segment, offset)
41
+ if segment <= @lst_points.size
42
+ a,b,c,d = get_segment_quaternion(segment)
43
+ coord_catmul_rom_kubika(a, b, c, d, offset)
44
+ else
45
+ Wx::RealPoint.new
46
+ end
47
+ end
48
+
49
+ protected
50
+
51
+ # Internal function used for drawing of completed line shape.
52
+ # @param [Wx::DC] dc Reference of the device context where the shape will be drawn to
53
+ def draw_complete_line(dc)
54
+ case @mode
55
+ when LINEMODE::READY
56
+ # draw line segments
57
+ b = c = nil
58
+ if !@lst_points.empty?
59
+ (0..@lst_points.size).each do |i|
60
+ a,b,c,d = get_segment_quaternion(i)
61
+ catmul_rom_kubika(a, b, c, d, dc)
62
+ end
63
+ else
64
+ b,c = get_direct_line
65
+ dc.draw_line(b.to_point, c.to_point)
66
+ end
67
+ # draw target arrow
68
+ @trg_arrow.draw(b, c, dc) if @trg_arrow
69
+ b, c = get_line_segment(0)
70
+ # draw source arrow
71
+ @src_arrow.draw(c, b, dc) if @src_arrow
72
+
73
+ when LINEMODE::UNDERCONSTRUCTION
74
+ # draw basic line parts
75
+ c = nil
76
+ unless @lst_points.empty?
77
+ @lst_points.size.times do |i|
78
+ a,b,c,d = get_segment_quaternion(i)
79
+ catmul_rom_kubika(a, b, c, d, dc)
80
+ end
81
+ end
82
+ # draw unfinished line segment if any (for interactive line creation)
83
+ dc.with_pen(Wx::Pen.new(Wx::BLACK, 1, Wx::PenStyle::PENSTYLE_DOT)) do
84
+ if @lst_points.size > 1
85
+ dc.draw_line(c.to_point, @unfinished_point)
86
+ elsif @src_shape_id
87
+ # draw unfinished line segment if any (for interactive line creation)
88
+ dc.with_pen(Wx::Pen.new(Wx::BLACK, 1, Wx::PenStyle::PENSTYLE_DOT)) do
89
+ src_shape = @diagram.find_shape(@src_shape_id)
90
+ if src_shape
91
+ if src_shape.get_connection_points.empty?
92
+ dc.draw_line((src_shape.get_border_point(src_shape.get_center, @unfinished_point.to_real)).to_point,
93
+ @unfinished_point)
94
+ else
95
+ dc.draw_line(get_mod_src_point.to_point, @unfinished_point)
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ when LINEMODE::SRCCHANGE
103
+ # draw basic line parts
104
+ c = nil
105
+ @lst_points.size.times do |i|
106
+ a,b,c,d = get_segment_quaternion(i+1)
107
+ catmul_rom_kubika(a, b, c, d, dc)
108
+ end
109
+ # draw linesegment being updated
110
+ dc.with_pen(Wx::Pen.new(Wx::BLACK, 1, Wx::PenStyle::PENSTYLE_DOT)) do
111
+ if !@lst_points.empty?
112
+ _,_,c,_ = get_segment_quaternion(0)
113
+ else
114
+ _,c = get_direct_line
115
+ end
116
+ dc.draw_line(@unfinished_point, c.to_point)
117
+ end
118
+
119
+ when LINEMODE::TRGCHANGE
120
+ # draw basic line parts
121
+ c = nil
122
+ if !@lst_points.empty?
123
+ @lst_points.size.times do |i|
124
+ a,b,c,d = get_segment_quaternion(i)
125
+ catmul_rom_kubika(a, b, c, d, dc)
126
+ end
127
+ else
128
+ c = get_src_point
129
+ end
130
+ # draw linesegment being updated
131
+ dc.with_pen(Wx::Pen.new(Wx::BLACK, 1, Wx::PenStyle::PENSTYLE_DOT)) do
132
+ dc.draw_line(@unfinished_point, c.to_point)
133
+ end
134
+ end
135
+ end
136
+
137
+ private
138
+
139
+ # Auxiliary drawing function.
140
+ # @param [Integer] segment
141
+ # @return [Array(Wx::RealPoint,Wx::RealPoint,Wx::RealPoint,Wx::RealPoint)]
142
+ def get_segment_quaternion(segment)
143
+ quart = [nil,nil,nil,nil]
144
+ # wxXS::RealPointList::compatibility_iterator node
145
+ index = 2 - segment
146
+
147
+ quart[index - 1] = get_src_point if (index - 1) >= 0
148
+ quart[index - 2] = get_mod_src_point if (index - 2) >= 0
149
+
150
+ if index >= 0
151
+ pt = @lst_points.at(ix_pt = 0)
152
+ else
153
+ pt = @lst_points.at(ix_pt = index.abs)
154
+ index = 0
155
+ end
156
+
157
+ while index < 4
158
+ if pt
159
+ quart[index] = pt
160
+ ix_pt += 1
161
+ pt = @lst_points.at(ix_pt)
162
+ else
163
+ if index == 2
164
+ quart[2] = get_trg_point
165
+ elsif index == 3
166
+ if @mode == LINEMODE::UNDERCONSTRUCTION
167
+ quart[3] = @unfinished_point.to_real
168
+ elsif @trg_shape_id
169
+ quart[3] = get_mod_trg_point
170
+ end
171
+ end
172
+ end
173
+ index += 1
174
+ end
175
+
176
+ quart
177
+ end
178
+
179
+ # Auxiliary drawing function.
180
+ # @param [Wx::RealPoint] a
181
+ # @param [Wx::RealPoint] b
182
+ # @param [Wx::RealPoint] c
183
+ # @param [Wx::RealPoint] d
184
+ # @param [Wx::DC] dc
185
+ def catmul_rom_kubika(a, b, c, d, dc)
186
+ # the beginning of the curve is in the B point
187
+ point0 = b
188
+
189
+ optim_steps = b.distance_to(c).to_f / 10
190
+ optim_steps = 10 if optim_steps < 10
191
+
192
+ # draw the curve
193
+ t = 0.0
194
+ while t <= (1 + (1.0 / optim_steps))
195
+ point1 = coord_catmul_rom_kubika(a,b,c,d,t)
196
+ dc.draw_line(point0.x.to_i, point0.y.to_i, point1.x.to_i, point1.y.to_i)
197
+ point0 = point1
198
+ t += 1.0 / (optim_steps-1)
199
+ end
200
+ point1 = coord_catmul_rom_kubika(a,b,c,d,1)
201
+ dc.draw_line(point0.x.to_i, point0.y.to_i, point1.x.to_i, point1.y.to_i)
202
+ end
203
+
204
+ # Auxiliary drawing function.
205
+ # @param [Wx::RealPoint] p1
206
+ # @param [Wx::RealPoint] p2
207
+ # @param [Wx::RealPoint] p3
208
+ # @param [Wx::RealPoint] p4
209
+ # @param [Float] t
210
+ # @return [Wx::RealPoint]
211
+ def coord_catmul_rom_kubika(p1, p2, p3, p4, t)
212
+ # auxiliary variables
213
+ pom1 = t - 1
214
+ pom2 = t * t
215
+
216
+ # used polynomials
217
+ c1 = (-pom2*t + 2*pom2 - t) / 2
218
+ c2 = (3*pom2*t - 5*pom2 + 2) / 2
219
+ c3 = (-3*pom2*t + 4*pom2 + t) / 2
220
+ c4 = pom1*pom2 / 2
221
+
222
+ # calculation of curve point for t = <0,1>
223
+ x = c1*p1.x + c2*p2.x + c3*p3.x + c4*p4.x
224
+ y = c1*p1.y + c2*p2.y + c3*p3.y + c4*p4.y
225
+
226
+ Wx::RealPoint.new(x,y)
227
+ end
228
+
229
+ end
230
+
231
+ end