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