wxruby3-shapes 0.9.0.pre.beta.3 → 0.9.6

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.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/INSTALL.md +5 -7
  3. data/README.md +38 -6
  4. data/assets/logo.svg +339 -0
  5. data/assets/logo.xpm +60 -0
  6. data/assets/screenshot.png +0 -0
  7. data/assets/social.png +0 -0
  8. data/bin/wx-shapes +1 -1
  9. data/lib/wx/shapes/arrow_base.rb +4 -11
  10. data/lib/wx/shapes/arrows/circle_arrow.rb +22 -11
  11. data/lib/wx/shapes/arrows/circle_prong_arrow.rb +48 -0
  12. data/lib/wx/shapes/arrows/cross_bar_arrow.rb +57 -0
  13. data/lib/wx/shapes/arrows/cross_bar_circle_arrow.rb +56 -0
  14. data/lib/wx/shapes/arrows/cross_bar_prong_arrow.rb +49 -0
  15. data/lib/wx/shapes/arrows/crossed_circle.rb +46 -0
  16. data/lib/wx/shapes/arrows/cup_arrow.rb +65 -0
  17. data/lib/wx/shapes/arrows/diamond_arrow.rb +8 -13
  18. data/lib/wx/shapes/arrows/double_cross_bar_arrow.rb +27 -0
  19. data/lib/wx/shapes/arrows/filled_arrow.rb +60 -0
  20. data/lib/wx/shapes/arrows/line_arrow.rb +67 -0
  21. data/lib/wx/shapes/arrows/open_arrow.rb +22 -23
  22. data/lib/wx/shapes/arrows/prong_arrow.rb +42 -0
  23. data/lib/wx/shapes/arrows/solid_arrow.rb +21 -35
  24. data/lib/wx/shapes/arrows/square_arrow.rb +37 -0
  25. data/lib/wx/shapes/auto_layout.rb +2 -2
  26. data/lib/wx/shapes/base.rb +1 -1
  27. data/lib/wx/shapes/canvas_history.rb +20 -0
  28. data/lib/wx/shapes/connection_point.rb +10 -6
  29. data/lib/wx/shapes/diagram.rb +98 -78
  30. data/lib/wx/shapes/events.rb +8 -8
  31. data/lib/wx/shapes/printout.rb +3 -16
  32. data/lib/wx/shapes/serializable.rb +2 -436
  33. data/lib/wx/shapes/serialize/wx.rb +30 -18
  34. data/lib/wx/shapes/shape.rb +211 -168
  35. data/lib/wx/shapes/shape_canvas.rb +728 -267
  36. data/lib/wx/shapes/shape_data_object.rb +99 -18
  37. data/lib/wx/shapes/shape_handle.rb +18 -11
  38. data/lib/wx/shapes/shape_list.rb +34 -67
  39. data/lib/wx/shapes/shapes/bitmap_shape.rb +23 -24
  40. data/lib/wx/shapes/shapes/box_shape.rb +389 -0
  41. data/lib/wx/shapes/shapes/circle_shape.rb +19 -22
  42. data/lib/wx/shapes/shapes/control_shape.rb +77 -41
  43. data/lib/wx/shapes/shapes/curve_shape.rb +38 -31
  44. data/lib/wx/shapes/shapes/diamond_shape.rb +7 -17
  45. data/lib/wx/shapes/shapes/edit_text_shape.rb +6 -9
  46. data/lib/wx/shapes/shapes/ellipse_shape.rb +12 -15
  47. data/lib/wx/shapes/shapes/flex_grid_shape.rb +58 -33
  48. data/lib/wx/shapes/shapes/grid_shape.rb +259 -161
  49. data/lib/wx/shapes/shapes/line_shape.rb +155 -161
  50. data/lib/wx/shapes/shapes/manager_shape.rb +77 -0
  51. data/lib/wx/shapes/shapes/multi_sel_rect.rb +8 -8
  52. data/lib/wx/shapes/shapes/ortho_shape.rb +31 -36
  53. data/lib/wx/shapes/shapes/polygon_shape.rb +23 -29
  54. data/lib/wx/shapes/shapes/rect_shape.rb +95 -53
  55. data/lib/wx/shapes/shapes/round_ortho_shape.rb +6 -8
  56. data/lib/wx/shapes/shapes/round_rect_shape.rb +20 -24
  57. data/lib/wx/shapes/shapes/square_shape.rb +14 -17
  58. data/lib/wx/shapes/shapes/text_shape.rb +95 -53
  59. data/lib/wx/shapes/version.rb +1 -1
  60. data/lib/wx/shapes/wx.rb +16 -7
  61. data/lib/wx/wx-shapes/cmd/test.rb +1 -1
  62. data/samples/demo/arrows.json +1 -0
  63. data/samples/demo/arrows.yaml +793 -0
  64. data/samples/demo/art/HBox.xpm +22 -0
  65. data/samples/demo/art/VBox.xpm +22 -0
  66. data/samples/demo/art/logo.xpm +60 -0
  67. data/samples/demo/class.json +1 -0
  68. data/samples/demo/class.yaml +5631 -0
  69. data/samples/demo/demo.rb +301 -91
  70. data/samples/demo/dialogs.rb +1405 -0
  71. data/samples/demo/erd.json +1 -0
  72. data/samples/demo/erd.yaml +4072 -0
  73. data/samples/demo/frame_canvas.rb +409 -33
  74. data/samples/sample1/art/logo.xpm +60 -0
  75. data/samples/sample1/sample.rb +11 -11
  76. data/samples/sample2/art/logo.xpm +60 -0
  77. data/samples/sample2/sample.rb +2 -2
  78. data/samples/sample2/sample_shape.rb +15 -15
  79. data/samples/sample3/art/logo.xpm +60 -0
  80. data/samples/sample3/sample.rb +3 -3
  81. data/samples/sample4/art/logo.xpm +60 -0
  82. data/samples/sample4/sample.rb +2 -2
  83. data/tests/lib/wxapp_runner.rb +4 -0
  84. data/tests/serializer_tests.rb +8 -441
  85. data/tests/test_grid_shapes.rb +2 -2
  86. data/tests/test_serialize_xml.rb +17 -0
  87. data/tests/test_serialize_yaml.rb +2 -2
  88. metadata +78 -28
  89. data/lib/wx/shapes/serialize/core.rb +0 -40
  90. data/lib/wx/shapes/serialize/id.rb +0 -82
  91. data/lib/wx/shapes/serializer/json.rb +0 -258
  92. data/lib/wx/shapes/serializer/yaml.rb +0 -125
  93. data/samples/demo/art/sample.xpm +0 -251
  94. data/samples/sample1/art/sample.xpm +0 -251
  95. data/samples/sample2/art/sample.xpm +0 -251
  96. data/samples/sample3/art/sample.xpm +0 -251
  97. data/samples/sample4/art/sample.xpm +0 -251
@@ -50,7 +50,7 @@ module Wx::SF
50
50
  # @see Wx::SF::Diagram
51
51
  class ShapeCanvas < Wx::ScrolledWindow
52
52
 
53
- # Working modes
53
+ # Working modes
54
54
  class MODE < Wx::Enum
55
55
  # The shape canvas is in ready state (no operation is pending)
56
56
  READY = self.new(0)
@@ -192,37 +192,56 @@ module Wx::SF
192
192
 
193
193
  # Default values
194
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) }
195
+ class << self
196
+ # Default value of Wx::SF::CanvasSettings @background_color data member
197
+ def background_color; @bgcolor ||= Wx::Colour.new(240, 240, 240); end
198
+ # Default value of Wx::SF::CanvasSettings @common_hover_color data member
199
+ def hover_color; @hvrcolor ||= Wx::Colour.new(120, 120, 255); end
200
+ # Default value of Wx::SF::CanvasSettings @common_border_pen data member
201
+ def border_pen; @border_pen ||= Wx::BLACK_PEN.dup; end
202
+ # Default value of Wx::SF::CanvasSettings @common_fill_brush data member
203
+ def fill_brush; @fill_brush ||= Wx::WHITE_BRUSH.dup; end
204
+ # Default value of Wx::SF::CanvasSettings @common_line_pen data member
205
+ def line_pen; @line_pen ||= border_pen; end
206
+ # Default value of Wx::SF::CanvasSettings @common_arrow_fill data member
207
+ def arrow_fill; @arrow_fill ||= fill_brush; end
208
+ # Default value of Wx::SF::CanvasSettings @common_text_color data member
209
+ def text_color; @text_color ||= Wx::BLACK.dup; end
210
+ # Default value of Wx::SF::CanvasSettings @common_text_fill data member
211
+ def text_fill; @text_fill ||= Wx::TRANSPARENT_BRUSH.dup; end
212
+ # Default value of Wx::SF::CanvasSettings @common_text_border data member
213
+ def text_border; @text_border ||= Wx::TRANSPARENT_PEN.dup; end
214
+ # Default value of Wx::SF::CanvasSettings @common_text_font data member
215
+ def text_font; begin; @text_font = Wx::SWISS_FONT.dup; @text_font.point_size = 12; end unless @text_font; @text_font; end
216
+ # Default value of Wx::SF::CanvasSettings @common_control_fill data member
217
+ def control_fill; @ctrl_fill ||= Wx::TRANSPARENT_BRUSH.dup; end
218
+ # Default value of Wx::SF::CanvasSettings @common_control_border data member
219
+ def control_border; @ctrl_border ||= Wx::TRANSPARENT_PEN.dup; end
220
+ # Default value of Wx::SF::CanvasSettings @common_control_mod_fill data member
221
+ def control_mod_fill; @ctrl_mod_fill ||= Wx::Brush.new(Wx::BLUE, Wx::BrushStyle::BRUSHSTYLE_BDIAGONAL_HATCH); end
222
+ # Default value of Wx::SF::CanvasSettings @common_control_mod_border data member
223
+ def control_mod_border; @ctrl_mod_border ||= Wx::Pen.new(Wx::BLUE, 1, Wx::PenStyle::PENSTYLE_SOLID); end
224
+ # Default value of Wx::SF::CanvasSettings @grid_color data member
225
+ def grid_color; @gridcolor ||= Wx::Colour.new(200, 200, 200); end
226
+ # Default value of Wx::SF::CanvasSettings @gradient_from data member
227
+ def gradient_from; @gradcolor_from ||= Wx::Colour.new(240, 240, 240); end
228
+ # Default value of Wx::SF::CanvasSettings @gradient_to data member
229
+ def gradient_to; @gradcolor_to ||= Wx::Colour.new(200, 200, 255); end
230
+ # Default shadow colour
231
+ def shadow_color; @shadowcolor ||= Wx::Colour.new(150, 150, 150, 128); end
232
+ # Default value of Wx::SF::CanvasSettings @shadow_fill data member
233
+ def shadow_brush; @shadowbrush ||= Wx::Brush.new(shadow_color, Wx::BrushStyle::BRUSHSTYLE_SOLID); end
234
+ end
198
235
  # Default value of Wx::SF::CanvasSettings @grid_size data member
199
- GRIDSIZE = Wx::Size.new(10, 10)
236
+ GRIDSIZE = 10
200
237
  # Default value of Wx::SF::CanvasSettings @grid_line_mult data member
