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,3285 @@
|
|
1
|
+
# Wx::SF::ShapeCanvas - shape canvas class
|
2
|
+
# Copyright (c) M.J.N. Corino, The Netherlands
|
3
|
+
|
4
|
+
require 'wx/shapes/shape_data_object'
|
5
|
+
require 'wx/shapes/canvas_history'
|
6
|
+
require 'wx/shapes/printout'
|
7
|
+
|
8
|
+
require 'tempfile'
|
9
|
+
require 'fileutils'
|
10
|
+
|
11
|
+
module Wx::SF
|
12
|
+
|
13
|
+
if Wx.has_feature?(:USE_DRAG_AND_DROP)
|
14
|
+
|
15
|
+
# Auxiliary class encapsulating shape drop target.
|
16
|
+
class CanvasDropTarget < Wx::DropTarget
|
17
|
+
|
18
|
+
# @param [Wx::DataObject] data
|
19
|
+
# @param [Wx::SF::ShapeCanvas] parent
|
20
|
+
def initialize(data, parent)
|
21
|
+
super(data)
|
22
|
+
@parent_canvas = parent
|
23
|
+
end
|
24
|
+
|
25
|
+
# @param [Integer] x
|
26
|
+
# @param [Integer] y
|
27
|
+
# @param [Wx::DragResult] deflt
|
28
|
+
# @return [Wx::DragResult]
|
29
|
+
def on_data(x, y, deflt)
|
30
|
+
return Wx::DragResult::DragNone unless get_data
|
31
|
+
|
32
|
+
@parent_canvas.__send__(:_on_drop, x, y, deflt, get_data_object)
|
33
|
+
deflt
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Class encapsulating a Shape canvas. The shape canvas is window control
|
40
|
+
# which extends the Wx::ScrolledWindow and is responsible for displaying of shapes diagrams.
|
41
|
+
# It also supports clipboard and drag&drop operations, undo/redo operations,
|
42
|
+
# and graphics exporting functions.
|
43
|
+
#
|
44
|
+
# This class is a core framework class and provides many member functions suitable for adding,
|
45
|
+
# removing, moving, resizing and drawing of shape objects. It can be used as it is or as a base class
|
46
|
+
# if necessary. In that case, the default class functionality can be enhanced by overriding
|
47
|
+
# its methods or by manual events handling. In both cases the user is responsible
|
48
|
+
# for invoking of default event handlers/virtual functions otherwise the
|
49
|
+
# built in functionality wont be available.
|
50
|
+
# @see Wx::SF::Diagram
|
51
|
+
class ShapeCanvas < Wx::ScrolledWindow
|
52
|
+
|
53
|
+
# Working modes
|
54
|
+
class MODE < Wx::Enum
|
55
|
+
# The shape canvas is in ready state (no operation is pending)
|
56
|
+
READY = self.new(0)
|
57
|
+
# Some shape handle is dragged
|
58
|
+
HANDLEMOVE = self.new(1)
|
59
|
+
# Handle of multiselection tool is dragged
|
60
|
+
MULTIHANDLEMOVE = self.new(2)
|
61
|
+
# Some shape/s is/are dragged
|
62
|
+
SHAPEMOVE = self.new(3)
|
63
|
+
# Multiple shape selection is in progress
|
64
|
+
MULTISELECTION = self.new(4)
|
65
|
+
# Interactive connection creation is in progress
|
66
|
+
CREATECONNECTION = self.new(5)
|
67
|
+
# Canvas is in the Drag&Drop mode
|
68
|
+
DND = self.new(6)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Selection modes
|
72
|
+
class SELECTIONMODE < Wx::Enum
|
73
|
+
NORMAL = self.new(0)
|
74
|
+
ADD = self.new(1)
|
75
|
+
REMOVE = self.new(2)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Search mode flags for GetShapeAtPosition function
|
79
|
+
class SEARCHMODE < Wx::Enum
|
80
|
+
# Search for selected shapes only
|
81
|
+
SELECTED = self.new(0)
|
82
|
+
# Search for unselected shapes only
|
83
|
+
UNSELECTED = self.new(1)
|
84
|
+
# Search for both selected and unselected shapes
|
85
|
+
BOTH = self.new(2)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Flags for AlignSelected function
|
89
|
+
class VALIGN < Wx::Enum
|
90
|
+
NONE = self.new(0)
|
91
|
+
TOP = self.new(1)
|
92
|
+
MIDDLE = self.new(2)
|
93
|
+
BOTTOM = self.new(3)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Flags for AlignSelected function
|
97
|
+
class HALIGN < Wx::Enum
|
98
|
+
NONE = self.new(0)
|
99
|
+
LEFT = self.new(1)
|
100
|
+
CENTER = self.new(2)
|
101
|
+
RIGHT = self.new(3)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Style flags
|
105
|
+
class STYLE < Wx::Enum
|
106
|
+
# Allow multiselection box.
|
107
|
+
MULTI_SELECTION = self.new(1)
|
108
|
+
# Allow shapes' size change done via the multiselection box.
|
109
|
+
MULTI_SIZE_CHANGE = self.new(2)
|
110
|
+
# Show grid.
|
111
|
+
GRID_SHOW = self.new(4)
|
112
|
+
# Use grid.
|
113
|
+
GRID_USE = self.new(8)
|
114
|
+
# Enable Drag & Drop operations.
|
115
|
+
DND = self.new(16)
|
116
|
+
# Enable Undo/Redo operations.
|
117
|
+
UNDOREDO = self.new(32)
|
118
|
+
# Enable the clipboard.
|
119
|
+
CLIPBOARD = self.new(64)
|
120
|
+
# Enable mouse hovering
|
121
|
+
HOVERING = self.new(128)
|
122
|
+
# Enable highlighting of shapes able to accept dragged shape(s).
|
123
|
+
HIGHLIGHTING = self.new(256)
|
124
|
+
# Use gradient color for the canvas background.
|
125
|
+
GRADIENT_BACKGROUND = self.new(512)
|
126
|
+
# Print also canvas background.
|
127
|
+
PRINT_BACKGROUND = self.new(1024)
|
128
|
+
# Process mouse wheel by the canvas (canvas scale will be changed).
|
129
|
+
PROCESS_MOUSEWHEEL = self.new(2048)
|
130
|
+
# Default canvas style.
|
131
|
+
DEFAULT_CANVAS_STYLE = MULTI_SELECTION | MULTI_SIZE_CHANGE | DND | UNDOREDO | CLIPBOARD | HOVERING | HIGHLIGHTING
|
132
|
+
end
|
133
|
+
|
134
|
+
# Flags for ShowShadow function.
|
135
|
+
class SHADOWMODE < Wx::Enum
|
136
|
+
# Show/hide shadow under topmost shapes only.
|
137
|
+
TOPMOST = self.new(0)
|
138
|
+
# Show/hide shadow under all shapes in the diagram.
|
139
|
+
ALL = self.new(1)
|
140
|
+
end
|
141
|
+
|
142
|
+
# Change mode flags
|
143
|
+
class CHANGE < Wx::Enum
|
144
|
+
SET_SCALE = self.new(0)
|
145
|
+
RESCALED = self.new(1)
|
146
|
+
VIRTUAL_SIZE = self.new(2)
|
147
|
+
FOCUS = self.new(3)
|
148
|
+
end
|
149
|
+
|
150
|
+
# Printing modes used by SetPrintMode() function.
|
151
|
+
class PRINTMODE < Wx::Enum
|
152
|
+
# This sets the user scale and origin of the DC so that the image fits
|
153
|
+
# within the paper rectangle (but the edges could be cut off by printers
|
154
|
+
# that can't print to the edges of the paper -- which is most of them. Use
|
155
|
+
# this if your image already has its own margins.
|
156
|
+
FIT_TO_PAPER = self.new(0)
|
157
|
+
# This sets the user scale and origin of the DC so that the image fits
|
158
|
+
# within the page rectangle, which is the printable area on Mac and MSW
|
159
|
+
# and is the entire page on other platforms.
|
160
|
+
FIT_TO_PAGE = self.new(1)
|
161
|
+
# This sets the user scale and origin of the DC so that the image fits
|
162
|
+
# within the page margins as specified by g_PageSetupData, which you can
|
163
|
+
# change (on some platforms, at least) in the Page Setup dialog. Note that
|
164
|
+
# on Mac, the native Page Setup dialog doesn't let you change the margins
|
165
|
+
# of a Wx::PageSetupDialogData object, so you'll have to write your own dialog or
|
166
|
+
# use the Mac-only Wx::MacPageMarginsDialog, as we do in this program.
|
167
|
+
FIT_TO_MARGINS = self.new(2)
|
168
|
+
# This sets the user scale and origin of the DC so that you could map the
|
169
|
+
# screen image to the entire paper at the same size as it appears on screen.
|
170
|
+
MAP_TO_PAPER = self.new(3)
|
171
|
+
# This sets the user scale and origin of the DC so that the image appears
|
172
|
+
# on the paper at the same size that it appears on screen (i.e., 10-point
|
173
|
+
# type on screen is 10-point on the printed page).
|
174
|
+
MAP_TO_PAGE = self.new(4)
|
175
|
+
# This sets the user scale and origin of the DC so that you could map the
|
176
|
+
# screen image to the page margins specified by the native Page Setup dialog at the same
|
177
|
+
# size as it appears on screen.
|
178
|
+
MAP_TO_MARGINS = self.new(5)
|
179
|
+
# This sets the user scale and origin of the DC so that you can to do you own
|
180
|
+
# scaling in order to draw objects at full native device resolution.
|
181
|
+
MAP_TO_DEVICE = self.new(6)
|
182
|
+
end
|
183
|
+
|
184
|
+
class PRECON_FINISH_STATE < Wx::Enum
|
185
|
+
# Finish line connection.
|
186
|
+
OK = self.new(0)
|
187
|
+
# Cancel line connection and abort the interactive connection.
|
188
|
+
FAILED_AND_CANCEL_LINE = self.new(1)
|
189
|
+
# Cancel line connection and continue with the interactive connection.
|
190
|
+
FAILED_AND_CONTINUE_EDIT = self.new(2)
|
191
|
+
end
|
192
|
+
|
193
|
+
# Default values
|
194
|
+
module DEFAULT
|
195
|
+
# Default value of Wx::SF::CanvasSettings @background_color data member
|
196
|
+
BACKGROUNDCOLOR = Wx::Colour.new(240, 240, 240) if Wx::App.is_main_loop_running
|
197
|
+
Wx.add_delayed_constant(self, :BACKGROUNDCOLOR) { Wx::Colour.new(240, 240, 240) }
|
198
|
+
# Default value of Wx::SF::CanvasSettings @grid_size data member
|
199
|
+
GRIDSIZE = Wx::Size.new(10, 10)
|
200
|
+
# Default value of Wx::SF::CanvasSettings @grid_line_mult data member
|
201
|
+
GRIDLINEMULT = 1
|
202
|
+
# Default value of Wx::SF::CanvasSettings @grid_color data member
|
203
|
+
GRIDCOLOR = Wx::Colour.new(200, 200, 200) if Wx::App.is_main_loop_running
|
204
|
+
Wx.add_delayed_constant(self, :GRIDCOLOR) { Wx::Colour.new(200, 200, 200) }
|
205
|
+
# Default value of Wx::SF::CanvasSettings @grid_style data member
|
206
|
+
GRIDSTYLE = Wx::PenStyle::PENSTYLE_SOLID
|
207
|
+
# Default value of Wx::SF::CanvasSettings @common_hover_color data member
|
208
|
+
HOVERCOLOR = Wx::Colour.new(120, 120, 255) if Wx::App.is_main_loop_running
|
209
|
+
Wx.add_delayed_constant(self, :HOVERCOLOR) { Wx::Colour.new(120, 120, 255) }
|
210
|
+
# Default value of Wx::SF::CanvasSettings @gradient_from data member
|
211
|
+
GRADIENT_FROM = Wx::Colour.new(240, 240, 240) if Wx::App.is_main_loop_running
|
212
|
+
Wx.add_delayed_constant(self, :GRADIENT_FROM) { Wx::Colour.new(240, 240, 240) }
|
213
|
+
# Default value of Wx::SF::CanvasSettings @gradient_to data member
|
214
|
+
GRADIENT_TO = Wx::Colour.new(200, 200, 255) if Wx::App.is_main_loop_running
|
215
|
+
Wx.add_delayed_constant(self, :GRADIENT_TO) { Wx::Colour.new(200, 200, 255) }
|
216
|
+
# Default value of Wx::SF::CanvasSettings @style data member
|
217
|
+
CANVAS_STYLE = STYLE::DEFAULT_CANVAS_STYLE
|
218
|
+
# Default value of Wx::SF::CanvasSettings @shadow_offset data member
|
219
|
+
SHADOWOFFSET = Wx::RealPoint.new(4, 4)
|
220
|
+
# Default shadow colour
|
221
|
+
SHADOWCOLOR = Wx::Colour.new(150, 150, 150, 128) if Wx::App.is_main_loop_running
|
222
|
+
Wx.add_delayed_constant(self, :SHADOWCOLOR) { Wx::Colour.new(150, 150, 150, 128) }
|
223
|
+
# Default value of Wx::SF::CanvasSettings @shadow_fill data member
|
224
|
+
SHADOWBRUSH = Wx::Brush.new(SHADOWCOLOR.call, Wx::BrushStyle::BRUSHSTYLE_SOLID) if Wx::App.is_main_loop_running
|
225
|
+
Wx.add_delayed_constant(self, :SHADOWBRUSH) { Wx::Brush.new(Wx::Colour.new(150, 150, 150, 128), Wx::BrushStyle::BRUSHSTYLE_SOLID) }
|
226
|
+
# Default value of Wx::SF::CanvasSettings @print_h_align data member
|
227
|
+
PRINT_HALIGN = HALIGN::CENTER
|
228
|
+
# Default value of Wx::SF::CanvasSettings @print_v_align data member
|
229
|
+
PRINT_VALIGN = VALIGN::MIDDLE
|
230
|
+
# Default value of Wx::SF::CanvasSettings @print_mode data member
|
231
|
+
PRINT_MODE = PRINTMODE::FIT_TO_MARGINS
|
232
|
+
# Default value of Wx::SF::CanvasSettings @min_scale data member
|
233
|
+
SCALE_MIN = 0.1
|
234
|
+
# Default value of Wx::SF::CanvasSettings @max_scale data member
|
235
|
+
SCALE_MAX = 5.0
|
236
|
+
end
|
237
|
+
|
238
|
+
class << self
|
239
|
+
|
240
|
+
def gc_enabled?
|
241
|
+
if @gc_enabled.nil?
|
242
|
+
@gc_enabled = Wx.has_feature?(:USE_GRAPHICS_CONTEXT)
|
243
|
+
end
|
244
|
+
@gc_enabled
|
245
|
+
end
|
246
|
+
|
247
|
+
def enable_gc(f = true)
|
248
|
+
if Wx.has_feature?(:USE_GRAPHICS_CONTEXT)
|
249
|
+
@gc_enabled = f
|
250
|
+
else
|
251
|
+
@gc_enabled = false
|
252
|
+
Wx.log_warning(%Q{Couldn't enable Graphics context due to missing USE_GRAPHICS_CONTEXT})
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
TLS_LOADING_VERSION_KEY = :loading_version.freeze
|
257
|
+
private_constant :TLS_LOADING_VERSION_KEY
|
258
|
+
|
259
|
+
def compat_loading?
|
260
|
+
!!::Thread::current[TLS_LOADING_VERSION_KEY]
|
261
|
+
end
|
262
|
+
|
263
|
+
def compat_loading_version
|
264
|
+
::Thread::current[TLS_LOADING_VERSION_KEY]
|
265
|
+
end
|
266
|
+
|
267
|
+
def set_compat_loading(ver_info)
|
268
|
+
::Thread::current[TLS_LOADING_VERSION_KEY] = ver_info
|
269
|
+
end
|
270
|
+
|
271
|
+
def reset_compat_loading
|
272
|
+
::Thread::current[TLS_LOADING_VERSION_KEY] = nil
|
273
|
+
end
|
274
|
+
|
275
|
+
def print_data
|
276
|
+
_init_printing unless @print_data
|
277
|
+
@print_data
|
278
|
+
end
|
279
|
+
|
280
|
+
def print_data=(pd)
|
281
|
+
@print_data = pd
|
282
|
+
end
|
283
|
+
|
284
|
+
def page_setup_data
|
285
|
+
_init_printing unless @page_setup_data
|
286
|
+
@page_setup_data
|
287
|
+
end
|
288
|
+
|
289
|
+
def page_setup_data=(psd)
|
290
|
+
@page_setup_data = psd
|
291
|
+
end
|
292
|
+
|
293
|
+
def _init_printing
|
294
|
+
@print_data = Wx::PRT::PrintData.new
|
295
|
+
# You could set an initial paper size here
|
296
|
+
# @print_data.set_paper_id(Wx::PaperSize::PAPER_LETTER) # for Americans
|
297
|
+
@print_data.set_paper_id(Wx::PaperSize::PAPER_A4) # for everyone else
|
298
|
+
|
299
|
+
# copy over initial paper size from print record
|
300
|
+
@page_setup_data = Wx::PRT::PageSetupDialogData.new(@print_data)
|
301
|
+
# Set some initial page margins in mm.
|
302
|
+
@page_setup_data.set_margin_top_left([15, 15])
|
303
|
+
@page_setup_data.set_margin_bottom_right([15, 15])
|
304
|
+
end
|
305
|
+
private :_init_printing
|
306
|
+
|
307
|
+
end
|
308
|
+
|
309
|
+
# Auxiliary serializable class encapsulating canvas version info
|
310
|
+
# and providing version check on loading.
|
311
|
+
class Version
|
312
|
+
|
313
|
+
class Exception < SFException; end
|
314
|
+
|
315
|
+
VersionInfo = ::Struct.new(:major, :minor, :release) do
|
316
|
+
def to_s
|
317
|
+
"#{major}.#{minor}.#{release}"
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
include Serializable
|
322
|
+
|
323
|
+
property :version_info
|
324
|
+
|
325
|
+
def initialize
|
326
|
+
# get version numbers as [major, minor, release]
|
327
|
+
@version_info = VersionInfo.new(*Wx::SF::VERSION.split(/\D/).shift(3).collect { |s| s.to_i })
|
328
|
+
end
|
329
|
+
|
330
|
+
attr_reader :version_info
|
331
|
+
|
332
|
+
# Deserialization only.
|
333
|
+
def set_version_info(ver_info)
|
334
|
+
if @version_info.major < ver_info.major ||
|
335
|
+
(@version_info.major == ver_info.major && @version_info.minor < ver_info.minor)
|
336
|
+
::Kernel.raise Version::Exception, "Incompatible Wx::SF diagram version #{ver_info} cannot be loaded (current Wx::SF version #{@version_info})."
|
337
|
+
elsif @version_info.major > ver_info.major ||
|
338
|
+
(@version_info.major == ver_info.major && @version_info.minor > ver_info.minor)
|
339
|
+
# this should normally work but may give trouble
|
340
|
+
# set compat loading info
|
341
|
+
ShapeCanvas.set_compat_loading(ver_info)
|
342
|
+
else
|
343
|
+
# this should never give any trouble
|
344
|
+
end
|
345
|
+
end
|
346
|
+
private :set_version_info
|
347
|
+
|
348
|
+
end
|
349
|
+
|
350
|
+
# Auxiliary serializable class encapsulating the canvas properties.
|
351
|
+
class Settings
|
352
|
+
|
353
|
+
include Serializable
|
354
|
+
|
355
|
+
include DEFAULT
|
356
|
+
|
357
|
+
property :scale, :min_scale, :max_scale, :background_color, :common_hover_color,
|
358
|
+
:grid_size, :grid_line_mult, :grid_color, :grid_style,
|
359
|
+
:gradient_from, :gradient_to, :style, :shadow_offset, :shadow_fill,
|
360
|
+
:print_h_align, :print_v_align, :print_mode
|
361
|
+
|
362
|
+
def initialize
|
363
|
+
@scale = 1.0
|
364
|
+
@min_scale = SCALE_MIN
|
365
|
+
@max_scale = SCALE_MAX
|
366
|
+
@background_color = BACKGROUNDCOLOR
|
367
|
+
@common_hover_color = HOVERCOLOR
|
368
|
+
@grid_size = GRIDSIZE.dup
|
369
|
+
@grid_line_mult = GRIDLINEMULT
|
370
|
+
@grid_color = GRIDCOLOR
|
371
|
+
@grid_style = GRIDSTYLE
|
372
|
+
@gradient_from = GRADIENT_FROM
|
373
|
+
@gradient_to = GRADIENT_TO
|
374
|
+
@style = CANVAS_STYLE
|
375
|
+
@shadow_offset = SHADOWOFFSET.dup
|
376
|
+
@shadow_fill = SHADOWBRUSH
|
377
|
+
@print_h_align = PRINT_HALIGN
|
378
|
+
@print_v_align = PRINT_VALIGN
|
379
|
+
@print_mode = PRINT_MODE
|
380
|
+
end
|
381
|
+
|
382
|
+
attr_accessor :scale, :min_scale, :max_scale, :background_color, :common_hover_color,
|
383
|
+
:grid_size, :grid_line_mult, :grid_color, :grid_style,
|
384
|
+
:gradient_from, :gradient_to, :style, :shadow_offset, :shadow_fill,
|
385
|
+
:print_h_align, :print_v_align, :print_mode
|
386
|
+
|
387
|
+
end
|
388
|
+
|
389
|
+
# @overload initialize()
|
390
|
+
# Default constructor
|
391
|
+
# @overload initialize(diagram, parent, id = Wx::ID_ANY, pos = Wx::DEFAULT_POSITION, size = Wx::DEFAULT_SIZE, style = Wx::HSCROLL | Wx::VSCROLL)
|
392
|
+
# Constructor
|
393
|
+
# @param [Wx::SF::Diagram] diagram shape diagram
|
394
|
+
# @param [Wx::Window] parent Parent window
|
395
|
+
# @param [Integer] id Window ID
|
396
|
+
# @param [Wx::Point] pos Initial position
|
397
|
+
# @param [Wx::Size] size Initial size
|
398
|
+
# @param [Integer] style Window style
|
399
|
+
def initialize(diagram = nil, *mixed_args)
|
400
|
+
super()
|
401
|
+
|
402
|
+
@dnd_started_here = false
|
403
|
+
@dnd_started_at = nil
|
404
|
+
@can_save_state_on_mouse_up = false
|
405
|
+
@working_mode = MODE::READY
|
406
|
+
@selection_mode = SELECTIONMODE::NORMAL
|
407
|
+
@selected_handle = nil
|
408
|
+
@selection_start = Wx::RealPoint.new
|
409
|
+
@new_line_shape = nil
|
410
|
+
@unselected_shape_under_cursor = nil
|
411
|
+
@selected_shape_under_cursor = nil
|
412
|
+
@topmost_shape_under_cursor = nil
|
413
|
+
@current_shapes = []
|
414
|
+
@invalidate_rect = nil
|
415
|
+
|
416
|
+
@prev_mouse_pos = Wx::Point.new
|
417
|
+
@prev_positions = {}
|
418
|
+
|
419
|
+
@settings = Settings.new
|
420
|
+
@canvas_history = CanvasHistory.new
|
421
|
+
|
422
|
+
if diagram
|
423
|
+
parent = mixed_args.first.is_a?(Wx::Window) ? mixed_args.shift : nil
|
424
|
+
real_args = []
|
425
|
+
begin
|
426
|
+
real_args = [parent] + Wx::ScrolledWindow.args_as_list(*mixed_args)
|
427
|
+
create(*real_args)
|
428
|
+
rescue => err
|
429
|
+
msg = "Error initializing #{self.inspect}\n" +
|
430
|
+
" : #{err.message} \n" +
|
431
|
+
"Provided are #{real_args} \n" +
|
432
|
+
"Correct parameters for #{self.class.name}.new are:\n" +
|
433
|
+
self.class.describe_constructor
|
434
|
+
|
435
|
+
new_err = err.class.new(msg)
|
436
|
+
new_err.set_backtrace(caller)
|
437
|
+
Kernel.raise new_err
|
438
|
+
end
|
439
|
+
|
440
|
+
self.diagram = diagram
|
441
|
+
|
442
|
+
save_canvas_state
|
443
|
+
end
|
444
|
+
|
445
|
+
# set up event handlers
|
446
|
+
evt_paint :_on_paint
|
447
|
+
evt_erase_background :_on_erase_background
|
448
|
+
evt_left_down :_on_left_down
|
449
|
+
evt_left_up :_on_left_up
|
450
|
+
evt_right_down :_on_right_down
|
451
|
+
evt_right_up :_on_right_up
|
452
|
+
evt_left_dclick :_on_left_double_click
|
453
|
+
evt_right_dclick :_on_right_double_click
|
454
|
+
evt_motion :_on_mouse_move
|
455
|
+
evt_mousewheel :_on_mouse_wheel
|
456
|
+
evt_key_down :_on_key_down
|
457
|
+
evt_enter_window :_on_enter_window
|
458
|
+
evt_leave_window :_on_leave_window
|
459
|
+
evt_size :_on_resize
|
460
|
+
end
|
461
|
+
|
462
|
+
# Creates the window in two-step construction mode. set_diagram() function must also be called to complete the canvas initialization.
|
463
|
+
# @param [Wx::Window] parent Parent window
|
464
|
+
# @param [Integer] id Window ID
|
465
|
+
# @param [Wx::Point] pos Initial position
|
466
|
+
# @param [Wx::Size] size Initial size
|
467
|
+
# @param [Integer] style Window style
|
468
|
+
# @param [String] name Window name
|
469
|
+
def create(parent, id = -1, pos = Wx::DEFAULT_POSITION, size = Wx::DEFAULT_SIZE, style = (Wx::HSCROLL | Wx::VSCROLL), name = "Wx::ScrolledWindow")
|
470
|
+
# NOTE: user must call Wx::SF::ShapeCanvas#set_diagram() to complete
|
471
|
+
# canvas initialization!
|
472
|
+
|
473
|
+
# perform basic window initialization
|
474
|
+
super
|
475
|
+
|
476
|
+
# set drop target
|
477
|
+
if Wx.has_feature?(:USE_DRAG_AND_DROP)
|
478
|
+
set_drop_target(Wx::SF::CanvasDropTarget.new(Wx::SF::ShapeDataObject.new, self))
|
479
|
+
end
|
480
|
+
|
481
|
+
# initialize selection rectangle
|
482
|
+
@shp_selection = MultiSelRect.new
|
483
|
+
@shp_selection.send(:set_id, nil)
|
484
|
+
@shp_selection.create_handles
|
485
|
+
@shp_selection.select(true)
|
486
|
+
@shp_selection.show(false)
|
487
|
+
@shp_selection.show_handles(true)
|
488
|
+
|
489
|
+
# initialize multi-edit rectangle
|
490
|
+
@shp_multi_edit = MultiSelRect.new
|
491
|
+
@shp_multi_edit.send(:set_id, nil)
|
492
|
+
@shp_multi_edit.create_handles
|
493
|
+
@shp_multi_edit.select(true)
|
494
|
+
@shp_multi_edit.show(false)
|
495
|
+
@shp_multi_edit.show_handles(true)
|
496
|
+
|
497
|
+
set_scrollbars(5, 5, 100, 100)
|
498
|
+
set_background_style(Wx::BG_STYLE_PAINT)
|
499
|
+
|
500
|
+
true
|
501
|
+
end
|
502
|
+
|
503
|
+
attr_reader :settings
|
504
|
+
|
505
|
+
# Returns the shape diagram which shapes are displayed on this canvas.
|
506
|
+
# @return [Wx::SF::Diagram]
|
507
|
+
def get_diagram
|
508
|
+
@diagram
|
509
|
+
end
|
510
|
+
alias :diagram :get_diagram
|
511
|
+
|
512
|
+
# Set the shape diagram to display on this canvas
|
513
|
+
# @param [Wx::SF::Diagram] diagram
|
514
|
+
def set_diagram(diagram)
|
515
|
+
@diagram = diagram
|
516
|
+
@shp_selection.set_diagram(@diagram)
|
517
|
+
@shp_multi_edit.set_diagram(@diagram)
|
518
|
+
@diagram.shape_canvas = self if @diagram
|
519
|
+
clear_temporaries
|
520
|
+
@diagram.update_all
|
521
|
+
end
|
522
|
+
|
523
|
+
# Load serialized canvas content (diagrams).
|
524
|
+
# @overload load_canvas(file)
|
525
|
+
# @param [String] file Full file name
|
526
|
+
# @return [self]
|
527
|
+
# @overload load_canvas(io)
|
528
|
+
# @param [IO] io IO object
|
529
|
+
# @return [self]
|
530
|
+
def load_canvas(io)
|
531
|
+
# get IO stream to read from
|
532
|
+
ios = io.is_a?(::String) ? File.open(io, 'r') : io
|
533
|
+
begin
|
534
|
+
_, @settings, diagram = Serializable.deserialize(ios)
|
535
|
+
rescue SFException
|
536
|
+
::Kernel.raise
|
537
|
+
rescue ::Exception
|
538
|
+
::Kernel.raise SFException, "Failed to load canvas: #{$!.message}"
|
539
|
+
ensure
|
540
|
+
ShapeCanvas.reset_compat_loading
|
541
|
+
ios.close if io.is_a?(::String) && ios
|
542
|
+
end
|
543
|
+
set_diagram(diagram)
|
544
|
+
clear_canvas_history
|
545
|
+
save_canvas_state
|
546
|
+
set_scale(@settings.scale)
|
547
|
+
update_virtual_size
|
548
|
+
refresh(false)
|
549
|
+
|
550
|
+
@diagram.set_modified(false)
|
551
|
+
|
552
|
+
self
|
553
|
+
end
|
554
|
+
|
555
|
+
# Save canvas content (diagrams).
|
556
|
+
# @overload save_canvas(file, compact: true)
|
557
|
+
# @param [String] file Full file name
|
558
|
+
# @param [Boolean] compact specifies whether to write content in compact mode (true) or not (false)
|
559
|
+
# @return [self]
|
560
|
+
# @overload save_canvas(io, compact: true)
|
561
|
+
# @param [IO] io IO object
|
562
|
+
# @param [Boolean] compact specifies whether to write content in compact mode (true) or not (false)
|
563
|
+
# @return [self]
|
564
|
+
def save_canvas(io, compact: true)
|
565
|
+
return self unless @diagram
|
566
|
+
# get IO stream to write to
|
567
|
+
ios = io.is_a?(::String) ? Tempfile.new(File.basename(io, '.*')) : io
|
568
|
+
# write canvas data to temp file
|
569
|
+
begin
|
570
|
+
[Version.new, @settings, @diagram].serialize(ios, pretty: !compact)
|
571
|
+
rescue SFException
|
572
|
+
::Kernel.raise
|
573
|
+
rescue Exception
|
574
|
+
::Kernel.raise SFException, "Error writing canvas: #{$!.message}"
|
575
|
+
end
|
576
|
+
if io.is_a?(::String)
|
577
|
+
ios.close(false) # close but keep temp file
|
578
|
+
full_path = File.absolute_path(io)
|
579
|
+
if File.exist?(full_path)
|
580
|
+
# create temporary backup
|
581
|
+
ftmp = Tempfile.new(File.basename(io))
|
582
|
+
ftmp_name = ftmp.path.dup
|
583
|
+
ftmp.close(true) # close AND unlink
|
584
|
+
FileUtils::mv(full_path, ftmp_name) # backup existing file
|
585
|
+
# replace original
|
586
|
+
begin
|
587
|
+
# rename newly generated file
|
588
|
+
FileUtils.mv(ios.path, full_path)
|
589
|
+
# preserve file mode
|
590
|
+
FileUtils.chmod(File.lstat(ftmp_name).mode, full_path)
|
591
|
+
rescue Exception
|
592
|
+
# restore backup
|
593
|
+
FileUtils.mv(ftmp_name, full_path)
|
594
|
+
::Kernel.raise SFException, "Unable to save canvas file #{io}: #{$!.message}"
|
595
|
+
end
|
596
|
+
# remove backup
|
597
|
+
FileUtils.rm_f(ftmp_name)
|
598
|
+
else
|
599
|
+
begin
|
600
|
+
# rename newly generated file
|
601
|
+
FileUtils.mv(ios.path, full_path)
|
602
|
+
rescue Exception
|
603
|
+
::Kernel.raise SFException, "Unable to save canvas file #{io}: #{$!.message}"
|
604
|
+
end
|
605
|
+
end
|
606
|
+
end
|
607
|
+
|
608
|
+
@diagram.set_modified(false)
|
609
|
+
|
610
|
+
self
|
611
|
+
end
|
612
|
+
|
613
|
+
# @overload save_canvas_to_image(type: Wx::BITMAP_TYPE_BMP, background: true, scale: -1.0)
|
614
|
+
# Export canvas content to image.
|
615
|
+
# @param [Wx::BitmapType] type Image type. See Wx::BitmapType for more details. Default type is Wx::BITMAP_TYPE_BMP.
|
616
|
+
# @param [Boolean] background Export also diagram background
|
617
|
+
# @param [Float] scale Image scale. If -1 then current canvas scale id used.
|
618
|
+
# @return [Wx::Bitmap,nil] exported canvas image or nil if failed to create bitmap
|
619
|
+
# @overload save_canvas_to_image(file, type: Wx::BITMAP_TYPE_BMP, background: true, scale: -1.0)
|
620
|
+
# Export canvas content to image file.
|
621
|
+
# @param [String] file Full file name
|
622
|
+
# @param [Wx::BitmapType] type Image type. See Wx::BitmapType for more details. Default type is Wx::BITMAP_TYPE_BMP.
|
623
|
+
# @param [Boolean] background Export also diagram background
|
624
|
+
# @param [Float] scale Image scale. If -1 then current canvas scale id used.
|
625
|
+
# @return [Boolean] true if saving the image to file succeeded, false otherwise
|
626
|
+
def save_canvas_to_image(file = nil, type: Wx::BITMAP_TYPE_BMP, background: true, scale: -1.0)
|
627
|
+
# create memory DC a draw the canvas content into
|
628
|
+
|
629
|
+
prev_scale = get_scale
|
630
|
+
scale = prev_scale if scale == -1
|
631
|
+
|
632
|
+
bmp_bb = get_total_bounding_box
|
633
|
+
|
634
|
+
bmp_bb.left = (bmp_bb.left * scale).to_i
|
635
|
+
bmp_bb.top = (bmp_bb.top * scale).to_i
|
636
|
+
bmp_bb.width = (bmp_bb.width * scale).to_i
|
637
|
+
bmp_bb.height = (bmp_bb.height * scale).to_i
|
638
|
+
|
639
|
+
bmp_bb.inflate!(@settings.grid_size * scale)
|
640
|
+
|
641
|
+
outbmp = Wx::Bitmap.new(bmp_bb.width, bmp_bb.height)
|
642
|
+
Wx::MemoryDC.draw_on(outbmp) do |mdc|
|
643
|
+
|
644
|
+
Wx::ScaledDC.draw_on(mdc, scale) do |outdc|
|
645
|
+
|
646
|
+
if outdc.ok?
|
647
|
+
set_scale(scale) if scale != prev_scale
|
648
|
+
|
649
|
+
outdc.set_device_origin(-bmp_bb.left, -bmp_bb.top)
|
650
|
+
|
651
|
+
prev_style = get_style
|
652
|
+
prev_colour = get_canvas_colour
|
653
|
+
|
654
|
+
unless background
|
655
|
+
remove_style(STYLE::GRADIENT_BACKGROUND)
|
656
|
+
remove_style(STYLE::GRID_SHOW)
|
657
|
+
set_canvas_colour(Wx::WHITE)
|
658
|
+
end
|
659
|
+
|
660
|
+
draw_background(outdc, NOT_FROM_PAINT)
|
661
|
+
draw_content(outdc, NOT_FROM_PAINT)
|
662
|
+
draw_foreground( outdc, NOT_FROM_PAINT)
|
663
|
+
|
664
|
+
unless background
|
665
|
+
set_style(prev_style)
|
666
|
+
set_canvas_colour(prev_colour)
|
667
|
+
end
|
668
|
+
|
669
|
+
set_scale(prev_scale) if scale != prev_scale
|
670
|
+
|
671
|
+
if file
|
672
|
+
return outbmp.save_file(file, type)
|
673
|
+
else
|
674
|
+
return outbmp
|
675
|
+
end
|
676
|
+
elsif file
|
677
|
+
Wx.message_box('Could not create output bitmap.', 'wxRuby ShapeFramework', Wx::OK | Wx::ICON_ERROR)
|
678
|
+
end
|
679
|
+
end
|
680
|
+
end
|
681
|
+
nil
|
682
|
+
end
|
683
|
+
|
684
|
+
def _start_interactive_connection(lpos, src_shape_id, cpt)
|
685
|
+
if @new_line_shape
|
686
|
+
@working_mode = MODE::CREATECONNECTION
|
687
|
+
@new_line_shape.send(:set_line_mode, LineShape::LINEMODE::UNDERCONSTRUCTION)
|
688
|
+
|
689
|
+
@new_line_shape.set_src_shape_id(src_shape_id)
|
690
|
+
|
691
|
+
# switch on the "under-construction" mode
|
692
|
+
@new_line_shape.send(:set_unfinished_point, lpos)
|
693
|
+
# assign starting point of new line shapes to the nearest connection point of
|
694
|
+
# connected shape if exists
|
695
|
+
@new_line_shape.set_starting_connection_point(cpt)
|
696
|
+
ERRCODE::OK
|
697
|
+
else
|
698
|
+
ERRCODE::NOT_CREATED
|
699
|
+
end
|
700
|
+
end
|
701
|
+
private :_start_interactive_connection
|
702
|
+
|
703
|
+
# Start interactive connection creation.
|
704
|
+
#
|
705
|
+
# This function switches the canvas to a mode in which a new shape connection
|
706
|
+
# can be created interactively (by mouse operations). Every connection must
|
707
|
+
# start and finish in some shape object or another connection. At the end of the
|
708
|
+
# process the on_connection_finished event handler is invoked so the user can
|
709
|
+
# set needed connection properties immediately.
|
710
|
+
#
|
711
|
+
# Function must be called from mouse event handler and the event must be passed
|
712
|
+
# to the function.
|
713
|
+
# @overload start_interactive_connection(shape_info, pos)
|
714
|
+
# @param [Class] shape_info Connection type
|
715
|
+
# @param [Wx::Point] pos Position where to start
|
716
|
+
# @return [Wx::SF::ERRCODE] operation result
|
717
|
+
# @overload start_interactive_connection(shape, pos)
|
718
|
+
# @param [Wx::SF::LineShape] shape existing line shape object which will be used as a connection.
|
719
|
+
# @param [Wx::Point] pos Position where to start
|
720
|
+
# @return [Wx::SF::ERRCODE] err operation result
|
721
|
+
# @overload start_interactive_connection(shape, connection_point, pos)
|
722
|
+
# @param [Wx::SF::LineShape] shape existing line shape object which will be used as a connection.
|
723
|
+
# @param [Wx::SF::ConnectionPoint] connection_point Initial connection point
|
724
|
+
# @param [Wx::Point] pos Position where to start
|
725
|
+
# @return [Wx::SF::ERRCODE] err operation result
|
726
|
+
# @see create_connection
|
727
|
+
def start_interactive_connection(*args)
|
728
|
+
return ERRCODE::INVALID_INPUT unless @diagram
|
729
|
+
|
730
|
+
shape_info = shape = pos = connection_point = nil
|
731
|
+
shape_klass = nil
|
732
|
+
case args.first
|
733
|
+
when Wx::SF::LineShape
|
734
|
+
shape = args.shift
|
735
|
+
shape_klass = shape.class.name
|
736
|
+
if args.first.is_a?(Wx::SF::ConnectionPoint)
|
737
|
+
connection_point = args.shift
|
738
|
+
end
|
739
|
+
pos = args.shift.to_point
|
740
|
+
when ::Class
|
741
|
+
shape_info = args.shift
|
742
|
+
pos = args.shift.to_point
|
743
|
+
shape_klass = shape_info.name
|
744
|
+
end
|
745
|
+
::Kernel.raise ArgumentError, "Invalid arguments #{args}" unless args.empty?
|
746
|
+
return ERRCODE::INVALID_INPUT unless pos
|
747
|
+
|
748
|
+
lpos = dp2lp(pos)
|
749
|
+
|
750
|
+
if @working_mode == MODE::READY && ((shape_info && shape_info <= Wx::SF::LineShape) || (shape.is_a?(Wx::SF::LineShape)))
|
751
|
+
|
752
|
+
if connection_point
|
753
|
+
|
754
|
+
if @diagram.contains?(shape)
|
755
|
+
@new_line_shape = shape
|
756
|
+
else
|
757
|
+
@new_line_shape = @diagram.add_shape(shape, nil, Wx::DEFAULT_POSITION, INITIALIZE, DONT_SAVE_STATE)
|
758
|
+
end
|
759
|
+
return _start_interactive_connection(lpos, connection_point.get_parent_shape.id, connection_point)
|
760
|
+
|
761
|
+
else
|
762
|
+
|
763
|
+
shape_under = get_shape_at_position(lpos)
|
764
|
+
if shape_info
|
765
|
+
# propagate request for interactive connection if requested
|
766
|
+
shape_under = shape_under.get_parent_shape while shape_under &&
|
767
|
+
shape_under.has_style?(Shape::STYLE::PROPAGATE_INTERACTIVE_CONNECTION)
|
768
|
+
end
|
769
|
+
|
770
|
+
# start the connection's creation process if possible
|
771
|
+
if shape_under&.id && shape_under.is_connection_accepted(shape_klass)
|
772
|
+
if shape && @diagram.contains?(shape)
|
773
|
+
@new_line_shape = shape
|
774
|
+
else
|
775
|
+
if shape
|
776
|
+
err = @diagram.add_shape(shape, nil, Wx::DEFAULT_POSITION, INITIALIZE, DONT_SAVE_STATE)
|
777
|
+
else
|
778
|
+
err, shape = @diagram.create_shape(shape_info, DONT_SAVE_STATE)
|
779
|
+
end
|
780
|
+
@new_line_shape = (err == ERRCODE::OK ? shape : nil)
|
781
|
+
end
|
782
|
+
return _start_interactive_connection(lpos, shape_under.id, shape_under.get_nearest_connection_point(lpos.to_real))
|
783
|
+
else
|
784
|
+
return ERRCODE::NOT_ACCEPTED
|
785
|
+
end
|
786
|
+
|
787
|
+
end
|
788
|
+
end
|
789
|
+
ERRCODE::INVALID_INPUT
|
790
|
+
end
|
791
|
+
|
792
|
+
# Abort interactive connection creation process
|
793
|
+
def abort_interactive_connection
|
794
|
+
return unless @diagram
|
795
|
+
|
796
|
+
if @new_line_shape
|
797
|
+
@diagram.remove_shape(@new_line_shape)
|
798
|
+
@new_line_shape = nil
|
799
|
+
on_connection_finished(nil)
|
800
|
+
end
|
801
|
+
@working_mode = MODE::READY
|
802
|
+
refresh(false)
|
803
|
+
end
|
804
|
+
|
805
|
+
# Select all shapes in the canvas
|
806
|
+
def select_all
|
807
|
+
return unless @diagram
|
808
|
+
|
809
|
+
shapes = @diagram.get_shapes
|
810
|
+
|
811
|
+
unless shapes.empty?
|
812
|
+
shapes.each { |shape| shape.select(true) }
|
813
|
+
|
814
|
+
validate_selection(get_selected_shapes)
|
815
|
+
|
816
|
+
hide_all_handles
|
817
|
+
update_multiedit_size
|
818
|
+
@shp_multi_edit.show(true)
|
819
|
+
@shp_multi_edit.show_handles(true)
|
820
|
+
|
821
|
+
refresh(false)
|
822
|
+
end
|
823
|
+
end
|
824
|
+
|
825
|
+
# Deselect all shapes
|
826
|
+
def deselect_all
|
827
|
+
return unless @diagram
|
828
|
+
|
829
|
+
@diagram.get_shapes.each { |shape| shape.select(false) }
|
830
|
+
|
831
|
+
@shp_multi_edit.show(false)
|
832
|
+
end
|
833
|
+
|
834
|
+
# Hide handles of all shapes
|
835
|
+
def hide_all_handles
|
836
|
+
return unless @diagram
|
837
|
+
|
838
|
+
@diagram.get_shapes.each { |shape| shape.show_handles(false) }
|
839
|
+
end
|
840
|
+
|
841
|
+
# Repaint the shape canvas.
|
842
|
+
# @param [Boolean] erase true if the canvas should be erased before repainting
|
843
|
+
# @param [Wx::Rect] rct Refreshed region (rectangle)
|
844
|
+
def refresh_canvas(erase, rct)
|
845
|
+
lpos = dp2lp(Wx::Point.new(0, 0))
|
846
|
+
|
847
|
+
upd_rct = rct.inflate((20/@settings.scale).to_i, (20/@settings.scale).to_i)
|
848
|
+
upd_rct.offset!(-lpos.x, -lpos.y)
|
849
|
+
|
850
|
+
refresh_rect(Wx::Rect.new((upd_rct.x*@settings.scale).to_i,
|
851
|
+
(upd_rct.y*@settings.scale).to_i,
|
852
|
+
(upd_rct.width*@settings.scale).to_i,
|
853
|
+
(upd_rct.height*@settings.scale).to_i),
|
854
|
+
erase)
|
855
|
+
end
|
856
|
+
|
857
|
+
# Mark given rectangle as an invalidated one, i.e. as a rectangle which should
|
858
|
+
# be refreshed (by using Wx::SF::ShapeCanvas::refresh_invalidated_rect).
|
859
|
+
# @param [Wx::Rect] rct Rectangle to be invalidated
|
860
|
+
def invalidate_rect(rct)
|
861
|
+
if @invalidate_rect.nil?
|
862
|
+
@invalidate_rect = rct.dup
|
863
|
+
else
|
864
|
+
@invalidate_rect.union!(rct)
|
865
|
+
end
|
866
|
+
end
|
867
|
+
|
868
|
+
# Mark whole visible canvas portion as an invalidated rectangle.
|
869
|
+
def invalidate_visible_rect
|
870
|
+
invalidate_rect(dp2lp(get_client_rect))
|
871
|
+
end
|
872
|
+
|
873
|
+
# Refresh all canvas rectangles marked as invalidated.
|
874
|
+
# @see Wx::SF::ShapeCanvas::invalidate_rect
|
875
|
+
def refresh_invalidated_rect
|
876
|
+
unless @invalidate_rect.nil? || @invalidate_rect.empty?
|
877
|
+
refresh_canvas(false, @invalidate_rect)
|
878
|
+
@invalidate_rect = nil
|
879
|
+
end
|
880
|
+
end
|
881
|
+
|
882
|
+
# Show shapes shadows (only current diagram shapes are affected).
|
883
|
+
#
|
884
|
+
# The functions sets/unsets SHOW_SHADOW flag for all shapes currently included in the diagram.
|
885
|
+
# @param [Boolean] show true if the shadow should be shown, otherwise false
|
886
|
+
# @param [SHADOWMODE] style Shadow style
|
887
|
+
# @see SHADOWMODE
|
888
|
+
def show_shadows(show, style)
|
889
|
+
return unless @diagram
|
890
|
+
|
891
|
+
shapes = @diagram.get_shapes
|
892
|
+
|
893
|
+
shapes.each do |shape|
|
894
|
+
shape.remove_style(Shape::STYLE::SHOW_SHADOW) if show
|
895
|
+
|
896
|
+
case style
|
897
|
+
when SHADOWMODE::TOPMOST
|
898
|
+
unless shape.get_parent_shape
|
899
|
+
if show
|
900
|
+
shape.add_style(Shape::STYLE::SHOW_SHADOW)
|
901
|
+
else
|
902
|
+
shape.remove_style(Shape::STYLE::SHOW_SHADOW)
|
903
|
+
end
|
904
|
+
end
|
905
|
+
|
906
|
+
when SHADOWMODE::ALL
|
907
|
+
if show
|
908
|
+
shape.add_style(Shape::STYLE::SHOW_SHADOW)
|
909
|
+
else
|
910
|
+
shape.remove_style(Shape::STYLE::SHOW_SHADOW)
|
911
|
+
end
|
912
|
+
end
|
913
|
+
end
|
914
|
+
end
|
915
|
+
|
916
|
+
if Wx.has_feature?(:USE_DRAG_AND_DROP)
|
917
|
+
|
918
|
+
# Start Drag&Drop operation with shapes included in the given list.
|
919
|
+
# @param [Array<Wx::SF::Shape>] shapes List of shapes which should be dragged
|
920
|
+
# @param [Wx::Point] start A point where the dragging operation has started
|
921
|
+
# @return [Wx::DragResult] Drag result
|
922
|
+
def do_drag_drop(shapes, start = Wx::Point.new(-1, -1))
|
923
|
+
return Wx::DragNone unless has_style?(STYLE::DND)
|
924
|
+
|
925
|
+
@working_mode = MODE::DND
|
926
|
+
|
927
|
+
result = Wx::DragNone
|
928
|
+
|
929
|
+
validate_selection_for_clipboard(shapes, true)
|
930
|
+
|
931
|
+
unless shapes.empty?
|
932
|
+
deselect_all
|
933
|
+
|
934
|
+
@dnd_started_here = true
|
935
|
+
@dnd_started_at = start.to_point
|
936
|
+
|
937
|
+
data_obj = Wx::SF::ShapeDataObject.new(shapes)
|
938
|
+
|
939
|
+
dnd_src = if Wx::PLATFORM == 'WXGTK'
|
940
|
+
Wx::DropSource.new(data_obj, self, Wx::Icon(:page), Wx::Icon(:page), Wx::Icon(:page))
|
941
|
+
else
|
942
|
+
Wx::DropSource.new(data_obj)
|
943
|
+
end
|
944
|
+
|
945
|
+
result = dnd_src.do_drag_drop(Wx::Drag_AllowMove)
|
946
|
+
case result
|
947
|
+
when Wx::DragResult::DragMove
|
948
|
+
@diagram.remove_shapes(shapes)
|
949
|
+
end
|
950
|
+
|
951
|
+
@dnd_started_here = false
|
952
|
+
|
953
|
+
restore_prev_positions
|
954
|
+
|
955
|
+
move_shapes_from_negatives
|
956
|
+
update_virtual_size
|
957
|
+
|
958
|
+
save_canvas_state
|
959
|
+
refresh(false)
|
960
|
+
end
|
961
|
+
|
962
|
+
@working_mode = MODE::READY
|
963
|
+
|
964
|
+
result
|
965
|
+
end
|
966
|
+
|
967
|
+
end # if Wx.has_feature?(:USE_DRAG_AND_DROP)
|
968
|
+
|
969
|
+
# Copy selected shapes to the clipboard
|
970
|
+
def copy
|
971
|
+
return unless has_style?(STYLE::CLIPBOARD)
|
972
|
+
return unless @diagram
|
973
|
+
|
974
|
+
# copy selected shapes to the clipboard
|
975
|
+
Wx::Clipboard.open do |clipboard|
|
976
|
+
lst_selection = get_selected_shapes
|
977
|
+
|
978
|
+
validate_selection_for_clipboard(lst_selection,true)
|
979
|
+
|
980
|
+
unless lst_selection.empty?
|
981
|
+
data_obj = Wx::SF::ShapeDataObject.new(lst_selection)
|
982
|
+
clipboard.place(data_obj)
|
983
|
+
|
984
|
+
restore_prev_positions
|
985
|
+
end
|
986
|
+
end
|
987
|
+
end
|
988
|
+
|
989
|
+
# Copy selected shapes to the clipboard and remove them from the canvas
|
990
|
+
def cut
|
991
|
+
return unless has_style?(STYLE::CLIPBOARD)
|
992
|
+
return unless @diagram
|
993
|
+
|
994
|
+
copy
|
995
|
+
|
996
|
+
clear_temporaries
|
997
|
+
|
998
|
+
# remove selected shapes
|
999
|
+
lst_selection = get_selected_shapes
|
1000
|
+
|
1001
|
+
validate_selection_for_clipboard(lst_selection,false)
|
1002
|
+
|
1003
|
+
unless lst_selection.empty?
|
1004
|
+
@diagram.remove_shapes(lst_selection)
|
1005
|
+
@shp_multi_edit.show(false)
|
1006
|
+
save_canvas_state
|
1007
|
+
refresh(false)
|
1008
|
+
end
|
1009
|
+
end
|
1010
|
+
|
1011
|
+
# Paste shapes stored in the clipboard to the canvas
|
1012
|
+
def paste
|
1013
|
+
return unless has_style?(STYLE::CLIPBOARD)
|
1014
|
+
return unless @diagram
|
1015
|
+
|
1016
|
+
Wx::Clipboard.open do |clipboard|
|
1017
|
+
# read data object from the clipboard
|
1018
|
+
data_obj = Wx::SF::ShapeDataObject.new
|
1019
|
+
if clipboard.fetch(data_obj)
|
1020
|
+
|
1021
|
+
# deserialize shapes
|
1022
|
+
new_shapes = Wx::SF::Serializable.deserialize(data_obj.get_data_here)
|
1023
|
+
# add new shapes to diagram and remove those that are not accepted
|
1024
|
+
new_shapes.select! do |shape|
|
1025
|
+
ERRCODE::OK == @diagram.add_shape(shape, nil, shape.get_relative_position, INITIALIZE, DONT_SAVE_STATE)
|
1026
|
+
end
|
1027
|
+
|
1028
|
+
# verify newly added shapes (may remove shapes from list)
|
1029
|
+
@diagram.send(:check_new_shapes, new_shapes)
|
1030
|
+
|
1031
|
+
update_virtual_size # update for new shapes
|
1032
|
+
|
1033
|
+
# call user-defined handler
|
1034
|
+
on_paste(new_shapes)
|
1035
|
+
|
1036
|
+
save_canvas_state
|
1037
|
+
refresh(false)
|
1038
|
+
end
|
1039
|
+
end
|
1040
|
+
end
|
1041
|
+
|
1042
|
+
# Perform Undo operation (if available)
|
1043
|
+
def undo
|
1044
|
+
return unless has_style?(STYLE::UNDOREDO)
|
1045
|
+
|
1046
|
+
clear_temporaries
|
1047
|
+
|
1048
|
+
restore_canvas_state(@canvas_history.restore_older_state)
|
1049
|
+
@shp_multi_edit.show(false)
|
1050
|
+
end
|
1051
|
+
|
1052
|
+
# Perform Redo operation (if available)
|
1053
|
+
def redo
|
1054
|
+
return unless has_style?(STYLE::UNDOREDO)
|
1055
|
+
|
1056
|
+
clear_temporaries
|
1057
|
+
|
1058
|
+
restore_canvas_state(@canvas_history.restore_newer_state)
|
1059
|
+
@shp_multi_edit.show(false)
|
1060
|
+
end
|
1061
|
+
|
1062
|
+
# Function returns true if some shapes can be copied to the clipboard (it means they are selected)
|
1063
|
+
# @return [Boolean]
|
1064
|
+
def can_copy
|
1065
|
+
return false unless has_style?(STYLE::CLIPBOARD)
|
1066
|
+
|
1067
|
+
!get_selected_shapes.empty?
|
1068
|
+
end
|
1069
|
+
alias :can_copy? :can_copy
|
1070
|
+
|
1071
|
+
# Function returns true if some shapes can be cut to the clipboard (it means they are selected)
|
1072
|
+
# @return [Boolean]
|
1073
|
+
def can_cut
|
1074
|
+
can_copy
|
1075
|
+
end
|
1076
|
+
alias :can_cut? :can_cut
|
1077
|
+
|
1078
|
+
# Function returns true if some shapes can be copied from the clipboard to the canvas
|
1079
|
+
# (it means the clipboard contains stored shapes)
|
1080
|
+
# @return [Boolean]
|
1081
|
+
def can_paste
|
1082
|
+
return false unless has_style?(STYLE::CLIPBOARD)
|
1083
|
+
|
1084
|
+
Wx::Clipboard.open do |clipboard|
|
1085
|
+
return clipboard.supported?(Wx::DataFormat.new(Wx::SF::ShapeDataObject::DataFormatID))
|
1086
|
+
end
|
1087
|
+
end
|
1088
|
+
alias :can_paste? :can_paste
|
1089
|
+
|
1090
|
+
# Function returns true if undo operation can be done
|
1091
|
+
# @return [Boolean]
|
1092
|
+
def can_undo
|
1093
|
+
return false unless has_style?(STYLE::UNDOREDO)
|
1094
|
+
|
1095
|
+
@canvas_history.can_undo
|
1096
|
+
end
|
1097
|
+
alias :can_undo? :can_undo
|
1098
|
+
|
1099
|
+
# Function returns TRUE if Redo operation can be done
|
1100
|
+
# @return [Boolean]
|
1101
|
+
def can_redo
|
1102
|
+
return false unless has_style?(STYLE::UNDOREDO)
|
1103
|
+
|
1104
|
+
@canvas_history.can_redo
|
1105
|
+
end
|
1106
|
+
alias :can_redo? :can_redo
|
1107
|
+
|
1108
|
+
# Function returns true if align_selected function can be invoked (if more than
|
1109
|
+
# @return [Boolean]
|
1110
|
+
def can_align_selected
|
1111
|
+
@shp_multi_edit.visible? && @working_mode == MODE::READY
|
1112
|
+
end
|
1113
|
+
alias :can_align_selected? :can_align_selected
|
1114
|
+
|
1115
|
+
# Save current canvas state (for Undo/Redo operations)
|
1116
|
+
def save_canvas_state
|
1117
|
+
return unless has_style?(STYLE::UNDOREDO)
|
1118
|
+
|
1119
|
+
@canvas_history.save_canvas_state(@diagram.serialize)
|
1120
|
+
end
|
1121
|
+
|
1122
|
+
# Clear all stored canvas states (no Undo/Redo operations will be available)
|
1123
|
+
def clear_canvas_history
|
1124
|
+
@canvas_history.clear
|
1125
|
+
end
|
1126
|
+
|
1127
|
+
# Restores given canvas state (unless nil given)
|
1128
|
+
# @param [String,nil] state to restore
|
1129
|
+
def restore_canvas_state(state)
|
1130
|
+
return unless state
|
1131
|
+
set_diagram(Wx::SF::Serializable.deserialize(state))
|
1132
|
+
update_virtual_size
|
1133
|
+
@diagram.set_modified
|
1134
|
+
refresh(false)
|
1135
|
+
end
|
1136
|
+
protected :restore_canvas_state
|
1137
|
+
|
1138
|
+
# @!group Print methods
|
1139
|
+
|
1140
|
+
# Print current canvas content.
|
1141
|
+
# @overload print(prompt = PROMPT)
|
1142
|
+
# @param [Boolean] prompt If true (PROMPT) then the the native print dialog will be displayed before printing
|
1143
|
+
# @overload print(printout, prompt = PROMPT)
|
1144
|
+
# @param [Wx::SF::Printout] printout user-defined printout object (inherited from Wx::SF::Printout class) for printing.
|
1145
|
+
# @param [Boolean] prompt If true (PROMPT) then the the native print dialog will be displayed before printing
|
1146
|
+
# @see Wx::SF::Printout
|
1147
|
+
def print(*args)
|
1148
|
+
if args.first.is_a?(Wx::PRT::Printout)
|
1149
|
+
printout, prompt = args
|
1150
|
+
else
|
1151
|
+
printout = Printout.new('wxRuby SF Printout', self)
|
1152
|
+
prompt = args.shift
|
1153
|
+
end
|
1154
|
+
prompt = PROMPT if prompt.nil?
|
1155
|
+
|
1156
|
+
print_dialog_data = Wx::PRT::PrintDialogData.new(ShapeCanvas.print_data)
|
1157
|
+
printer = Wx::PRT::Printer.new(print_dialog_data)
|
1158
|
+
|
1159
|
+
deselect_all
|
1160
|
+
|
1161
|
+
if !printer.print(self, printout, prompt)
|
1162
|
+
if Wx::PRT::Printer.get_last_error == Wx::PRT::PrinterError::PRINTER_ERROR
|
1163
|
+
Wx.message_box("There was a problem printing.\nPerhaps your current printer is not set correctly?",
|
1164
|
+
'wxRuby SF Printing',
|
1165
|
+
Wx::OK | Wx::ICON_ERROR)
|
1166
|
+
end
|
1167
|
+
else
|
1168
|
+
ShapeCanvas.print_data = printer.get_print_dialog_data.get_print_data
|
1169
|
+
end
|
1170
|
+
end
|
1171
|
+
|
1172
|
+
# Show print preview.
|
1173
|
+
# @overload print_preview()
|
1174
|
+
# @overload print_preview(preview, printout = nil)
|
1175
|
+
# @param [Wx::SF::Printout] preview user-defined printout object (inherited from Wx::SF::Printout class) used for print preview.
|
1176
|
+
# @param [Wx::SF::Printout] printout user-defined printout class (inherited from Wx::SF::Printout class) used for printing.
|
1177
|
+
# This parameter can be nil (in this case a print button will not be available in the print preview window).
|
1178
|
+
# @see Wx::SF::Printout
|
1179
|
+
def print_preview(*args)
|
1180
|
+
if args.empty?
|
1181
|
+
preview = Printout.new('wxRuby SF Preview', self)
|
1182
|
+
printout = Printout.new('wxRuby SF Printout', self)
|
1183
|
+
else
|
1184
|
+
preview, printout = args
|
1185
|
+
end
|
1186
|
+
|
1187
|
+
deselect_all
|
1188
|
+
|
1189
|
+
# Pass two printout objects: for preview, and possible printing.
|
1190
|
+
print_dialog_data = Wx::PRT::PrintDialogData.new(ShapeCanvas.print_data)
|
1191
|
+
prn_preview = Wx::PRT::PrintPreview.new(preview, printout, print_dialog_data)
|
1192
|
+
unless prn_preview.ok?
|
1193
|
+
Wx.message_box("There was a problem previewing.\nPerhaps your current printer is not set correctly?",
|
1194
|
+
'wxRuby SF Previewing',
|
1195
|
+
Wx::OK | Wx::ICON_ERROR)
|
1196
|
+
return
|
1197
|
+
end
|
1198
|
+
|
1199
|
+
frame = Wx::PRT::PreviewFrame.new(prn_preview, self, 'wxRuby SF Print Preview', [100, 100], [800, 700])
|
1200
|
+
frame.centre(Wx::BOTH)
|
1201
|
+
frame.init
|
1202
|
+
frame.show
|
1203
|
+
end
|
1204
|
+
|
1205
|
+
# Show page setup dialog for printing.
|
1206
|
+
def page_setup
|
1207
|
+
ShapeCanvas.page_setup_data.set_print_data(ShapeCanvas.print_data)
|
1208
|
+
|
1209
|
+
Wx::PRT::PageSetupDialog(self, ShapeCanvas.page_setup_data) do |dlg|
|
1210
|
+
dlg.show_modal
|
1211
|
+
ShapeCanvas.print_data = dlg.get_page_setup_data.get_print_data
|
1212
|
+
ShapeCanvas.page_setup_data = dlg.get_page_setup_data
|
1213
|
+
end
|
1214
|
+
end
|
1215
|
+
|
1216
|
+
# @!endgroup
|
1217
|
+
|
1218
|
+
# Convert device position to logical position.
|
1219
|
+
#
|
1220
|
+
# The function returns unscrolled unscaled canvas position.
|
1221
|
+
# @overload dp2lp(pos)
|
1222
|
+
# @param [Wx::Point] pos Device position (for example mouse position)
|
1223
|
+
# @return [Wx::Point] Logical position
|
1224
|
+
# @overload dp2lp(rct)
|
1225
|
+
# @param [Wx::Rect] rct Device position (for example mouse position)
|
1226
|
+
# @return [Wx::Rect] Logical position
|
1227
|
+
def dp2lp(arg)
|
1228
|
+
if arg.is_a?(Wx::Rect)
|
1229
|
+
x, y = calc_unscrolled_position(arg.x, arg.y)
|
1230
|
+
Wx::Rect.new((x/@settings.scale).to_i, (y/@settings.scale).to_i,
|
1231
|
+
(arg.width/@settings.scale).to_i, (arg.height/@settings.scale).to_i)
|
1232
|
+
else
|
1233
|
+
arg = arg.to_point
|
1234
|
+
x, y = calc_unscrolled_position(arg.x, arg.y)
|
1235
|
+
Wx::Point.new((x/@settings.scale).to_i, (y/@settings.scale).to_i)
|
1236
|
+
end
|
1237
|
+
end
|
1238
|
+
|
1239
|
+
# Convert logical position to device position.
|
1240
|
+
#
|
1241
|
+
# The function returns scrolled scaled canvas position.
|
1242
|
+
# @overload lp2dp(pos)
|
1243
|
+
# @param [Wx::Point] pos Logical position (for example shape position)
|
1244
|
+
# @return [Wx::Point] Device position
|
1245
|
+
# @overload lp2dp(rct)
|
1246
|
+
# @param [Wx::Rect] rct Logical position (for example shape position)
|
1247
|
+
# @return [Wx::Rect] Device position
|
1248
|
+
def lp2dp(arg)
|
1249
|
+
if arg.is_a?(Wx::Rect)
|
1250
|
+
x, y = calc_unscrolled_position(arg.x, arg.y)
|
1251
|
+
Wx::Rect.new((x*@settings.scale).to_i, (y*@settings.scale).to_i,
|
1252
|
+
(arg.width*@settings.scale).to_i, (arg.height*@settings.scale).to_i)
|
1253
|
+
else
|
1254
|
+
arg = arg.to_point
|
1255
|
+
x, y = calc_unscrolled_position(arg.x, arg.y)
|
1256
|
+
Wx::Point.new((x*@settings.scale).to_i, (y*@settings.scale).to_i)
|
1257
|
+
end
|
1258
|
+
end
|
1259
|
+
|
1260
|
+
# Search for any shape located at the (mouse cursor) position (result used by #get_shape_under_cursor)
|
1261
|
+
# @param [Wx::Point] lpos
|
1262
|
+
def update_shape_under_cursor_cache(lpos)
|
1263
|
+
sel_shape = unsel_shape = top_shape = nil
|
1264
|
+
sel_line = unsel_line = top_line = nil
|
1265
|
+
lpos = lpos.to_point
|
1266
|
+
|
1267
|
+
@topmost_shape_under_cursor = nil
|
1268
|
+
|
1269
|
+
@current_shapes.replace(@diagram.get_all_shapes) if @diagram
|
1270
|
+
|
1271
|
+
@current_shapes.reverse_each do |shape|
|
1272
|
+
if shape.visible? && shape.active? && shape.contains?(lpos)
|
1273
|
+
if shape.is_a?(Wx::SF::LineShape)
|
1274
|
+
top_line ||= shape
|
1275
|
+
if shape.selected?
|
1276
|
+
sel_line ||= shape
|
1277
|
+
else
|
1278
|
+
unsel_line ||= shape
|
1279
|
+
end
|
1280
|
+
else
|
1281
|
+
top_shape ||= shape
|
1282
|
+
if shape.selected?
|
1283
|
+
sel_shape ||= shape
|
1284
|
+
else
|
1285
|
+
unsel_shape ||= shape
|
1286
|
+
end
|
1287
|
+
end
|
1288
|
+
end
|
1289
|
+
end
|
1290
|
+
|
1291
|
+
# set reference to logically topmost selected and unselected shape under the mouse cursor
|
1292
|
+
@topmost_shape_under_cursor = top_line ? top_line : top_shape
|
1293
|
+
|
1294
|
+
@selected_shape_under_cursor = sel_line ? sel_line : sel_shape
|
1295
|
+
|
1296
|
+
@unselected_shape_under_cursor = unsel_line ? unsel_line : unsel_shape
|
1297
|
+
end
|
1298
|
+
|
1299
|
+
# Get shape under current mouse cursor position (fast implementation - use everywhere
|
1300
|
+
# it is possible instead of much slower GetShapeAtPosition()).
|
1301
|
+
# @param [SEARCHMODE] mode Search mode
|
1302
|
+
# @return [Wx::SF::Shape,nil] shape if found, otherwise nil
|
1303
|
+
# @see SEARCHMODE, Wx::SF::ShapeCanvas#dp2lp, Wx::SF::ShapeCanvas#get_shape_at_position
|
1304
|
+
def get_shape_under_cursor(mode = SEARCHMODE::BOTH)
|
1305
|
+
case mode
|
1306
|
+
when SEARCHMODE::BOTH
|
1307
|
+
@topmost_shape_under_cursor
|
1308
|
+
when SEARCHMODE::SELECTED
|
1309
|
+
@selected_shape_under_cursor
|
1310
|
+
when SEARCHMODE::UNSELECTED
|
1311
|
+
@unselected_shape_under_cursor
|
1312
|
+
else
|
1313
|
+
nil
|
1314
|
+
end
|
1315
|
+
end
|
1316
|
+
|
1317
|
+
# Get shape at given logical position
|
1318
|
+
# @param [Wx::Point] pos Logical position
|
1319
|
+
# @param [Integer] zorder Z-order of searched shape (useful if several shapes are located
|
1320
|
+
# at the given position)
|
1321
|
+
# @param [SEARCHMODE] mode Search mode
|
1322
|
+
# @return [Wx::SF::Shape,nil] shape if found, otherwise nil
|
1323
|
+
# @see SEARCHMODE, Wx::SF::ShapeCanvas#dp2lp, Wx::SF::ShapeCanvas#get_shape_under_cursor
|
1324
|
+
def get_shape_at_position(pos, zorder = 1, mode = SEARCHMODE::BOTH)
|
1325
|
+
return nil unless @diagram
|
1326
|
+
|
1327
|
+
@diagram.get_shape_at_position(pos, zorder, mode)
|
1328
|
+
end
|
1329
|
+
|
1330
|
+
# Get topmost handle at given position
|
1331
|
+
# @param [Wx::Point] pos Logical position
|
1332
|
+
# @return [Wx::SF::Shape::Handle,nil] shape handle if found, otherwise nil
|
1333
|
+
# @see Wx::SF::ShapeCanvas#dp2lp
|
1334
|
+
def get_topmost_handle_at_position(pos)
|
1335
|
+
return nil unless @diagram
|
1336
|
+
|
1337
|
+
pos = pos.to_point
|
1338
|
+
# first test multiedit handles...
|
1339
|
+
if @shp_multi_edit.visible?
|
1340
|
+
@shp_multi_edit.handles.each do |handle|
|
1341
|
+
return handle if handle.visible? && handle.contains?(pos)
|
1342
|
+
end
|
1343
|
+
end
|
1344
|
+
|
1345
|
+
# ... then test normal handles
|
1346
|
+
@diagram.get_shapes.each do |shape|
|
1347
|
+
# iterate through all shape's handles
|
1348
|
+
if shape.has_style?(Shape::STYLE::SIZE_CHANGE)
|
1349
|
+
shape.handles.each do |handle|
|
1350
|
+
return handle if handle.visible? && handle.contains?(pos)
|
1351
|
+
end
|
1352
|
+
end
|
1353
|
+
end
|
1354
|
+
|
1355
|
+
nil
|
1356
|
+
end
|
1357
|
+
|
1358
|
+
# Get list of all shapes located at given position
|
1359
|
+
# @param [Wx::Point] pos Logical position
|
1360
|
+
# @param [Array<Wx::SF::Shape>] shapes shape list where pointers to all found shapes will be stored
|
1361
|
+
# @return [Array<Wx::SF::Shape>] shapes shape list
|
1362
|
+
# @see Wx::SF::ShapeCanvas#dp2lp
|
1363
|
+
def get_shapes_at_position(pos, shapes = [])
|
1364
|
+
@diagram.get_shapes_at_position(pos, shapes) if @diagram
|
1365
|
+
shapes
|
1366
|
+
end
|
1367
|
+
|
1368
|
+
# Get list of shapes located inside given rectangle
|
1369
|
+
# @param [Wx::Rect] rct Examined rectangle
|
1370
|
+
# @param [Array<Wx::SF::Shape>] shapes shape list where pointers to all found shapes will be stored
|
1371
|
+
# @return [Array<Wx::SF::Shape>] shapes shape list
|
1372
|
+
def get_shapes_inside(rct, shapes = [])
|
1373
|
+
@diagram.get_shapes_inside(rct, shapes) if @diagram
|
1374
|
+
shapes
|
1375
|
+
end
|
1376
|
+
|
1377
|
+
# Get list of selected shapes.
|
1378
|
+
# @param [Array<Wx::SF::Shape>] selection shape list where pointers to all selected shapes will be stored
|
1379
|
+
# @return [Array<Wx::SF::Shape>] shapes shape list
|
1380
|
+
def get_selected_shapes(selection = [])
|
1381
|
+
return selection unless @diagram
|
1382
|
+
|
1383
|
+
selection.clear
|
1384
|
+
@diagram.get_shapes.each do |shape|
|
1385
|
+
selection << shape if shape.selected?
|
1386
|
+
end
|
1387
|
+
selection
|
1388
|
+
end
|
1389
|
+
|
1390
|
+
# Get box bounding all shapes in the canvas.
|
1391
|
+
# @return [Wx::Rect] Total bounding box
|
1392
|
+
def get_total_bounding_box
|
1393
|
+
virt_rct = nil
|
1394
|
+
if @diagram
|
1395
|
+
# calculate total bounding box (includes all shapes)
|
1396
|
+
@diagram.get_shapes.each_with_index do |shape, ix|
|
1397
|
+
if ix == 0
|
1398
|
+
virt_rct = shape.get_bounding_box
|
1399
|
+
else
|
1400
|
+
virt_rct.union!(shape.get_bounding_box)
|
1401
|
+
end
|
1402
|
+
end
|
1403
|
+
end
|
1404
|
+
virt_rct || Wx::Rect.new
|
1405
|
+
end
|
1406
|
+
|
1407
|
+
# Get bounding box of all selected shapes.
|
1408
|
+
# @return [Wx::Rect] Selection bounding box
|
1409
|
+
def get_selection_bb
|
1410
|
+
bb_rct = Wx::Rect.new
|
1411
|
+
# get selected shapes
|
1412
|
+
get_selected_shapes.each do |shape|
|
1413
|
+
shape.get_complete_bounding_box(
|
1414
|
+
bb_rct,
|
1415
|
+
Shape::BBMODE::SELF | Shape::BBMODE::CHILDREN | Shape::BBMODE::CONNECTIONS | Shape::BBMODE::SHADOW)
|
1416
|
+
end
|
1417
|
+
bb_rct
|
1418
|
+
end
|
1419
|
+
|
1420
|
+
# Align selected shapes in given directions.
|
1421
|
+
#
|
1422
|
+
# Shapes will be aligned according to most far shape in appropriate direction.
|
1423
|
+
# @param [HALIGN] halign Horizontal alignment
|
1424
|
+
# @param [VALIGN] valign Vertical alignment
|
1425
|
+
def align_selected(halign, valign)
|
1426
|
+
cnt = 0
|
1427
|
+
min_pos = max_pos = nil
|
1428
|
+
|
1429
|
+
lst_selection = get_selected_shapes
|
1430
|
+
|
1431
|
+
upd_rct = get_selection_bb
|
1432
|
+
upd_rct.inflate!(DEFAULT_ME_OFFSET, DEFAULT_ME_OFFSET)
|
1433
|
+
|
1434
|
+
# find most distant position
|
1435
|
+
lst_selection.each do |shape|
|
1436
|
+
unless shape.is_a?(LineShape)
|
1437
|
+
pos = shape.get_absolute_position
|
1438
|
+
shape_bb = shape.get_bounding_box
|
1439
|
+
|
1440
|
+
if cnt == 0
|
1441
|
+
min_pos = pos
|
1442
|
+
max_pos = Wx::RealPoint.new(pos.x + shape_bb.width, pos.y + shape_bb.height)
|
1443
|
+
else
|
1444
|
+
min_pos.x = pos.x if pos.x < min_pos.x
|
1445
|
+
min_pos.y = pos.y if pos.y < min_pos.y
|
1446
|
+
max_pos.x = pos.x + shape_bb.width if (pos.x + shape_bb.width) > max_pos.x
|
1447
|
+
max_pos.y = pos.y + shape_bb.height if (pos.y + shape_bb.height) > max_pos.y
|
1448
|
+
end
|
1449
|
+
|
1450
|
+
cnt += 1
|
1451
|
+
end
|
1452
|
+
end
|
1453
|
+
|
1454
|
+
# if only one non-line shape is in the selection then alignment has no sense so exit...
|
1455
|
+
return if cnt < 2
|
1456
|
+
|
1457
|
+
# set new positions
|
1458
|
+
lst_selection.each do |shape|
|
1459
|
+
unless shape.is_a?(LineShape)
|
1460
|
+
pos = shape.get_absolute_position
|
1461
|
+
shape_bb = shape.get_bounding_box
|
1462
|
+
|
1463
|
+
case halign
|
1464
|
+
when HALIGN::LEFT
|
1465
|
+
shape.move_to(min_pos.x, pos.y)
|
1466
|
+
|
1467
|
+
when HALIGN::RIGHT
|
1468
|
+
shape.move_to(max_pos.x - shape_bb.width, pos.y)
|
1469
|
+
|
1470
|
+
when HALIGN::CENTER
|
1471
|
+
shape.move_to((max_pos.x + min_pos.x)/2 - shape_bb.width/2, pos.y)
|
1472
|
+
end
|
1473
|
+
|
1474
|
+
case valign
|
1475
|
+
when VALIGN::TOP
|
1476
|
+
shape.move_to(pos.x, min_pos.y)
|
1477
|
+
|
1478
|
+
when VALIGN::BOTTOM
|
1479
|
+
shape.move_to(pos.x, max_pos.y - shape_bb.height)
|
1480
|
+
|
1481
|
+
when VALIGN::MIDDLE
|
1482
|
+
shape.move_to(pos.x, (max_pos.y + min_pos.y)/2 - shape_bb.height/2)
|
1483
|
+
end
|
1484
|
+
|
1485
|
+
# update the shape and its parent
|
1486
|
+
shape.update
|
1487
|
+
parent = shape.get_parent_shape
|
1488
|
+
parent.update if parent
|
1489
|
+
end
|
1490
|
+
end
|
1491
|
+
|
1492
|
+
unless upd_rct.empty?
|
1493
|
+
update_multiedit_size
|
1494
|
+
save_canvas_state
|
1495
|
+
refresh_canvas(false, upd_rct)
|
1496
|
+
end
|
1497
|
+
end
|
1498
|
+
|
1499
|
+
# @!group Style accessors
|
1500
|
+
|
1501
|
+
# Set canvas style.
|
1502
|
+
#
|
1503
|
+
# Default value is STYLE::MULTI_SELECTION | STYLE::MULTI_SIZE_CHANGE | STYLE::DND | STYLE::UNDOREDO | STYLE::CLIPBOARD | STYLE::HOVERING | STYLE::HIGHLIGHTING
|
1504
|
+
# @param [STYLE] style Combination of the canvas styles
|
1505
|
+
# @see STYLE
|
1506
|
+
def set_style(style)
|
1507
|
+
@settings.style = style
|
1508
|
+
end
|
1509
|
+
alias :style= :set_style
|
1510
|
+
|
1511
|
+
# Get current canvas style.
|
1512
|
+
def get_style
|
1513
|
+
@settings.style
|
1514
|
+
end
|
1515
|
+
alias :style :get_style
|
1516
|
+
|
1517
|
+
# Add new style flag.
|
1518
|
+
# @param [STYLE] style canvas style to add
|
1519
|
+
def add_style(style)
|
1520
|
+
@settings.style |= style
|
1521
|
+
end
|
1522
|
+
|
1523
|
+
# Remove given style flag.
|
1524
|
+
# @param [STYLE] style canvas style to remove
|
1525
|
+
def remove_style(style)
|
1526
|
+
@settings.style &= ~style
|
1527
|
+
end
|
1528
|
+
|
1529
|
+
# Check whether given style flag is used.
|
1530
|
+
# @param [STYLE] style canvas style to check
|
1531
|
+
def contains_style(style)
|
1532
|
+
(@settings.style & style) != 0
|
1533
|
+
end
|
1534
|
+
alias :contains_style? :contains_style
|
1535
|
+
alias :has_style? :contains_style
|
1536
|
+
|
1537
|
+
# @!endgroup
|
1538
|
+
|
1539
|
+
# @!group Public attribute accessors
|
1540
|
+
|
1541
|
+
# Set canvas background color.
|
1542
|
+
# @param [Wx::Colour] col Background color
|
1543
|
+
def set_canvas_colour(col)
|
1544
|
+
@settings.background_color = col
|
1545
|
+
end
|
1546
|
+
alias :canvas_colour= :set_canvas_colour
|
1547
|
+
|
1548
|
+
# Get canvas background color.
|
1549
|
+
# @return [Wx::Colour] Background color
|
1550
|
+
def get_canvas_colour
|
1551
|
+
@settings.background_color
|
1552
|
+
end
|
1553
|
+
alias :canvas_colour :get_canvas_colour
|
1554
|
+
|
1555
|
+
# Set starting gradient color.
|
1556
|
+
# @param [Wx::Colour] col Color
|
1557
|
+
def set_gradient_from(col)
|
1558
|
+
@settings.gradient_from = col
|
1559
|
+
end
|
1560
|
+
alias :gradient_from= :set_gradient_from
|
1561
|
+
|
1562
|
+
# Get starting gradient color.
|
1563
|
+
# @return [Wx::Colour] Color
|
1564
|
+
def get_gradient_from
|
1565
|
+
@settings.gradient_from
|
1566
|
+
end
|
1567
|
+
alias :gradient_from :get_gradient_from
|
1568
|
+
|
1569
|
+
# Set ending gradient color.
|
1570
|
+
# @param [Wx::Colour] col Color
|
1571
|
+
def set_gradient_to(col)
|
1572
|
+
@settings.gradient_to = col
|
1573
|
+
end
|
1574
|
+
alias :gradient_to= :set_gradient_to
|
1575
|
+
|
1576
|
+
# Get ending gradient color.
|
1577
|
+
# @return [Wx::Colour] Color
|
1578
|
+
def get_gradient_to
|
1579
|
+
@settings.gradient_to
|
1580
|
+
end
|
1581
|
+
alias :gradient_to :get_gradient_to
|
1582
|
+
|
1583
|
+
# Get grid size.
|
1584
|
+
# @return [Wx::Size] Grid size
|
1585
|
+
def get_grid_size
|
1586
|
+
@settings.grid_size
|
1587
|
+
end
|
1588
|
+
alias :grid_size :get_grid_size
|
1589
|
+
|
1590
|
+
# Set grid size.
|
1591
|
+
# @param [Wx::Size] grid Grid size
|
1592
|
+
def set_grid_size(grid)
|
1593
|
+
@settings.grid_size = grid.to_size
|
1594
|
+
end
|
1595
|
+
alias :grid_size= :set_grid_size
|
1596
|
+
|
1597
|
+
# Set grid line multiple.
|
1598
|
+
#
|
1599
|
+
# Grid lines will be drawn in a distance calculated as grid size multiplicated by this value.
|
1600
|
+
# Default value is 1.
|
1601
|
+
# @param [Integer] multiple Multiple value
|
1602
|
+
def set_grid_line_mult(multiple)
|
1603
|
+
@settings.grid_line_mult = multiple
|
1604
|
+
end
|
1605
|
+
alias :grid_line_mult= :set_grid_line_mult
|
1606
|
+
|
1607
|
+
# Get grid line multiple.
|
1608
|
+
# @return [Integer] Value by which a grid size will be multiplicated to determine grid lines distance
|
1609
|
+
def get_grid_line_mult
|
1610
|
+
@settings.grid_line_mult
|
1611
|
+
end
|
1612
|
+
alias :grid_line_mult :get_grid_line_mult
|
1613
|
+
|
1614
|
+
# Set grid color.
|
1615
|
+
# @param [Wx::Colour] col Grid color
|
1616
|
+
def set_grid_colour(col)
|
1617
|
+
@settings.grid_color = col
|
1618
|
+
end
|
1619
|
+
alias :grid_colour= :set_grid_colour
|
1620
|
+
|
1621
|
+
# Get grid color.
|
1622
|
+
# @return [Wx::Colour] Grid color
|
1623
|
+
def get_grid_colour
|
1624
|
+
@settings.grid_color
|
1625
|
+
end
|
1626
|
+
alias :grid_colour :get_grid_colour
|
1627
|
+
|
1628
|
+
# Set grid line style.
|
1629
|
+
# @param [Wx::PenStyle] style Line style
|
1630
|
+
def set_grid_style(style)
|
1631
|
+
@settings.grid_style = style
|
1632
|
+
end
|
1633
|
+
alias :grid_style= :set_grid_style
|
1634
|
+
|
1635
|
+
# Get grid line style.
|
1636
|
+
# @return [Wx::PenStyle] Line style
|
1637
|
+
def get_grid_style
|
1638
|
+
@settings.grid_style
|
1639
|
+
end
|
1640
|
+
alias :grid_style :get_grid_style
|
1641
|
+
|
1642
|
+
# Set shadow offset.
|
1643
|
+
# @param [Wx::RealPoint] offset Shadow offset
|
1644
|
+
def set_shadow_offset(offset)
|
1645
|
+
@settings.shadow_offset = offset.to_real_point
|
1646
|
+
end
|
1647
|
+
alias :shadow_offset= :set_shadow_offset
|
1648
|
+
|
1649
|
+
# Get shadow offset.
|
1650
|
+
# @return [Wx::RealPoint] Shadow offset
|
1651
|
+
def get_shadow_offset
|
1652
|
+
@settings.shadow_offset
|
1653
|
+
end
|
1654
|
+
alias :shadow_offset :get_shadow_offset
|
1655
|
+
|
1656
|
+
# Set shadow fill (used for shadows of non-text shapes only).
|
1657
|
+
# @param [Wx::Brush] brush Reference to brush object
|
1658
|
+
def set_shadow_fill(brush)
|
1659
|
+
@settings.shadow_fill = brush
|
1660
|
+
end
|
1661
|
+
alias :shadow_fill= :set_shadow_fill
|
1662
|
+
|
1663
|
+
# Get shadow fill.
|
1664
|
+
# @return [Wx::Brush] Current shadow brush
|
1665
|
+
def get_shadow_fill
|
1666
|
+
@settings.shadow_fill
|
1667
|
+
end
|
1668
|
+
alias :shadow_fill :get_shadow_fill
|
1669
|
+
|
1670
|
+
# Set horizontal align of printed drawing.
|
1671
|
+
# @param [HALIGN] val Horizontal align
|
1672
|
+
# @see HALIGN
|
1673
|
+
def set_print_h_align(val)
|
1674
|
+
@settings.print_h_align = val
|
1675
|
+
end
|
1676
|
+
alias :print_h_align= :set_print_h_align
|
1677
|
+
|
1678
|
+
# Get horizontal align of printed drawing.
|
1679
|
+
# @return [HALIGN] Current horizontal align
|
1680
|
+
# @see HALIGN
|
1681
|
+
def get_print_h_align
|
1682
|
+
@settings.print_h_align
|
1683
|
+
end
|
1684
|
+
alias :print_h_align :get_print_h_align
|
1685
|
+
|
1686
|
+
# Set vertical align of printed drawing.
|
1687
|
+
# @param [VALIGN] val Vertical align
|
1688
|
+
# @see VALIGN
|
1689
|
+
def set_print_v_align(val)
|
1690
|
+
@settings.print_v_align = val
|
1691
|
+
end
|
1692
|
+
alias :print_v_align= :set_print_v_align
|
1693
|
+
|
1694
|
+
# Get vertical align of printed drawing.
|
1695
|
+
# @return [VALIGN] Current vertical align
|
1696
|
+
# @see VALIGN
|
1697
|
+
def get_print_v_align
|
1698
|
+
@settings.print_v_align
|
1699
|
+
end
|
1700
|
+
alias :print_v_align :get_print_v_align
|
1701
|
+
|
1702
|
+
# Set printing mode for this canvas.
|
1703
|
+
# @param [PRINTMODE] mode Printing mode
|
1704
|
+
# @see PRINTMODE
|
1705
|
+
def set_print_mode(mode)
|
1706
|
+
@settings.print_mode = mode
|
1707
|
+
end
|
1708
|
+
alias :print_mode= :set_print_mode
|
1709
|
+
|
1710
|
+
# Get printing mode for this canvas.
|
1711
|
+
# @return [PRINTMODE] Current printing mode
|
1712
|
+
# #see PRINTMODE
|
1713
|
+
def get_print_mode
|
1714
|
+
@settings.print_mode
|
1715
|
+
end
|
1716
|
+
alias :print_mode :get_print_mode
|
1717
|
+
|
1718
|
+
# Set canvas scale.
|
1719
|
+
# @param [Float] scale Scale value
|
1720
|
+
def set_scale(scale)
|
1721
|
+
return unless @diagram
|
1722
|
+
|
1723
|
+
if scale != 1.0
|
1724
|
+
# query shapes
|
1725
|
+
msg = ''
|
1726
|
+
unless _query_canvas_change(CHANGE::SET_SCALE, msg)
|
1727
|
+
Wx.message_box("Cannot change scale of shape canvas: #{msg}.", 'wxRuby ShapeFramework', Wx::ICON_WARNING | Wx::OK)
|
1728
|
+
scale = 1.0
|
1729
|
+
end
|
1730
|
+
end
|
1731
|
+
|
1732
|
+
@settings.scale = scale != 0.0 ? scale : 1.0
|
1733
|
+
|
1734
|
+
# inform shapes
|
1735
|
+
_notify_canvas_change(CHANGE::RESCALED)
|
1736
|
+
|
1737
|
+
update_virtual_size
|
1738
|
+
end
|
1739
|
+
alias :scale= :set_scale
|
1740
|
+
|
1741
|
+
# Set minimal allowed scale (for mouse wheel scale change).
|
1742
|
+
# @param [Float] scale Minimal scale
|
1743
|
+
def set_min_scale(scale)
|
1744
|
+
@settings.min_scale = scale
|
1745
|
+
end
|
1746
|
+
alias :min_scale= :set_min_scale
|
1747
|
+
|
1748
|
+
# Get minimal allowed scale (for mouse wheel scale change).
|
1749
|
+
# @return [Float] Minimal scale
|
1750
|
+
def get_min_scale
|
1751
|
+
@settings.min_scale
|
1752
|
+
end
|
1753
|
+
alias :min_scale :get_min_scale
|
1754
|
+
|
1755
|
+
# Set maximal allowed scale (for mouse wheel scale change).
|
1756
|
+
# @param [Float] scale Maximal scale
|
1757
|
+
def set_max_scale(scale)
|
1758
|
+
@settings.max_scale = scale
|
1759
|
+
end
|
1760
|
+
alias :max_scale= :set_max_scale
|
1761
|
+
|
1762
|
+
# Set maximal allowed scale (for mouse wheel scale change).
|
1763
|
+
# @return [FLOAT] Maximal scale
|
1764
|
+
def get_max_scale
|
1765
|
+
@settings.max_scale
|
1766
|
+
end
|
1767
|
+
alias :max_scale :get_max_scale
|
1768
|
+
|
1769
|
+
# Get the canvas scale.
|
1770
|
+
# @return [Float] Canvas scale
|
1771
|
+
def get_scale
|
1772
|
+
@settings.scale
|
1773
|
+
end
|
1774
|
+
alias :scale :get_scale
|
1775
|
+
|
1776
|
+
# @!endgroup
|
1777
|
+
|
1778
|
+
# Set the canvas scale so a whole diagram is visible.
|
1779
|
+
def set_scale_to_view_all
|
1780
|
+
phys_rct = get_client_size
|
1781
|
+
virt_rct = get_total_bounding_box
|
1782
|
+
|
1783
|
+
hz = phys_rct.width.to_f / virt_rct.right
|
1784
|
+
vz = phys_rct.height.to_f / virt_rct.bottom
|
1785
|
+
|
1786
|
+
if hz < vz
|
1787
|
+
set_scale(hz < 1 ? hz : 1.0)
|
1788
|
+
else
|
1789
|
+
set_scale(vz < 1 ? vz : 1.0)
|
1790
|
+
end
|
1791
|
+
end
|
1792
|
+
|
1793
|
+
# Scroll the shape canvas so the given shape will be located in its center.
|
1794
|
+
# @param [Wx::SF::Shape] shape Pointer to focused shape
|
1795
|
+
def scroll_to_shape(shape)
|
1796
|
+
if shape
|
1797
|
+
ux, uy = get_scroll_pixels_per_unit
|
1798
|
+
sz_canvas = get_client_size
|
1799
|
+
pt_pos = shape.center
|
1800
|
+
|
1801
|
+
scroll(((pt_pos.x * @settings.scale) - sz_canvas.x/2)/ux, ((pt_pos.y * @settings.scale) - sz_canvas.y/2)/uy)
|
1802
|
+
end
|
1803
|
+
end
|
1804
|
+
|
1805
|
+
# Get canvas working mode.
|
1806
|
+
# @return [MODE] Working mode
|
1807
|
+
# @see MODE
|
1808
|
+
def get_mode
|
1809
|
+
@working_mode
|
1810
|
+
end
|
1811
|
+
alias :mode :get_mode
|
1812
|
+
|
1813
|
+
# Set default hover color.
|
1814
|
+
# @param [Wx::Colour] col Hover color.
|
1815
|
+
def set_hover_colour(col)
|
1816
|
+
return unless @diagram
|
1817
|
+
|
1818
|
+
@settings.common_hover_color = col
|
1819
|
+
|
1820
|
+
# update Hover color in all existing shapes
|
1821
|
+
@diagram.get_shapes.each { |shape| shape.set_hover_colour(col) }
|
1822
|
+
end
|
1823
|
+
alias :hover_colour= :set_hover_colour
|
1824
|
+
|
1825
|
+
# Get default hover colour.
|
1826
|
+
# @return [Wx::Colour] Hover colour
|
1827
|
+
def get_hover_colour
|
1828
|
+
@settings.common_hover_color
|
1829
|
+
end
|
1830
|
+
alias :hover_colour :get_hover_colour
|
1831
|
+
|
1832
|
+
# Get canvas history manager.
|
1833
|
+
# @return [Wx::SF::CanvasHistory] the canvas history manager
|
1834
|
+
# @see Wx::SF::CanvasHistory
|
1835
|
+
def get_history_manager
|
1836
|
+
@canvas_history
|
1837
|
+
end
|
1838
|
+
alias :history_manager :get_history_manager
|
1839
|
+
|
1840
|
+
# Update given position so it will fit canvas grid (if enabled).
|
1841
|
+
# @param [Wx::Point] pos Position which should be updated
|
1842
|
+
# @return [Wx::Point] Updated position
|
1843
|
+
def fit_position_to_grid(pos)
|
1844
|
+
pos = pos.to_point
|
1845
|
+
if has_style?(STYLE::GRID_USE)
|
1846
|
+
Wx::Point.new(pos.x / @settings.grid_size.x * @settings.grid_size.x,
|
1847
|
+
pos.y / @settings.grid_size.y * @settings.grid_size.y)
|
1848
|
+
else
|
1849
|
+
pos
|
1850
|
+
end
|
1851
|
+
end
|
1852
|
+
|
1853
|
+
# Update size of multi selection rectangle
|
1854
|
+
def update_multiedit_size
|
1855
|
+
# calculate bounding box
|
1856
|
+
union_rct = nil
|
1857
|
+
get_selected_shapes.each_with_index do |shape, ix|
|
1858
|
+
if ix == 0
|
1859
|
+
union_rct = shape.get_bounding_box
|
1860
|
+
else
|
1861
|
+
union_rct.union!(shape.get_bounding_box)
|
1862
|
+
end
|
1863
|
+
end
|
1864
|
+
union_rct ||= Wx::Rect.new
|
1865
|
+
union_rct.inflate!([DEFAULT_ME_OFFSET, DEFAULT_ME_OFFSET])
|
1866
|
+
|
1867
|
+
# draw rectangle
|
1868
|
+
@shp_multi_edit.set_relative_position(Wx::RealPoint.new(union_rct.x.to_f, union_rct.y.to_f))
|
1869
|
+
@shp_multi_edit.set_rect_size(Wx::RealPoint.new(union_rct.width.to_f, union_rct.height.to_f))
|
1870
|
+
end
|
1871
|
+
|
1872
|
+
# Update scroll window virtual size so it can display all shape canvas
|
1873
|
+
def update_virtual_size
|
1874
|
+
virt_rct = get_total_bounding_box
|
1875
|
+
|
1876
|
+
# allow user to modify calculated virtual canvas size
|
1877
|
+
on_update_virtual_size(virt_rct)
|
1878
|
+
|
1879
|
+
# update virtual area of the scrolled window if necessary
|
1880
|
+
if virt_rct.empty?
|
1881
|
+
set_virtual_size(500, 500)
|
1882
|
+
else
|
1883
|
+
set_virtual_size((virt_rct.right*@settings.scale).to_i, (virt_rct.bottom*@settings.scale).to_i)
|
1884
|
+
end
|
1885
|
+
_notify_canvas_change(CHANGE::VIRTUAL_SIZE)
|
1886
|
+
end
|
1887
|
+
|
1888
|
+
# Move all shapes so none of it will be located in negative position
|
1889
|
+
def move_shapes_from_negatives
|
1890
|
+
@diagram.move_shapes_from_negatives if @diagram
|
1891
|
+
end
|
1892
|
+
|
1893
|
+
# Center diagram in accordance to the shape canvas extent.
|
1894
|
+
def center_shapes
|
1895
|
+
rct_prev_bb = get_total_bounding_box
|
1896
|
+
|
1897
|
+
rct_bb = rct_prev_bb.center_in(Wx::Rect.new(Wx::Point.new(0, 0), get_size))
|
1898
|
+
|
1899
|
+
dx = (rct_bb.left - rct_prev_bb.left).to_f
|
1900
|
+
dy = (rct_bb.top - rct_prev_bb.top).to_f
|
1901
|
+
|
1902
|
+
@current_shapes.each do |shape|
|
1903
|
+
shape.move_by(dx, dy) unless shape.get_parent_shape
|
1904
|
+
end
|
1905
|
+
|
1906
|
+
move_shapes_from_negatives
|
1907
|
+
end
|
1908
|
+
|
1909
|
+
# Validate selection (remove redundantly selected shapes etc...).
|
1910
|
+
# @param [Array<Wx::SF::Shape>] selection List of selected shapes that should be validated
|
1911
|
+
def validate_selection(selection)
|
1912
|
+
return unless @diagram
|
1913
|
+
|
1914
|
+
# find child shapes that have parents in the list and deselect and remove those
|
1915
|
+
# so we only have regular toplevel shapes and orphaned child shapes
|
1916
|
+
selection.select! do |shape|
|
1917
|
+
if selection.include?(shape.get_parent_shape)
|
1918
|
+
shape.select(false)
|
1919
|
+
false
|
1920
|
+
else
|
1921
|
+
true
|
1922
|
+
end
|
1923
|
+
end
|
1924
|
+
|
1925
|
+
# move selected (toplevel) shapes to the back of the shapes list in the diagram
|
1926
|
+
# this gives a higher Z-order so they will float on top of other shapes when dragging
|
1927
|
+
selection.each do |shape|
|
1928
|
+
# in case of child shapes find the toplevel parent it belongs to and move that one
|
1929
|
+
shape = shape.get_parent_shape while shape.get_parent_shape
|
1930
|
+
@diagram.move_to_end(shape)
|
1931
|
+
end
|
1932
|
+
end
|
1933
|
+
|
1934
|
+
# Function responsible for drawing of the canvas's content to given DC. The default
|
1935
|
+
# implementation draws actual objects managed by assigned diagram manager.
|
1936
|
+
# @param [Wx::DC] dc device context where the shapes will be drawn to
|
1937
|
+
# @param [Boolean] from_paint Set the argument to true if the dc argument refers to the Wx::PaintDC instance
|
1938
|
+
# or derived classes (i.e. the function is called as a response to Wx::EVT_PAINT event)
|
1939
|
+
def draw_content(dc, from_paint)
|
1940
|
+
return unless @diagram
|
1941
|
+
|
1942
|
+
if from_paint
|
1943
|
+
# wxRect updRct
|
1944
|
+
bb_rct = Wx::Rect.new
|
1945
|
+
#
|
1946
|
+
# ShapeList m_lstToDraw
|
1947
|
+
lst_lines_to_draw = []
|
1948
|
+
|
1949
|
+
# get all existing shapes
|
1950
|
+
lst_to_draw = @diagram.get_shapes(Shape, Shape::SEARCHMODE::DFS)
|
1951
|
+
|
1952
|
+
upd_rct = nil
|
1953
|
+
# get the update rect list
|
1954
|
+
Wx::RegionIterator.for_region(get_update_region) do |region_it|
|
1955
|
+
# combine updated rectangles
|
1956
|
+
region_it.each do |rct|
|
1957
|
+
if upd_rct.nil?
|
1958
|
+
upd_rct = dp2lp(rct.inflate(5, 5))
|
1959
|
+
else
|
1960
|
+
upd_rct.union!(dp2lp(rct.inflate(5, 5)))
|
1961
|
+
end
|
1962
|
+
end
|
1963
|
+
end
|
1964
|
+
upd_rct ||= Wx::Rect.new
|
1965
|
+
|
1966
|
+
if @working_mode == MODE::SHAPEMOVE
|
1967
|
+
# draw unselected non line-based shapes first...
|
1968
|
+
lst_to_draw.each do |shape|
|
1969
|
+
parent_shape = shape.get_parent_shape
|
1970
|
+
|
1971
|
+
if !shape.is_a?(LineShape) || shape.stand_alone?
|
1972
|
+
if shape.intersects?(upd_rct)
|
1973
|
+
if parent_shape
|
1974
|
+
shape.draw(dc, WITHOUTCHILDREN) if !parent_shape.is_a?(LineShape) || parent_shape.stand_alone?
|
1975
|
+
else
|
1976
|
+
shape.draw(dc, WITHOUTCHILDREN)
|
1977
|
+
end
|
1978
|
+
end
|
1979
|
+
else
|
1980
|
+
lst_lines_to_draw << shape
|
1981
|
+
end
|
1982
|
+
end
|
1983
|
+
|
1984
|
+
# ... and draw connections
|
1985
|
+
lst_lines_to_draw.each do |line|
|
1986
|
+
line.get_complete_bounding_box(bb_rct, Shape::BBMODE::SELF | Shape::BBMODE::CHILDREN | Shape::BBMODE::SHADOW)
|
1987
|
+
line.draw(dc, line.get_line_mode == LineShape::LINEMODE::READY) if bb_rct.intersects(upd_rct)
|
1988
|
+
end
|
1989
|
+
else
|
1990
|
+
# draw parent shapes (children are processed by parent objects)
|
1991
|
+
lst_to_draw.each do |shape|
|
1992
|
+
parent_shape = shape.get_parent_shape
|
1993
|
+
|
1994
|
+
if !shape.is_a?(LineShape) || shape.stand_alone?
|
1995
|
+
if shape.intersects?(upd_rct)
|
1996
|
+
if parent_shape
|
1997
|
+
shape.draw(dc, WITHOUTCHILDREN) if !parent_shape.is_a?(LineShape) || shape.stand_alone?
|
1998
|
+
else
|
1999
|
+
shape.draw(dc, WITHOUTCHILDREN)
|
2000
|
+
end
|
2001
|
+
end
|
2002
|
+
else
|
2003
|
+
lst_lines_to_draw << shape
|
2004
|
+
end
|
2005
|
+
end
|
2006
|
+
|
2007
|
+
# draw connections
|
2008
|
+
lst_lines_to_draw.each do |line|
|
2009
|
+
line.get_complete_bounding_box(bb_rct, Shape::BBMODE::SELF | Shape::BBMODE::CHILDREN)
|
2010
|
+
line.draw(dc, line.get_line_mode == LineShape::LINEMODE::READY) if bb_rct.intersects(upd_rct)
|
2011
|
+
end
|
2012
|
+
end
|
2013
|
+
|
2014
|
+
# draw multiselection if necessary
|
2015
|
+
@shp_selection.draw(dc) if @shp_selection.visible?
|
2016
|
+
@shp_multi_edit.draw(dc) if @shp_multi_edit.visible?
|
2017
|
+
else
|
2018
|
+
# draw parent shapes (children are processed by parent objects)
|
2019
|
+
@diagram.get_top_shapes.each do |shape|
|
2020
|
+
shape.draw(dc) if !shape.is_a?(LineShape) || shape.stand_alone?
|
2021
|
+
end
|
2022
|
+
|
2023
|
+
# draw connections
|
2024
|
+
@diagram.get_top_shapes.each do |shape|
|
2025
|
+
shape.draw(dc) if shape.is_a?(LineShape) && !shape.stand_alone?
|
2026
|
+
end
|
2027
|
+
end
|
2028
|
+
end
|
2029
|
+
|
2030
|
+
# Function responsible for drawing of the canvas's background to given DC. The default
|
2031
|
+
# implementation draws canvas background and grid.
|
2032
|
+
# @param [Wx::DC] dc device context where the shapes will be drawn to
|
2033
|
+
# @param [Boolean] _from_paint Set the argument to true if the dc argument refers to the Wx::PaintDC instance
|
2034
|
+
# or derived classes (i.e. the function is called as a response to Wx::EVT_PAINT event)
|
2035
|
+
def draw_background(dc, _from_paint)
|
2036
|
+
# erase background
|
2037
|
+
if has_style?(STYLE::GRADIENT_BACKGROUND)
|
2038
|
+
bcg_size = @settings.grid_size + get_virtual_size
|
2039
|
+
if @settings.scale != 1.0
|
2040
|
+
dc.gradient_fill_linear(Wx::Rect.new([0, 0], [(bcg_size.x/@settings.scale).to_i, (bcg_size.y/@settings.scale).to_i]),
|
2041
|
+
@settings.gradient_from, @settings.gradient_to, Wx::SOUTH)
|
2042
|
+
else
|
2043
|
+
dc.gradient_fill_linear(Wx::Rect.new(Wx::Point.new(0, 0), bcg_size),
|
2044
|
+
@settings.gradient_from, @settings.gradient_to, Wx::SOUTH)
|
2045
|
+
end
|
2046
|
+
else
|
2047
|
+
dc.set_background(Wx::Brush.new(@settings.background_color))
|
2048
|
+
dc.clear
|
2049
|
+
end
|
2050
|
+
|
2051
|
+
# show grid
|
2052
|
+
if has_style?(STYLE::GRID_SHOW)
|
2053
|
+
linedist = @settings.grid_size.x * @settings.grid_line_mult
|
2054
|
+
|
2055
|
+
if (linedist * @settings.scale) > 3.0
|
2056
|
+
grid_rct = Wx::Rect.new([0, 0], @settings.grid_size + get_virtual_size)
|
2057
|
+
max_x = (grid_rct.right/@settings.scale).to_i
|
2058
|
+
max_y = (grid_rct.bottom/@settings.scale).to_i
|
2059
|
+
|
2060
|
+
dc.set_pen(Wx::Pen.new(@settings.grid_color, 1, @settings.grid_style))
|
2061
|
+
(grid_rct.left..max_x).step(linedist) do |x|
|
2062
|
+
dc.draw_line(x, 0, x, max_y)
|
2063
|
+
end
|
2064
|
+
(grid_rct.top..max_y).step(linedist) do |y|
|
2065
|
+
dc.draw_line(0, y, max_x, y)
|
2066
|
+
end
|
2067
|
+
end
|
2068
|
+
end
|
2069
|
+
end
|
2070
|
+
|
2071
|
+
# Function responsible for drawing of the canvas's foreground to given DC. The default
|
2072
|
+
# do nothing.
|
2073
|
+
# @param [Wx::DC] _dc device context where the shapes will be drawn to
|
2074
|
+
# @param [Boolean] _from_paint Set the argument to true if the dc argument refers to the Wx::PaintDC instance
|
2075
|
+
# or derived classes (i.e. the function is called as a response to Wx::EVT_PAINT event)
|
2076
|
+
def draw_foreground(_dc, _from_paint)
|
2077
|
+
# do nothing here...
|
2078
|
+
end
|
2079
|
+
|
2080
|
+
# Get reference to multiselection box
|
2081
|
+
# @return [Wx::SF::MultiSelRect] multiselection box object
|
2082
|
+
def get_multiselection_box
|
2083
|
+
@shp_multi_edit
|
2084
|
+
end
|
2085
|
+
|
2086
|
+
# @!group Public event handlers
|
2087
|
+
|
2088
|
+
# Event handler called when the canvas is clicked by
|
2089
|
+
# the left mouse button. The function can be overridden if necessary.
|
2090
|
+
#
|
2091
|
+
# The function is called by the framework and provides basic functionality
|
2092
|
+
# needed for proper management of displayed shape. It is necessary to call
|
2093
|
+
# this function from overridden methods if the default canvas behaviour
|
2094
|
+
# should be preserved.
|
2095
|
+
# @param [Wx::MouseEvent] event Mouse event
|
2096
|
+
# @see _on_left_down
|
2097
|
+
def on_left_down(event)
|
2098
|
+
# HINT: override it for custom actions...
|
2099
|
+
return unless @diagram
|
2100
|
+
|
2101
|
+
_notify_canvas_change(CHANGE::FOCUS)
|
2102
|
+
set_focus
|
2103
|
+
|
2104
|
+
lpos = dp2lp(event.get_position)
|
2105
|
+
|
2106
|
+
@can_save_state_on_mouse_up = false
|
2107
|
+
|
2108
|
+
case @working_mode
|
2109
|
+
when MODE::READY
|
2110
|
+
@selected_handle = get_topmost_handle_at_position(lpos)
|
2111
|
+
|
2112
|
+
if event.control_down && event.shift_down
|
2113
|
+
@selection_mode = SELECTIONMODE::REMOVE
|
2114
|
+
elsif event.shift_down
|
2115
|
+
@selection_mode = SELECTIONMODE::ADD
|
2116
|
+
else
|
2117
|
+
@selection_mode = SELECTIONMODE::NORMAL
|
2118
|
+
end
|
2119
|
+
|
2120
|
+
if @selected_handle.nil?
|
2121
|
+
selected_shape = get_shape_at_position(lpos)
|
2122
|
+
|
2123
|
+
selected_top_shape = selected_shape
|
2124
|
+
while selected_top_shape && selected_top_shape.has_style?(Shape::STYLE::PROPAGATE_SELECTION)
|
2125
|
+
selected_top_shape = selected_top_shape.get_parent_shape
|
2126
|
+
end
|
2127
|
+
|
2128
|
+
if selected_shape
|
2129
|
+
# perform selection
|
2130
|
+
lst_selection = get_selected_shapes
|
2131
|
+
|
2132
|
+
# cancel previous selections if necessary...
|
2133
|
+
if @selection_mode == SELECTIONMODE::NORMAL && (selected_top_shape.nil? || !lst_selection.include?(selected_top_shape))
|
2134
|
+
deselect_all
|
2135
|
+
end
|
2136
|
+
selected_top_shape.select(@selection_mode != SELECTIONMODE::REMOVE) if selected_top_shape
|
2137
|
+
|
2138
|
+
get_selected_shapes(lst_selection)
|
2139
|
+
|
2140
|
+
# remove child shapes from the selection
|
2141
|
+
validate_selection(lst_selection)
|
2142
|
+
|
2143
|
+
if lst_selection.size > 1
|
2144
|
+
hide_all_handles
|
2145
|
+
elsif @selection_mode == SELECTIONMODE::REMOVE && lst_selection.size == 1
|
2146
|
+
lst_selection.first.select(true)
|
2147
|
+
end
|
2148
|
+
|
2149
|
+
fit_pos = fit_position_to_grid(lpos)
|
2150
|
+
|
2151
|
+
# call user defined actions
|
2152
|
+
selected_shape.on_left_click(fit_pos)
|
2153
|
+
|
2154
|
+
# inform selected shapes about begin of dragging...
|
2155
|
+
lst_connections = []
|
2156
|
+
|
2157
|
+
lst_selection.each do |shape|
|
2158
|
+
shape.send(:_on_begin_drag, fit_pos)
|
2159
|
+
|
2160
|
+
# inform also connections assigned to the shape and its children
|
2161
|
+
lst_connections.clear
|
2162
|
+
append_assigned_connections(shape, lst_connections, true)
|
2163
|
+
|
2164
|
+
lst_connections.each do |line|
|
2165
|
+
line.send(:_on_begin_drag, fit_pos)
|
2166
|
+
end
|
2167
|
+
end
|
2168
|
+
|
2169
|
+
if @selection_mode == SELECTIONMODE::NORMAL
|
2170
|
+
@shp_multi_edit.show(false)
|
2171
|
+
@working_mode = MODE::SHAPEMOVE
|
2172
|
+
else
|
2173
|
+
if lst_selection.size > 1
|
2174
|
+
@shp_multi_edit.show(true)
|
2175
|
+
@shp_multi_edit.show_handles(true)
|
2176
|
+
else
|
2177
|
+
@shp_multi_edit.show(false)
|
2178
|
+
end
|
2179
|
+
@working_mode = MODE::READY
|
2180
|
+
end
|
2181
|
+
else
|
2182
|
+
if has_style?(STYLE::MULTI_SELECTION)
|
2183
|
+
deselect_all if @selection_mode == SELECTIONMODE::NORMAL
|
2184
|
+
@selection_start = Wx::RealPoint.new(lpos.x, lpos.y)
|
2185
|
+
@shp_selection.show(true)
|
2186
|
+
@shp_selection.show_handles(false)
|
2187
|
+
@shp_selection.set_relative_position(@selection_start)
|
2188
|
+
@shp_selection.set_rect_size(Wx::RealPoint.new(0, 0))
|
2189
|
+
@working_mode = MODE::MULTISELECTION
|
2190
|
+
else
|
2191
|
+
deselect_all
|
2192
|
+
@working_mode = MODE::READY
|
2193
|
+
end
|
2194
|
+
end
|
2195
|
+
|
2196
|
+
# update canvas
|
2197
|
+
invalidate_visible_rect
|
2198
|
+
else
|
2199
|
+
if @selected_handle.get_parent_shape == @shp_multi_edit
|
2200
|
+
if has_style?(STYLE::MULTI_SIZE_CHANGE)
|
2201
|
+
@working_mode = MODE::MULTIHANDLEMOVE
|
2202
|
+
else
|
2203
|
+
@working_mode = MODE::READY
|
2204
|
+
end
|
2205
|
+
else
|
2206
|
+
@working_mode = MODE::HANDLEMOVE
|
2207
|
+
case @selected_handle.get_type
|
2208
|
+
when Shape::Handle::TYPE::LINESTART
|
2209
|
+
line = @selected_handle.get_parent_shape
|
2210
|
+
line.send(:set_line_mode, LineShape::LINEMODE::SRCCHANGE)
|
2211
|
+
line.send(:set_unfinished_point, lpos)
|
2212
|
+
|
2213
|
+
when Shape::Handle::TYPE::LINEEND
|
2214
|
+
line = @selected_handle.get_parent_shape
|
2215
|
+
line.send(:set_line_mode, LineShape::LINEMODE::TRGCHANGE)
|
2216
|
+
line.send(:set_unfinished_point, lpos)
|
2217
|
+
end
|
2218
|
+
end
|
2219
|
+
@selected_handle.send(:_on_begin_drag, fit_position_to_grid(lpos))
|
2220
|
+
end
|
2221
|
+
|
2222
|
+
when MODE::CREATECONNECTION
|
2223
|
+
# update the line shape being created
|
2224
|
+
if @new_line_shape
|
2225
|
+
shape_under = get_shape_under_cursor
|
2226
|
+
# propagate request for interactive connection if requested
|
2227
|
+
while shape_under && shape_under.has_style?(Shape::STYLE::PROPAGATE_INTERACTIVE_CONNECTION)
|
2228
|
+
shape_under = shape_under.get_parent_shape
|
2229
|
+
end
|
2230
|
+
# finish connection's creation process if possible
|
2231
|
+
if shape_under && !event.control_down
|
2232
|
+
if @new_line_shape.get_trg_shape_id.nil? && (shape_under != @new_line_shape) &&
|
2233
|
+
shape_under.get_id && (shape_under.is_connection_accepted(@new_line_shape.class))
|
2234
|
+
# find out whether the target shape can be connected to the source shape
|
2235
|
+
source_shape = @diagram.find_shape(@new_line_shape.get_src_shape_id)
|
2236
|
+
|
2237
|
+
if source_shape &&
|
2238
|
+
shape_under.is_src_neighbour_accepted(source_shape.class) &&
|
2239
|
+
source_shape.is_trg_neighbour_accepted(shape_under.class)
|
2240
|
+
@new_line_shape.set_trg_shape_id(shape_under.get_id)
|
2241
|
+
@new_line_shape.set_ending_connection_point(shape_under.get_nearest_connection_point(lpos.to_real))
|
2242
|
+
|
2243
|
+
# inform user that the line is completed
|
2244
|
+
case on_pre_connection_finished(@new_line_shape)
|
2245
|
+
when PRECON_FINISH_STATE::OK
|
2246
|
+
when PRECON_FINISH_STATE::FAILED_AND_CANCEL_LINE
|
2247
|
+
@new_line_shape.set_trg_shape_id(nil)
|
2248
|
+
@diagram.remove_shape(@new_line_shape)
|
2249
|
+
@working_mode = MODE::READY
|
2250
|
+
@new_line_shape = nil
|
2251
|
+
return
|
2252
|
+
when PRECON_FINISH_STATE::FAILED_AND_CONTINUE_EDIT
|
2253
|
+
@new_line_shape.set_trg_shape_id(nil)
|
2254
|
+
return
|
2255
|
+
end
|
2256
|
+
@new_line_shape.create_handles
|
2257
|
+
|
2258
|
+
# switch off the "under-construction" mode
|
2259
|
+
@new_line_shape.send(:set_line_mode, LineShape::LINEMODE::READY)
|
2260
|
+
|
2261
|
+
on_connection_finished(@new_line_shape)
|
2262
|
+
|
2263
|
+
@new_line_shape.update
|
2264
|
+
@new_line_shape.refresh(DELAYED)
|
2265
|
+
|
2266
|
+
@working_mode = MODE::READY
|
2267
|
+
@new_line_shape = nil
|
2268
|
+
|
2269
|
+
save_canvas_state
|
2270
|
+
end
|
2271
|
+
end
|
2272
|
+
else
|
2273
|
+
if @new_line_shape.get_src_shape_id
|
2274
|
+
fit_pos = fit_position_to_grid(lpos)
|
2275
|
+
@new_line_shape.get_control_points << Wx::RealPoint.new(fit_pos.x, fit_pos.y)
|
2276
|
+
end
|
2277
|
+
end
|
2278
|
+
end
|
2279
|
+
|
2280
|
+
else
|
2281
|
+
@working_mode = MODE::READY
|
2282
|
+
end
|
2283
|
+
|
2284
|
+
refresh_invalidated_rect
|
2285
|
+
end
|
2286
|
+
|
2287
|
+
# Event handler called when the canvas is double-clicked by
|
2288
|
+
# the left mouse button. The function can be overridden if necessary.
|
2289
|
+
#
|
2290
|
+
# The function is called by the framework and provides basic functionality
|
2291
|
+
# needed for proper management of displayed shape. It is necessary to call
|
2292
|
+
# this function from overridden methods if the default canvas behaviour
|
2293
|
+
# should be preserved.
|
2294
|
+
# @param [Wx::MouseEvent] event Mouse event
|
2295
|
+
# @see _on_left_double_click
|
2296
|
+
def on_left_double_click(event)
|
2297
|
+
# HINT: override it for custom actions...
|
2298
|
+
|
2299
|
+
_notify_canvas_change(CHANGE::FOCUS)
|
2300
|
+
set_focus
|
2301
|
+
|
2302
|
+
lpos = dp2lp(event.get_position)
|
2303
|
+
|
2304
|
+
if @working_mode == MODE::READY
|
2305
|
+
shape = get_shape_under_cursor
|
2306
|
+
if shape
|
2307
|
+
shape.on_left_double_click(lpos)
|
2308
|
+
|
2309
|
+
# double click onto a line shape always change its set of
|
2310
|
+
# control points so the canvas state should be saved now...
|
2311
|
+
save_canvas_state if shape.is_a?(LineShape)
|
2312
|
+
end
|
2313
|
+
end
|
2314
|
+
|
2315
|
+
refresh_invalidated_rect
|
2316
|
+
end
|
2317
|
+
|
2318
|
+
# Event handler called when the left mouse button is released.
|
2319
|
+
# The function can be overridden if necessary.
|
2320
|
+
#
|
2321
|
+
# The function is called by the framework and provides basic functionality
|
2322
|
+
# needed for proper management of displayed shape. It is necessary to call
|
2323
|
+
# this function from overridden methods if the default canvas behaviour
|
2324
|
+
# should be preserved.
|
2325
|
+
# @param [Wx::MouseEvent] event Mouse event
|
2326
|
+
# @see _on_left_up
|
2327
|
+
def on_left_up(event)
|
2328
|
+
# HINT: override it for custom actions...
|
2329
|
+
lpos = dp2lp(event.get_position)
|
2330
|
+
|
2331
|
+
case @working_mode
|
2332
|
+
when MODE::MULTIHANDLEMOVE, MODE::HANDLEMOVE
|
2333
|
+
# resize parent shape to fit all its children if necessary
|
2334
|
+
if @selected_handle.get_parent_shape.get_parent_shape
|
2335
|
+
@selected_handle.get_parent_shape.get_parent_shape.update
|
2336
|
+
end
|
2337
|
+
|
2338
|
+
# if the handle is line handle then return the line to normal state
|
2339
|
+
# and re-assign line's source/target shape
|
2340
|
+
case @selected_handle.get_type
|
2341
|
+
when Shape::Handle::TYPE::LINESTART, Shape::Handle::TYPE::LINEEND
|
2342
|
+
line = @selected_handle.get_parent_shape
|
2343
|
+
line.send(:set_line_mode, LineShape::LINEMODE::READY)
|
2344
|
+
|
2345
|
+
parent_shape = get_shape_under_cursor
|
2346
|
+
|
2347
|
+
if parent_shape && (parent_shape != line) && (parent_shape.is_connection_accepted(line.class))
|
2348
|
+
if @selected_handle.get_type == Shape::Handle::TYPE::LINESTART
|
2349
|
+
trg_shape = @diagram.find_shape(line.get_trg_shape_id)
|
2350
|
+
if trg_shape && parent_shape.is_trg_neighbour_accepted(trg_shape.class)
|
2351
|
+
line.set_src_shape_id(parent_shape.get_id)
|
2352
|
+
end
|
2353
|
+
else
|
2354
|
+
src_shape = @diagram.find_shape(line.get_src_shape_id)
|
2355
|
+
if src_shape && parent_shape.is_src_neighbour_accepted(src_shape.class)
|
2356
|
+
line.set_trg_shape_id(parent_shape.get_id)
|
2357
|
+
end
|
2358
|
+
end
|
2359
|
+
end
|
2360
|
+
end
|
2361
|
+
|
2362
|
+
@selected_handle.send(:_on_end_drag, lpos)
|
2363
|
+
|
2364
|
+
@selected_handle = nil
|
2365
|
+
save_canvas_state if @can_save_state_on_mouse_up
|
2366
|
+
|
2367
|
+
when MODE::SHAPEMOVE
|
2368
|
+
lst_selection = get_selected_shapes
|
2369
|
+
|
2370
|
+
lst_selection.each do |shape|
|
2371
|
+
# notify shape
|
2372
|
+
shape.send(:_on_end_drag, lpos)
|
2373
|
+
# reparent based on new position
|
2374
|
+
reparent_shape(shape, lpos)
|
2375
|
+
end
|
2376
|
+
|
2377
|
+
if lst_selection.size>1
|
2378
|
+
@shp_multi_edit.show(true)
|
2379
|
+
@shp_multi_edit.show_handles(true)
|
2380
|
+
else
|
2381
|
+
@shp_multi_edit.show(false)
|
2382
|
+
end
|
2383
|
+
|
2384
|
+
move_shapes_from_negatives
|
2385
|
+
|
2386
|
+
save_canvas_state if @can_save_state_on_mouse_up
|
2387
|
+
|
2388
|
+
when MODE::MULTISELECTION
|
2389
|
+
lst_selection = get_selected_shapes
|
2390
|
+
|
2391
|
+
sel_rect = @shp_selection.get_bounding_box
|
2392
|
+
@current_shapes.each do |shape|
|
2393
|
+
if shape.active? && sel_rect.contains?(shape.get_bounding_box)
|
2394
|
+
shape = shape.get_parent_shape while shape && shape.has_style?(Shape::STYLE::PROPAGATE_SELECTION)
|
2395
|
+
if shape
|
2396
|
+
shape.select(@selection_mode != SELECTIONMODE::REMOVE)
|
2397
|
+
shape_pos = lst_selection.index(shape)
|
2398
|
+
if @selection_mode != SELECTIONMODE::REMOVE && shape_pos.nil?
|
2399
|
+
lst_selection << shape
|
2400
|
+
elsif @selection_mode == SELECTIONMODE::REMOVE && shape_pos
|
2401
|
+
lst_selection.delete_at(shape_pos)
|
2402
|
+
end
|
2403
|
+
end
|
2404
|
+
end
|
2405
|
+
end
|
2406
|
+
|
2407
|
+
validate_selection(lst_selection)
|
2408
|
+
|
2409
|
+
if lst_selection.empty?
|
2410
|
+
@shp_multi_edit.show(false)
|
2411
|
+
else
|
2412
|
+
hide_all_handles
|
2413
|
+
@shp_multi_edit.show(true)
|
2414
|
+
@shp_multi_edit.show_handles(true)
|
2415
|
+
end
|
2416
|
+
|
2417
|
+
@shp_selection.show(false)
|
2418
|
+
end
|
2419
|
+
|
2420
|
+
if @working_mode != MODE::CREATECONNECTION
|
2421
|
+
# update canvas
|
2422
|
+
@working_mode = MODE::READY
|
2423
|
+
update_multiedit_size
|
2424
|
+
update_virtual_size
|
2425
|
+
refresh(false)
|
2426
|
+
else
|
2427
|
+
refresh_invalidated_rect
|
2428
|
+
end
|
2429
|
+
end
|
2430
|
+
|
2431
|
+
# Event handler called when the canvas is clicked by
|
2432
|
+
# the right mouse button. The function can be overridden if necessary.
|
2433
|
+
#
|
2434
|
+
# The function is called by the framework and provides basic functionality
|
2435
|
+
# needed for proper management of displayed shape. It is necessary to call
|
2436
|
+
# this function from overridden methods if the default canvas behaviour
|
2437
|
+
# should be preserved.
|
2438
|
+
# @param [Wx::MouseEvent] event Mouse event
|
2439
|
+
# @see _on_right_down
|
2440
|
+
def on_right_down(event)
|
2441
|
+
# HINT: override it for custom actions...
|
2442
|
+
|
2443
|
+
_notify_canvas_change(CHANGE::FOCUS)
|
2444
|
+
set_focus
|
2445
|
+
|
2446
|
+
lpos = dp2lp(event.get_position)
|
2447
|
+
|
2448
|
+
if @working_mode == MODE::READY
|
2449
|
+
deselect_all
|
2450
|
+
|
2451
|
+
shape = get_shape_under_cursor
|
2452
|
+
if shape
|
2453
|
+
shape.select(true)
|
2454
|
+
shape.on_right_click(lpos)
|
2455
|
+
end
|
2456
|
+
end
|
2457
|
+
|
2458
|
+
refresh(false)
|
2459
|
+
end
|
2460
|
+
|
2461
|
+
# Event handler called when the canvas is double-clicked by
|
2462
|
+
# the right mouse button. The function can be overridden if necessary.
|
2463
|
+
#
|
2464
|
+
# The function is called by the framework and provides basic functionality
|
2465
|
+
# needed for proper management of displayed shape. It is necessary to call
|
2466
|
+
# this function from overridden methods if the default canvas behaviour
|
2467
|
+
# should be preserved.
|
2468
|
+
# @param [Wx::MouseEvent] event Mouse event
|
2469
|
+
# @see _on_right_double_click
|
2470
|
+
def on_right_double_click(event)
|
2471
|
+
# HINT: override it for custom actions...
|
2472
|
+
|
2473
|
+
_notify_canvas_change(CHANGE::FOCUS)
|
2474
|
+
set_focus
|
2475
|
+
|
2476
|
+
lpos = dp2lp(event.get_position)
|
2477
|
+
|
2478
|
+
if @working_mode == MODE::READY
|
2479
|
+
shape = get_shape_under_cursor
|
2480
|
+
shape.on_right_double_click(lpos) if shape
|
2481
|
+
end
|
2482
|
+
|
2483
|
+
refresh_invalidated_rect
|
2484
|
+
end
|
2485
|
+
|
2486
|
+
# Event handler called when the right mouse button is released.
|
2487
|
+
# The function can be overridden if necessary.
|
2488
|
+
#
|
2489
|
+
# The function is called by the framework and provides basic functionality
|
2490
|
+
# needed for proper management of displayed shape. It is necessary to call
|
2491
|
+
# this function from overridden methods if the default canvas behaviour
|
2492
|
+
# should be preserved.
|
2493
|
+
# @param [Wx::MouseEvent] _event Mouse event
|
2494
|
+
# @see _on_right_up
|
2495
|
+
def on_right_up(_event)
|
2496
|
+
# HINT: override it for custom actions...
|
2497
|
+
end
|
2498
|
+
|
2499
|
+
# Event handler called when the mouse pointer is moved.
|
2500
|
+
# The function can be overridden if necessary.
|
2501
|
+
#
|
2502
|
+
# The function is called by the framework and provides basic functionality
|
2503
|
+
# needed for proper management of displayed shape. It is necessary to call
|
2504
|
+
# this function from overridden methods if the default canvas behaviour
|
2505
|
+
# should be preserved.
|
2506
|
+
# @param [Wx::MouseEvent] event Mouse event
|
2507
|
+
# @see _on_mouse_move
|
2508
|
+
def on_mouse_move(event)
|
2509
|
+
# HINT: override it for custom actions...
|
2510
|
+
return unless @diagram
|
2511
|
+
|
2512
|
+
lpos = dp2lp(event.get_position)
|
2513
|
+
|
2514
|
+
case @working_mode
|
2515
|
+
when MODE::READY, MODE::CREATECONNECTION
|
2516
|
+
unless event.dragging
|
2517
|
+
# send event to multiedit shape
|
2518
|
+
@shp_multi_edit.send(:_on_mouse_move, lpos) if @shp_multi_edit.visible?
|
2519
|
+
|
2520
|
+
# send event to all user shapes
|
2521
|
+
@current_shapes.each { |shape| shape.send(:_on_mouse_move, lpos) }
|
2522
|
+
|
2523
|
+
# update unfinished line if any
|
2524
|
+
if @new_line_shape
|
2525
|
+
line_rct = Wx::Rect.new
|
2526
|
+
upd_line_rct = Wx::Rect.new
|
2527
|
+
@new_line_shape.get_complete_bounding_box(line_rct, Shape::BBMODE::SELF | Shape::BBMODE::CHILDREN)
|
2528
|
+
|
2529
|
+
@new_line_shape.send(:set_unfinished_point, fit_position_to_grid(lpos))
|
2530
|
+
@new_line_shape.update
|
2531
|
+
|
2532
|
+
@new_line_shape.get_complete_bounding_box(upd_line_rct, Shape::BBMODE::SELF | Shape::BBMODE::CHILDREN)
|
2533
|
+
|
2534
|
+
line_rct.union!(upd_line_rct)
|
2535
|
+
|
2536
|
+
invalidate_rect(line_rct)
|
2537
|
+
end
|
2538
|
+
end
|
2539
|
+
|
2540
|
+
when MODE::HANDLEMOVE, MODE::MULTIHANDLEMOVE, MODE::SHAPEMOVE
|
2541
|
+
if @working_mode != MODE::SHAPEMOVE
|
2542
|
+
if event.dragging
|
2543
|
+
@selected_handle.send(:_on_dragging, fit_position_to_grid(lpos)) if @selected_handle
|
2544
|
+
update_multiedit_size if @working_mode == MODE::MULTIHANDLEMOVE
|
2545
|
+
@can_save_state_on_mouse_up = true
|
2546
|
+
else
|
2547
|
+
@selected_handle.send(:_on_end_drag, lpos) if @selected_handle
|
2548
|
+
@selected_handle = nil
|
2549
|
+
@working_mode = MODE::READY
|
2550
|
+
end
|
2551
|
+
end
|
2552
|
+
unless @working_mode == MODE::MULTIHANDLEMOVE
|
2553
|
+
if event.dragging
|
2554
|
+
if has_style?(STYLE::GRID_USE)
|
2555
|
+
return if (event.get_position.x - @prev_mouse_pos.x).abs < @settings.grid_size.x &&
|
2556
|
+
(event.get_position.y - @prev_mouse_pos.y).abs < @settings.grid_size.y
|
2557
|
+
end
|
2558
|
+
@prev_mouse_pos = event.get_position
|
2559
|
+
|
2560
|
+
if event.control_down || event.shift_down
|
2561
|
+
lst_selection = get_selected_shapes
|
2562
|
+
deselect_all
|
2563
|
+
if Wx.has_feature?(:USE_DRAG_AND_DROP)
|
2564
|
+
do_drag_drop(lst_selection, lpos)
|
2565
|
+
end
|
2566
|
+
else
|
2567
|
+
lst_connections = []
|
2568
|
+
@current_shapes.each do |shape|
|
2569
|
+
if shape.selected? && @working_mode == MODE::SHAPEMOVE
|
2570
|
+
shape.send(:_on_dragging, fit_position_to_grid(lpos))
|
2571
|
+
|
2572
|
+
# move also connections assigned to this shape and its children
|
2573
|
+
lst_connections.clear
|
2574
|
+
|
2575
|
+
append_assigned_connections(shape, lst_connections,true)
|
2576
|
+
|
2577
|
+
lst_connections.each { |line| line.send(:_on_dragging, fit_position_to_grid(lpos)) }
|
2578
|
+
|
2579
|
+
# update connections assigned to this shape
|
2580
|
+
lst_connections = @diagram.get_assigned_connections(shape, LineShape, Shape::CONNECTMODE::BOTH)
|
2581
|
+
lst_connections.each { |line| line.update }
|
2582
|
+
else
|
2583
|
+
shape.send(:_on_mouse_move, lpos)
|
2584
|
+
end
|
2585
|
+
end
|
2586
|
+
|
2587
|
+
@can_save_state_on_mouse_up = true
|
2588
|
+
end
|
2589
|
+
else
|
2590
|
+
@working_mode = MODE::READY
|
2591
|
+
end
|
2592
|
+
end
|
2593
|
+
|
2594
|
+
when MODE::MULTISELECTION
|
2595
|
+
selection_pos = Wx::RealPoint.new(*@selection_start.to_ary)
|
2596
|
+
selection_size = Wx::RealPoint.new(lpos.x - @selection_start.x, lpos.y - @selection_start.y)
|
2597
|
+
if selection_size.x < 0
|
2598
|
+
selection_pos.x += selection_size.x
|
2599
|
+
selection_size.x = -selection_size.x
|
2600
|
+
end
|
2601
|
+
if selection_size.y < 0
|
2602
|
+
selection_pos.y += selection_size.y
|
2603
|
+
selection_size.y = -selection_size.y
|
2604
|
+
end
|
2605
|
+
@shp_selection.set_relative_position(selection_pos)
|
2606
|
+
@shp_selection.set_rect_size(selection_size)
|
2607
|
+
|
2608
|
+
invalidate_visible_rect
|
2609
|
+
end
|
2610
|
+
|
2611
|
+
refresh_invalidated_rect
|
2612
|
+
end
|
2613
|
+
|
2614
|
+
# Event handler called when the mouse wheel position is changed.
|
2615
|
+
# The function can be overridden if necessary.
|
2616
|
+
#
|
2617
|
+
# The function is called by the framework and provides basic functionality
|
2618
|
+
# needed for proper management of displayed shape. It is necessary to call
|
2619
|
+
# this function from overridden methods if the default canvas behaviour
|
2620
|
+
# should be preserved.
|
2621
|
+
# @param [Wx::MouseEvent] event Mouse event
|
2622
|
+
def on_mouse_wheel(event)
|
2623
|
+
# HINT: override it for custom actions...
|
2624
|
+
|
2625
|
+
if event.control_down
|
2626
|
+
scale = get_scale
|
2627
|
+
scale += (event.get_wheel_rotation/(event.get_wheel_delta*10)).to_f
|
2628
|
+
|
2629
|
+
scale = @settings.min_scale if scale < @settings.min_scale
|
2630
|
+
scale = @settings.max_scale if scale > @settings.max_scale
|
2631
|
+
|
2632
|
+
set_scale(scale)
|
2633
|
+
refresh(false)
|
2634
|
+
end
|
2635
|
+
|
2636
|
+
event.skip
|
2637
|
+
end
|
2638
|
+
|
2639
|
+
# Event handler called when any key is pressed.
|
2640
|
+
# The function can be overridden if necessary.
|
2641
|
+
#
|
2642
|
+
# The function is called by the framework and provides basic functionality
|
2643
|
+
# needed for proper management of displayed shape. It is necessary to call
|
2644
|
+
# this function from overridden methods if the default canvas behaviour
|
2645
|
+
# should be preserved.
|
2646
|
+
# @param [Wx::KeyEvent] event Keyboard event
|
2647
|
+
# @see _on_key_down
|
2648
|
+
def on_key_down(event)
|
2649
|
+
# HINT: override it for custom actions...
|
2650
|
+
return unless @diagram
|
2651
|
+
|
2652
|
+
lst_selection = get_selected_shapes
|
2653
|
+
|
2654
|
+
case event.get_key_code
|
2655
|
+
when Wx::K_DELETE
|
2656
|
+
# send event to selected shapes
|
2657
|
+
lst_selection.delete_if do |shape|
|
2658
|
+
if shape.has_style?(Shape::STYLE::PROCESS_DEL)
|
2659
|
+
shape.send(:_on_key, event.get_key_code)
|
2660
|
+
true
|
2661
|
+
else
|
2662
|
+
false
|
2663
|
+
end
|
2664
|
+
end
|
2665
|
+
|
2666
|
+
clear_temporaries
|
2667
|
+
|
2668
|
+
# delete selected shapes
|
2669
|
+
@diagram.remove_shapes(lst_selection)
|
2670
|
+
@shp_multi_edit.show(false)
|
2671
|
+
save_canvas_state
|
2672
|
+
refresh(false)
|
2673
|
+
|
2674
|
+
when Wx::K_ESCAPE
|
2675
|
+
case @working_mode
|
2676
|
+
when MODE::CREATECONNECTION
|
2677
|
+
abort_interactive_connection
|
2678
|
+
|
2679
|
+
when MODE::HANDLEMOVE
|
2680
|
+
if @selected_handle && @selected_handle.get_parent_shape.is_a?(LineShape)
|
2681
|
+
@selected_handle.send(:_on_end_drag, Wx::Point.new(0, 0))
|
2682
|
+
|
2683
|
+
line = @selected_handle.get_parent_shape
|
2684
|
+
line.send(:set_line_mode, LineShape::LINEMODE::READY)
|
2685
|
+
@selected_handle = nil
|
2686
|
+
end
|
2687
|
+
|
2688
|
+
else
|
2689
|
+
# send event to selected shapes
|
2690
|
+
lst_selection.each { |shape| shape.send(:_on_key, event.get_key_code) }
|
2691
|
+
end
|
2692
|
+
@working_mode = MODE::READY
|
2693
|
+
refresh(false)
|
2694
|
+
|
2695
|
+
when Wx::K_LEFT, Wx::K_RIGHT, Wx::K_UP, Wx::K_DOWN
|
2696
|
+
lst_connections = []
|
2697
|
+
lst_selection.each do |shape|
|
2698
|
+
shape.send(:_on_key, event.get_key_code)
|
2699
|
+
|
2700
|
+
# inform also connections assigned to this shape
|
2701
|
+
lst_connections.clear
|
2702
|
+
append_assigned_connections(shape, lst_connections, true)
|
2703
|
+
|
2704
|
+
lst_connections.each do |line|
|
2705
|
+
line.send(:_on_key, event.get_key_code) unless line.selected?
|
2706
|
+
end
|
2707
|
+
end
|
2708
|
+
|
2709
|
+
# send the event to multiedit ctrl if displayed
|
2710
|
+
@shp_multi_edit.send(:_on_key, event.get_key_code) if @shp_multi_edit.visible?
|
2711
|
+
|
2712
|
+
refresh_invalidated_rect
|
2713
|
+
save_canvas_state
|
2714
|
+
|
2715
|
+
else
|
2716
|
+
lst_selection.each { |shape| shape.send(:_on_key, event.get_key_code) }
|
2717
|
+
update_multiedit_size if @shp_multi_edit.visible?
|
2718
|
+
end
|
2719
|
+
end
|
2720
|
+
|
2721
|
+
# Event handler called when any editable text shape is changed.
|
2722
|
+
# The function can be overridden if necessary.
|
2723
|
+
# The function is called by the framework and its default implementation
|
2724
|
+
# generates Wx::SF::EVT_SF_TEXT_CHANGE event.
|
2725
|
+
# @param [Wx::SF::EditTextShape] shape Changed Wx::SF::EditTextShape object
|
2726
|
+
# @see Wx::SF::EditTextShape#edit_label
|
2727
|
+
# @see Wx::SF::ShapeTextEvent
|
2728
|
+
def on_text_change(shape)
|
2729
|
+
# HINT: override it for custom actions...
|
2730
|
+
|
2731
|
+
# ... standard implementation generates the Wx::SF::EVT_SF_TEXT_CHANGE event.
|
2732
|
+
id = shape ? shape.get_id : nil
|
2733
|
+
|
2734
|
+
event = ShapeTextEvent.new(Wx::SF::EVT_SF_TEXT_CHANGE, id)
|
2735
|
+
event.set_shape(shape)
|
2736
|
+
event.set_text(shape.get_text)
|
2737
|
+
process_event(event)
|
2738
|
+
end
|
2739
|
+
|
2740
|
+
# Event handler called after (successful or cancelled) connection creation. The function
|
2741
|
+
# can be overridden if necessary. The default implementation
|
2742
|
+
# generates Wx::SF::EVT_SF_LINE_DONE event.
|
2743
|
+
# @param [Wx::SF::LineShape,nil] connection new connection object (nil if cancelled)
|
2744
|
+
# @see start_interactive_connection
|
2745
|
+
# @see Wx::SF::ShapeEvent
|
2746
|
+
def on_connection_finished(connection)
|
2747
|
+
# HINT: override to perform user-defined actions...
|
2748
|
+
|
2749
|
+
# ... standard implementation generates the Wx::SF::EVT_SF_LINE_DONE event.
|
2750
|
+
id = connection ? connection.get_id : -1
|
2751
|
+
|
2752
|
+
event = ShapeEvent.new(Wx::SF::EVT_SF_LINE_DONE, id)
|
2753
|
+
event.set_shape(connection)
|
2754
|
+
process_event(event)
|
2755
|
+
end
|
2756
|
+
|
2757
|
+
# Event handler called after successful connection creation in
|
2758
|
+
# order to allow developer to perform some kind of checks
|
2759
|
+
# before the connection is really added to the diagram. The function
|
2760
|
+
# can be overridden if necessary. The default implementation
|
2761
|
+
# generates Wx::SF::EVT_SF_LINE_DONE event.
|
2762
|
+
# @param [Wx::SF::LineShape] connection new connection object
|
2763
|
+
# @return [PRECON_FINISH_STATE] PRECONNECTIONFINISHEDSTATE::OK if the connection is accepted, otherwise
|
2764
|
+
# if the generated event has been vetoed the connection creation is cancelled
|
2765
|
+
# @see start_interactive_connection
|
2766
|
+
# @see Wx::SF::ShapeEvent
|
2767
|
+
def on_pre_connection_finished(connection)
|
2768
|
+
# HINT: override to perform user-defined actions...
|
2769
|
+
|
2770
|
+
# ... standard implementation generates the Wx::SF::EVT_SF_LINE_DONE event.
|
2771
|
+
id = connection ? connection.get_id : -1
|
2772
|
+
|
2773
|
+
event = ShapeEvent.new(Wx::SF::EVT_SF_LINE_BEFORE_DONE, id)
|
2774
|
+
event.set_shape(connection)
|
2775
|
+
process_event(event)
|
2776
|
+
|
2777
|
+
return PRECON_FINISH_STATE::FAILED_AND_CANCEL_LINE if event.vetoed?
|
2778
|
+
|
2779
|
+
PRECON_FINISH_STATE::OK
|
2780
|
+
end
|
2781
|
+
|
2782
|
+
if Wx.has_feature?(:USE_DRAG_AND_DROP)
|
2783
|
+
|
2784
|
+
# Event handler called by the framework after any dragged shapes
|
2785
|
+
# are dropped to the canvas. The default implementation
|
2786
|
+
# generates Wx::SF::EVT_SF_ON_DROP event.
|
2787
|
+
# @param [Integer] x X-coordinate of a position the data was dropped to
|
2788
|
+
# @param [Integer] y Y-coordinate of a position the data was dropped to
|
2789
|
+
# @param [Wx::DragResult] deflt Drag result
|
2790
|
+
# @param [Array<Wx::SF::Shape>] dropped a list containing the dropped data
|
2791
|
+
# @see Wx::SF::CanvasDropTarget
|
2792
|
+
# @see Wx::SF::ShapeDropEvent
|
2793
|
+
def on_drop(x, y, deflt, dropped)
|
2794
|
+
# HINT: override it for custom actions...
|
2795
|
+
|
2796
|
+
# ... standard implementation generates the Wx::SF::EVT_SF_ON_DROP event.
|
2797
|
+
return unless has_style?(STYLE::DND)
|
2798
|
+
|
2799
|
+
# create the drop event and process it
|
2800
|
+
event = ShapeDropEvent.new(Wx::SF::EVT_SF_ON_DROP, x, y, self, deflt, Wx::ID_ANY)
|
2801
|
+
event.set_dropped_shapes(dropped)
|
2802
|
+
process_event(event)
|
2803
|
+
end
|
2804
|
+
|
2805
|
+
end
|
2806
|
+
|
2807
|
+
# Event handler called by the framework after pasting of shapes
|
2808
|
+
# from the clipboard to the canvas. The default implementation
|
2809
|
+
# generates Wx::SF::EVT_SF_ON_PASTE event.
|
2810
|
+
# @param [Array<Wx::SF::Shape>] pasted a list containing the pasted data
|
2811
|
+
# @see Wx::SF::ShapeCanvas#paste
|
2812
|
+
# @see Wx::SF::ShapePasteEvent
|
2813
|
+
def on_paste(pasted)
|
2814
|
+
# HINT: override it for custom actions...
|
2815
|
+
|
2816
|
+
# ... standard implementation generates the Wx::SF::EVT_SF_ON_PASTE event.
|
2817
|
+
return unless has_style?(STYLE::CLIPBOARD)
|
2818
|
+
|
2819
|
+
# create the drop event and process it
|
2820
|
+
event = ShapePasteEvent.new(Wx::SF::EVT_SF_ON_PASTE, self, Wx::ID_ANY)
|
2821
|
+
event.set_pasted_shapes(pasted)
|
2822
|
+
process_event(event)
|
2823
|
+
end
|
2824
|
+
|
2825
|
+
# Event handler called if canvas virtual size is going to be updated.
|
2826
|
+
# The default implementation does nothing but the function can be overridden by
|
2827
|
+
# a user to modify calculated virtual canvas size.
|
2828
|
+
# @param [Wx::Rect] _virtrct Calculated canvas virtual size
|
2829
|
+
def on_update_virtual_size(_virtrct)
|
2830
|
+
# HINT: override it for custom actions...
|
2831
|
+
end
|
2832
|
+
|
2833
|
+
# @!endgroup
|
2834
|
+
|
2835
|
+
def inspect
|
2836
|
+
"#<Wx::SF::ShapeCanvas:#{object_id}>"
|
2837
|
+
end
|
2838
|
+
|
2839
|
+
private
|
2840
|
+
|
2841
|
+
# Validate selection so the shapes in the given list can be processed by the clipboard functions
|
2842
|
+
# @param [Array<Wx::SF::Shape>] selection
|
2843
|
+
# @param [Boolean] storeprevpos
|
2844
|
+
def validate_selection_for_clipboard(selection, storeprevpos)
|
2845
|
+
selection.dup.each do |shape|
|
2846
|
+
if shape.get_parent_shape
|
2847
|
+
# remove child shapes without parent in the selection and without STYLE::PARENT_CHANGE style
|
2848
|
+
# defined from the selection
|
2849
|
+
if !shape.has_style?(Shape::STYLE::PARENT_CHANGE) && !selection.include?(shape.get_parent_shape)
|
2850
|
+
selection.delete(shape)
|
2851
|
+
else
|
2852
|
+
# convert relative position to absolute position if the shape is copied
|
2853
|
+
# without its parent
|
2854
|
+
unless selection.include?(shape.get_parent_shape)
|
2855
|
+
store_prev_position(shape) if storeprevpos
|
2856
|
+
shape.set_relative_position(shape.get_absolute_position)
|
2857
|
+
end
|
2858
|
+
end
|
2859
|
+
end
|
2860
|
+
|
2861
|
+
append_assigned_connections(shape, selection, false)
|
2862
|
+
end
|
2863
|
+
end
|
2864
|
+
|
2865
|
+
# Append connections assigned to shapes in given list to this list as well
|
2866
|
+
# @param [Wx::SF::Shape] shape
|
2867
|
+
# @param [Array<Wx::SF::Shape>] selection
|
2868
|
+
# @param [Boolean] childrenonly
|
2869
|
+
def append_assigned_connections(shape, selection, childrenonly)
|
2870
|
+
# add connections assigned to copied topmost shapes and their children to the copy list
|
2871
|
+
lst_children = shape.get_child_shapes(ANY, RECURSIVE)
|
2872
|
+
|
2873
|
+
# get connections assigned to the parent shape
|
2874
|
+
lst_connections = @diagram.get_assigned_connections(shape, LineShape, Shape::CONNECTMODE::BOTH) unless childrenonly
|
2875
|
+
lst_connections ||= []
|
2876
|
+
# get connections assigned to its child shape
|
2877
|
+
lst_children.each do |child|
|
2878
|
+
# get connections assigned to the child shape
|
2879
|
+
@diagram.get_assigned_connections(child, LineShape, Shape::CONNECTMODE::BOTH, lst_connections)
|
2880
|
+
end
|
2881
|
+
|
2882
|
+
# insert connections to the copy list
|
2883
|
+
lst_connections.each do |line|
|
2884
|
+
selection << line unless selection.include?(line)
|
2885
|
+
end
|
2886
|
+
end
|
2887
|
+
|
2888
|
+
# Remove given shape for temporary containers
|
2889
|
+
# @param [Wx::SF::Shape] shape
|
2890
|
+
def remove_from_temporaries(shape)
|
2891
|
+
if shape
|
2892
|
+
@current_shapes.delete(shape)
|
2893
|
+
@new_line_shape = nil if @new_line_shape == shape
|
2894
|
+
@unselected_shape_under_cursor = nil if @unselected_shape_under_cursor == shape
|
2895
|
+
@selected_shape_under_cursor = nil if @selected_shape_under_cursor == shape
|
2896
|
+
@topmost_shape_under_cursor = nil if @topmost_shape_under_cursor == shape
|
2897
|
+
end
|
2898
|
+
end
|
2899
|
+
|
2900
|
+
# Clear all temporary containers
|
2901
|
+
def clear_temporaries
|
2902
|
+
@current_shapes.clear
|
2903
|
+
@new_line_shape = nil
|
2904
|
+
@unselected_shape_under_cursor = nil
|
2905
|
+
@selected_shape_under_cursor = nil
|
2906
|
+
@topmost_shape_under_cursor = nil
|
2907
|
+
end
|
2908
|
+
|
2909
|
+
# Assign give shape to parent at given location (if exists)
|
2910
|
+
# @param [Wx::SF::Shape] shape
|
2911
|
+
# @param [Wx::Point] parentpos
|
2912
|
+
def reparent_shape(shape, parentpos)
|
2913
|
+
return unless @diagram
|
2914
|
+
# is shape dropped into accepting shape?
|
2915
|
+
parent_shape = get_shape_at_position(parentpos, 1, SEARCHMODE::UNSELECTED)
|
2916
|
+
|
2917
|
+
parent_shape = nil if parent_shape && !parent_shape.is_child_accepted(shape.class)
|
2918
|
+
|
2919
|
+
# set new parent
|
2920
|
+
if shape.has_style?(Shape::STYLE::PARENT_CHANGE) && !shape.is_a?(LineShape)
|
2921
|
+
prev_parent = shape.get_parent_shape
|
2922
|
+
|
2923
|
+
if parent_shape
|
2924
|
+
if parent_shape.get_parent_shape != shape
|
2925
|
+
# update relative position to new parent
|
2926
|
+
apos = shape.get_absolute_position - parent_shape.get_absolute_position
|
2927
|
+
shape.set_relative_position(apos)
|
2928
|
+
# reparent
|
2929
|
+
@diagram.reparent_shape(shape, parent_shape)
|
2930
|
+
# notify the parent shape about dropped child
|
2931
|
+
parent_shape.on_child_dropped(apos, shape)
|
2932
|
+
end
|
2933
|
+
else
|
2934
|
+
if @diagram.is_top_shape_accepted(shape.class)
|
2935
|
+
# move relative to previous parent
|
2936
|
+
shape.move_by(prev_parent.get_absolute_position) if prev_parent
|
2937
|
+
# reparent
|
2938
|
+
@diagram.reparent_shape(shape, parent_shape)
|
2939
|
+
end
|
2940
|
+
end
|
2941
|
+
|
2942
|
+
prev_parent.update if prev_parent
|
2943
|
+
parent_shape.update if parent_shape
|
2944
|
+
end
|
2945
|
+
end
|
2946
|
+
|
2947
|
+
# Store previous shape's position modified in validate_selection_for_clipboard() function
|
2948
|
+
# @param [Wx::SF::Shape] shape
|
2949
|
+
def store_prev_position(shape)
|
2950
|
+
@prev_positions[shape] = shape.get_relative_position.dup
|
2951
|
+
end
|
2952
|
+
|
2953
|
+
# Restore previously stored shape positions and clear the storage
|
2954
|
+
def restore_prev_positions
|
2955
|
+
@prev_positions.each_pair { |shape, pos| shape.set_relative_position(pos) }
|
2956
|
+
@prev_positions.clear
|
2957
|
+
end
|
2958
|
+
|
2959
|
+
# private event handlers
|
2960
|
+
|
2961
|
+
# Event handler called when the canvas should be repainted.
|
2962
|
+
# @param [Wx::PaintEvent] _event Paint event
|
2963
|
+
def _on_paint(_event)
|
2964
|
+
paint_buffered do |paint_dc|
|
2965
|
+
if ShapeCanvas.gc_enabled?
|
2966
|
+
Wx::GCDC.draw_on(paint_dc) do |gdc|
|
2967
|
+
prepare_dc(gdc)
|
2968
|
+
|
2969
|
+
# scale GC
|
2970
|
+
gc = gdc.get_graphics_context
|
2971
|
+
gc.scale(@settings.scale, @settings.scale)
|
2972
|
+
|
2973
|
+
draw_background(gdc, FROM_PAINT)
|
2974
|
+
draw_content(gdc, FROM_PAINT)
|
2975
|
+
draw_foreground(gdc, FROM_PAINT)
|
2976
|
+
end
|
2977
|
+
else
|
2978
|
+
Wx::ScaledDC.draw_on(paint_dc, @settings.scale) do |dc|
|
2979
|
+
prepare_dc(dc)
|
2980
|
+
draw_background(dc, FROM_PAINT)
|
2981
|
+
draw_content(dc, FROM_PAINT)
|
2982
|
+
draw_foreground(dc, FROM_PAINT)
|
2983
|
+
end
|
2984
|
+
end
|
2985
|
+
end
|
2986
|
+
end
|
2987
|
+
|
2988
|
+
# Event handler called when the canvas should be erased.
|
2989
|
+
# @param [Wx::EraseEvent] _event Erase event
|
2990
|
+
def _on_erase_background(_event)
|
2991
|
+
# do nothing to suppress window flickering
|
2992
|
+
end
|
2993
|
+
|
2994
|
+
# Event handler called when the mouse pointer leaves the canvas window.
|
2995
|
+
# @param [Wx::MouseEvent] event Mouse event
|
2996
|
+
def _on_leave_window(event)
|
2997
|
+
case @working_mode
|
2998
|
+
when MODE::MULTISELECTION
|
2999
|
+
when MODE::SHAPEMOVE
|
3000
|
+
when MODE::CREATECONNECTION
|
3001
|
+
when MODE::HANDLEMOVE
|
3002
|
+
when MODE::MULTIHANDLEMOVE
|
3003
|
+
else
|
3004
|
+
@working_mode = MODE::READY
|
3005
|
+
end
|
3006
|
+
|
3007
|
+
event.skip
|
3008
|
+
end
|
3009
|
+
|
3010
|
+
# Event handler called when the mouse pointer enters the canvas window.
|
3011
|
+
# @param [Wx::MouseEvent] event Mouse event
|
3012
|
+
def _on_enter_window(event)
|
3013
|
+
@prev_mouse_pos = event.get_position
|
3014
|
+
|
3015
|
+
lpos = dp2lp(event.get_position)
|
3016
|
+
|
3017
|
+
case @working_mode
|
3018
|
+
when MODE::MULTISELECTION
|
3019
|
+
unless event.left_is_down
|
3020
|
+
update_multiedit_size
|
3021
|
+
@shp_multi_edit.show(false)
|
3022
|
+
@working_mode = MODE::READY
|
3023
|
+
|
3024
|
+
invalidate_visible_rect
|
3025
|
+
end
|
3026
|
+
|
3027
|
+
when MODE::HANDLEMOVE
|
3028
|
+
unless event.left_is_down
|
3029
|
+
if @selected_handle
|
3030
|
+
if @selected_handle.get_parent_shape.is_a?(LineShape)
|
3031
|
+
@selected_handle.get_parent_shape.send(:set_line_mode, LineShape::LINEMODE::READY)
|
3032
|
+
end
|
3033
|
+
|
3034
|
+
@selected_handle.send(:_on_end_drag, lpos)
|
3035
|
+
|
3036
|
+
save_canvas_state
|
3037
|
+
@working_mode = MODE::READY
|
3038
|
+
@selected_handle = nil
|
3039
|
+
|
3040
|
+
invalidate_visible_rect
|
3041
|
+
end
|
3042
|
+
end
|
3043
|
+
|
3044
|
+
when MODE::MULTIHANDLEMOVE
|
3045
|
+
unless event.left_is_down
|
3046
|
+
if @selected_handle
|
3047
|
+
@selected_handle.send(:_on_end_drag, lpos)
|
3048
|
+
|
3049
|
+
save_canvas_state
|
3050
|
+
@working_mode = MODE::READY
|
3051
|
+
|
3052
|
+
invalidate_visible_rect
|
3053
|
+
end
|
3054
|
+
end
|
3055
|
+
|
3056
|
+
when MODE::SHAPEMOVE
|
3057
|
+
unless event.left_is_down
|
3058
|
+
lst_selection = get_selected_shapes
|
3059
|
+
|
3060
|
+
move_shapes_from_negatives
|
3061
|
+
update_virtual_size
|
3062
|
+
|
3063
|
+
if lst_selection.size > 1
|
3064
|
+
update_multiedit_size
|
3065
|
+
@shp_multi_edit.show(true)
|
3066
|
+
@shp_multi_edit.show_handles(true)
|
3067
|
+
end
|
3068
|
+
|
3069
|
+
lst_selection.each { |shape| shape.send(:_on_end_drag, lpos) }
|
3070
|
+
|
3071
|
+
@working_mode = MODE::READY
|
3072
|
+
|
3073
|
+
invalidate_visible_rect
|
3074
|
+
end
|
3075
|
+
end
|
3076
|
+
|
3077
|
+
refresh_invalidated_rect
|
3078
|
+
|
3079
|
+
event.skip
|
3080
|
+
end
|
3081
|
+
|
3082
|
+
# Event handler called when the canvas size has changed.
|
3083
|
+
# @param [Wx::SizeEvent] event Size event
|
3084
|
+
def _on_resize(event)
|
3085
|
+
refresh(false) if has_style?(STYLE::GRADIENT_BACKGROUND)
|
3086
|
+
|
3087
|
+
event.skip
|
3088
|
+
end
|
3089
|
+
|
3090
|
+
# original private event handlers
|
3091
|
+
|
3092
|
+
# Original private event handler called when the canvas is clicked by
|
3093
|
+
# left mouse button. The handler calls user-overridable event handler function
|
3094
|
+
# and skips the event for next possible processing.
|
3095
|
+
# @param [Wx::MouseEvent] event Mouse event
|
3096
|
+
# @see Wx::SF::ShapeCanvas#on_left_down
|
3097
|
+
def _on_left_down(event)
|
3098
|
+
on_left_down(event)
|
3099
|
+
|
3100
|
+
event.skip
|
3101
|
+
end
|
3102
|
+
|
3103
|
+
# Original private event handler called when the canvas is double-clicked by
|
3104
|
+
# left mouse button. The handler calls user-overridable event handler function
|
3105
|
+
# and skips the event for next possible processing.
|
3106
|
+
# @param [Wx::MouseEvent] event Mouse event
|
3107
|
+
# @see Wx::SF::ShapeCanvas#on_left_double_click
|
3108
|
+
def _on_left_double_click(event)
|
3109
|
+
on_left_double_click(event)
|
3110
|
+
|
3111
|
+
event.skip
|
3112
|
+
end
|
3113
|
+
|
3114
|
+
# Original private event handler called when the left mouse button
|
3115
|
+
# is release above the canvas. The handler calls user-overridable event handler function
|
3116
|
+
# and skips the event for next possible processing.
|
3117
|
+
# @param [Wx::MouseEvent] event Mouse event
|
3118
|
+
# @see Wx::SF::ShapeCanvas#on_left_up
|
3119
|
+
def _on_left_up(event)
|
3120
|
+
on_left_up(event)
|
3121
|
+
|
3122
|
+
event.skip
|
3123
|
+
end
|
3124
|
+
|
3125
|
+
# Original private event handler called when the canvas is clicked by
|
3126
|
+
# right mouse button. The handler calls user-overridable event handler function
|
3127
|
+
# and skips the event for next possible processing.
|
3128
|
+
# @param [Wx::MouseEvent] event Mouse event
|
3129
|
+
# @see Wx::SF::ShapeCanvas#on_right_down
|
3130
|
+
def _on_right_down(event)
|
3131
|
+
on_right_down(event)
|
3132
|
+
|
3133
|
+
event.skip
|
3134
|
+
end
|
3135
|
+
|
3136
|
+
# Original private event handler called when the canvas is double-clicked by
|
3137
|
+
# right mouse button. The handler calls user-overridable event handler function
|
3138
|
+
# and skips the event for next possible processing.
|
3139
|
+
# @param [Wx::MouseEvent] event Mouse event
|
3140
|
+
# @see Wx::SF::ShapeCanvas#on_right_double_click
|
3141
|
+
def _on_right_double_click(event)
|
3142
|
+
on_right_double_click(event)
|
3143
|
+
|
3144
|
+
event.skip
|
3145
|
+
end
|
3146
|
+
|
3147
|
+
# Original private event handler called when the right mouse button
|
3148
|
+
# is release above the canvas. The handler calls user-overridable event handler function
|
3149
|
+
# and skips the event for next possible processing.
|
3150
|
+
# @param [Wx::MouseEvent] event Mouse event
|
3151
|
+
# @see Wx::SF::ShapeCanvas#on_right_up
|
3152
|
+
def _on_right_up(event)
|
3153
|
+
on_right_up(event)
|
3154
|
+
|
3155
|
+
event.skip
|
3156
|
+
end
|
3157
|
+
|
3158
|
+
# Original private event handler called when the mouse pointer is moving above
|
3159
|
+
# the canvas. The handler calls user-overridable event handler function
|
3160
|
+
# and skips the event for next possible processing.
|
3161
|
+
# @param [Wx::MouseEvent] event Mouse event
|
3162
|
+
# @see Wx::SF::ShapeCanvas#on_mouse_move
|
3163
|
+
def _on_mouse_move(event)
|
3164
|
+
lpos = dp2lp(event.get_position)
|
3165
|
+
|
3166
|
+
update_shape_under_cursor_cache(lpos)
|
3167
|
+
|
3168
|
+
# call user event handler
|
3169
|
+
on_mouse_move(event)
|
3170
|
+
|
3171
|
+
event.skip
|
3172
|
+
end
|
3173
|
+
|
3174
|
+
# Original private event handler called when the mouse wheel position is changed.
|
3175
|
+
# The handler calls user-overridable event handler function and skips the event
|
3176
|
+
# for next possible processing.
|
3177
|
+
# @param [Wx::MouseEvent] event Mouse event
|
3178
|
+
# @see Wx::SF::ShapeCanvas#on_mouse_wheel
|
3179
|
+
def _on_mouse_wheel(event)
|
3180
|
+
on_mouse_wheel(event) if has_style?(STYLE::PROCESS_MOUSEWHEEL)
|
3181
|
+
|
3182
|
+
event.skip
|
3183
|
+
end
|
3184
|
+
|
3185
|
+
# Original private event handler called when any key is pressed.
|
3186
|
+
# The handler calls user-overridable event handler function
|
3187
|
+
# and skips the event for next possible processing.
|
3188
|
+
# @param [Wx::KeyEvent] event Keyboard event
|
3189
|
+
# @see Wx::SF::ShapeCanvas#on_key_down
|
3190
|
+
def _on_key_down(event)
|
3191
|
+
on_key_down(event)
|
3192
|
+
|
3193
|
+
event.skip
|
3194
|
+
end
|
3195
|
+
|
3196
|
+
if Wx.has_feature?(:USE_DRAG_AND_DROP)
|
3197
|
+
|
3198
|
+
# Function is called by associated wxSFCanvasDropTarget after any dragged shapes
|
3199
|
+
# are dropped to the canvas.
|
3200
|
+
# @param [Integer] x X-coordinate of a position the data was dropped to
|
3201
|
+
# @param [Integer] y Y-coordinate of a position the data was dropped to
|
3202
|
+
# @param [Wx::DragResult] deflt Drag result
|
3203
|
+
# @param [Wx::ShapeDataObject] data a data object encapsulating dropped data
|
3204
|
+
# @see Wx::SF::CanvasDropTarget
|
3205
|
+
def _on_drop(x, y, deflt, data)
|
3206
|
+
if data && Wx::SF::ShapeDataObject === data
|
3207
|
+
lst_new_content = Wx::SF::Serializable.deserialize(data.get_data_here)
|
3208
|
+
if lst_new_content && !lst_new_content.empty?
|
3209
|
+
lst_parents_to_update = []
|
3210
|
+
lpos = dp2lp(Wx::Point.new(x, y))
|
3211
|
+
|
3212
|
+
dx = 0
|
3213
|
+
dy = 0
|
3214
|
+
if @dnd_started_here
|
3215
|
+
dx = lpos.x - @dnd_started_at.x
|
3216
|
+
dy = lpos.y - @dnd_started_at.y
|
3217
|
+
end
|
3218
|
+
|
3219
|
+
parent = @diagram.get_shape_at_position(lpos, 1, SEARCHMODE::UNSELECTED)
|
3220
|
+
|
3221
|
+
# add each shape to diagram keeping only those that are accepted
|
3222
|
+
lst_new_content.select! do |shape|
|
3223
|
+
shape.move_by(dx, dy)
|
3224
|
+
# do not reparent connection lines
|
3225
|
+
rc = if shape.is_a?(LineShape) && !shape.stand_alone?
|
3226
|
+
@diagram.add_shape(shape,
|
3227
|
+
nil,
|
3228
|
+
lp2dp(shape.get_absolute_position.to_point),
|
3229
|
+
INITIALIZE,
|
3230
|
+
DONT_SAVE_STATE)
|
3231
|
+
else
|
3232
|
+
@diagram.add_shape(shape,
|
3233
|
+
parent,
|
3234
|
+
lp2dp((shape.get_absolute_position - parent.get_absolute_position).to_point),
|
3235
|
+
INITIALIZE,
|
3236
|
+
DONT_SAVE_STATE)
|
3237
|
+
end
|
3238
|
+
rc == ERRCODE::OK # keep or remove?
|
3239
|
+
end
|
3240
|
+
|
3241
|
+
# verify newly added shapes (may remove shapes from list)
|
3242
|
+
@diagram.send(:check_new_shapes, lst_new_content)
|
3243
|
+
|
3244
|
+
update_virtual_size # update for new shapes
|
3245
|
+
|
3246
|
+
# notify parents and collect for update
|
3247
|
+
lst_new_content.each do |shape|
|
3248
|
+
if (parent_shape = shape.get_parent_shape)
|
3249
|
+
parent_shape.on_child_dropped(shape.get_absolute_position - parent_shape.get_absolute_position,
|
3250
|
+
shape)
|
3251
|
+
lst_parents_to_update << parent_shape unless lst_parents_to_update.include?(parent_shape)
|
3252
|
+
end
|
3253
|
+
end
|
3254
|
+
|
3255
|
+
deselect_all
|
3256
|
+
|
3257
|
+
lst_parents_to_update.each { |shape| shape.update }
|
3258
|
+
|
3259
|
+
unless @dnd_started_here
|
3260
|
+
save_canvas_state
|
3261
|
+
refresh(false)
|
3262
|
+
end
|
3263
|
+
|
3264
|
+
# call user-defined drop handler
|
3265
|
+
on_drop(x, y, deflt, lst_new_content)
|
3266
|
+
end
|
3267
|
+
end
|
3268
|
+
end
|
3269
|
+
|
3270
|
+
end
|
3271
|
+
|
3272
|
+
def _notify_canvas_change(change, *args)
|
3273
|
+
@diagram.get_all_shapes.each { |shape| shape.send(:_on_canvas, change, *args) } if @diagram
|
3274
|
+
end
|
3275
|
+
|
3276
|
+
def _query_canvas_change(change, *args)
|
3277
|
+
return true unless @diagram
|
3278
|
+
@diagram.get_all_shapes.all? { |shape| shape.send(:_on_canvas, change, *args) }
|
3279
|
+
end
|
3280
|
+
|
3281
|
+
end # class ShapeCanvas
|
3282
|
+
|
3283
|
+
end
|
3284
|
+
|
3285
|
+
# module Wx::SF
|