ifmapper 0.6 → 0.7

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.
@@ -0,0 +1,15 @@
1
+
2
+ class FXDCPrint
3
+ alias :old_font= :font=
4
+ alias :old_font :font
5
+
6
+ def font
7
+ return @f
8
+ end
9
+
10
+ def font=(x)
11
+ old_font=(x)
12
+ @f = x
13
+ end
14
+
15
+ end
@@ -41,13 +41,16 @@ class FXMap < Map
41
41
  @@cursor_w = nil
42
42
  @@cursor_nw = nil
43
43
 
44
-
44
+
45
45
  def _changed
46
46
  create_pathmap
47
47
  update_title
48
48
  draw
49
49
  end
50
50
 
51
+ #
52
+ # Jump to a certain section #
53
+ #
51
54
  def section=(x)
52
55
  super
53
56
  @complexConnection = false
@@ -57,28 +60,43 @@ class FXMap < Map
57
60
  end
58
61
  end
59
62
 
60
-
63
+ #
64
+ # Go to previous section (if any)
65
+ #
61
66
  def previous_section
62
67
  self.section = @section - 1
63
68
  _changed
64
69
  end
65
70
 
71
+ #
72
+ # Go to next section (if any)
73
+ #
66
74
  def next_section
67
75
  self.section = @section + 1
68
76
  _changed
69
77
  end
70
78
 
79
+ #
80
+ # Map has been modified. Update also pathmap.
81
+ #
71
82
  def modified=(x)
72
83
  @modified = true
73
84
  _changed
74
85
  end
75
86
 
87
+ #
88
+ # Popup the section properties to allow renaming it
89
+ #
76
90
  def rename_section
77
91
  @sections[@section].properties(self)
78
92
  modified = true
79
93
  end
80
94
 
95
+ #
96
+ # Delete current section from map
97
+ #
81
98
  def delete_section
99
+ return navigation_warning if @navigation
82
100
  w = FXWarningBox.new(@window, "Are you sure you want to delete this section?")
83
101
  return if w.execute == 0
84
102
 
@@ -86,7 +104,11 @@ class FXMap < Map
86
104
  modified = true
87
105
  end
88
106
 
89
- def new_section
107
+ #
108
+ # Add a new section to map and make it current
109
+ #
110
+ def new_section
111
+ return navigation_warning if @navigation
90
112
  @sections.push( FXSection.new )
91
113
  @section = @sections.size - 1
92
114
  end
@@ -113,6 +135,48 @@ class FXMap < Map
113
135
  puts m
114
136
  end
115
137
 
138
+ #
139
+ # Determine whether a pathmap area from x,y to x+w,y+h is free
140
+ # of rooms
141
+ #
142
+ def _free_area?(x, y, w, h)
143
+ x.upto(x+w) { |xx|
144
+ y.upto(x+h) { |yy|
145
+ return false if @pmap[xx][yy].kind_of?(Room)
146
+ }
147
+ }
148
+ return true
149
+ end
150
+
151
+ #
152
+ # Given a list of rooms, find an area in the map where we could
153
+ # place them. If found, return x/y offset so rooms can be moved
154
+ # there. This is used for pasting rooms.
155
+ #
156
+ def find_empty_area(rooms)
157
+ return nil if rooms.empty?
158
+ minx = maxx = rooms[0].x
159
+ miny = maxy = rooms[0].y
160
+ rooms.each { |r|
161
+ minx = r.x if r.x < minx
162
+ maxx = r.x if r.x > maxx
163
+ miny = r.y if r.y < miny
164
+ maxy = r.y if r.y > maxy
165
+ }
166
+ w = maxx - minx
167
+ h = maxy - miny
168
+
169
+ # Find an area in pathmap that has w x h empty rooms
170
+ 0.upto(@width-1-w) { |x|
171
+ 0.upto(@height-1-h) { |y|
172
+ if _free_area?(x, y, w, h)
173
+ return [x - minx, y - miny]
174
+ end
175
+ }
176
+ }
177
+ return nil
178
+ end
179
+
116
180
  #
117
181
  # Reinitialize the pathmap to an empty matrix
118
182
  #