201
238
  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
239
  # Default value of Wx::SF::CanvasSettings @grid_style data member
206
240
  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
241
  # Default value of Wx::SF::CanvasSettings @style data member
217
242
  CANVAS_STYLE = STYLE::DEFAULT_CANVAS_STYLE
218
243
  # Default value of Wx::SF::CanvasSettings @shadow_offset data member
219
244
  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
245
  # Default value of Wx::SF::CanvasSettings @print_h_align data member
227
246
  PRINT_HALIGN = HALIGN::CENTER
228
247
  # Default value of Wx::SF::CanvasSettings @print_v_align data member
@@ -318,7 +337,7 @@ module Wx::SF
318
337
  end
319
338
  end
320
339
 
321
- include Serializable
340
+ include FIRM::Serializable
322
341
 
323
342
  property :version_info
324
343
 
@@ -350,36 +369,68 @@ module Wx::SF
350
369
  # Auxiliary serializable class encapsulating the canvas properties.
351
370
  class Settings
352
371
 
353
- include Serializable
372
+ include FIRM::Serializable
354
373
 
355
374
  include DEFAULT
356
375
 
357
- property :scale, :min_scale, :max_scale, :background_color, :common_hover_color,
358
- :grid_size, :grid_line_mult, :grid_color, :grid_style,
376
+ property :scale, :min_scale, :max_scale, :background_color,
377
+ :common_hover_color, :common_border_pen, :common_fill_brush, :common_line_pen,
378
+ :common_arrow_fill, :common_text_color, :common_text_fill, :common_text_border, :common_text_font,
379
+ :common_control_fill, :common_control_border, :common_control_mod_fill, :common_control_mod_border,
380
+ :grid_line_mult, :grid_color, :grid_style,
359
381
  :gradient_from, :gradient_to, :style, :shadow_offset, :shadow_fill,
360
382
  :print_h_align, :print_v_align, :print_mode
383
+ property grid_size: ->(obj, *val) {
384
+ unless val.empty?
385
+ obj.grid_size = Wx::Size === val.first ? val.first.x : val.first
386
+ end
387
+ obj.grid_size
388
+ }
361
389
 
362
390
  def initialize
363
391
  @scale = 1.0
364
392
  @min_scale = SCALE_MIN
365
393
  @max_scale = SCALE_MAX
366
- @background_color = BACKGROUNDCOLOR
367
- @common_hover_color = HOVERCOLOR
368
- @grid_size = GRIDSIZE.dup
394
+ @background_color = DEFAULT.background_color
395
+
396
+ # common shape property
397
+ @common_hover_color = DEFAULT.hover_color
398
+ # common rect shape properties
399
+ @common_border_pen = DEFAULT.border_pen
400
+ @common_fill_brush = DEFAULT.fill_brush
401
+ # common line shape property
402
+ @common_line_pen = DEFAULT.line_pen
403
+ # common arrow property
404
+ @common_arrow_fill = DEFAULT.arrow_fill
405
+ # common text shape properties (fill and border overrule common rect properties)
406
+ @common_text_color = DEFAULT.text_color
407
+ @common_text_fill = DEFAULT.text_fill
408
+ @common_text_border = DEFAULT.text_border
409
+ @common_text_font = DEFAULT.text_font
410
+ # common control shape properties
411
+ @common_control_fill = DEFAULT.control_fill
412
+ @common_control_border = DEFAULT.control_border
413
+ @common_control_mod_fill = DEFAULT.control_mod_fill
414
+ @common_control_mod_border = DEFAULT.control_mod_border
415
+
416
+ @grid_size = GRIDSIZE
369
417
  @grid_line_mult = GRIDLINEMULT
370
- @grid_color = GRIDCOLOR
418
+ @grid_color = DEFAULT.grid_color
371
419
  @grid_style = GRIDSTYLE
372
- @gradient_from = GRADIENT_FROM
373
- @gradient_to = GRADIENT_TO
420
+ @gradient_from = DEFAULT.gradient_from
421
+ @gradient_to = DEFAULT.gradient_to
374
422
  @style = CANVAS_STYLE
375
423
  @shadow_offset = SHADOWOFFSET.dup
376
- @shadow_fill = SHADOWBRUSH
424
+ @shadow_fill = DEFAULT.shadow_brush
377
425
  @print_h_align = PRINT_HALIGN
378
426
  @print_v_align = PRINT_VALIGN
379
427
  @print_mode = PRINT_MODE
380
428
  end
381
429
 
382
- attr_accessor :scale, :min_scale, :max_scale, :background_color, :common_hover_color,
430
+ attr_accessor :scale, :min_scale, :max_scale, :background_color,
431
+ :common_hover_color, :common_border_pen, :common_fill_brush, :common_line_pen,
432
+ :common_arrow_fill, :common_text_color, :common_text_fill, :common_text_border, :common_text_font,
433
+ :common_control_fill, :common_control_border, :common_control_mod_fill, :common_control_mod_border,
383
434
  :grid_size, :grid_line_mult, :grid_color, :grid_style,
384
435
  :gradient_from, :gradient_to, :style, :shadow_offset, :shadow_fill,
385
436
  :print_h_align, :print_v_align, :print_mode
@@ -441,7 +492,7 @@ module Wx::SF
441
492
 
442
493
  save_canvas_state
443
494
  end
444
-
495
+
445
496
  # set up event handlers
446
497
  evt_paint :_on_paint
447
498
  evt_erase_background :_on_erase_background
@@ -480,7 +531,6 @@ module Wx::SF
480
531
 
481
532
  # initialize selection rectangle
482
533
  @shp_selection = MultiSelRect.new
483
- @shp_selection.send(:set_id, nil)
484
534
  @shp_selection.create_handles
485
535
  @shp_selection.select(true)
486
536
  @shp_selection.show(false)
@@ -488,7 +538,6 @@ module Wx::SF
488
538
 
489
539
  # initialize multi-edit rectangle
490
540
  @shp_multi_edit = MultiSelRect.new
491
- @shp_multi_edit.send(:set_id, nil)
492
541
  @shp_multi_edit.create_handles
493
542
  @shp_multi_edit.select(true)
494
543
  @shp_multi_edit.show(false)
@@ -523,29 +572,61 @@ module Wx::SF
523
572
  # Load serialized canvas content (diagrams).
524
573
  # @overload load_canvas(file)
525
574
  # @param [String] file Full file name
575
+ # @param [Symbol,nil] format specifies the serialization format to use;
576
+ # determined from file extension if not specified defaulting to :json for unknown extensions
526
577
  # @return [self]
527
578
  # @overload load_canvas(io)
528
579
  # @param [IO] io IO object
580
+ # @param [Symbol,nil] format specifies the serialization format to use (by default :json)
529
581
  # @return [self]
530
- def load_canvas(io)
582
+ def load_canvas(io, format: nil)
531
583
  # get IO stream to read from
532
- ios = io.is_a?(::String) ? File.open(io, 'r') : io
584
+ ios = if io.is_a?(::String)
585
+ format ||= case File.extname(io)
586
+ when '.json' then :json
587
+ when '.yaml', '.yml' then :yaml
588
+ when '.xml' then :xml
589
+ else
590
+ :json
591
+ end
592
+ File.open(io, 'r')
593
+ else
594
+ format ||= :json
595
+ io
596
+ end
597
+ old_diagram = @diagram
598
+ old_settings = @settings
533
599
  begin
534
- _, @settings, diagram = Serializable.deserialize(ios)
535
- rescue SFException
600
+ begin
601
+ _, @settings, diagram = FIRM.deserialize(ios, format: format)
602
+ rescue SFException
603
+ ::Kernel.raise
604
+ rescue ::Exception
605
+ $stderr.puts "#{$!}\n#{$!.backtrace.join("\n")}\n"
606
+ ::Kernel.raise SFException, "Failed to load canvas: #{$!.message}"
607
+ ensure
608
+ ShapeCanvas.reset_compat_loading
609
+ ios.close if io.is_a?(::String) && ios
610
+ end
611
+ set_diagram(diagram)
612
+ clear_canvas_history
613
+ save_canvas_state
614
+ set_scale(@settings.scale)
615
+ update_virtual_size
616
+ refresh(false)
617
+ rescue Exception
618
+ $stderr.puts "#{$!}\n#{$!.backtrace.join("\n")}\n"
619
+ # restore previous state
620
+ @settings = old_settings
621
+ set_diagram(old_diagram)
622
+ clear_canvas_history
623
+ save_canvas_state
624
+ set_scale(@settings.scale)
625
+ update_virtual_size
626
+ refresh(false)
627
+ # propagate exception
536
628
  ::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)
629
+ end
549
630
 
550
631
  @diagram.set_modified(false)
551
632
 
@@ -556,18 +637,33 @@ module Wx::SF
556
637
  # @overload save_canvas(file, compact: true)
557
638
  # @param [String] file Full file name
558
639
  # @param [Boolean] compact specifies whether to write content in compact mode (true) or not (false)
640
+ # @param [Symbol,nil] format specifies the serialization format to use;
641
+ # determined from file extension if not specified defaulting to :json for unknown extensions
559
642
  # @return [self]
560
643
  # @overload save_canvas(io, compact: true)
561
644
  # @param [IO] io IO object
562
645
  # @param [Boolean] compact specifies whether to write content in compact mode (true) or not (false)
646
+ # @param [Symbol,nil] format specifies the serialization format to use (by default :json)
563
647
  # @return [self]
564
- def save_canvas(io, compact: true)
648
+ def save_canvas(io, compact: true, format: nil)
565
649
  return self unless @diagram
566
650
  # get IO stream to write to
567
- ios = io.is_a?(::String) ? Tempfile.new(File.basename(io, '.*')) : io
651
+ ios = if io.is_a?(::String)
652
+ format ||= case File.extname(io)
653
+ when '.json' then :json
654
+ when '.yaml', '.yml' then :yaml
655
+ when '.xml' then :xml
656
+ else
657
+ :json
658
+ end
659
+ Tempfile.new(File.basename(io, '.*'))
660
+ else
661
+ format ||= :json
662
+ io
663
+ end
568
664
  # write canvas data to temp file
569
665
  begin
570
- [Version.new, @settings, @diagram].serialize(ios, pretty: !compact)
666
+ [Version.new, @settings, @diagram].serialize(ios, pretty: !compact, format: format)
571
667
  rescue SFException
572
668
  ::Kernel.raise
573
669
  rescue Exception
@@ -636,7 +732,7 @@ module Wx::SF
636
732
  bmp_bb.width = (bmp_bb.width * scale).to_i
637
733
  bmp_bb.height = (bmp_bb.height * scale).to_i
638
734
 
639
- bmp_bb.inflate!(@settings.grid_size * scale)
735
+ bmp_bb.inflate!(Wx::Size.new(@settings.grid_size, @settings.grid_size) * scale)
640
736
 
641
737
  outbmp = Wx::Bitmap.new(bmp_bb.width, bmp_bb.height)
642
738
  Wx::MemoryDC.draw_on(outbmp) do |mdc|
