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.
- checksums.yaml +7 -0
- data/.yardopts +12 -0
- data/CREDITS.md +18 -0
- data/INSTALL.md +39 -0
- data/LICENSE +21 -0
- data/README.md +118 -0
- data/assets/screenshot.png +0 -0
- data/bin/wx-shapes +9 -0
- data/lib/wx/shapes/arrow_base.rb +86 -0
- data/lib/wx/shapes/arrows/circle_arrow.rb +39 -0
- data/lib/wx/shapes/arrows/diamond_arrow.rb +33 -0
- data/lib/wx/shapes/arrows/open_arrow.rb +56 -0
- data/lib/wx/shapes/arrows/solid_arrow.rb +69 -0
- data/lib/wx/shapes/art/shape_canvas/page.xpm +73 -0
- data/lib/wx/shapes/auto_layout.rb +358 -0
- data/lib/wx/shapes/base.rb +33 -0
- data/lib/wx/shapes/canvas_history.rb +84 -0
- data/lib/wx/shapes/connection_point.rb +238 -0
- data/lib/wx/shapes/core.rb +19 -0
- data/lib/wx/shapes/diagram.rb +659 -0
- data/lib/wx/shapes/events.rb +389 -0
- data/lib/wx/shapes/printout.rb +136 -0
- data/lib/wx/shapes/serializable.rb +440 -0
- data/lib/wx/shapes/serialize/core.rb +40 -0
- data/lib/wx/shapes/serialize/id.rb +82 -0
- data/lib/wx/shapes/serialize/wx.rb +104 -0
- data/lib/wx/shapes/serializer/json.rb +258 -0
- data/lib/wx/shapes/serializer/yaml.rb +125 -0
- data/lib/wx/shapes/shape.rb +2129 -0
- data/lib/wx/shapes/shape_canvas.rb +3285 -0
- data/lib/wx/shapes/shape_data_object.rb +43 -0
- data/lib/wx/shapes/shape_handle.rb +287 -0
- data/lib/wx/shapes/shape_list.rb +161 -0
- data/lib/wx/shapes/shapes/bitmap_shape.rb +257 -0
- data/lib/wx/shapes/shapes/circle_shape.rb +136 -0
- data/lib/wx/shapes/shapes/control_shape.rb +483 -0
- data/lib/wx/shapes/shapes/curve_shape.rb +231 -0
- data/lib/wx/shapes/shapes/diamond_shape.rb +62 -0
- data/lib/wx/shapes/shapes/edit_text_shape.rb +317 -0
- data/lib/wx/shapes/shapes/ellipse_shape.rb +106 -0
- data/lib/wx/shapes/shapes/flex_grid_shape.rb +78 -0
- data/lib/wx/shapes/shapes/grid_shape.rb +404 -0
- data/lib/wx/shapes/shapes/line_shape.rb +907 -0
- data/lib/wx/shapes/shapes/multi_sel_rect.rb +214 -0
- data/lib/wx/shapes/shapes/ortho_shape.rb +357 -0
- data/lib/wx/shapes/shapes/polygon_shape.rb +294 -0
- data/lib/wx/shapes/shapes/rect_shape.rb +378 -0
- data/lib/wx/shapes/shapes/round_ortho_shape.rb +131 -0
- data/lib/wx/shapes/shapes/round_rect_shape.rb +142 -0
- data/lib/wx/shapes/shapes/square_shape.rb +119 -0
- data/lib/wx/shapes/shapes/text_shape.rb +324 -0
- data/lib/wx/shapes/thumbnail.rb +234 -0
- data/lib/wx/shapes/version.rb +12 -0
- data/lib/wx/shapes/wx.rb +29 -0
- data/lib/wx/shapes.rb +18 -0
- data/lib/wx/wx-shapes/base.rb +87 -0
- data/lib/wx/wx-shapes/cmd/sampler.rb +58 -0
- data/lib/wx/wx-shapes/cmd/test.rb +27 -0
- data/rakelib/yard/templates/default/fulldoc/html/css/wxruby3.css +7 -0
- data/rakelib/yard/templates/default/layout/html/setup.rb +5 -0
- data/rakelib/yard/yard/relative_markdown_links/version.rb +8 -0
- data/rakelib/yard/yard/relative_markdown_links.rb +39 -0
- data/rakelib/yard/yard-custom-templates.rb +2 -0
- data/rakelib/yard/yard-relative_markdown_links.rb +4 -0
- data/samples/demo/art/AlignBottom.xpm +35 -0
- data/samples/demo/art/AlignCenter.xpm +35 -0
- data/samples/demo/art/AlignLeft.xpm +35 -0
- data/samples/demo/art/AlignMiddle.xpm +35 -0
- data/samples/demo/art/AlignRight.xpm +35 -0
- data/samples/demo/art/AlignTop.xpm +35 -0
- data/samples/demo/art/Bitmap.xpm +25 -0
- data/samples/demo/art/Circle.xpm +22 -0
- data/samples/demo/art/Curve.xpm +21 -0
- data/samples/demo/art/Diamond.xpm +22 -0
- data/samples/demo/art/EditText.xpm +21 -0
- data/samples/demo/art/Ellipse.xpm +22 -0
- data/samples/demo/art/FixedRect.xpm +22 -0
- data/samples/demo/art/FlexGrid.xpm +22 -0
- data/samples/demo/art/GC.xpm +23 -0
- data/samples/demo/art/Grid.xpm +22 -0
- data/samples/demo/art/Line.xpm +21 -0
- data/samples/demo/art/NoSource.xpm +69 -0
- data/samples/demo/art/OrthoLine.xpm +21 -0
- data/samples/demo/art/Rect.xpm +22 -0
- data/samples/demo/art/RoundOrthoLine.xpm +21 -0
- data/samples/demo/art/RoundRect.xpm +22 -0
- data/samples/demo/art/Shadow.xpm +23 -0
- data/samples/demo/art/StandAloneLine.xpm +22 -0
- data/samples/demo/art/Text.xpm +21 -0
- data/samples/demo/art/Tool.xpm +23 -0
- data/samples/demo/art/sample.xpm +251 -0
- data/samples/demo/demo.rb +658 -0
- data/samples/demo/frame_canvas.rb +422 -0
- data/samples/demo/images/motyl.bmp +0 -0
- data/samples/demo/images/motyl2.bmp +0 -0
- data/samples/sample1/art/sample.xpm +251 -0
- data/samples/sample1/sample.rb +263 -0
- data/samples/sample2/art/sample.xpm +251 -0
- data/samples/sample2/sample.rb +133 -0
- data/samples/sample2/sample_canvas.rb +35 -0
- data/samples/sample2/sample_shape.rb +108 -0
- data/samples/sample3/art/sample.xpm +251 -0
- data/samples/sample3/sample.rb +281 -0
- data/samples/sample4/art/sample.xpm +251 -0
- data/samples/sample4/sample.rb +180 -0
- data/tests/art/motyl.bmp +0 -0
- data/tests/lib/wxapp_runner.rb +64 -0
- data/tests/serializer_tests.rb +521 -0
- data/tests/test_grid_shapes.rb +42 -0
- data/tests/test_serialize.rb +7 -0
- data/tests/test_serialize_yaml.rb +17 -0
- 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
|