@@ -166,8 +230,6 @@ class FXMap < Map
166
230
  a = c.roomA
167
231
  b = c.roomB
168
232
 
169
- # First, check the neighboring starting/ending nodes in grid
170
- # are empty. If not, we need to abort and remove path from list.
171
233
  dirA = a.exits.index(c)
172
234
  raise "connection not found #{c} at #{a}" unless dirA
173
235
 
@@ -231,7 +293,7 @@ class FXMap < Map
231
293
  # We succeeded. Get the path
232
294
  c.failed = false
233
295
  c.gpts = aStar.path
234
- # Put path in pathmap so subsequent paths will avoid crossing it.
296
+ # Put path in pathmap so subsequent paths will try to avoid crossing it.
235
297
  c.gpts.each { |p| @pmap[p[0]][p[1]] = c }
236
298
 
237
299
  # Okay, we have a valid path.
@@ -289,20 +351,14 @@ class FXMap < Map
289
351
  end
290
352
 
291
353
  #
292
- # Function used to add a new room. x and y are absolute pixel positions
293
- # in canvas.
354
+ # Create a new room for the current section
294
355
  #
295
- def new_xy_room(x, y)
356
+ def new_room(x, y)
296
357
  @modified = true
297
- x = x / WW
298
- y = y / HH
299
358
  r = @sections[@section].new_room(x, y)
300
359
  @pmap[x][y] = r
301
360
 
302
- buf = FXMapperWindow::copy_buffer()
303
- r.copy(buf) if buf
304
361
  r.selected = true
305
-
306
362
 
307
363
  if @options['Edit on Creation']
308
364
  if not r.modal_properties(self)
@@ -311,10 +367,19 @@ class FXMap < Map
311
367
  return nil
312
368
  end
313
369
  end
314
-
315
370
  return r
316
371
  end
317
372
 
373
+ #
374
+ # Function used to add a new room. x and y are absolute pixel positions
375
+ # in canvas.
376
+ #
377
+ def new_xy_room(x, y)
378
+ x = x / WW
379
+ y = y / HH
380
+ return new_room(x, y)
381
+ end
382
+
318
383
 
319
384
  # Given a canvas (mouse) x/y position, return:
320
385
  #
@@ -360,14 +425,19 @@ class FXMap < Map
360
425
  next if not b # Complex connection in progress
361
426
 
362
427
  if c.gpts.size > 0