@@ -681,12 +777,12 @@ module Wx::SF
681
777
  nil
682
778
  end
683
779
 
684
- def _start_interactive_connection(lpos, src_shape_id, cpt)
780
+ def _start_interactive_connection(lpos, src_shape, cpt)
685
781
  if @new_line_shape
686
782
  @working_mode = MODE::CREATECONNECTION
687
783
  @new_line_shape.send(:set_line_mode, LineShape::LINEMODE::UNDERCONSTRUCTION)
688
784
 
689
- @new_line_shape.set_src_shape_id(src_shape_id)
785
+ @new_line_shape.set_src_shape(src_shape)
690
786
 
691
787
  # switch on the "under-construction" mode
692
788
  @new_line_shape.send(:set_unfinished_point, lpos)
@@ -732,7 +828,7 @@ module Wx::SF
732
828
  case args.first
733
829
  when Wx::SF::LineShape
734
830
  shape = args.shift
735
- shape_klass = shape.class.name
831
+ shape_klass = shape.class
736
832
  if args.first.is_a?(Wx::SF::ConnectionPoint)
737
833
  connection_point = args.shift
738
834
  end
@@ -740,7 +836,7 @@ module Wx::SF
740
836
  when ::Class
741
837
  shape_info = args.shift
742
838
  pos = args.shift.to_point
743
- shape_klass = shape_info.name
839
+ shape_klass = shape_info
744
840
  end
745
841
  ::Kernel.raise ArgumentError, "Invalid arguments #{args}" unless args.empty?
746
842
  return ERRCODE::INVALID_INPUT unless pos
@@ -756,7 +852,7 @@ module Wx::SF
756
852
  else
757
853
  @new_line_shape = @diagram.add_shape(shape, nil, Wx::DEFAULT_POSITION, INITIALIZE, DONT_SAVE_STATE)
758
854
  end
759
- return _start_interactive_connection(lpos, connection_point.get_parent_shape.id, connection_point)
855
+ return _start_interactive_connection(lpos, connection_point.get_parent_shape, connection_point)
760
856
 
761
857
  else
762
858
 
@@ -768,7 +864,7 @@ module Wx::SF
768
864
  end
769
865
 
770
866
  # start the connection's creation process if possible
771
- if shape_under&.id && shape_under.is_connection_accepted(shape_klass)
867
+ if shape_under && shape_under.is_connection_accepted(shape_klass)
772
868
  if shape && @diagram.contains?(shape)
773
869
  @new_line_shape = shape
774
870
  else
@@ -779,7 +875,7 @@ module Wx::SF
779
875
  end
780
876
  @new_line_shape = (err == ERRCODE::OK ? shape : nil)
781
877
  end
782
- return _start_interactive_connection(lpos, shape_under.id, shape_under.get_nearest_connection_point(lpos.to_real))
878
+ return _start_interactive_connection(lpos, shape_under, shape_under.get_nearest_connection_point(lpos.to_real))
783
879
  else
784
880
  return ERRCODE::NOT_ACCEPTED
785
881
  end
@@ -806,7 +902,7 @@ module Wx::SF
806
902
  def select_all
807
903
  return unless @diagram
808
904
 
809
- shapes = @diagram.get_shapes
905
+ shapes = @diagram.get_all_shapes
810
906
 
811
907
  unless shapes.empty?
812
908
  shapes.each { |shape| shape.select(true) }
@@ -826,7 +922,7 @@ module Wx::SF
826
922
  def deselect_all
827
923
  return unless @diagram
828
924
 
829
- @diagram.get_shapes.each { |shape| shape.select(false) }
925
+ @diagram.get_all_shapes.each { |shape| shape.select(false) }
830
926
 
831
927
  @shp_multi_edit.show(false)
832
928
  end
@@ -835,7 +931,7 @@ module Wx::SF
835
931
  def hide_all_handles
836
932
  return unless @diagram
837
933
 
838
- @diagram.get_shapes.each { |shape| shape.show_handles(false) }
934
+ @diagram.get_all_shapes.each { |shape| shape.show_handles(false) }
839
935
  end
840
936
 
841
937
  # Repaint the shape canvas.
@@ -888,7 +984,7 @@ module Wx::SF
888
984
  def show_shadows(show, style)
889
985
  return unless @diagram
890
986
 
891
- shapes = @diagram.get_shapes
987
+ shapes = @diagram.get_all_shapes
892
988
 
893
989
  shapes.each do |shape|
894
990
  shape.remove_style(Shape::STYLE::SHOW_SHADOW) if show
@@ -979,6 +1075,7 @@ module Wx::SF
979
1075
 
980
1076
  unless lst_selection.empty?
981
1077
  data_obj = Wx::SF::ShapeDataObject.new(lst_selection)
1078
+
982
1079
  clipboard.place(data_obj)
983
1080
 
984
1081
  restore_prev_positions
@@ -1018,15 +1115,14 @@ module Wx::SF
1018
1115
  data_obj = Wx::SF::ShapeDataObject.new
1019
1116
  if clipboard.fetch(data_obj)
1020
1117
 
1021
- # deserialize shapes
1022
- new_shapes = Wx::SF::Serializable.deserialize(data_obj.get_data_here)
1118
+ new_shapes = data_obj.get_as_shapes
1023
1119
  # add new shapes to diagram and remove those that are not accepted
1024
1120
  new_shapes.select! do |shape|
1025
1121
  ERRCODE::OK == @diagram.add_shape(shape, nil, shape.get_relative_position, INITIALIZE, DONT_SAVE_STATE)
1026
1122
  end
1027
1123
 
1028
1124
  # verify newly added shapes (may remove shapes from list)
1029
- @diagram.send(:check_new_shapes, new_shapes)
1125
+ @diagram.send(:on_import, new_shapes)
1030
1126
 
1031
1127
  update_virtual_size # update for new shapes
1032
1128
 
@@ -1082,8 +1178,8 @@ module Wx::SF
1082
1178
  return false unless has_style?(STYLE::CLIPBOARD)
1083
1179
 
1084
1180
  Wx::Clipboard.open do |clipboard|
1085
- return clipboard.supported?(Wx::DataFormat.new(Wx::SF::ShapeDataObject::DataFormatID))
1086
- end
1181
+ return clipboard.supported?(Wx::SF::ShapeDataObject::DataFormatID)
1182
+ end rescue false
1087
1183
  end
1088
1184
  alias :can_paste? :can_paste
1089
1185
 
@@ -1128,13 +1224,24 @@ module Wx::SF
1128
1224
  # @param [String,nil] state to restore
1129
1225
  def restore_canvas_state(state)
1130
1226
  return unless state
1131
- set_diagram(Wx::SF::Serializable.deserialize(state))
1227
+ set_diagram(FIRM.deserialize(state))
1132
1228
  update_virtual_size
1133
1229
  @diagram.set_modified
1134
1230
  refresh(false)
1135
1231
  end
1136
1232
  protected :restore_canvas_state
1137
1233
 
1234
+ # Restores current last saved canvas state.
1235
+ def restore_current_state
1236
+ return unless has_style?(STYLE::UNDOREDO)
1237
+
1238
+ clear_temporaries
1239
+
1240
+ restore_canvas_state(@canvas_history.current_state)
1241
+ @shp_multi_edit.show(false)
1242
+ end
1243
+ protected :restore_current_state
1244
+
1138
1245
  # @!group Print methods
1139
1246
 
1140
1247
  # Print current canvas content.
@@ -1247,12 +1354,12 @@ module Wx::SF
1247
1354
  # @return [Wx::Rect] Device position
1248
1355
  def lp2dp(arg)
1249
1356
  if arg.is_a?(Wx::Rect)
1250
- x, y = calc_unscrolled_position(arg.x, arg.y)
1357
+ x, y = calc_scrolled_position(arg.x, arg.y)
1251
1358
  Wx::Rect.new((x*@settings.scale).to_i, (y*@settings.scale).to_i,
1252
1359
  (arg.width*@settings.scale).to_i, (arg.height*@settings.scale).to_i)
1253
1360
  else
1254
1361
  arg = arg.to_point
1255
- x, y = calc_unscrolled_position(arg.x, arg.y)
1362
+ x, y = calc_scrolled_position(arg.x, arg.y)
1256
1363
  Wx::Point.new((x*@settings.scale).to_i, (y*@settings.scale).to_i)
1257
1364
  end
1258
1365
  end
@@ -1281,7 +1388,7 @@ module Wx::SF
1281
1388
  top_shape ||= shape
1282
1389
  if shape.selected?
1283
1390
  sel_shape ||= shape
1284
- else
1391
+ elsif !shape.has_selected_parent?
1285
1392
  unsel_shape ||= shape
1286
1393
  end
1287
1394
  end
@@ -1343,7 +1450,7 @@ module Wx::SF
1343
1450
  end
1344
1451
 
1345
1452
  # ... then test normal handles
1346
- @diagram.get_shapes.each do |shape|
1453
+ @diagram.get_all_shapes.each do |shape|
1347
1454
  # iterate through all shape's handles
1348
1455
  if shape.has_style?(Shape::STYLE::SIZE_CHANGE)
1349
1456
  shape.handles.each do |handle|
@@ -1381,7 +1488,7 @@ module Wx::SF
1381
1488
  return selection unless @diagram
1382
1489
 
1383
1490
  selection.clear
1384
- @diagram.get_shapes.each do |shape|
1491
+ @diagram.get_all_shapes.each do |shape|
1385
1492
  selection << shape if shape.selected?
1386
1493
  end
1387
1494
  selection
@@ -1393,7 +1500,7 @@ module Wx::SF
1393
1500
  virt_rct = nil
1394
1501
  if @diagram
1395
1502
  # calculate total bounding box (includes all shapes)
1396
- @diagram.get_shapes.each_with_index do |shape, ix|
1503
+ @diagram.get_all_shapes.each_with_index do |shape, ix|
1397
1504
  if ix == 0
1398
1505
  virt_rct = shape.get_bounding_box
1399
1506
  else
@@ -1407,14 +1514,14 @@ module Wx::SF
1407
1514
  # Get bounding box of all selected shapes.
1408
1515
  # @return [Wx::Rect] Selection bounding box
1409
1516
  def get_selection_bb
1410
- bb_rct = Wx::Rect.new
1517
+ bb_rct = nil
1411
1518
  # get selected shapes
1412
1519
  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)
1520
+ bb_rct = shape.get_complete_bounding_box(bb_rct,
1521
+ Shape::BBMODE::SELF | Shape::BBMODE::CHILDREN |
1522
+ Shape::BBMODE::CONNECTIONS | Shape::BBMODE::SHADOW)
1416
1523
  end
1417
- bb_rct
1524
+ bb_rct || Wx::Rect.new
1418
1525
  end
1419
1526
 
1420
1527
  # Align selected shapes in given directions.
@@ -1438,7 +1545,7 @@ module Wx::SF
1438
1545
  shape_bb = shape.get_bounding_box
1439
1546
 
1440
1547
  if cnt == 0
1441
- min_pos = pos
1548
+ min_pos = pos.dup
1442
1549
  max_pos = Wx::RealPoint.new(pos.x + shape_bb.width, pos.y + shape_bb.height)
