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