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