1443
1550
  else
1444
1551
  min_pos.x = pos.x if pos.x < min_pos.x
@@ -1543,14 +1650,18 @@ module Wx::SF
1543
1650
  def set_canvas_colour(col)
1544
1651
  @settings.background_color = col
1545
1652
  end
1653
+ alias :set_canvas_color :set_canvas_colour
1546
1654
  alias :canvas_colour= :set_canvas_colour
1655
+ alias :canvas_color= :set_canvas_colour
1547
1656
 
1548
1657
  # Get canvas background color.
1549
1658
  # @return [Wx::Colour] Background color
1550
1659
  def get_canvas_colour
1551
1660
  @settings.background_color
1552
1661
  end
1662
+ alias :get_canvas_color :get_canvas_colour
1553
1663
  alias :canvas_colour :get_canvas_colour
1664
+ alias :canvas_color :get_canvas_colour
1554
1665
 
1555
1666
  # Set starting gradient color.
1556
1667
  # @param [Wx::Colour] col Color
@@ -1580,17 +1691,18 @@ module Wx::SF
1580
1691
  end
1581
1692
  alias :gradient_to :get_gradient_to
1582
1693
 
1583
- # Get grid size.
1584
- # @return [Wx::Size] Grid size
1694
+ # Get grid size (px).
1695
+ # @return [Integer] Grid size
1585
1696
  def get_grid_size
1586
1697
  @settings.grid_size
1587
1698
  end
1588
1699
  alias :grid_size :get_grid_size
1589
1700
 
1590
- # Set grid size.
1591
- # @param [Wx::Size] grid Grid size
1592
- def set_grid_size(grid)
1593
- @settings.grid_size = grid.to_size
1701
+ # Set grid size (px).
1702
+ # @param [Integer] sz Grid size
1703
+ def set_grid_size(sz)
1704
+ raise ArgumentError, 'Grid size must be integer > 0' if sz.to_i <= 0
1705
+ @settings.grid_size = sz.to_i
1594
1706
  end
1595
1707
  alias :grid_size= :set_grid_size
1596
1708
 
@@ -1616,14 +1728,18 @@ module Wx::SF
1616
1728
  def set_grid_colour(col)
1617
1729
  @settings.grid_color = col
1618
1730
  end
1731
+ alias :set_grid_color :set_grid_colour
1619
1732
  alias :grid_colour= :set_grid_colour
1733
+ alias :grid_color= :set_grid_colour
1620
1734
 
1621
1735
  # Get grid color.
1622
1736
  # @return [Wx::Colour] Grid color
1623
1737
  def get_grid_colour
1624
1738
  @settings.grid_color
1625
1739
  end
1740
+ alias :get_grid_color :get_grid_colour
1626
1741
  alias :grid_colour :get_grid_colour
1742
+ alias :grid_color :get_grid_colour
1627
1743
 
1628
1744
  # Set grid line style.
1629
1745
  # @param [Wx::PenStyle] style Line style
@@ -1654,9 +1770,19 @@ module Wx::SF
1654
1770
  alias :shadow_offset :get_shadow_offset
1655
1771
 
1656
1772
  # 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
1773
+ # @overload set_shadow_fill(brush)
1774
+ # @param [Wx::Brush] brush
1775
+ # @overload set_shadow_fill(color, style=Wx::BrushStyle::BRUSHSTYLE_SOLID)
1776
+ # @param [Wx::Colour,Symbol,String] color brush color
1777
+ # @param [Wx::BrushStyle] style
1778
+ # @overload set_shadow_fill(stipple_bitmap)
1779
+ # @param [Wx::Bitmap] stipple_bitmap
1780
+ def set_shadow_fill(*args)
1781
+ @settings.shadow_fill = if args.size == 1 && Wx::Brush === args.first
1782
+ args.first
1783
+ else
1784
+ Wx::Brush.new(*args)
1785
+ end
1660
1786
  end
1661
1787
  alias :shadow_fill= :set_shadow_fill
1662
1788
 
@@ -1811,23 +1937,304 @@ module Wx::SF
1811
1937
  alias :mode :get_mode
1812
1938
 
1813
1939
  # Set default hover color.
1814
- # @param [Wx::Colour] col Hover color.
1940
+ # @param [Wx::Colour,Symbol,String] col Hover color.
1815
1941
  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) }
1942
+ @settings.common_hover_color = Wx::Colour === col ? col : Wx::Colour.new(col)
1822
1943
  end
1944
+ alias :set_hover_color :set_hover_colour
1823
1945
  alias :hover_colour= :set_hover_colour
1946
+ alias :hover_color= :set_hover_colour
1824
1947
 
1825
1948
  # Get default hover colour.
1826
1949
  # @return [Wx::Colour] Hover colour
1827
1950
  def get_hover_colour
1828
1951
  @settings.common_hover_color
1829
1952
  end
1953
+ alias :get_hover_color :get_hover_colour
1830
1954
  alias :hover_colour :get_hover_colour
