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,19 @@
|
|
1
|
+
# Core class extensions
|
2
|
+
# Copyright (c) M.J.N. Corino, The Netherlands
|
3
|
+
|
4
|
+
class ::Array
|
5
|
+
|
6
|
+
def resize(sz, obj=nil, &block)
|
7
|
+
if sz > self.size
|
8
|
+
if block
|
9
|
+
self.fill(sz-1, 0, &block)
|
10
|
+
else
|
11
|
+
self.fill(obj, sz-1, 0)
|
12
|
+
end
|
13
|
+
elsif sz < self.size
|
14
|
+
self.slice!(0, sz)
|
15
|
+
end
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,659 @@
|
|
1
|
+
# Wx::SF::Diagram - diagram class
|
2
|
+
# Copyright (c) M.J.N. Corino, The Netherlands
|
3
|
+
|
4
|
+
require 'set'
|
5
|
+
|
6
|
+
require 'wx/shapes/serializable'
|
7
|
+
require 'wx/shapes/shape'
|
8
|
+
require 'wx/shapes/shape_canvas'
|
9
|
+
|
10
|
+
module Wx::SF
|
11
|
+
|
12
|
+
class Diagram
|
13
|
+
|
14
|
+
include Serializable
|
15
|
+
|
16
|
+
property shapes: :serialize_shapes
|
17
|
+
property :accepted_shapes, :accepted_top_shapes
|
18
|
+
|
19
|
+
# Search mode flags for get_shape_at_position method
|
20
|
+
SEARCHMODE = ShapeCanvas::SEARCHMODE
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@shapes = ShapeList.new
|
24
|
+
@shape_canvas = nil
|
25
|
+
@is_modified = false
|
26
|
+
@accepted_shapes = ::Set.new([ACCEPT_ALL])
|
27
|
+
@accepted_top_shapes = ::Set.new([ACCEPT_ALL])
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the shape canvas.
|
31
|
+
# @return [Wx::SF::ShapeCanvas]
|
32
|
+
def get_shape_canvas
|
33
|
+
@shape_canvas
|
34
|
+
end
|
35
|
+
alias :shape_canvas :get_shape_canvas
|
36
|
+
|
37
|
+
# Set the shape canvas.
|
38
|
+
# @param [Wx::SF::ShapeCanvas] canvas
|
39
|
+
def set_shape_canvas(canvas)
|
40
|
+
@shape_canvas = canvas
|
41
|
+
end
|
42
|
+
alias :shape_canvas= :set_shape_canvas
|
43
|
+
|
44
|
+
# Get information about managed diagram's modification.
|
45
|
+
#
|
46
|
+
# The function returns TRUE if the diagram has been modified and its content
|
47
|
+
# should be saved. The modification flag is cleared when the content is saved.
|
48
|
+
# @return [Boolean] true if managed diagram is modified, otherwise false.
|
49
|
+
def is_modified
|
50
|
+
@is_modified
|
51
|
+
end
|
52
|
+
alias :modified? :is_modified
|
53
|
+
|
54
|
+
# Set diagram's modification flag manually.
|
55
|
+
# @param [Boolean] state State of diagram's modification flag.
|
56
|
+
def set_modified(state = true)
|
57
|
+
@is_modified = state
|
58
|
+
end
|
59
|
+
alias :modified= :set_modified
|
60
|
+
|
61
|
+
# Create new direct connection between two shapes.
|
62
|
+
#
|
63
|
+
# This function creates new simple connection line (without arrows) between given
|
64
|
+
# shapes.
|
65
|
+
# @overload create_connection(src_id, trg_id, save_state = true)
|
66
|
+
# @param [Wx::Serializable::ID] src_id id of a source shape
|
67
|
+
# @param [Wx::Serializable::ID] trg_id id of target shape
|
68
|
+
# @param [Boolean] save_state set the parameter true if you wish to save canvas state after the operation
|
69
|
+
# @return [Array(Wx::SF::ERRCODE, Wx::SF::Shape)] operation result and new connection object. the object is added to the shape canvas automatically.
|
70
|
+
# @overload create_connection(src_id, trg_id, line_info, save_state = true)
|
71
|
+
# @param [Wx::Serializable::ID] src_id id of a source shape
|
72
|
+
# @param [Wx::Serializable::ID] trg_id id of target shape
|
73
|
+
# @param [Class] line_info Connection type (any class inherited from Wx::SF::LineShape)
|
74
|
+
# @param [Boolean] save_state set the parameter true if you wish to save canvas state after the operation
|
75
|
+
# @return [Array(Wx::SF::ERRCODE, Wx::SF::Shape)] operation result and new connection object. the object is added to the shape canvas automatically.
|
76
|
+
# @overload create_connection(src_id, trg_id, line, save_state = true)
|
77
|
+
# @param [Wx::Serializable::ID] src_id id of a source shape
|
78
|
+
# @param [Wx::Serializable::ID] trg_id id of target shape
|
79
|
+
# @param [Wx::SF::LineShape] line the line shape
|
80
|
+
# @param [Boolean] save_state set the parameter true if you wish to save canvas state after the operation
|
81
|
+
# @return [Array(Wx::SF::ERRCODE, Wx::SF::Shape)] operation result and new connection object. the object is added to the shape canvas automatically.
|
82
|
+
# @see start_interactive_connection
|
83
|
+
def create_connection(src_id, trg_id, *rest)
|
84
|
+
err = shape = nil
|
85
|
+
if rest.first.is_a?(LineShape)
|
86
|
+
line = rest.shift
|
87
|
+
save_state = rest.empty? ? true : rest.shift
|
88
|
+
err = add_shape(line, nil, Wx::DEFAULT_POSITION, INITIALIZE, DONT_SAVE_STATE)
|
89
|
+
shape = line if err == ERRCODE::OK
|
90
|
+
else
|
91
|
+
line_type = (rest.empty? || !rest.first.is_a?(::Class)) ? LineShape : rest.shift
|
92
|
+
save_state = rest.empty? ? true : rest.shift
|
93
|
+
err, shape = create_shape(line_type, DONT_SAVE_STATE)
|
94
|
+
end
|
95
|
+
if shape
|
96
|
+
shape.set_src_shape_id(src_id)
|
97
|
+
shape.set_trg_shape_id(trg_id)
|
98
|
+
|
99
|
+
if @shape_canvas
|
100
|
+
@shape_canvas.save_canvas_state if save_state
|
101
|
+
shape.refresh
|
102
|
+
end
|
103
|
+
end
|
104
|
+
[err, shape]
|
105
|
+
end
|
106
|
+
|
107
|
+
# Create new shape and add it to the shape canvas.
|
108
|
+
# @overload create_shape(shape_info, save_state = true)
|
109
|
+
# @param [Class] shape_info Shape type
|
110
|
+
# @param [Boolean] save_state Set the parameter true if you wish to save canvas state after the operation
|
111
|
+
# @return [Array(Wx::SF::ERRCODE, Wx::SF::Shape)] operation result and new shape. the object is added to the shape canvas automatically.
|
112
|
+
# @overload create_shape(shape_info, pos, save_state = true)
|
113
|
+
# @param [Class] shape_info Shape type
|
114
|
+
# @param [Wx::Point] pos shape position
|
115
|
+
# @param [Boolean] save_state Set the parameter true if you wish to save canvas state after the operation
|
116
|
+
# @return [Array(Wx::SF::ERRCODE, Wx::SF::Shape)] operation result and new shape. the object is added to the shape canvas automatically.
|
117
|
+
def create_shape(shape_info, *rest)
|
118
|
+
pos = if rest.first.respond_to?(:to_point)
|
119
|
+
rest.shift.to_point
|
120
|
+
elsif @shape_canvas
|
121
|
+
clt_rect = @shape_canvas.get_client_rect
|
122
|
+
Wx::Point.new((clt_rect.right - clt_rect.left)/2,
|
123
|
+
(clt_rect.bottom - clt_rect.top)/2)
|
124
|
+
else
|
125
|
+
Wx::Point.new
|
126
|
+
end
|
127
|
+
save_state = rest.empty? ? true : rest.shift
|
128
|
+
if shape_info && is_shape_accepted(shape_info)
|
129
|
+
# create shape object from class info
|
130
|
+
shape = shape_info.new
|
131
|
+
|
132
|
+
parent_shape = nil
|
133
|
+
# update given position
|
134
|
+
lpos = pos;
|
135
|
+
lpos = @shape_canvas.fit_position_to_grid(@shape_canvas.dp2lp(pos)) if @shape_canvas
|
136
|
+
# line shapes can be assigned to root only
|
137
|
+
parent_shape = get_shape_at_position(lpos) unless shape.is_a?(LineShape)
|
138
|
+
|
139
|
+
if parent_shape && parent_shape.is_child_accepted(shape_info)
|
140
|
+
err = add_shape(shape, parent_shape, pos - parent_shape.get_absolute_position.to_point, INITIALIZE, save_state)
|
141
|
+
else
|
142
|
+
err = add_shape(shape, nil, pos, INITIALIZE, save_state)
|
143
|
+
end
|
144
|
+
|
145
|
+
parent_shape.update if parent_shape
|
146
|
+
|
147
|
+
[err, shape]
|
148
|
+
else
|
149
|
+
[ERRCODE::NOT_ACCEPTED, nil]
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Add an existing shape to the canvas.
|
154
|
+
# @param [Wx::SF::Shape] shape new shape
|
155
|
+
# @param [Wx::SF::Shape] parent the parent shape
|
156
|
+
# @param [Wx::Point] pos shape position
|
157
|
+
# @param [Boolean] initialize true if the shape should be reinitialized, otherwise false
|
158
|
+
# @param [Boolean] save_state true if the canvas state should be saved
|
159
|
+
# @return [Wx::SF::ERRCODE] operation result
|
160
|
+
def add_shape(shape, parent, pos, initialize, save_state = true)
|
161
|
+
if shape
|
162
|
+
if shape.is_a?(Shape) && is_shape_accepted(shape.class)
|
163
|
+
pos = pos.to_point
|
164
|
+
if @shape_canvas
|
165
|
+
new_pos = @shape_canvas.fit_position_to_grid(@shape_canvas.dp2lp(pos))
|
166
|
+
shape.set_relative_position(new_pos.to_real)
|
167
|
+
else
|
168
|
+
shape.set_relative_position(pos.to_real)
|
169
|
+
end
|
170
|
+
|
171
|
+
# add shape
|
172
|
+
if parent
|
173
|
+
shape.set_parent_shape(parent)
|
174
|
+
else
|
175
|
+
if is_top_shape_accepted(shape.class)
|
176
|
+
@shapes << shape
|
177
|
+
shape.set_diagram(self)
|
178
|
+
else
|
179
|
+
return ERRCODE::NOT_ACCEPTED
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# initialize added shape
|
184
|
+
if initialize
|
185
|
+
shape.create_handles
|
186
|
+
|
187
|
+
shape.set_hover_colour(@shape_canvas.get_hover_colour) if @shape_canvas
|
188
|
+
|
189
|
+
if has_children(shape)
|
190
|
+
# get shape's children (if exist)
|
191
|
+
lst_children = shape.get_child_shapes(ANY, RECURSIVE)
|
192
|
+
# initialize shape's children
|
193
|
+
lst_children.each do |child|
|
194
|
+
child.create_handles
|
195
|
+
child.update
|
196
|
+
|
197
|
+
child.set_hover_colour(@shape_canvas.get_hover_colour) if @shape_canvas
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
# reset scale of assigned shape canvas (if exists and it is necessary...)
|
203
|
+
if @shape_canvas && shape.is_a?(ControlShape)
|
204
|
+
@shape_canvas.set_scale(1.0)
|
205
|
+
end
|
206
|
+
|
207
|
+
@shape_canvas.save_canvas_state if @shape_canvas && save_state
|
208
|
+
|
209
|
+
@is_modified = true
|
210
|
+
|
211
|
+
ERRCODE::OK
|
212
|
+
else
|
213
|
+
ERRCODE::NOT_ACCEPTED
|
214
|
+
end
|
215
|
+
else
|
216
|
+
ERRCODE::INVALID_INPUT
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
# Remove given shape from the shape canvas.
|
221
|
+
# @param [Wx::SF::Shape] shape shape object that should be deleted
|
222
|
+
# @param [Boolean] refresh Set the parameter to true if you wish to repaint the canvas
|
223
|
+
def remove_shape(shape, refresh = true)
|
224
|
+
return unless shape
|
225
|
+
|
226
|
+
parent = shape.get_parent_shape
|
227
|
+
|
228
|
+
# get all shape's children
|
229
|
+
lst_children = shape.get_child_shapes(ANY, RECURSIVE)
|
230
|
+
lst_children << shape # and shape itself
|
231
|
+
|
232
|
+
# retrieve all assigned lines
|
233
|
+
lst_connections = []
|
234
|
+
lst_children.each do |child|
|
235
|
+
get_assigned_connections(child, LineShape, Shape::CONNECTMODE::BOTH, lst_connections)
|
236
|
+
end
|
237
|
+
|
238
|
+
# remove all assigned lines
|
239
|
+
lst_removed_connections = []
|
240
|
+
lst_connections.each do |line|
|
241
|
+
# one connection may be used by the parent and also by his child
|
242
|
+
unless lst_removed_connections.include?(line)
|
243
|
+
lst_removed_connections << line
|
244
|
+
remove_shape(line,false)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
# remove the shape and it's children from canvas cache and shape index list
|
249
|
+
lst_children.each do |child|
|
250
|
+
@shape_canvas.send(:remove_from_temporaries, shape) if @shape_canvas
|
251
|
+
end
|
252
|
+
|
253
|
+
# remove the shape
|
254
|
+
shape.set_parent_shape(nil) # also removes shape from parent if it had a parent
|
255
|
+
shape.set_diagram(nil)
|
256
|
+
@shapes.delete(shape)
|
257
|
+
|
258
|
+
@is_modified = true
|
259
|
+
|
260
|
+
parent.update if parent
|
261
|
+
|
262
|
+
@shape_canvas.refresh(false) if refresh && @shape_canvas
|
263
|
+
end
|
264
|
+
|
265
|
+
# Remove shapes from the shape canvas
|
266
|
+
# @param [Array<Wx::SF::Shape>] selection List of shapes which should be removed from the canvas
|
267
|
+
def remove_shapes(selection)
|
268
|
+
selection.each { |shape| remove_shape(shape, false) if contains?(shape) }
|
269
|
+
end
|
270
|
+
|
271
|
+
# Change shape's parent (possibly making it unparented i.e. toplevel)
|
272
|
+
# @param [Wx::SF::Shape] shape shape to reparent
|
273
|
+
# @param [Wx::SF::Shape,nil] parent new parent or nil
|
274
|
+
# @return [Wx::SF::Shape] re-parented shape
|
275
|
+
def reparent_shape(shape, parent)
|
276
|
+
prev_parent = shape.get_parent_shape
|
277
|
+
if prev_parent.nil? && parent
|
278
|
+
@shapes.delete(shape) # remove from top level list if the shape will become parented
|
279
|
+
elsif prev_parent && parent.nil?
|
280
|
+
@shapes << shape # add to toplevel shapes if the shape will become unparented
|
281
|
+
shape.set_diagram(self) # make sure the right diagram is set
|
282
|
+
end
|
283
|
+
shape.set_parent_shape(parent)
|
284
|
+
shape
|
285
|
+
end
|
286
|
+
|
287
|
+
# Returns true if the given shape is part of the diagram, false otherwise
|
288
|
+
# @param [Wx::SF::Shape] shape
|
289
|
+
# @return [Boolean]
|
290
|
+
def contains_shape(shape)
|
291
|
+
@shapes.include?(shape.id,true)
|
292
|
+
end
|
293
|
+
alias :contains_shape? :contains_shape
|
294
|
+
alias :contains? :contains_shape
|
295
|
+
|
296
|
+
# Remove all shapes from canvas
|
297
|
+
def clear
|
298
|
+
@shapes.clear
|
299
|
+
if @shape_canvas
|
300
|
+
@shape_canvas.get_multiselection_box.show(false)
|
301
|
+
@shape_canvas.update_virtual_size
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
# Move given shape to the end of the shapes list
|
306
|
+
# @param [Wx::SF::Shape] shape
|
307
|
+
def move_to_end(shape)
|
308
|
+
if (a_shape = @shapes.delete(shape))
|
309
|
+
@shapes << a_shape
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
# Move all shapes so none of it will be located in negative position
|
314
|
+
def move_shapes_from_negatives
|
315
|
+
min_x = min_y = 0.0
|
316
|
+
|
317
|
+
# find the maximal negative position value
|
318
|
+
shapes = get_shapes
|
319
|
+
|
320
|
+
shapes.each_with_index do |shape, ix|
|
321
|
+
shape_pos = shape.get_absolute_position
|
322
|
+
if ix == 0
|
323
|
+
min_x = shape_pos.x
|
324
|
+
min_y = shape_pos.y
|
325
|
+
else
|
326
|
+
min_x = shape_pos.x if shape_pos.x < min_x
|
327
|
+
min_y = shape_pos.y if shape_pos.y < min_y
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
# move all parents shape so they (and their children) will be located in the positive values only
|
332
|
+
if min_x < 0.0 || min_y < 0.0
|
333
|
+
shapes.each do |shape|
|
334
|
+
unless shape.get_parent_shape
|
335
|
+
shape.move_by(min_x.to_i.abs, 0) if min_x < 0.0
|
336
|
+
shape.move_by(0, min_y.to_i.abs) if min_y < 0.0
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
# Update all shapes in the diagram manager
|
343
|
+
def update_all
|
344
|
+
get_shapes.each { |shape| shape.update unless shape.has_children? }
|
345
|
+
end
|
346
|
+
|
347
|
+
# Add given shape type to an acceptance list. The acceptance list contains class
|
348
|
+
# names of the shapes which can be inserted into this instance of shapes canvas.
|
349
|
+
# Note: Constant value {Wx::SF::ACCEPT_ALL} behaves like any class.
|
350
|
+
# @param [Class] type Class of accepted shape object
|
351
|
+
# @see is_shape_accepted
|
352
|
+
def accept_shape(type)
|
353
|
+
::Kernel.raise ArgumentError, 'Class or ACCEPT_ALL expected' unless type.is_a?(::Class) || type == ACCEPT_ALL
|
354
|
+
@accepted_shapes << type
|
355
|
+
end
|
356
|
+
|
357
|
+
# Tells whether the given shape type is accepted by this canvas instance (it means
|
358
|
+
# whether this shape can be inserted into it).
|
359
|
+
#
|
360
|
+
# The function is typically used by the framework for determination whether class type supplied
|
361
|
+
# by add_shape or create_shape function can be inserted into shape canvas.
|
362
|
+
# @param [Class] type Class of examined shape object
|
363
|
+
# @return [Boolean] true if the shape type is accepted, otherwise false.
|
364
|
+
def is_shape_accepted(type)
|
365
|
+
@accepted_shapes.include?(type) || @accepted_shapes.include?(ACCEPT_ALL)
|
366
|
+
end
|
367
|
+
alias :shape_accepted? :is_shape_accepted
|
368
|
+
|
369
|
+
# Clear shape object acceptance list
|
370
|
+
# @see accept_shape
|
371
|
+
def clear_accepted_shapes
|
372
|
+
@accepted_shapes.clear
|
373
|
+
end
|
374
|
+
|
375
|
+
# Get reference to shape acceptance list
|
376
|
+
# @return [Set<String>]
|
377
|
+
def get_accepted_shapes
|
378
|
+
@accepted_shapes
|
379
|
+
end
|
380
|
+
alias :accepted_shapes :get_accepted_shapes
|
381
|
+
|
382
|
+
# Add given shape type to list of accepted top shapes. The acceptance list contains class
|
383
|
+
# names of the shapes which can be inserted into this instance of shapes canvas as a shape without
|
384
|
+
# any parent (i.e. shape placed directly onto the canvas).
|
385
|
+
# Note: Constant value {Wx::SF::ACCEPT_ALL} behaves like any class.
|
386
|
+
# @param [Class] type Class of accepted shape object
|
387
|
+
# @see is_top_shape_accepted
|
388
|
+
def accept_top_shape(type)
|
389
|
+
::Kernel.raise ArgumentError, 'Class or ACCEPT_ALL expected' unless type.is_a?(::Class) || type == ACCEPT_ALL
|
390
|
+
@accepted_top_shapes << type
|
391
|
+
end
|
392
|
+
|
393
|
+
# Tells whether the given shape type is accepted by this canvas instance as a top shape
|
394
|
+
# (it means whether this shape can be inserted directly into it without any parent).
|
395
|
+
#
|
396
|
+
# The function is typically used by the framework for determination whether class type supplied
|
397
|
+
# by add_shape or create_shape function can be inserted directly onto shape canvas.
|
398
|
+
# @param [Class] type Class of examined shape object
|
399
|
+
# @return [Boolean] true if the shape type is accepted, otherwise false.
|
400
|
+
def is_top_shape_accepted(type)
|
401
|
+
@accepted_top_shapes.include?(type) || @accepted_top_shapes.include?(ACCEPT_ALL)
|
402
|
+
end
|
403
|
+
alias :top_shape_accepted? :is_top_shape_accepted
|
404
|
+
|
405
|
+
# Clear top shapes acceptance list
|
406
|
+
# @see accept_shape
|
407
|
+
def clear_accepted_top_shapes
|
408
|
+
@accepted_top_shapes.clear
|
409
|
+
end
|
410
|
+
|
411
|
+
# Get reference to top shapes acceptance list
|
412
|
+
# @return [Set<String>]
|
413
|
+
def get_accepted_top_shapes
|
414
|
+
@accepted_top_shapes
|
415
|
+
end
|
416
|
+
alias :accepted_top_shapes :get_accepted_top_shapes
|
417
|
+
|
418
|
+
# Find shape with given ID.
|
419
|
+
# @param [Wx::SF::Serializable::ID] id Shape's ID
|
420
|
+
# @return [Wx::SF::Shape] shape if exists, otherwise nil
|
421
|
+
def find_shape(id)
|
422
|
+
@shapes.get(id, true)
|
423
|
+
end
|
424
|
+
|
425
|
+
# Get list of connections assigned to given parent shape.
|
426
|
+
# @param [Wx::SF::Shape] parent parent shape
|
427
|
+
# @param [Class] shape_info Line object type
|
428
|
+
# @param [Wx::SF::Shape::CONNECTMODE] mode Search mode
|
429
|
+
# @param [Array<Wx::SF::Shape>] lines shape list where all found connections will be stored
|
430
|
+
# @return [Array<Wx::SF::Shape>] shape list
|
431
|
+
# @see Wx::SF::Shape::CONNECTMODE
|
432
|
+
def get_assigned_connections(parent, shape_info, mode, lines = [])
|
433
|
+
return lines unless parent && parent.get_id
|
434
|
+
|
435
|
+
# lines are all toplevel so we do not have to search recursively...
|
436
|
+
lst_lines = @shapes.select { |shape| shape.is_a?(shape_info) }
|
437
|
+
|
438
|
+
lst_lines.each do |line|
|
439
|
+
case mode
|
440
|
+
when Shape::CONNECTMODE::STARTING
|
441
|
+
lines << line if line.get_src_shape_id == parent.get_id
|
442
|
+
when Shape::CONNECTMODE::ENDING
|
443
|
+
lines << line if line.get_trg_shape_id == parent.get_id
|
444
|
+
when Shape::CONNECTMODE::BOTH
|
445
|
+
lines << line if line.get_src_shape_id == parent.get_id || line.get_trg_shape_id == parent.get_id
|
446
|
+
end
|
447
|
+
end
|
448
|
+
lines
|
449
|
+
end
|
450
|
+
|
451
|
+
# Returns the list of top level shapes
|
452
|
+
def get_top_shapes
|
453
|
+
@shapes
|
454
|
+
end
|
455
|
+
|
456
|
+
def get_all_shapes
|
457
|
+
@shapes.all
|
458
|
+
end
|
459
|
+
|
460
|
+
# Get list of shapes of given type.
|
461
|
+
# @param [Class] shape_info Line object type
|
462
|
+
# @param [Shape::SEARCHMODE] mode Search algorithm
|
463
|
+
# @param [Array<Wx::SF::Shape>] shapes shape list where all found shapes will be stored
|
464
|
+
# @return [Array<Wx::SF::Shape>] shape list
|
465
|
+
# @see Shape::SEARCHMODE
|
466
|
+
def get_shapes(shape_info = Wx::SF::Shape, mode = Shape::SEARCHMODE::BFS, shapes = [])
|
467
|
+
@shapes.each do |shape|
|
468
|
+
shapes << shape if shape.is_a?(shape_info)
|
469
|
+
shape.get_children_recursively(shape_info, mode, shapes) if mode == Shape::SEARCHMODE::DFS
|
470
|
+
end
|
471
|
+
if mode == Shape::SEARCHMODE::BFS
|
472
|
+
@shapes.each { |shape| shape.get_children_recursively(shape_info, mode, shapes) }
|
473
|
+
end
|
474
|
+
shapes
|
475
|
+
end
|
476
|
+
|
477
|
+
# Get shape at given logical position
|
478
|
+
# @param [Wx::Point] pos Logical position
|
479
|
+
# @param [Integer] zorder Z-order of searched shape (useful if several shapes are located at the given position)
|
480
|
+
# @param [SEARCHMODE] mode Search mode
|
481
|
+
# @return [Wx::SF::Shape] shape if found, otherwise nil
|
482
|
+
# @see SEARCHMODE
|
483
|
+
# @see Wx::SF::ShapeCanvas::dp2lp
|
484
|
+
# @see Wx::SF::ShapeCanvas#get_shape_under_cursor
|
485
|
+
def get_shape_at_position(pos, zorder = 1, mode = SEARCHMODE::BOTH)
|
486
|
+
# sort shapes list in the way that the line shapes will be at the top of the list
|
487
|
+
# and all non-line shapes get listed in reversed order as returned from get_shapes (for z order)
|
488
|
+
ins_pos = 0
|
489
|
+
pos = pos.to_point
|
490
|
+
shapes = get_shapes.inject([]) do |list, shape|
|
491
|
+
if shape.is_a?(LineShape)
|
492
|
+
list.prepend(shape)
|
493
|
+
ins_pos += 1
|
494
|
+
else
|
495
|
+
list.insert(ins_pos, shape)
|
496
|
+
end
|
497
|
+
list
|
498
|
+
end
|
499
|
+
# find the topmost shape according to the given rules
|
500
|
+
counter = 1
|
501
|
+
shapes.each do |shape|
|
502
|
+
if shape.visible? && shape.active? && shape.contains?(pos)
|
503
|
+
case mode
|
504
|
+
when SEARCHMODE::SELECTED
|
505
|
+
if shape.selected?
|
506
|
+
return shape if counter == zorder
|
507
|
+
counter += 1
|
508
|
+
end
|
509
|
+
when SEARCHMODE::UNSELECTED
|
510
|
+
unless shape.selected?
|
511
|
+
return shape if counter == zorder
|
512
|
+
counter += 1
|
513
|
+
end
|
514
|
+
when SEARCHMODE::BOTH
|
515
|
+
return shape if counter == zorder
|
516
|
+
counter += 1
|
517
|
+
end
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
521
|
+
nil
|
522
|
+
end
|
523
|
+
|
524
|
+
# Get list of all shapes located at given position
|
525
|
+
# @param [Wx::Point] pos Logical position
|
526
|
+
# @param [Array<Wx::SF::Shape>] shapes shape list where all found shapes will be stored
|
527
|
+
# @return [Array<Wx::SF::Shape>] shape list
|
528
|
+
# @see Wx::SF::ShapeCanvas::dp2lp
|
529
|
+
def get_shapes_at_position(pos, shapes = [])
|
530
|
+
pos = pos.to_point
|
531
|
+
get_shapes.each do |shape|
|
532
|
+
shapes << shape if shape.visible? && shape.active? && shape.contains?(pos)
|
533
|
+
end
|
534
|
+
end
|
535
|
+
|
536
|
+
# Get list of shapes located inside given rectangle
|
537
|
+
# @param [Wx::Rect] rct Examined rectangle
|
538
|
+
# @param [Array<Wx::SF::Shape>] shapes shape list where all found shapes will be stored
|
539
|
+
# @return [Array<Wx::SF::Shape>] shape list
|
540
|
+
def get_shapes_inside(rct, shapes = [])
|
541
|
+
get_shapes.each do |shape|
|
542
|
+
shapes << shape if shape.visible? && shape.active? && shape.intersects?(rct)
|
543
|
+
end
|
544
|
+
end
|
545
|
+
|
546
|
+
# Determines whether the diagram contains some shapes.
|
547
|
+
# @return true if there are no shapes in the diagram, otherwise false
|
548
|
+
def is_empty
|
549
|
+
@shapes.empty?
|
550
|
+
end
|
551
|
+
alias :empty? :is_empty
|
552
|
+
|
553
|
+
# Function finds out whether given shape has some children.
|
554
|
+
# @param [Wx::SF::Shape] parent potential parent shape
|
555
|
+
# @return [Boolean] true if the parent shape has children, otherwise false
|
556
|
+
def has_children(parent)
|
557
|
+
parent.has_children?
|
558
|
+
end
|
559
|
+
alias :has_children? :has_children
|
560
|
+
|
561
|
+
# Get neighbour shapes connected to given parent shape.
|
562
|
+
# @param [Wx::SF::Shape] parent parent shape (can be nil for all topmost shapes)
|
563
|
+
# @param [Class] shape_info Line object type
|
564
|
+
# @param [Wx::SF::Shape::CONNECTMODE] condir Connection direction
|
565
|
+
# @param [Boolean] direct set this flag to true if only closest shapes should be found
|
566
|
+
# otherwise also shapes connected by forked lines will be found (also
|
567
|
+
# constants DIRECT and INDIRECT can be used)
|
568
|
+
# @param [Array<Wx::SF::Shape>] neighbours List to add neighbour shapes to
|
569
|
+
# @return [Array<Wx::SF::Shape>] shape list
|
570
|
+
# @see Wx::SF::Shape::CONNECTMODE
|
571
|
+
def get_neighbours(parent, shape_info, condir, direct = true, neighbours = [])
|
572
|
+
if parent
|
573
|
+
parent.get_neighbours(shape_info, condir, direct, neighbours)
|
574
|
+
else
|
575
|
+
@shapes.each do |shape|
|
576
|
+
shape.get_neighbours(shape_info, condir, direct, neighbours)
|
577
|
+
end
|
578
|
+
end
|
579
|
+
end
|
580
|
+
|
581
|
+
private
|
582
|
+
|
583
|
+
# Update connection shapes after importing/dropping of new shapes
|
584
|
+
def check_new_shapes(new_shapes)
|
585
|
+
# deserializing will create unique ids synchronized across all deserialized shapes
|
586
|
+
# lines and both connected shapes should have matching ids
|
587
|
+
# we will remove any lines for which one or both connected shapes are missing (not copied)
|
588
|
+
new_shapes.select! do |shape|
|
589
|
+
if shape.is_a?(LineShape)
|
590
|
+
# so that lines with both connected shapes will have matching ids
|
591
|
+
# we will remove any lines for which one or both connected shapes are missing (not copied)
|
592
|
+
if @shapes.include?(shape.get_src_shape_id) && @shapes.include?(shape.get_trg_shape_id)
|
593
|
+
shape.create_handles
|
594
|
+
true # keep
|
595
|
+
else
|
596
|
+
# remove from diagram
|
597
|
+
@shapes.delete(shape)
|
598
|
+
false # remove from new_shapes
|
599
|
+
end
|
600
|
+
else
|
601
|
+
true # keep
|
602
|
+
end
|
603
|
+
end
|
604
|
+
# deserializing will create unique ids synchronized across all deserialized shapes
|
605
|
+
# so that grids and shapes linked to it's cells should have matching ids
|
606
|
+
# we will clear any cells for which shapes are missing (not copied)
|
607
|
+
update_grids(new_shapes) unless new_shapes.empty?
|
608
|
+
end
|
609
|
+
|
610
|
+
# Update grid shapes after importing/dropping of new shapes
|
611
|
+
def update_grids(new_shapes)
|
612
|
+
# deserializing will create unique ids synchronized across all deserialized shapes
|
613
|
+
# so that grids and shapes linked to it's cells will have matching ids
|
614
|
+
# we will clear any cells for which shapes are missing (not copied)
|
615
|
+
new_shapes.each do |shape|
|
616
|
+
if shape.is_a?(GridShape)
|
617
|
+
grid.each_cell do |row, col, id|
|
618
|
+
grid.clear_cell(row, col) unless id.nil? || @shapes.include?(id)
|
619
|
+
end
|
620
|
+
elsif shape.has_children?
|
621
|
+
shape.get_children_recursively(nil, Shape::SEARCHMODE::DFS).each do |child|
|
622
|
+
if shape.is_a?(GridShape)
|
623
|
+
grid.each_cell do |row, col, id|
|
624
|
+
grid.clear_cell(row, col) unless id.nil? || @shapes.include?(id)
|
625
|
+
end
|
626
|
+
end
|
627
|
+
end
|
628
|
+
end
|
629
|
+
end
|
630
|
+
end
|
631
|
+
|
632
|
+
# Shape lis (de-)serialization
|
633
|
+
def serialize_shapes(*arg)
|
634
|
+
unless arg.empty?
|
635
|
+
@shapes = arg.shift
|
636
|
+
@shapes.each { |shape| shape.set_diagram(self); shape.create_handles }
|
637
|
+
end
|
638
|
+
@shapes
|
639
|
+
end
|
640
|
+
|
641
|
+
# Set accepted shapes. Deserialization only.
|
642
|
+
# @param [Array<String>] shp_names
|
643
|
+
def set_accepted_shapes(shp_names)
|
644
|
+
@accepted_shapes.merge(shp_names.collect { |e| e.is_a?(::String) ? ::Object.const_get(e) : e })
|
645
|
+
end
|
646
|
+
|
647
|
+
# Set accepted top shapes. Deserialization only.
|
648
|
+
# @param [Array<String>] shp_names
|
649
|
+
def set_accepted_top_shapes(shp_names)
|
650
|
+
@accepted_top_shapes.merge(shp_names.collect { |e| e.is_a?(::String) ? ::Object.const_get(e) : e })
|
651
|
+
end
|
652
|
+
|
653
|
+
public def inspect
|
654
|
+
"#<Wx::SF::Diagram:#{object_id}>"
|
655
|
+
end
|
656
|
+
|
657
|
+
end
|
658
|
+
|
659
|
+
end
|