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

Sign up to get free protection for your applications and to get access to all the features.
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)