1955
+ alias :hover_color :get_hover_colour
1956
+
1957
+ # Set default fill brush.
1958
+ # @overload set_fill_brush(brush)
1959
+ # @param [Wx::Brush] brush
1960
+ # @overload set_fill_brush(color, style=Wx::BrushStyle::BRUSHSTYLE_SOLID)
1961
+ # @param [Wx::Colour,Symbol,String] color brush color
1962
+ # @param [Wx::BrushStyle] style
1963
+ # @overload set_fill_brush(stipple_bitmap)
1964
+ # @param [Wx::Bitmap] stipple_bitmap
1965
+ def set_fill_brush(*args)
1966
+ @settings.common_fill_brush = if args.size == 1 && Wx::Brush === args.first
1967
+ args.first
1968
+ else
1969
+ Wx::Brush.new(*args)
1970
+ end
1971
+ end
1972
+ alias :fill_brush= :set_fill_brush
1973
+
1974
+ # Get default fill brush.
1975
+ # @return [Wx::Brush] Fill brush
1976
+ def get_fill_brush
1977
+ @settings.common_fill_brush
1978
+ end
1979
+ alias :fill_brush :get_fill_brush
1980
+
1981
+ # Set default border pen.
1982
+ # @overload set_border_pen(pen)
1983
+ # @param [Wx::Pen] pen
1984
+ # @overload set_border_pen(color, width=1, style=Wx::PenStyle::PENSTYLE_SOLID)
1985
+ # @param [Wx::Colour,String,Symbol] color
1986
+ # @param [Integer] width
1987
+ # @param [Wx::PenStyle] style
1988
+ def set_border_pen(*args)
1989
+ @settings.common_border_pen = if args.size == 1 && Wx::Pen === args.first
1990
+ args.first
1991
+ else
1992
+ Wx::Pen.new(*args)
1993
+ end
1994
+ end
1995
+ alias :border_pen= :set_border_pen
1996
+
1997
+ # Get default border pen.
1998
+ # @return [Wx::Pen]
1999
+ def get_border_pen
2000
+ @settings.common_border_pen
2001
+ end
2002
+ alias :border_pen :get_border_pen
2003
+
2004
+ # Set default line pen.
2005
+ # @overload set_line_pen(pen)
2006
+ # @param [Wx::Pen] pen
2007
+ # @overload set_line_pen(color, width=1, style=Wx::PenStyle::PENSTYLE_SOLID)
2008
+ # @param [Wx::Colour,String,Symbol] color
2009
+ # @param [Integer] width
2010
+ # @param [Wx::PenStyle] style
2011
+ def set_line_pen(*args)
2012
+ @settings.common_line_pen = if args.size == 1 && Wx::Pen === args.first
2013
+ args.first
2014
+ else
2015
+ Wx::Pen.new(*args)
2016
+ end
2017
+ end
2018
+ alias :line_pen= :set_line_pen
2019
+
2020
+ # Get default line pen.
2021
+ # @return [Wx::Pen]
2022
+ def get_line_pen
2023
+ @settings.common_line_pen
2024
+ end
2025
+ alias :line_pen :get_line_pen
2026
+
2027
+ # Set default arrow fill brush.
2028
+ # @overload set_arrow_fill(brush)
2029
+ # @param [Wx::Brush] brush
2030
+ # @overload set_arrow_fill(color, style=Wx::BrushStyle::BRUSHSTYLE_SOLID)
2031
+ # @param [Wx::Colour,Symbol,String] color brush color
2032
+ # @param [Wx::BrushStyle] style
2033
+ # @overload set_arrow_fill(stipple_bitmap)
2034
+ # @param [Wx::Bitmap] stipple_bitmap
2035
+ def set_arrow_fill(*args)
2036
+ @settings.common_arrow_fill = if args.size == 1 && Wx::Brush === args.first
2037
+ args.first
2038
+ else
2039
+ Wx::Brush.new(*args)
2040
+ end
2041
+ end
2042
+ alias :arrow_fill= :set_arrow_fill
2043
+
2044
+ # Get default arrow fill brush.
2045
+ # @return [Wx::Brush] Fill brush
2046
+ def get_arrow_fill
2047
+ @settings.common_arrow_fill
2048
+ end
2049
+ alias :arrow_fill :get_arrow_fill
2050
+
2051
+ # Set default text color.
2052
+ # @param [Wx::Colour,Symbol,String] col text color.
2053
+ def set_text_colour(col)
2054
+ @settings.common_text_color = Wx::Colour === col ? col : Wx::Colour.new(col)
2055
+ end
2056
+ alias :set_text_color= :set_text_colour
2057
+ alias :text_colour= :set_text_colour
2058
+ alias :text_color= :set_text_colour
2059
+
2060
+ # Get default text colour.
2061
+ # @return [Wx::Colour] text colour
2062
+ def get_text_colour
2063
+ @settings.common_text_color
2064
+ end
2065
+ alias :get_text_color :get_text_colour
2066
+ alias :text_colour :get_text_colour
2067
+ alias :text_color :get_text_colour
2068
+
2069
+ # Set default text fill brush.
2070
+ # @overload set_text_fill(brush)
2071
+ # @param [Wx::Brush] brush
2072
+ # @overload set_text_fill(color, style=Wx::BrushStyle::BRUSHSTYLE_SOLID)
2073
+ # @param [Wx::Colour,Symbol,String] color brush color
2074
+ # @param [Wx::BrushStyle] style
2075
+ # @overload set_text_fill(stipple_bitmap)
2076
+ # @param [Wx::Bitmap] stipple_bitmap
2077
+ def set_text_fill(*args)
2078
+ @settings.common_text_fill = if args.size == 1 && Wx::Brush === args.first
2079
+ args.first
2080
+ else
2081
+ Wx::Brush.new(*args)
2082
+ end
2083
+ end
2084
+ alias :text_fill= :set_text_fill
2085
+
2086
+ # Get default text fill brush.
2087
+ # @return [Wx::Brush] Fill brush
2088
+ def get_text_fill
2089
+ @settings.common_text_fill
2090
+ end
2091
+ alias :text_fill :get_text_fill
2092
+
2093
+ # Set default text border.
2094
+ # @overload set_text_border(pen)
2095
+ # @param [Wx::Pen] pen
2096
+ # @overload set_text_border(color, width=1, style=Wx::PenStyle::PENSTYLE_SOLID)
2097
+ # @param [Wx::Colour,String,Symbol] color
2098
+ # @param [Integer] width
2099
+ # @param [Wx::PenStyle] style
2100
+ def set_text_border(*args)
2101
+ @settings.common_text_border = if args.size == 1 && Wx::Pen === args.first
2102
+ args.first
2103
+ else
2104
+ Wx::Pen.new(*args)
2105
+ end
2106
+ end
2107
+ alias :text_border= :set_text_border
2108
+
2109
+ # Get default text border.
2110
+ # @return [Wx::Pen]
2111
+ def get_text_border
2112
+ @settings.common_text_border
2113
+ end
2114
+ alias :text_border :get_text_border
2115
+
2116
+ # Set default text font.
2117
+ # @overload set_text_font(font)
2118
+ # @param [Wx::Font] font
2119
+ # @overload set_text_font(font_info)
2120
+ # @param [Wx::FontInfo] font_info
2121
+ # @overload set_text_font(pointSize, family, style, weight, underline=false, faceName=(''), encoding=Wx::FontEncoding::FONTENCODING_DEFAULT)
2122
+ # @param pointSize [Integer] Size in points. See {Wx::Font#initialize}.
2123
+ # @param family [Wx::FontFamily] The font family. See {Wx::Font#initialize}.
2124
+ # @param style [Wx::FontStyle] One of {Wx::FontStyle::FONTSTYLE_NORMAL}, {Wx::FontStyle::FONTSTYLE_SLANT} and {Wx::FontStyle::FONTSTYLE_ITALIC}. See {Wx::Font#initialize}.
2125
+ # @param weight [Wx::FontWeight] Font weight. One of the {Wx::FontWeight} enumeration values. See {Wx::Font#initialize}.
2126
+ # @param underline [Boolean] The value can be true or false. See {Wx::Font#initialize}.
2127
+ # @param faceName [String] An optional string specifying the face name to be used. See {Wx::Font#initialize}.
2128
+ # @param encoding [Wx::FontEncoding] An encoding which may be one of the enumeration values of {Wx::FontEncoding}. See {Wx::Font#initialize}.
2129
+ def set_text_font(*args)
2130
+ @settings.common_text_font = if args.size == 1 && Wx::Font === args.first
2131
+ args.first
2132
+ else
2133
+ Wx::Font.new(*args)
2134
+ end
2135
+ end
2136
+ alias :text_font= :set_text_font
2137
+
2138
+ # Get default text font.
2139
+ # @return [Wx::Font]
2140
+ def get_text_font
2141
+ @settings.common_text_font
2142
+ end
2143
+ alias :text_font :get_text_font
2144
+
2145
+ # Set default control fill brush.
2146
+ # @overload set_control_fill(brush)
2147
+ # @param [Wx::Brush] brush
2148
+ # @overload set_control_fill(color, style=Wx::BrushStyle::BRUSHSTYLE_SOLID)
2149
+ # @param [Wx::Colour,Symbol,String] color brush color
2150
+ # @param [Wx::BrushStyle] style
2151
+ # @overload set_control_fill(stipple_bitmap)
2152
+ # @param [Wx::Bitmap] stipple_bitmap
2153
+ def set_control_fill(*args)
2154
+ @settings.common_control_fill = if args.size == 1 && Wx::Brush === args.first
2155
+ args.first
2156
+ else
2157
+ Wx::Brush.new(*args)
2158
+ end
2159
+ end
2160
+ alias :control_fill= :set_control_fill
2161
+
2162
+ # Get default control fill brush.
2163
+ # @return [Wx::Brush] Fill brush
2164
+ def get_control_fill
2165
+ @settings.common_control_fill
2166
+ end
2167
+ alias :control_fill :get_control_fill
2168
+
2169
+ # Set default control border.
2170
+ # @overload set_control_border(pen)
2171
+ # @param [Wx::Pen] pen
2172
+ # @overload set_control_border(color, width=1, style=Wx::PenStyle::PENSTYLE_SOLID)
2173
+ # @param [Wx::Colour,String,Symbol] color
2174
+ # @param [Integer] width
2175
+ # @param [Wx::PenStyle] style
2176
+ def set_control_border(*args)
2177
+ @settings.common_control_border = if args.size == 1 && Wx::Pen === args.first
2178
+ args.first
2179
+ else
2180
+ Wx::Pen.new(*args)
2181
+ end
2182
+ end
2183
+ alias :control_border= :set_control_border
2184
+
2185
+ # Get default control border.
2186
+ # @return [Wx::Pen]
2187
+ def get_control_border
2188
+ @settings.common_control_border
2189
+ end
2190
+ alias :control_border :get_control_border
2191
+
2192
+ # Set default control modification fill brush.
2193
+ # @overload set_control_mod_fill(brush)
2194
+ # @param [Wx::Brush] brush
2195
+ # @overload set_control_mod_fill(color, style=Wx::BrushStyle::BRUSHSTYLE_SOLID)
2196
+ # @param [Wx::Colour,Symbol,String] color brush color
2197
+ # @param [Wx::BrushStyle] style
2198
+ # @overload set_control_mod_fill(stipple_bitmap)
2199
+ # @param [Wx::Bitmap] stipple_bitmap
2200
+ def set_control_mod_fill(*args)
2201
+ @settings.common_control_mod_fill = if args.size == 1 && Wx::Brush === args.first
2202
+ args.first
2203
+ else
2204
+ Wx::Brush.new(*args)
2205
+ end
2206
+ end
2207
+ alias :control_mod_fill= :set_control_mod_fill
2208
+
2209
+ # Get default control modification fill brush.
2210
+ # @return [Wx::Brush] Fill brush
2211
+ def get_control_mod_fill
2212
+ @settings.common_control_mod_fill
2213
+ end
2214
+ alias :control_mod_fill :get_control_mod_fill
2215
+
2216
+ # Set default control modification border.
2217
+ # @overload set_control_mod_border(pen)
2218
+ # @param [Wx::Pen] pen
2219
+ # @overload set_control_mod_border(color, width=1, style=Wx::PenStyle::PENSTYLE_SOLID)
2220
+ # @param [Wx::Colour,String,Symbol] color
2221
+ # @param [Integer] width
2222
+ # @param [Wx::PenStyle] style
2223
+ def set_control_mod_border(*args)
2224
+ @settings.common_control_mod_border = if args.size == 1 && Wx::Pen === args.first
2225
+ args.first
2226
+ else
2227
+ Wx::Pen.new(*args)
2228
+ end
2229
+ end
2230
+ alias :control_mod_border= :set_control_mod_border
2231
+
2232
+ # Get default control modification border.
2233
+ # @return [Wx::Pen]
2234
+ def get_control_mod_border
2235
+ @settings.common_control_mod_border
2236
+ end
2237
+ alias :control_mod_border :get_control_mod_border
1831
2238
 
1832
2239
  # Get canvas history manager.
1833
2240
  # @return [Wx::SF::CanvasHistory] the canvas history manager
@@ -1843,8 +2250,8 @@ module Wx::SF
1843
2250
  def fit_position_to_grid(pos)
1844
2251
  pos = pos.to_point
1845
2252
  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)
2253
+ Wx::Point.new(pos.x / @settings.grid_size * @settings.grid_size,
2254
+ pos.y / @settings.grid_size * @settings.grid_size)
1848
2255
  else
1849
2256
  pos
1850
2257
  end
@@ -1905,7 +2312,7 @@ module Wx::SF
1905
2312
 
1906
2313
  move_shapes_from_negatives
1907
2314
  end
1908
-
2315
+
1909
2316
  # Validate selection (remove redundantly selected shapes etc...).
1910
2317
  # @param [Array<Wx::SF::Shape>] selection List of selected shapes that should be validated
1911
2318
  def validate_selection(selection)
@@ -1931,6 +2338,40 @@ module Wx::SF
1931
2338
  end
1932
2339
  end
1933
2340
 
2341
+ # Draws shapes intersecting the update region
2342
+ def draw_shape_updates(dc, upd_rct, lst_to_draw, exclude_selected = false)
2343
+ lst_selected = exclude_selected ? [] : nil
2344
+ lst_lines_to_draw = []
2345
+ # draw unselected non line-based shapes first...
2346
+ lst_to_draw.each do |shape|
2347
+ if exclude_selected && (shape.selected? || shape.has_selected_parent?)
2348
+ lst_selected << shape
2349
+ else
2350
+ if !shape.is_a?(LineShape) || shape.stand_alone?
2351
+ if shape.intersects?(upd_rct)
2352
+ parent_shape = shape.get_parent_shape
2353
+ if parent_shape
2354
+ shape.draw(dc, WITHOUTCHILDREN) if !parent_shape.is_a?(LineShape) || parent_shape.stand_alone?
2355
+ else
2356
+ shape.draw(dc, WITHOUTCHILDREN)
2357
+ end
2358
+ end
2359
+ else
2360
+ lst_lines_to_draw << shape
2361
+ end
2362
+ end
2363
+ end
2364
+
2365
+ # ... and draw connections
2366
+ bb_rct = nil
2367
+ lst_lines_to_draw.each do |line|
2368
+ bb_rct = line.get_complete_bounding_box(bb_rct, Shape::BBMODE::SELF | Shape::BBMODE::CHILDREN | Shape::BBMODE::SHADOW)
2369
+ line.draw(dc, line.get_line_mode == LineShape::LINEMODE::READY) if bb_rct.intersects(upd_rct)
2370
+ end
2371
+ lst_selected
2372
+ end
2373
+ private :draw_shape_updates
2374
+
1934
2375
  # Function responsible for drawing of the canvas's content to given DC. The default
1935
2376
  # implementation draws actual objects managed by assigned diagram manager.
1936
2377
  # @param [Wx::DC] dc device context where the shapes will be drawn to
@@ -1940,12 +2381,6 @@ module Wx::SF
1940
2381
  return unless @diagram
1941
2382
 
1942
2383
  if from_paint
1943
- # wxRect updRct
1944
- bb_rct = Wx::Rect.new
1945
- #
1946
- # ShapeList m_lstToDraw
1947
- lst_lines_to_draw = []
1948
-
1949
2384
  # get all existing shapes
1950
2385
  lst_to_draw = @diagram.get_shapes(Shape, Shape::SEARCHMODE::DFS)
1951
2386
 
@@ -1964,51 +2399,13 @@ module Wx::SF
1964
2399
  upd_rct ||= Wx::Rect.new
1965
2400
 
1966
2401
  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
2402
+ # draw unselected shapes first and filter and return selected shapes
2403
+ lst_selected = draw_shape_updates(dc, upd_rct, lst_to_draw, true)
1983
2404
 
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
2405
+ # ... and now draw the selected shapes being moved
2406
+ draw_shape_updates(dc, upd_rct, lst_selected)
1989
2407
  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
2408
+ draw_shape_updates(dc, upd_rct, lst_to_draw)
2012
2409
  end
2013
2410
 
2014
2411
  # draw multiselection if necessary
@@ -2035,7 +2432,7 @@ module Wx::SF
2035
2432
  def draw_background(dc, _from_paint)