363
- [a, b].each { |r|
428
+ 2.times { |t|
429
+ if t == 0
430
+ r = a
431
+ dir = r.exits.index(c)
432
+ else
433
+ r = b
434
+ dir = r.exits.rindex(c)
435
+ end
364
436
  next if not r
365
- dir = r.exits.index(c)
366
437
  x1, y1 = r.corner(c, 1, dir)
367
438
  v = FXRoom::DIR_TO_VECTOR[dir]
368
439
  x2 = x1 + v[0] * WS
369
440
  y2 = y1 + v[1] * HS
370
-
371
441
  if x1 == x2
372
442
  x1 -= W / 2
373
443
  x2 += W / 2
@@ -376,6 +446,9 @@ class FXMap < Map
376
446
  y1 -= H / 2
377
447
  y2 += H / 2
378
448
  end
449
+ x1, x2 = x2, x1 if x2 < x1
450
+ y1, y2 = y2, y1 if y2 < y1
451
+
379
452
  if x >= x1 and x <= x2 and
380
453
  y >= y1 and y < y2
381
454
  return c
@@ -401,11 +474,7 @@ class FXMap < Map
401
474
  end
402
475
  }
403
476
 
404
- # Then, get "rooms" being connected to check if we get
405
- # a complex connection instead.
406
- roomA, roomB, a, b = quadrant_to_rooms( exitA, x, y )
407
- return roomA if roomA.kind_of?(Connection)
408
- return roomB if roomB.kind_of?(Connection)
477
+ return nil
409
478
  end
410
479
 
411
480
  return nil
@@ -547,7 +616,8 @@ class FXMap < Map
547
616
  roomB = to_room(x2, y2)
548
617
  # Oops, user tried to create rooms where we already
549
618
  # have a complex connection. Don't create anything, then.
550
- if roomA.kind_of?(Connection) or roomB.kind_of?(Connection)
619
+ if roomA.kind_of?(Connection) or
620
+ (roomB.kind_of?(Connection) and not @complexConnection)
551
621
  return [roomA, roomB, nil, nil]
552
622
  end
553
623
 
@@ -563,8 +633,13 @@ class FXMap < Map
563
633
  raise "not a connection"
564
634
  end
565
635
  roomA, roomB, a, b = quadrant_to_rooms( exitA, x, y )
566
- return unless a and roomA # User click outside map or not near room
567
-
636
+ unless a and roomA and roomA[exitA] == nil
637
+ return
638
+ end
639
+ _new_complex_connection(roomA, exitA)
640
+ end
641
+
642
+ def _new_complex_connection(roomA, exitA)
568
643
  if @complexConnection.kind_of?(TrueClass)
569
644
  @complexConnection = [roomA, exitA]
570
645
  c = new_connection( roomA, exitA, nil )
@@ -662,6 +737,9 @@ class FXMap < Map
662
737
  s.normalText = s.text = msg
663
738
  end
664
739
 
740
+ #
741
+ # Based on x,y coordinate, switch mouse icon shape
742
+ #
665
743
  def cursor_switch(x, y)
666
744
  if not @options['Use Room Cursor']
667
745
  @canvas.defaultCursor = @@cursor_arrow
@@ -726,42 +804,18 @@ class FXMap < Map
726
804
  # to cursor position
727
805
  #
728
806
  def mousewheel_cb(sender, sel, event)
729
- pos = @scrollwindow.position
730
- pos = [ pos[0] / @zoom, pos[1] / @zoom ]
731
- x = (event.last_x / @zoom).to_i
732
- y = (event.last_y / @zoom).to_i
807
+ # x = event.last_x / @zoom
808
+ # y = event.last_y / @zoom
733
809
  case event.code
734
810
  when -120
735
811
  zoom_out
736
- # pos[0] -= x / 2
737
- # pos[1] -= y / 2
738
- pos = [ pos[0] * @zoom, pos[1] * @zoom ]
739
- @scrollwindow.setPosition(pos[0], pos[1])
740
812
  when 120
741
813
  zoom_in
742
-
743
- #pos[0] = -x # * @zoom * 2
744
- #pos[1] = -y # * @zoom * 2
745
-
746
- # pos = [ pos[0], pos[1] ]
747
- # pos = [ pos[0] * @zoom, pos[1] * @zoom ]
748
- @scrollwindow.setPosition(pos[0], pos[1])
749
814
  end
750
- # x = (x * @zoom).to_i
751
- # y = (y * @zoom).to_i
752
- # if x > @canvas.width
753
- # x = @canvas.width
754
- # end
755
- # if y > @canvas.height
756
- # y = @canvas.height
757
- # end
758
-
759
- # pos = [ pos[0] - x, pos[1] - y ]
760
- # p "SET: #{pos.join(',')}"
761
-
762
- # # @scrollwindow.setPosition(pos[0], pos[1])
763
- # p @scrollwindow.position
764
- draw
815
+ # x *= @zoom
816
+ # y *= @zoom
817
+ # center_view_on_xy(x.to_i, y.to_i)
818
+ # draw
765
819
  end
766
820
 
767
821
  #
@@ -795,10 +849,28 @@ class FXMap < Map
795
849
  if r.x >= x1 and r.x <= x2 and
796
850
  r.y >= y1 and r.y <= y2
797
851
  r.selected = true
852
+ r.update_properties(self)
798
853
  else
799
854
  r.selected = false
800
855
  end
801
856
  }
857
+
858
+ @sections[@section].connections.each { |c|
859
+ next if not c.roomB
860
+ a = c.roomA
861
+ b = c.roomB
862
+
863
+ if (a.x >= x1 and a.x <= x2 and
864
+ a.y >= y1 and a.y <= y2) or
865
+ (b.x >= x1 and b.x <= x2 and
866
+ b.y >= y1 and b.y <= y2)
867
+ c.selected = true
868
+ c.update_properties(self)
869
+ else
870
+ c.selected = false
871
+ end
872
+ }
873
+
802
874
  draw
