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