2036
2433
  # erase background
2037
2434
  if has_style?(STYLE::GRADIENT_BACKGROUND)
2038
- bcg_size = @settings.grid_size + get_virtual_size
2435
+ bcg_size = get_virtual_size.to_size + @settings.grid_size
2039
2436
  if @settings.scale != 1.0
2040
2437
  dc.gradient_fill_linear(Wx::Rect.new([0, 0], [(bcg_size.x/@settings.scale).to_i, (bcg_size.y/@settings.scale).to_i]),
2041
2438
  @settings.gradient_from, @settings.gradient_to, Wx::SOUTH)
@@ -2050,10 +2447,10 @@ module Wx::SF
2050
2447
 
2051
2448
  # show grid
2052
2449
  if has_style?(STYLE::GRID_SHOW)
2053
- linedist = @settings.grid_size.x * @settings.grid_line_mult
2450
+ linedist = @settings.grid_size * @settings.grid_line_mult
2054
2451
 
2055
2452
  if (linedist * @settings.scale) > 3.0
2056
- grid_rct = Wx::Rect.new([0, 0], @settings.grid_size + get_virtual_size)
2453
+ grid_rct = Wx::Rect.new([0, 0], get_virtual_size.to_size + @settings.grid_size)
2057
2454
  max_x = (grid_rct.right/@settings.scale).to_i
2058
2455
  max_y = (grid_rct.bottom/@settings.scale).to_i
2059
2456
 
@@ -2100,11 +2497,11 @@ module Wx::SF
2100
2497
 
2101
2498
  _notify_canvas_change(CHANGE::FOCUS)
2102
2499
  set_focus
2103
-
2500
+
2104
2501
  lpos = dp2lp(event.get_position)
2105
-
2502
+
2106
2503
  @can_save_state_on_mouse_up = false
2107
-
2504
+
2108
2505
  case @working_mode
2109
2506
  when MODE::READY
2110
2507
  @selected_handle = get_topmost_handle_at_position(lpos)
@@ -2129,6 +2526,10 @@ module Wx::SF
2129
2526
  # perform selection
2130
2527
  lst_selection = get_selected_shapes
2131
2528
 
2529
+ if @selection_mode == SELECTIONMODE::NORMAL
2530
+ save_canvas_state if @canvas_history.empty?
2531
+ end
2532
+
2132
2533
  # cancel previous selections if necessary...
2133
2534
  if @selection_mode == SELECTIONMODE::NORMAL && (selected_top_shape.nil? || !lst_selection.include?(selected_top_shape))
2134
2535
  deselect_all
@@ -2159,7 +2560,7 @@ module Wx::SF
2159
2560
 
2160
2561
  # inform also connections assigned to the shape and its children
2161
2562
  lst_connections.clear
2162
- append_assigned_connections(shape, lst_connections, true)
2563
+ append_assigned_connections(shape, lst_connections)
2163
2564
 
2164
2565
  lst_connections.each do |line|
2165
2566
  line.send(:_on_begin_drag, fit_pos)
@@ -2196,6 +2597,7 @@ module Wx::SF
2196
2597
  # update canvas
2197
2598
  invalidate_visible_rect
2198
2599
  else
2600
+ save_canvas_state if @canvas_history.empty?
2199
2601
  if @selected_handle.get_parent_shape == @shp_multi_edit
2200
2602
  if has_style?(STYLE::MULTI_SIZE_CHANGE)
2201
2603
  @working_mode = MODE::MULTIHANDLEMOVE
@@ -2229,28 +2631,28 @@ module Wx::SF
2229
2631
  end
2230
2632
  # finish connection's creation process if possible
2231
2633
  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))
2634
+ if @new_line_shape.get_trg_shape.nil? && (shape_under != @new_line_shape) &&
2635
+ (shape_under.is_connection_accepted(@new_line_shape.class))
2234
2636
  # 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)
2637
+ source_shape = @new_line_shape.get_src_shape
2236
2638
 
2237
2639
  if source_shape &&
2238
2640
  shape_under.is_src_neighbour_accepted(source_shape.class) &&
2239
2641
  source_shape.is_trg_neighbour_accepted(shape_under.class)
2240
- @new_line_shape.set_trg_shape_id(shape_under.get_id)
2642
+ @new_line_shape.set_trg_shape(shape_under)
2241
2643
  @new_line_shape.set_ending_connection_point(shape_under.get_nearest_connection_point(lpos.to_real))
2242
2644
 
2243
2645
  # inform user that the line is completed
2244
2646
  case on_pre_connection_finished(@new_line_shape)
2245
2647
  when PRECON_FINISH_STATE::OK
2246
2648
  when PRECON_FINISH_STATE::FAILED_AND_CANCEL_LINE
2247
- @new_line_shape.set_trg_shape_id(nil)
2649
+ @new_line_shape.set_trg_shape(nil)
2248
2650
  @diagram.remove_shape(@new_line_shape)
2249
2651
  @working_mode = MODE::READY
2250
2652
  @new_line_shape = nil
2251
2653
  return
2252
2654
  when PRECON_FINISH_STATE::FAILED_AND_CONTINUE_EDIT
2253
- @new_line_shape.set_trg_shape_id(nil)
2655
+ @new_line_shape.set_trg_shape(nil)
2254
2656
  return
2255
2657
  end
2256
2658
  @new_line_shape.create_handles
@@ -2270,7 +2672,7 @@ module Wx::SF
2270
2672
  end
2271
2673
  end
2272
2674
  else
2273
- if @new_line_shape.get_src_shape_id
2675
+ if @new_line_shape.get_src_shape
2274
2676
  fit_pos = fit_position_to_grid(lpos)
2275
2677
  @new_line_shape.get_control_points << Wx::RealPoint.new(fit_pos.x, fit_pos.y)
2276
2678
  end
@@ -2280,7 +2682,7 @@ module Wx::SF
2280
2682
  else
2281
2683
  @working_mode = MODE::READY
2282
2684
  end
2283
-
2685
+
2284
2686
  refresh_invalidated_rect
2285
2687
  end
2286
2688
 
@@ -2327,7 +2729,7 @@ module Wx::SF
2327
2729
  def on_left_up(event)
2328
2730
  # HINT: override it for custom actions...
2329
2731
  lpos = dp2lp(event.get_position)
2330
-
2732
+
2331
2733
  case @working_mode
2332
2734
  when MODE::MULTIHANDLEMOVE, MODE::HANDLEMOVE
2333
2735
  # resize parent shape to fit all its children if necessary
@@ -2343,17 +2745,21 @@ module Wx::SF
2343
2745
  line.send(:set_line_mode, LineShape::LINEMODE::READY)
2344
2746
 
2345
2747
  parent_shape = get_shape_under_cursor
2748
+ # propagate request for interactive connection if requested
2749
+ while parent_shape && parent_shape.has_style?(Shape::STYLE::PROPAGATE_INTERACTIVE_CONNECTION)
2750
+ parent_shape = parent_shape.get_parent_shape
2751
+ end
2346
2752
 
2347
2753
  if parent_shape && (parent_shape != line) && (parent_shape.is_connection_accepted(line.class))
2348
2754
  if @selected_handle.get_type == Shape::Handle::TYPE::LINESTART
2349
- trg_shape = @diagram.find_shape(line.get_trg_shape_id)
2755
+ trg_shape = line.get_trg_shape
2350
2756
  if trg_shape && parent_shape.is_trg_neighbour_accepted(trg_shape.class)
2351
- line.set_src_shape_id(parent_shape.get_id)
2757
+ line.set_src_shape(parent_shape)
2352
2758
  end
2353
2759
  else
2354
- src_shape = @diagram.find_shape(line.get_src_shape_id)
2760
+ src_shape = line.get_src_shape
2355
2761
  if src_shape && parent_shape.is_src_neighbour_accepted(src_shape.class)
2356
- line.set_trg_shape_id(parent_shape.get_id)
2762
+ line.set_trg_shape(parent_shape)
2357
2763
  end
2358
2764
  end
2359
2765
  end
@@ -2362,29 +2768,29 @@ module Wx::SF
2362
2768
  @selected_handle.send(:_on_end_drag, lpos)
2363
2769
 
2364
2770
  @selected_handle = nil
2365
- save_canvas_state if @can_save_state_on_mouse_up
2771
+ save_canvas_state if @can_save_state_on_mouse_up
2366
2772
 
2367
2773
  when MODE::SHAPEMOVE
2368
2774
  lst_selection = get_selected_shapes
2369
-
2775
+
2370
2776
  lst_selection.each do |shape|
2371
2777
  # notify shape
2372
2778
  shape.send(:_on_end_drag, lpos)
2373
2779
  # reparent based on new position
2374
- reparent_shape(shape, lpos)
2780
+ reparent_dropped_shape(shape, lpos)
2375
2781
  end
2376
-
2782
+
2377
2783
  if lst_selection.size>1
2378
2784
  @shp_multi_edit.show(true)
2379
2785
  @shp_multi_edit.show_handles(true)
2380
2786
  else
2381
2787
  @shp_multi_edit.show(false)
2382
2788
  end
2383
-
2789
+
2384
2790
  move_shapes_from_negatives
2385
2791
 
2386
2792
  save_canvas_state if @can_save_state_on_mouse_up
2387
-
2793
+
2388
2794
  when MODE::MULTISELECTION
2389
2795
  lst_selection = get_selected_shapes
2390
2796
 
@@ -2416,7 +2822,7 @@ module Wx::SF
2416
2822
 
2417
2823
  @shp_selection.show(false)
2418
2824
  end
2419
-
2825
+
2420
2826
  if @working_mode != MODE::CREATECONNECTION
2421
2827
  # update canvas
2422
2828
  @working_mode = MODE::READY
@@ -2442,19 +2848,22 @@ module Wx::SF
2442
2848
 
2443
2849
  _notify_canvas_change(CHANGE::FOCUS)
2444
2850
  set_focus
2445
-
2851
+
2446
2852
  lpos = dp2lp(event.get_position)
2447
-
2853
+
2448
2854
  if @working_mode == MODE::READY
2449
2855
  deselect_all
2450
-
2856
+
2451
2857
  shape = get_shape_under_cursor
2858
+ while shape && shape.has_style?(Shape::STYLE::PROPAGATE_SELECTION)
2859
+ shape = shape.get_parent_shape
2860
+ end
2452
2861
  if shape
2453
2862
  shape.select(true)
2454
2863
  shape.on_right_click(lpos)
2455
2864
  end
2456
2865
  end
2457
-
2866
+
2458
2867
  refresh(false)
2459
2868
  end
2460
2869
 
@@ -2472,9 +2881,9 @@ module Wx::SF
2472
2881
 
2473
2882
  _notify_canvas_change(CHANGE::FOCUS)
2474
2883
  set_focus
2475
-
2884
+
2476
2885
  lpos = dp2lp(event.get_position)
2477
-
2886
+
2478
2887
  if @working_mode == MODE::READY
2479
2888
  shape = get_shape_under_cursor
2480
2889
  shape.on_right_double_click(lpos) if shape