803
875
  dc = FXDCWindow.new(@canvas)
804
876
  dc.drawRectangle(x, y, w * @zoom, h * @zoom)
@@ -842,6 +914,52 @@ class FXMap < Map
842
914
  end
843
915
  end
844
916
 
917
+ #
918
+ # Given a room, center scrollwindow so room is visible
919
+ #
920
+ def center_view_on_room(r)
921
+ xx = (r.xx + W / 2) * @zoom
922
+ yy = (r.yy + H / 2) * @zoom
923
+ center_view_on_xy(xx, yy)
924
+ end
925
+
926
+ #
927
+ # Given an x and y coordinate for the canvas, center on it
928
+ #
929
+ def center_view_on_xy(xx, yy)
930
+ cw = @scrollwindow.getContentWidth
931
+ ch = @scrollwindow.getContentHeight
932
+ w = @scrollwindow.getViewportWidth
933
+ h = @scrollwindow.getViewportHeight
934
+
935
+ xx -= w / 2
936
+ yy -= h / 2
937
+ maxx = cw - w / 2
938
+ maxy = ch - h / 2
939
+ if xx > maxx
940
+ xx = maxx
941
+ elsif xx < 0
942
+ xx = 0
943
+ end
944
+ if yy > maxy
945
+ yy = maxy
946
+ elsif yy < 0
947
+ yy = 0
948
+ end
949
+
950
+ @scrollwindow.setPosition( -xx, -yy )
951
+ end
952
+
953
+ #
954
+ # Return current selection as an array of rooms and an array of
955
+ # connections
956
+ #
957
+ def get_selection
958
+ rooms = @sections[@section].rooms.find_all { |r| r.selected }
959
+ conns = @sections[@section].connections.find_all { |r| r.selected }
960
+ return rooms, conns
961
+ end
962
+
845
963
  #
846
964
  # Clear rooms/connections selected
847
965
  #
@@ -850,6 +968,66 @@ class FXMap < Map
850
968
  @sections[@section].connections.each { |r| r.selected = false }
851
969
  end
852
970
 
971
+ #
972
+ # Add a proper link submenu option for an exit
973
+ #
974
+ def rmb_link_menu(submenu, c, room, idx, old_idx)
975
+ return if room[idx] == c
976
+ dir = Room::DIRECTIONS[idx]
977
+ dir = dir.upcase
978
+ if room[idx]
979
+ cmd = FXMenuCommand.new(submenu, "Switch with link in #{dir} Exit")
980
+ cmd.connect(SEL_COMMAND) {
981
+ c2 = room[idx]
982
+ room[old_idx] = c2
983
+ room[idx] = c
984
+ create_pathmap
985
+ draw
986
+ }
987
+ else
988
+ cmd = FXMenuCommand.new(submenu, "Move link to #{dir} Exit")
989
+ cmd.connect(SEL_COMMAND) {
990
+ room[old_idx] = nil
991
+ room[idx] = c
992
+ create_pathmap
993
+ draw
994
+ }
995
+ end
996
+ end
997
+
998
+ #
999
+ # Handle right mouse button click
1000
+ #
1001
+ def rmb_click_cb(sender, sel, event)
1002
+ rooms, links = get_selection
1003
+
1004
+ menu = nil
1005
+ if not links.empty? and links.size == 1
1006
+ c = links[0]
1007
+ a = c.roomA
1008
+ b = c.roomB
1009
+ menu = FXMenuPane.new(@window)
1010
+ submenu = FXMenuPane.new(@window)
1011
+ old_idx = a.exits.index(c)
1012
+ 0.upto(7) { |idx|
1013
+ rmb_link_menu( submenu, c, a, idx, old_idx )
1014
+ }
1015
+ FXMenuCascade.new(menu, a.name, nil, submenu)
1016
+ submenu = FXMenuPane.new(@window)
1017
+ old_idx = b.exits.rindex(c)
1018
+ 0.upto(7) { |idx|
1019
+ rmb_link_menu( submenu, c, b, idx, old_idx )
1020
+ }
1021
+ FXMenuCascade.new(menu, b.name, nil, submenu)
1022
+ end
1023
+ if menu
1024
+ menu.create
1025
+ menu.popup(nil, event.root_x, event.root_y)
1026
+ @window.getApp.runModalWhileShown(menu)
1027
+ end
1028
+ end
1029
+
1030
+
853
1031
  #