@@ -2508,9 +2917,9 @@ module Wx::SF
2508
2917
  def on_mouse_move(event)
2509
2918
  # HINT: override it for custom actions...
2510
2919
  return unless @diagram
2511
-
2920
+
2512
2921
  lpos = dp2lp(event.get_position)
2513
-
2922
+
2514
2923
  case @working_mode
2515
2924
  when MODE::READY, MODE::CREATECONNECTION
2516
2925
  unless event.dragging
@@ -2522,16 +2931,12 @@ module Wx::SF
2522
2931
 
2523
2932
  # update unfinished line if any
2524
2933
  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)
2934
+ line_rct = @new_line_shape.get_complete_bounding_box(nil, Shape::BBMODE::SELF | Shape::BBMODE::CHILDREN)
2528
2935
 
2529
2936
  @new_line_shape.send(:set_unfinished_point, fit_position_to_grid(lpos))
2530
2937
  @new_line_shape.update
2531
2938
 
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)
2939
+ line_rct = @new_line_shape.get_complete_bounding_box(line_rct, Shape::BBMODE::SELF | Shape::BBMODE::CHILDREN)
2535
2940
 
2536
2941
  invalidate_rect(line_rct)
2537
2942
  end
@@ -2552,8 +2957,8 @@ module Wx::SF
2552
2957
  unless @working_mode == MODE::MULTIHANDLEMOVE
2553
2958
  if event.dragging
2554
2959
  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
2960
+ return if (event.get_position.x - @prev_mouse_pos.x).abs < @settings.grid_size &&
2961
+ (event.get_position.y - @prev_mouse_pos.y).abs < @settings.grid_size
2557
2962
  end
2558
2963
  @prev_mouse_pos = event.get_position
2559
2964
 
@@ -2572,7 +2977,7 @@ module Wx::SF
2572
2977
  # move also connections assigned to this shape and its children
2573
2978
  lst_connections.clear
2574
2979
 
2575
- append_assigned_connections(shape, lst_connections,true)
2980
+ append_assigned_connections(shape, lst_connections)
2576
2981
 
2577
2982
  lst_connections.each { |line| line.send(:_on_dragging, fit_position_to_grid(lpos)) }
2578
2983
 
@@ -2607,7 +3012,7 @@ module Wx::SF
2607
3012
 
2608
3013
  invalidate_visible_rect
2609
3014
  end
2610
-
3015
+
2611
3016
  refresh_invalidated_rect
2612
3017
  end
2613
3018
 
@@ -2621,18 +3026,17 @@ module Wx::SF
2621
3026
  # @param [Wx::MouseEvent] event Mouse event
2622
3027
  def on_mouse_wheel(event)
2623
3028
  # HINT: override it for custom actions...
2624
-
2625
3029
  if event.control_down
2626
3030
  scale = get_scale
2627
- scale += (event.get_wheel_rotation/(event.get_wheel_delta*10)).to_f
3031
+ scale += event.get_wheel_rotation.to_f/(event.get_wheel_delta*10)
2628
3032
 
2629
3033
  scale = @settings.min_scale if scale < @settings.min_scale
2630
3034
  scale = @settings.max_scale if scale > @settings.max_scale
2631
-
3035
+
2632
3036
  set_scale(scale)
2633
3037
  refresh(false)
2634
3038
  end
2635
-
3039
+
2636
3040
  event.skip
2637
3041
  end
2638
3042
 
@@ -2650,7 +3054,7 @@ module Wx::SF
2650
3054
  return unless @diagram
2651
3055
 
2652
3056
  lst_selection = get_selected_shapes
2653
-
3057
+
2654
3058
  case event.get_key_code
2655
3059
  when Wx::K_DELETE
2656
3060
  # send event to selected shapes
@@ -2684,6 +3088,13 @@ module Wx::SF
2684
3088
  line.send(:set_line_mode, LineShape::LINEMODE::READY)
2685
3089
  @selected_handle = nil
2686
3090
  end
3091
+ restore_current_state
3092
+
3093
+ when MODE::MULTIHANDLEMOVE
3094
+ restore_current_state
3095
+
3096
+ when MODE::SHAPEMOVE
3097
+ restore_current_state
2687
3098
 
2688
3099
  else
2689
3100
  # send event to selected shapes
@@ -2696,16 +3107,16 @@ module Wx::SF
2696
3107
  lst_connections = []
2697
3108
  lst_selection.each do |shape|
2698
3109
  shape.send(:_on_key, event.get_key_code)
2699
-
3110
+
2700
3111
  # inform also connections assigned to this shape
2701
3112
  lst_connections.clear
2702
- append_assigned_connections(shape, lst_connections, true)
2703
-
3113
+ append_assigned_connections(shape, lst_connections)
3114
+
2704
3115
  lst_connections.each do |line|
2705
3116
  line.send(:_on_key, event.get_key_code) unless line.selected?
2706
3117
  end
2707
3118
  end
2708
-
3119
+
2709
3120
  # send the event to multiedit ctrl if displayed
2710
3121
  @shp_multi_edit.send(:_on_key, event.get_key_code) if @shp_multi_edit.visible?
2711
3122
 
@@ -2727,9 +3138,9 @@ module Wx::SF
2727
3138
  # @see Wx::SF::ShapeTextEvent
2728
3139
  def on_text_change(shape)
2729
3140
  # HINT: override it for custom actions...
2730
-
3141
+
2731
3142
  # ... standard implementation generates the Wx::SF::EVT_SF_TEXT_CHANGE event.
2732
- id = shape ? shape.get_id : nil
3143
+ id = shape ? shape.object_id : nil
2733
3144
 
2734
3145
  event = ShapeTextEvent.new(Wx::SF::EVT_SF_TEXT_CHANGE, id)
2735
3146
  event.set_shape(shape)
@@ -2745,9 +3156,9 @@ module Wx::SF
2745
3156
  # @see Wx::SF::ShapeEvent
2746
3157
  def on_connection_finished(connection)
2747
3158
  # HINT: override to perform user-defined actions...
2748
-
3159
+
2749
3160
  # ... standard implementation generates the Wx::SF::EVT_SF_LINE_DONE event.
2750
- id = connection ? connection.get_id : -1
3161
+ id = connection ? connection.object_id : -1
2751
3162
 
2752
3163
  event = ShapeEvent.new(Wx::SF::EVT_SF_LINE_DONE, id)
2753
3164
  event.set_shape(connection)
@@ -2766,10 +3177,10 @@ module Wx::SF
2766
3177
  # @see Wx::SF::ShapeEvent
2767
3178
  def on_pre_connection_finished(connection)
2768
3179
  # HINT: override to perform user-defined actions...
2769
-
3180
+
2770
3181
  # ... standard implementation generates the Wx::SF::EVT_SF_LINE_DONE event.
2771
- id = connection ? connection.get_id : -1
2772
-
3182
+ id = connection ? connection.object_id : -1
3183
+
2773
3184
  event = ShapeEvent.new(Wx::SF::EVT_SF_LINE_BEFORE_DONE, id)
2774
3185
  event.set_shape(connection)
2775
3186
  process_event(event)
@@ -2792,10 +3203,10 @@ module Wx::SF
2792
3203
  # @see Wx::SF::ShapeDropEvent
2793
3204
  def on_drop(x, y, deflt, dropped)
2794
3205
  # HINT: override it for custom actions...
2795
-
3206
+
2796
3207
  # ... standard implementation generates the Wx::SF::EVT_SF_ON_DROP event.
2797
3208
  return unless has_style?(STYLE::DND)
2798
-
3209
+
2799
3210
  # create the drop event and process it
2800
3211
  event = ShapeDropEvent.new(Wx::SF::EVT_SF_ON_DROP, x, y, self, deflt, Wx::ID_ANY)
2801
3212
  event.set_dropped_shapes(dropped)
@@ -2812,10 +3223,10 @@ module Wx::SF
2812
3223
  # @see Wx::SF::ShapePasteEvent
2813
3224
  def on_paste(pasted)
2814
3225
  # HINT: override it for custom actions...
2815
-
3226
+
2816
3227
  # ... standard implementation generates the Wx::SF::EVT_SF_ON_PASTE event.
2817
3228
  return unless has_style?(STYLE::CLIPBOARD)
2818
-
3229
+
2819
3230
  # create the drop event and process it
2820
3231
  event = ShapePasteEvent.new(Wx::SF::EVT_SF_ON_PASTE, self, Wx::ID_ANY)
2821
3232
  event.set_pasted_shapes(pasted)
@@ -2842,12 +3253,14 @@ module Wx::SF
2842
3253
  # @param [Array<Wx::SF::Shape>] selection
2843
3254
  # @param [Boolean] storeprevpos
2844
3255
  def validate_selection_for_clipboard(selection, storeprevpos)
2845
- selection.dup.each do |shape|
3256
+ # first remove any shapes not eligible for copying
3257
+ selection.reject! do |shape|
3258
+ do_reject = false
2846
3259
  if shape.get_parent_shape
2847
3260
  # remove child shapes without parent in the selection and without STYLE::PARENT_CHANGE style
2848
3261
  # defined from the selection
2849
3262
  if !shape.has_style?(Shape::STYLE::PARENT_CHANGE) && !selection.include?(shape.get_parent_shape)
2850
- selection.delete(shape)
3263
+ do_reject = true
2851
3264
  else
2852
3265
  # convert relative position to absolute position if the shape is copied
2853
3266
  # without its parent
@@ -2856,32 +3269,49 @@ module Wx::SF
2856
3269
  shape.set_relative_position(shape.get_absolute_position)
2857
3270
  end
2858
3271
  end
3272
+ elsif LineShape === shape && !shape.stand_alone?
3273
+ # remove any stand alone LineShape for which the source or target shape are not included in the selection
3274
+ # (or any of it's child shapes)
3275
+ unless (selection.include?(shape.get_src_shape) ||
3276
+ selection.any? { |selshp| selshp.include_child_shape?(shape.get_src_shape, true) }) &&
3277
+ (selection.include?(shape.get_trg_shape) ||
3278
+ selection.any? { |selshp| selshp.include_child_shape?(shape.get_trg_shape, true) })
3279
+ do_reject = true
3280
+ end
2859
3281
  end
2860
-
2861
- append_assigned_connections(shape, selection, false)
3282
+ do_reject
3283
+ end
3284
+
3285
+ # now append all connections for which source AND target are included in the selection
3286
+ selection.each do |shape|
3287
+ append_assigned_connections(shape, selection, children_only: false, complete_only: true)
2862
3288
  end
2863
3289
  end
2864
3290
 
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)
3291
+ # Append connections assigned to shapes in given list to this list as well
3292
+ # @param [Wx::SF::Shape] shape shape
3293
+ # @param [Array<Wx::SF::Shape>] selection selected shapes
3294
+ # @param [Boolean] children_only appends connections from child shapes only
3295
+ # @param [Boolean] complete_only append complete (src and trg shapes in selection) only
3296
+ def append_assigned_connections(shape, selection, children_only: true, complete_only: false)
2870
3297
  # add connections assigned to copied topmost shapes and their children to the copy list
2871
3298
  lst_children = shape.get_child_shapes(ANY, RECURSIVE)
2872
-
3299
+
2873
3300
  # get connections assigned to the parent shape
2874
- lst_connections = @diagram.get_assigned_connections(shape, LineShape, Shape::CONNECTMODE::BOTH) unless childrenonly
3301
+ lst_connections = @diagram.get_assigned_connections(shape, LineShape, Shape::CONNECTMODE::BOTH) unless children_only
2875
3302
  lst_connections ||= []
2876
- # get connections assigned to its child shape
3303
+ # get connections assigned to its child shape(s)
2877
3304
  lst_children.each do |child|
2878
- # get connections assigned to the child shape
3305
+ # get connections assigned to the child shape(s)
2879
3306
  @diagram.get_assigned_connections(child, LineShape, Shape::CONNECTMODE::BOTH, lst_connections)
2880
3307
  end
2881
-
3308
+
2882
3309
  # insert connections to the copy list
2883
3310
  lst_connections.each do |line|
2884
- selection << line unless selection.include?(line)
3311
+ selection << line unless selection.include?(line) ||
3312
+ (complete_only &&
3313
+ !(selection.include?(line.get_src_shape) &&
3314
+ selection.include?(line.get_trg_shape)))
2885
3315
  end
2886
3316
  end
2887
3317
 
@@ -2897,7 +3327,7 @@ module Wx::SF
2897
3327
  end
2898
3328
  end
2899
3329
 
2900
- # Clear all temporary containers
3330
+ # Clear all temporary containers
2901
3331
  def clear_temporaries
2902
3332
  @current_shapes.clear
2903
3333
  @new_line_shape = nil
@@ -2909,19 +3339,42 @@ module Wx::SF
2909
3339
  # Assign give shape to parent at given location (if exists)
2910
3340
  # @param [Wx::SF::Shape] shape
2911
3341
  # @param [Wx::Point] parentpos
2912
- def reparent_shape(shape, parentpos)
3342
+ def reparent_dropped_shape(shape, parentpos)
2913
3343
  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
3344
 
2919
- # set new parent
3345
+ # set new parent if possible
2920
3346
  if shape.has_style?(Shape::STYLE::PARENT_CHANGE) && !shape.is_a?(LineShape)
3347
+ # is shape dropped into accepting shape?
3348
+
3349
+ # get all shapes at drop position in reversed z-order
3350
+ shapes_at_pos = get_shapes_at_position(parentpos).reverse
3351
+ # see if we can find a non-LineShape drop target
3352
+ parent_shape = shapes_at_pos.find do |s|
3353
+ # consider non-LineShapes that are unselected and not the dropped shape itself or one of it's (grand-)children
3354
+ !s.is_a?(Wx::SF::LineShape) && !s.selected? && shape != s && !shape.include_child_shape?(s)
3355
+ end
3356
+ # if none found consider line shapes
3357
+ parent_shape = shapes_at_pos.find do |s|
3358
+ # consider LineShapes that are unselected and not the dropped shape itself or one of it's (grand-)children
3359
+ s.is_a?(Wx::SF::LineShape) && !s.selected? && shape != s && !shape.include_child_shape?(s)
3360
+ end unless parent_shape
3361
+ # In case the matching shape does not accept the dropped child and has style PROPAGATE_DROPPING
3362
+ # see if this shape has a parent that does also matches the position and DOES accept the child.
3363
+ # This allows dropping shapes onto child shapes inside a (container) shapes like
3364
+ # grids and/or boxes.
3365
+ while parent_shape && !parent_shape.is_child_accepted(shape.class)
3366
+ parent_shape = parent_shape.has_style?(Shape::STYLE::PROPAGATE_DROPPING) ? parent_shape.parent_shape : nil
3367
+ parent_shape = nil if parent_shape && !parent_shape.get_bounding_box.contains?(parentpos)
3368
+ end
3369
+ # parent_shape = nil if parent_shape && !parent_shape.is_child_accepted(shape.class)
3370
+
2921
3371
  prev_parent = shape.get_parent_shape
2922
-
3372
+
2923
3373
  if parent_shape
2924
- if parent_shape.get_parent_shape != shape
3374
+ # in rare cases (where childs are expanded to fill a parent and have PROPAGATE_SELECTION)
3375
+ # the matched drop parent may actually a child of the shape being dropped
3376
+ # guard against that (since that would lead to illegal circular references)
3377
+ if parent_shape != shape && !shape.include_child_shape?(parent_shape, true)
2925
3378
  # update relative position to new parent
2926
3379
  apos = shape.get_absolute_position - parent_shape.get_absolute_position
2927
3380
  shape.set_relative_position(apos)
@@ -2938,9 +3391,9 @@ module Wx::SF
2938
3391
  @diagram.reparent_shape(shape, parent_shape)
2939
3392
  end
2940
3393
  end
2941
-
3394
+
2942
3395
  prev_parent.update if prev_parent
2943
- parent_shape.update if parent_shape
3396
+ parent_shape.update if parent_shape && parent_shape != prev_parent
2944
3397
  end
2945
3398
  end
2946
3399
 
@@ -3003,7 +3456,7 @@ module Wx::SF
3003
3456
  else
3004
3457
  @working_mode = MODE::READY
3005
3458
  end
3006
-
3459
+
3007
3460
  event.skip
3008
3461
  end
3009
3462
 
@@ -3011,16 +3464,16 @@ module Wx::SF
3011
3464
  # @param [Wx::MouseEvent] event Mouse event
3012
3465
  def _on_enter_window(event)
3013
3466
  @prev_mouse_pos = event.get_position
3014
-
3467
+
3015
3468
  lpos = dp2lp(event.get_position)
3016
-
3469
+
3017
3470
  case @working_mode
3018
3471
  when MODE::MULTISELECTION
3019
3472
  unless event.left_is_down
3020
3473
  update_multiedit_size
3021
3474
  @shp_multi_edit.show(false)
3022
3475
  @working_mode = MODE::READY
3023
-
3476
+
3024
3477
  invalidate_visible_rect
3025
3478
  end
3026
3479
 
@@ -3030,13 +3483,13 @@ module Wx::SF
3030
3483
  if @selected_handle.get_parent_shape.is_a?(LineShape)
3031
3484
  @selected_handle.get_parent_shape.send(:set_line_mode, LineShape::LINEMODE::READY)
3032
3485
  end
3033
-
3486
+
3034
3487
  @selected_handle.send(:_on_end_drag, lpos)
3035
-
3488
+
3036
3489
  save_canvas_state
3037
3490
  @working_mode = MODE::READY
3038
3491
  @selected_handle = nil
3039
-
3492
+
3040
3493
  invalidate_visible_rect
3041
3494
  end
3042
3495
  end
@@ -3045,10 +3498,10 @@ module Wx::SF
3045
3498
  unless event.left_is_down
3046
3499
  if @selected_handle
3047
3500
  @selected_handle.send(:_on_end_drag, lpos)
3048
-
3501
+
3049
3502
  save_canvas_state
3050
3503
  @working_mode = MODE::READY
3051
-
3504
+
3052
3505
  invalidate_visible_rect
3053
3506
  end
3054
3507
  end
@@ -3056,26 +3509,26 @@ module Wx::SF
3056
3509
  when MODE::SHAPEMOVE
3057
3510
  unless event.left_is_down
3058
3511
  lst_selection = get_selected_shapes
3059
-
3512
+
3060
3513
  move_shapes_from_negatives
3061
3514
  update_virtual_size
3062
-
3515
+
3063
3516
  if lst_selection.size > 1
3064
3517
  update_multiedit_size
3065
3518
  @shp_multi_edit.show(true)
3066
3519
  @shp_multi_edit.show_handles(true)
3067
3520
  end
3068
-
3521
+
3069
3522
  lst_selection.each { |shape| shape.send(:_on_end_drag, lpos) }
3070
3523
 
3071
3524
  @working_mode = MODE::READY
3072
-
3525
+
3073
3526
  invalidate_visible_rect
3074
3527
  end
3075
3528
  end
3076
-
3529
+
3077
3530
  refresh_invalidated_rect
3078
-
3531
+
3079
3532
  event.skip
3080
3533
  end
3081
3534
 
@@ -3086,9 +3539,9 @@ module Wx::SF
3086
3539
 
3087
3540
  event.skip
3088
3541
  end
3089
-
3542
+
3090
3543
  # original private event handlers
3091
-
3544
+
3092
3545
  # Original private event handler called when the canvas is clicked by
3093
3546
  # left mouse button. The handler calls user-overridable event handler function
3094
3547
  # and skips the event for next possible processing.
@@ -3204,7 +3657,7 @@ module Wx::SF
3204
3657
  # @see Wx::SF::CanvasDropTarget
3205
3658
  def _on_drop(x, y, deflt, data)
3206
3659
  if data && Wx::SF::ShapeDataObject === data
3207
- lst_new_content = Wx::SF::Serializable.deserialize(data.get_data_here)
3660
+ lst_new_content = data.get_as_shapes
3208
3661
  if lst_new_content && !lst_new_content.empty?
3209
3662
  lst_parents_to_update = []
3210
3663
  lpos = dp2lp(Wx::Point.new(x, y))
@@ -3217,12 +3670,20 @@ module Wx::SF
3217
3670
  end
3218
3671
 
3219
3672
  parent = @diagram.get_shape_at_position(lpos, 1, SEARCHMODE::UNSELECTED)
3673
+ # In case the located shape does not accept ANY children and has style PROPAGATE_DROPPING
3674
+ # see if this shape has a parent that does also match the position and DOES accept children.
3675
+ # This allows dropping shapes onto child shapes inside a (container) shapes like
3676
+ # grids and/or boxes.
3677
+ while parent&.does_not_accept_children?
3678
+ parent = parent.has_style?(Shape::STYLE::PROPAGATE_DROPPING) ? parent.parent_shape : nil
3679
+ parent = nil if parent && !parent.get_bounding_box.contains?(lpos)
3680
+ end
3220
3681
 
3221
3682
  # add each shape to diagram keeping only those that are accepted
3222
3683
  lst_new_content.select! do |shape|
3223
3684
  shape.move_by(dx, dy)
3224
3685
  # do not reparent connection lines
3225
- rc = if shape.is_a?(LineShape) && !shape.stand_alone?
3686
+ rc = if (shape.is_a?(LineShape) && !shape.stand_alone?) || parent.nil?
3226
3687
  @diagram.add_shape(shape,
3227
3688
  nil,
3228
3689
  lp2dp(shape.get_absolute_position.to_point),
@@ -3239,7 +3700,7 @@ module Wx::SF
3239
3700
  end
3240
3701
 
3241
3702
  # verify newly added shapes (may remove shapes from list)
3242
- @diagram.send(:check_new_shapes, lst_new_content)
3703
+ @diagram.send(:on_import, lst_new_content)
3243
3704
 
3244
3705
  update_virtual_size # update for new shapes
3245
3706
 
@@ -3266,7 +3727,7 @@ module Wx::SF
3266
3727
  end
3267
3728
  end
3268
3729
  end
3269
-
3730
+
3270
3731
  end
3271
3732
 
3272
3733
  def _notify_canvas_change(change, *args)