854
1032
  # Handle left mouse button click
855
1033
  #
@@ -911,9 +1089,11 @@ class FXMap < Map
911
1089
  if not roomA
912
1090
  new_xy_connection( x, y )
913
1091
  else
914
- @complexConnection = [roomA, exitA]
915
- new_connection( roomA, exitA, nil )
916
- new_complex_connection( x, y )
1092
+ if a and roomA
1093
+ @complexConnection = [roomA, exitA]
1094
+ new_connection( roomA, exitA, nil )
1095
+ _new_complex_connection( roomA, exitA )
1096
+ end
917
1097
  end
918
1098
  else
919
1099
  new_xy_connection( x, y )
@@ -932,6 +1112,7 @@ class FXMap < Map
932
1112
  clear_selection
933
1113
  end
934
1114
  # Select the stuff
1115
+ selection.update_properties(self)
935
1116
  selection.selected = true
936
1117
  end
937
1118
  end
@@ -1057,6 +1238,7 @@ class FXMap < Map
1057
1238
  @canvas.connect(SEL_MOTION, method(:motion_cb))
1058
1239
  @canvas.connect(SEL_MIDDLEBUTTONPRESS, method(:mmb_click_cb))
1059
1240
  @canvas.connect(SEL_MIDDLEBUTTONRELEASE, method(:mmb_release_cb))
1241
+ @canvas.connect(SEL_RIGHTBUTTONPRESS, method(:rmb_click_cb))
1060
1242
  @canvas.connect(SEL_KEYPRESS, method(:keypress_cb))
1061
1243
  end
1062
1244
 
@@ -1167,6 +1349,9 @@ class FXMap < Map
1167
1349
  @modified = true
1168
1350
  end
1169
1351
 
1352
+ #
1353
+ # Give user some warning if he tries to modify a read-only mode.
1354
+ #
1170
1355
  def navigation_warning
1171
1356
  w = FXWarningBox.new(@window, "Map is in Read Only Mode. Go to Map Information to change this.")
1172
1357
  w.execute
@@ -1446,6 +1631,32 @@ class FXMap < Map
1446
1631
  # Print map
1447
1632
  #
1448
1633
  def print(printer)
1634
+ # dc = FXDCPrint.new(@window.getApp)
1635
+ require 'IFMapper/FXDCPostscript'
1636
+ oldzoom = @zoom
1637
+ oldsection = @section
1638
+ begin
1639
+ dc = FXDCPostscript.new(@window.getApp)
1640
+ xmax = @width * WW
1641
+ ymax = @height * HH
1642
+ @zoom = 1.0
1643
+ dc.setContentRange(0, 0, xmax, ymax)
1644
+ dc.beginPrint(printer) {
1645
+ 0.upto(@sections.size-1) { |p|
1646
+ self.section = p
1647
+ clear_selection
1648
+ dc.beginPage(p+1) {
1649
+ dc.lineCap = CAP_ROUND
1650
+ draw_connections(dc)
1651
+ draw_rooms(dc)
1652
+ }
1653
+ }
1654
+ }
1655
+ rescue => e
1656
+ status "#{e}"
1657
+ end
1658
+ self.section = oldsection
1659
+ @zoom = oldzoom
1449
1660
  end
1450
1661
 
1451
1662
  #
@@ -1459,7 +1670,7 @@ class FXMap < Map
1459
1670
  dc = FXDCWindow.new(@image)
1460
1671
  dc.setClipRectangle( -pos[0]-5, -pos[1]-5, w, h)
1461
1672
  dc.font = @font
1462
- # dc.lineCap = CAP_ROUND
1673
+ #dc.lineCap = CAP_ROUND
1463
1674
  draw_background(dc, event)
1464
1675
  draw_grid(dc, event) if @options['Grid Boxes']
1465
1676
  if @options['Grid Straight Connections']