ifmapper 0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. data/HISTORY.txt +2 -0
  2. data/IFMapper.gemspec +21 -0
  3. data/IFMapper.rb +27 -0
  4. data/icons/copy.png +0 -0
  5. data/icons/cut.png +0 -0
  6. data/icons/filenew.png +0 -0
  7. data/icons/fileopen.png +0 -0
  8. data/icons/filesave.png +0 -0
  9. data/icons/filesaveas.png +0 -0
  10. data/icons/help.png +0 -0
  11. data/icons/kill.png +0 -0
  12. data/icons/nextpage.png +0 -0
  13. data/icons/paste.png +0 -0
  14. data/icons/prevpage.png +0 -0
  15. data/icons/printicon.png +0 -0
  16. data/icons/redo.png +0 -0
  17. data/icons/saveas.png +0 -0
  18. data/icons/undo.png +0 -0
  19. data/icons/winapp.png +0 -0
  20. data/icons/zoom.png +0 -0
  21. data/lib/IFMapper/Connection.rb +63 -0
  22. data/lib/IFMapper/FXAboutDialogBox.rb +32 -0
  23. data/lib/IFMapper/FXConnection.rb +283 -0
  24. data/lib/IFMapper/FXConnectionDialogBox.rb +126 -0
  25. data/lib/IFMapper/FXMap.rb +1614 -0
  26. data/lib/IFMapper/FXMapDialogBox.rb +51 -0
  27. data/lib/IFMapper/FXMapFileDialog.rb +29 -0
  28. data/lib/IFMapper/FXMapperSettings.rb +45 -0
  29. data/lib/IFMapper/FXMapperWindow.rb +1051 -0
  30. data/lib/IFMapper/FXPage.rb +24 -0
  31. data/lib/IFMapper/FXPageDialogBox.rb +38 -0
  32. data/lib/IFMapper/FXRoom.rb +218 -0
  33. data/lib/IFMapper/FXRoomDialogBox.rb +119 -0
  34. data/lib/IFMapper/FXSearchDialogBox.rb +51 -0
  35. data/lib/IFMapper/FXSpline.rb +54 -0
  36. data/lib/IFMapper/FXWarningBox.rb +45 -0
  37. data/lib/IFMapper/IFMReader.rb +613 -0
  38. data/lib/IFMapper/Map.rb +110 -0
  39. data/lib/IFMapper/PDFMapExporter.rb +315 -0
  40. data/lib/IFMapper/Page.rb +158 -0
  41. data/lib/IFMapper/Room.rb +104 -0
  42. data/maps/Bureaucracy.ifm +75 -0
  43. data/maps/Hollywood_Hijinx.ifm +149 -0
  44. data/maps/Jigsaw.ifm +806 -0
  45. data/maps/LGOP.ifm +705 -0
  46. data/maps/Mercy.ifm +76 -0
  47. data/maps/Planetfall.ifm +186 -0
  48. data/maps/Plundered_Hearts.ifm +251 -0
  49. data/maps/Ralph.ifm +50 -0
  50. data/maps/Robots_of_Dawn.ifm +224 -0
  51. data/maps/Seastalker.ifm +149 -0
  52. data/maps/Sherlock.ifm +209 -0
  53. data/maps/SoFar.ifm +72 -0
  54. data/maps/Starcross.ifm +170 -0
  55. data/maps/Suspended.ifm +82 -0
  56. data/maps/Wishbringer.ifm +277 -0
  57. data/maps/Wishbringer2.ifm +246 -0
  58. data/maps/Zork1.ifm +410 -0
  59. data/maps/Zork2.ifm +150 -0
  60. data/maps/Zork3.ifm +136 -0
  61. data/maps/Zork_Zero.ifm +557 -0
  62. data/maps/anchor.ifm +645 -0
  63. data/maps/atrox.ifm +134 -0
  64. data/maps/awaken.ifm +116 -0
  65. data/maps/babel.ifm +279 -0
  66. data/maps/bse.ifm +150 -0
  67. data/maps/change.ifm +128 -0
  68. data/maps/curses.ifm +307 -0
  69. data/maps/curves.ifm +529 -0
  70. data/maps/edifice.ifm +158 -0
  71. data/maps/frozen.ifm +126 -0
  72. data/maps/glow.ifm +101 -0
  73. data/maps/library.ifm +93 -0
  74. data/maps/mindelec.ifm +89 -0
  75. data/maps/minster.ifm +234 -0
  76. data/maps/muse.ifm +154 -0
  77. data/maps/paperchase.ifm +110 -0
  78. data/maps/space_st.ifm +104 -0
  79. data/maps/stationfall.ifm +320 -0
  80. data/maps/theatre.ifm +182 -0
  81. data/maps/toonesia.ifm +54 -0
  82. data/maps/tortoise.ifm +72 -0
  83. data/maps/vgame.ifm +219 -0
  84. data/maps/weather.ifm +98 -0
  85. data/maps/windhall.ifm +154 -0
  86. data/maps/zebulon.ifm +68 -0
  87. metadata +144 -0
@@ -0,0 +1,51 @@
1
+
2
+
3
+ class FXMapDialogBox < FXDialogBox
4
+
5
+ def copy_to()
6
+ @map.name = @name.text
7
+ @map.creator = @creator.text
8
+ @map.navigation = (@read.checkState == 1)
9
+ @map.update_title
10
+ end
11
+
12
+ def copy_from(map)
13
+ @name.text = map.name
14
+ @creator.text = map.creator
15
+ @read.checkState = map.navigation
16
+ @map = map
17
+ end
18
+
19
+ def initialize(parent)
20
+ decor = DECOR_TITLE|DECOR_BORDER|DECOR_CLOSE
21
+
22
+ super( parent, "Map Information", decor, 40, 40, 0, 0 )
23
+ mainFrame = FXVerticalFrame.new(self,
24
+ FRAME_SUNKEN|FRAME_THICK|
25
+ LAYOUT_FILL_X|LAYOUT_FILL_Y)
26
+
27
+ frame = FXHorizontalFrame.new(mainFrame, LAYOUT_SIDE_TOP|LAYOUT_FILL_X)
28
+
29
+ FXLabel.new(frame, "Name: ", nil, 0, LAYOUT_FILL_X)
30
+ @name = FXTextField.new(frame, 40, nil, 0, LAYOUT_FILL_ROW)
31
+
32
+ frame = FXVerticalFrame.new(mainFrame,
33
+ LAYOUT_SIDE_TOP|LAYOUT_FILL_X|LAYOUT_FILL_Y)
34
+
35
+ FXLabel.new(frame, "Creator: ", nil, 0, LAYOUT_FILL_X)
36
+ @creator = FXTextField.new(frame, 40, nil, 0, LAYOUT_FILL_ROW)
37
+
38
+ @read = FXCheckButton.new(frame, "Read Only Map", nil, 0,
39
+ ICON_BEFORE_TEXT|LAYOUT_LEFT|
40
+ LAYOUT_SIDE_RIGHT)
41
+
42
+ @name.connect(SEL_CHANGED) { copy_to() }
43
+ @creator.connect(SEL_CHANGED) { copy_to() }
44
+ @read.connect(SEL_COMMAND) { copy_to() }
45
+
46
+ @parent = parent
47
+
48
+ # We need to create the dialog box first, so we can use select text.
49
+ create
50
+ end
51
+ end
@@ -0,0 +1,29 @@
1
+
2
+
3
+ #
4
+ # A simple class to show a filerequester that remembers the
5
+ # last directory visited.
6
+ #
7
+ class FXMapFileDialog < FXFileDialog
8
+ @@last_path = nil
9
+
10
+ KNOWN_EXTENSIONS = [
11
+ "Map Files (*.map,*.ifm)",
12
+ "All Files (*)",
13
+ ]
14
+
15
+
16
+ def initialize(parent, title, patterns = KNOWN_EXTENSIONS)
17
+ opts = 0
18
+ if RUBY_PLATFORM =~ /mswin/
19
+ opts |= FILEMATCH_NOESCAPE
20
+ end
21
+ super(parent, title, opts)
22
+ setPatternList(patterns)
23
+ self.directory = @@last_path if @@last_path
24
+ if execute != 0
25
+ @@last_path = filename.sub(/[\\\/][^\\\/]+$/, '')
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,45 @@
1
+
2
+ #
3
+ # This class contains the applicatio and map's settings
4
+ #
5
+ class FXMapperSettings < Hash
6
+ def write
7
+ file = File.expand_path('~/.ifmapper')
8
+ f = File.open(file, 'w')
9
+ f.puts YAML.dump(self)
10
+ f.close
11
+ end
12
+
13
+ def initialize
14
+ f = File.expand_path('~/.ifmapper')
15
+ begin
16
+ self.replace( YAML.load_file(f) )
17
+ rescue
18
+ self.replace( {
19
+ # Colors
20
+ 'BG Color' => 'dark grey',
21
+ 'Arrow Color' => 'black',
22
+ 'Box BG Color' => 'white',
23
+ 'Box Darkness Color' => 'grey',
24
+ 'Box Border Color' => 'black',
25
+ 'Box Number Color' => 'grey',
26
+
27
+ # Fonts
28
+ 'Font Text' => 'times new roman',
29
+ 'Font Objects' => 'times new roman',
30
+
31
+ # Creation options
32
+ 'Create on Connection' => true,
33
+ 'Edit on Creation' => false,
34
+ 'Automatic Connection' => true,
35
+
36
+ # Display options
37
+ 'Paths as Curves' => true,
38
+ 'Location Numbers' => true,
39
+ 'Grid Boxes' => true,
40
+ 'Grid Straight Connections' => true,
41
+ 'Grid Diagonal Connections' => false,
42
+ } )
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,1051 @@
1
+
2
+ begin
3
+ require 'fox12'
4
+ rescue => e
5
+ $stderr.puts "Please install the fox library v1.2 or later."
6
+ if $rubygems
7
+ $stderr.puts "You can usually do so if you do 'gem install fox'."
8
+ end
9
+ exit(1)
10
+ end
11
+
12
+ begin
13
+ require 'yaml'
14
+ rescue => e
15
+ $stderr.puts "Please install the yaml library."
16
+ $stderr.puts "Without it, preferences cannot be saved."
17
+ exit(1)
18
+ end
19
+
20
+
21
+ include Fox
22
+
23
+ require 'IFMapper/FXMap'
24
+ require 'IFMapper/FXMapperSettings'
25
+ require 'IFMapper/FXWarningBox'
26
+ require 'IFMapper/FXAboutDialogBox'
27
+ require 'IFMapper/FXSearchDialogBox'
28
+ require 'IFMapper/FXMapFileDialog'
29
+ require 'IFMapper/IFMReader'
30
+
31
+
32
+ class FXMapperWindow < FXMainWindow
33
+
34
+ PROGRAM_NAME = "Interactive Fiction Mapper"
35
+ VERSION = 0.5
36
+ AUTHOR = "Gonzalo Garramuno"
37
+ TITLE = "#{PROGRAM_NAME} v#{VERSION} - Written by #{AUTHOR}"
38
+
39
+ @@copy_buffer = nil
40
+ @@default_options = FXMapperSettings.new
41
+
42
+ def open_map(file)
43
+ tmp = nil
44
+ begin
45
+ f = File.open(file, 'rb')
46
+ tmp = Marshal.load(f)
47
+ f.close
48
+ tmp.filename = file
49
+ rescue => e
50
+ tmp = e
51
+ end
52
+ return tmp
53
+ end
54
+
55
+ def open_ifm(file, map)
56
+ IFMReader.new(file, map)
57
+ return map
58
+ end
59
+
60
+ def open_cb(sender, sel, ptr)
61
+ file = FXMapFileDialog.new(self, "Load New Map").filename
62
+ return if file == ''
63
+
64
+ # First, make sure we don't have it loaded already...
65
+ @maps.each { |m|
66
+ if m.filename == file
67
+ @mdiclient.setActiveChild(m.window)
68
+ return
69
+ end
70
+ }
71
+
72
+ # Then, check if we have a single and empty map.
73
+ # If so, we can just use that one to load the file in.
74
+ # If not, we need to create a new map
75
+ make_new_map = false
76
+ if @maps.size == 1
77
+ @maps[0].pages.each { |p|
78
+ if p.rooms.size != 0
79
+ make_new_map = true
80
+ break
81
+ end
82
+ }
83
+ else
84
+ make_new_map = true
85
+ end
86
+
87
+ if make_new_map
88
+ map = new_map
89
+ else
90
+ map = @maps[0]
91
+ end
92
+ status "Loading '#{file}'..."
93
+
94
+ tmp = nil
95
+ if file =~ /\.ifm$/i
96
+ tmp = open_ifm(file, map)
97
+ else
98
+ tmp = open_map(file)
99
+ end
100
+
101
+ if not tmp.kind_of?(Map) and not tmp.kind_of?(FXMap)
102
+ status "Could not load '#{file}'. Error: #{tmp}."
103
+ if map.close_cb
104
+ @maps.delete(map) if make_new_map
105
+ end
106
+ sleep 2
107
+ return
108
+ end
109
+
110
+ map.copy(tmp)
111
+ map.create_pathmap
112
+ map.update_title
113
+ map.window.create
114
+ update_page
115
+ status "Loaded '#{file}'."
116
+ end
117
+
118
+ def status(msg)
119
+ @statusbar.statusLine.text = msg
120
+ end
121
+
122
+ def current_map
123
+ window = @mdiclient.activeChild
124
+ return nil unless window
125
+
126
+ @maps.each { |m|
127
+ return m if m.window == window
128
+ }
129
+ return nil
130
+ end
131
+
132
+ def save_cb(sender, sel, ptr)
133
+ map = current_map
134
+ return unless map
135
+ map.save
136
+ end
137
+
138
+ def save_as_cb(sender, sel, ptr)
139
+ map = current_map
140
+ return unless map
141
+ map.save_as
142
+ end
143
+
144
+ def new_map_cb(*args)
145
+ m = new_map
146
+ m.window.create
147
+ end
148
+
149
+ def new_map
150
+ mapname = "Empty Map \##{@maps.size+1}"
151
+ @maps.push( FXMap.new(mapname, @mdiclient, @@default_options.dup,
152
+ @mdiicon, @mdimenu, MDI_NORMAL, 0, 0, 790, 500) )
153
+ @maps[-1].window.connect(SEL_PAINT) { |sender, sel, event|
154
+ map = current_map
155
+ map.draw
156
+ }
157
+ @maps[-1].window.connect(SEL_CLOSE) { |sender, sel, event|
158
+ map = current_map
159
+ if map.close_cb
160
+ @maps.delete(map)
161
+ end
162
+ }
163
+
164
+ # Make it active
165
+ @mdiclient.setActiveChild(@maps[-1].window)
166
+ return @maps[-1]
167
+ end
168
+
169
+ # Load the named PNG icon from a file
170
+ def load_icon(filename)
171
+ begin
172
+ filename = File.join("icons", filename) + ".png"
173
+ icon = nil
174
+ File.open(filename, "rb") { |f|
175
+ icon = FXPNGIcon.new(getApp(), f.read)
176
+ }
177
+ icon
178
+ rescue
179
+ raise RuntimeError, "Couldn't load icon: #{filename}"
180
+ end
181
+ end
182
+
183
+ def complex_connection_cb(sender, sel, msg)
184
+ map = current_map
185
+ return unless map
186
+ map.complex_connection
187
+ end
188
+
189
+ def delete_selected_cb(sender, sel, msg)
190
+ map = current_map
191
+ return unless map
192
+ map.delete_selected
193
+ end
194
+
195
+ #
196
+ # Export current map as a Postscript file
197
+ #
198
+ def ps_export
199
+ map = current_map
200
+ return unless map
201
+ raise "Postscript Exporting not yet supported."
202
+
203
+ begin
204
+ require "IFMapper/PSMapExporter"
205
+ rescue => e
206
+ # bring up fox warning dialog box
207
+ return
208
+ end
209
+
210
+ printer = printer_dialog
211
+ map.ps_export(printer) if printer
212
+ end
213
+
214
+
215
+ #
216
+ # Popup a printer dialog and return the settings or false if user cancels
217
+ #
218
+ def printer_dialog
219
+ map = current_map
220
+ dlg = FXPrintDialog.new(self, "Print Map #{map.name}")
221
+ # Set printer to always use Landscape printing
222
+ dlg.printer.flags |= PRINT_DEST_PAPER | PRINT_LANDSCAPE | PRINT_NOBOUNDS
223
+ return dlg.printer if dlg.execute != 0
224
+ return false
225
+ end
226
+
227
+ def print_locations_cb(sender, sel, msg)
228
+ map = current_map
229
+ return unless map
230
+
231
+ w = FXWarningBox.new( self, "Sorry, but printing is not yet implemented\n" +
232
+ "in this version.")
233
+ w.execute
234
+ return
235
+
236
+ printer = printer_dialog
237
+ map.print_locations( printer ) if printer
238
+ end
239
+
240
+ def ifm_export_cb(sender, sel, msg)
241
+ map = current_map
242
+ return unless map
243
+
244
+ w = FXWarningBox.new( self, "Sorry, but IFM exporting is not yet implemented\n" +
245
+ "in this version.")
246
+ w.execute
247
+ return
248
+ end
249
+
250
+ def ps_export_cb(sender, sel, msg)
251
+ map = current_map
252
+ return unless map
253
+
254
+ w = FXWarningBox.new( self, "Sorry, but Postscript exporting is not implemented\n" +
255
+ "in this version.")
256
+ w.execute
257
+ return
258
+ end
259
+
260
+ def pdf_export_cb(sender, sel, msg)
261
+ map = current_map
262
+ return unless map
263
+
264
+ begin
265
+ require 'IFMapper/PDFMapExporter'
266
+ rescue => e
267
+ w = FXWarningBox.new( self, "#{e}")
268
+ w.execute
269
+ return
270
+ end
271
+
272
+
273
+ d = FXMapFileDialog.new(self, "Save Acrobat PDF File",
274
+ [
275
+ "Acrobat PDF (*.pdf)"
276
+ ])
277
+ if d.filename != ''
278
+ map.pdf_export(d.filename)
279
+ end
280
+ end
281
+
282
+ def print_cb(sender, sel, msg)
283
+ map = current_map
284
+ return unless map
285
+
286
+ w = FXWarningBox.new( self, "Sorry, but printing is not yet implemented\n" +
287
+ "in this version.")
288
+ w.execute
289
+ return
290
+
291
+ printer = printer_dialog
292
+ map.print( printer ) if printer
293
+ end
294
+
295
+ def create_mdiclient
296
+ # MDI Client
297
+ @maps = []
298
+
299
+ @mdiclient = FXMDIClient.new(self, LAYOUT_FILL_X|LAYOUT_FILL_Y)
300
+
301
+ # MDI buttons in menu:- note the message ID's!!!!!
302
+ # Normally, MDI commands are simply sensitized or desensitized;
303
+ # Under the @menubar, however, they're hidden if the MDI Client is
304
+ # not maximized. To do this, they must have different ID's.
305
+ FXMDIWindowButton.new(@menubar, @mdimenu, @mdiclient,
306
+ FXMDIClient::ID_MDI_MENUWINDOW, LAYOUT_LEFT)
307
+ FXMDIDeleteButton.new(@menubar, @mdiclient,
308
+ FXMDIClient::ID_MDI_MENUCLOSE, FRAME_RAISED|LAYOUT_RIGHT)
309
+ FXMDIRestoreButton.new(@menubar, @mdiclient,
310
+ FXMDIClient::ID_MDI_MENURESTORE, FRAME_RAISED|LAYOUT_RIGHT)
311
+ FXMDIMinimizeButton.new(@menubar, @mdiclient,
312
+ FXMDIClient::ID_MDI_MENUMINIMIZE, FRAME_RAISED|LAYOUT_RIGHT)
313
+
314
+ # Icon for MDI Child
315
+ @mdiicon = load_icon("winapp")
316
+
317
+ # Make MDI Window Menu
318
+ @mdimenu = FXMDIMenu.new(self, @mdiclient)
319
+ end
320
+
321
+ def self.copy_buffer
322
+ return @@copy_buffer
323
+ end
324
+
325
+ def self.copy_selected(map)
326
+ selection = map.pages[map.page].rooms.find_all { |r| r.selected }
327
+ return if selection.size > 1
328
+ @@copy_buffer = selection[0]
329
+ end
330
+
331
+ def self.cut_selected(map)
332
+ FXMapperWindow::copy_selected(map)
333
+ map.delete_selected
334
+ end
335
+
336
+ def self.paste_selected(map)
337
+ selection = map.pages[map.page].rooms.find_all { |r| r.selected }
338
+ selection.each { |r| r.copy(@@copy_buffer) }
339
+ end
340
+
341
+ def cut_selected_cb(*o)
342
+ map = current_map
343
+ return unless map
344
+ FXMapperWindow::cut_selected(map)
345
+ end
346
+
347
+ def copy_selected_cb(*o)
348
+ map = current_map
349
+ return unless map
350
+ FXMapperWindow::copy_selected(map)
351
+ end
352
+
353
+ def paste_selected_cb(*o)
354
+ map = current_map
355
+ return if not map or not @@copy_buffer
356
+ FXMapperWindow::paste_selected(map)
357
+ end
358
+
359
+ def hilite_matches(map, matches, re, idx = 0 )
360
+ if matches.size == 0
361
+ status "No objects match regex '#{re}'."
362
+ return
363
+ end
364
+
365
+ # sort matches by page
366
+ matches.sort_by { |a| a[0] }
367
+
368
+ # Jump to first page of match
369
+ map.page = matches[idx][0]
370
+ map.pages[map.page].rooms.each { |r| r.selected = false }
371
+
372
+ matches.each { |p, r| r.selected = true }
373
+
374
+ num = matches.find_all { |p, r| p == matches[idx][0] }
375
+
376
+ status "#{matches.size} in map, #{num.size} in page."
377
+ map.draw
378
+ end
379
+
380
+ def find_in_map(s, m, e)
381
+ map = current_map
382
+ return unless map
383
+
384
+ re = /#{s.text}/
385
+ matches = []
386
+ (0...map.pages.size).each { |p|
387
+ map.pages[p].rooms.each { |r|
388
+ next unless r.name =~ re
389
+ matches.push( [p, r] )
390
+ }
391
+ }
392
+ idx = @search.index
393
+ @search.index = matches.size-1 if idx >= matches.size
394
+ hilite_matches(map, matches, re, @search.index)
395
+ end
396
+
397
+ def find_in_map_cb(s, m, e)
398
+ map = current_map
399
+ return unless map
400
+
401
+ title = "Find Location In Map"
402
+ if not @search
403
+ @search = FXSearchDialogBox.new(self)
404
+ end
405
+ @search.proc = method(:find_in_map)
406
+ @search.title = title
407
+ @search.text = ''
408
+ @search.show
409
+ end
410
+
411
+ def find_in_page(s, m, e)
412
+ map = current_map
413
+ return unless map
414
+
415
+ re = /#{s.text}/
416
+ matches = []
417
+ map.pages[map.page].rooms.each { |r|
418
+ next unless r.name =~ re
419
+ matches.push( [ map.page, r] )
420
+ }
421
+ hilite_matches(map, matches, re)
422
+ end
423
+
424
+ def find_in_page_cb(s, m, e)
425
+ map = current_map
426
+ return unless map
427
+
428
+ title = "Find Location in this Section"
429
+ if not @search
430
+ @search = FXSearchDialogBox.new(self)
431
+ end
432
+ @search.proc = method(:find_in_page)
433
+ @search.title = title
434
+ @search.text = ''
435
+ @search.show
436
+ end
437
+
438
+ def find_object_in_map(s, m, e)
439
+ map = current_map
440
+ return unless map
441
+
442
+ re = /#{s.text}/
443
+ matches = []
444
+ (0...map.pages.size).each { |p|
445
+ map.pages[p].rooms.each { |r|
446
+ next unless r.objects =~ re
447
+ matches.push( [p, r] )
448
+ }
449
+ }
450
+ idx = @search.index
451
+ @search.index = matches.size-1 if idx >= matches.size
452
+ hilite_matches(map, matches, re, @search.index)
453
+ end
454
+
455
+ def find_object_in_map_cb(s, m, e)
456
+ map = current_map
457
+ return unless map
458
+
459
+ title = "Find Object In Map"
460
+ if not @search
461
+ @search = FXSearchDialogBox.new(self)
462
+ end
463
+ @search.proc = method(:find_object_in_map)
464
+ @search.title = title
465
+ @search.text = ''
466
+ @search.show
467
+ end
468
+
469
+ def about_cb(sender, id, msg)
470
+ FXAboutDialogBox.new(self, "About This Software...", <<"EOF").execute
471
+ #{TITLE} - #{VERSION}
472
+
473
+ A WYSIWYG mapping tool for interactive fiction.
474
+ Written by Gonzalo Garramuno.
475
+
476
+ ggarra@advancedsl.com.ar
477
+ GGarramuno@aol.com
478
+ EOF
479
+ end
480
+
481
+ def create_menus
482
+ # Construct these icons
483
+ newdoc = load_icon("filenew")
484
+ opendoc = load_icon("fileopen")
485
+ savedoc = load_icon("filesave")
486
+ saveasdoc = load_icon("filesaveas")
487
+
488
+ # File Menu
489
+ filemenu = FXMenuPane.new(self)
490
+ FXMenuTitle.new(@menubar, "&File", nil, filemenu)
491
+ cmd = FXMenuCommand.new(filemenu, "&New...\tCtl-N\tCreate new document.", newdoc)
492
+ cmd.connect(SEL_COMMAND, method(:new_map_cb))
493
+
494
+ cmd = FXMenuCommand.new(filemenu, "&Open...\tCtl-O\tOpen document file.", opendoc)
495
+ cmd.connect(SEL_COMMAND, method(:open_cb))
496
+ cmd = FXMenuCommand.new(filemenu, "&Save\tCtl-S\tSave document.", savedoc)
497
+ cmd.connect(SEL_COMMAND, method(:save_cb))
498
+ cmd = FXMenuCommand.new(filemenu, "Save &As...\t\tSave document to another file.",
499
+ saveasdoc)
500
+ cmd.connect(SEL_COMMAND, method(:save_as_cb))
501
+
502
+ # Export submenu
503
+ submenu = FXMenuPane.new(self)
504
+
505
+ cmd = FXMenuCommand.new(submenu, "&Export as PDF...\t\tExport map as Acrobat PDF document.", nil)
506
+ cmd.connect(SEL_COMMAND, method(:pdf_export_cb))
507
+
508
+ cmd = FXMenuCommand.new(submenu, "&Export as Postscript...\t\tExport map as Postscript document.", nil)
509
+ cmd.connect(SEL_COMMAND, method(:ps_export_cb))
510
+
511
+ cmd = FXMenuCommand.new(submenu, "&Export as IFM...\t\tExport map as an IFM map.", nil)
512
+ cmd.connect(SEL_COMMAND, method(:ifm_export_cb))
513
+ FXMenuCascade.new(filemenu, "Export", nil, submenu)
514
+
515
+
516
+ submenu = FXMenuPane.new(self)
517
+ cmd = FXMenuCommand.new(submenu, "&Map...\t\tPrint map (as graph).",
518
+ nil)
519
+ cmd.connect(SEL_COMMAND, method(:print_cb))
520
+
521
+ cmd = FXMenuCommand.new(submenu, "Map &Locations...\t\tPrint map locations as text list.", nil)
522
+ cmd.connect(SEL_COMMAND, method(:print_locations_cb))
523
+ FXMenuCascade.new(filemenu, "Print", nil, submenu)
524
+
525
+
526
+ FXMenuCommand.new(filemenu, "&Quit\tCtl-Q\tQuit the application.", nil,
527
+ getApp(), FXApp::ID_QUIT)
528
+
529
+ # Edit Menu
530
+ editmenu = FXMenuPane.new(self)
531
+ FXMenuTitle.new(@menubar, "&Edit", nil, editmenu)
532
+ cmd = FXMenuCommand.new(editmenu, "&Copy\tCtl-C\tCopy Location Data", nil)
533
+ cmd.connect(SEL_COMMAND, method(:copy_selected_cb))
534
+ cmd = FXMenuCommand.new(editmenu, "Cu&t\tCtl-X\tCut Location", nil)
535
+ cmd.connect(SEL_COMMAND, method(:cut_selected_cb))
536
+ cmd = FXMenuCommand.new(editmenu, "&Paste\tCtl-V\tPaste Location Data",
537
+ nil)
538
+ cmd.connect(SEL_COMMAND, method(:paste_selected_cb))
539
+
540
+ # Searching menu
541
+ FXMenuSeparator.new(editmenu)
542
+ cmd = FXMenuCommand.new(editmenu, "&Search Location in Map\tCtl-F\tFind a Location Name in Map")
543
+ cmd.connect(SEL_COMMAND, method(:find_in_map_cb))
544
+ cmd = FXMenuCommand.new(editmenu, "&Search Location in Section\tAlt-F\tFind a Location Name in Current Page")
545
+ cmd.connect(SEL_COMMAND, method(:find_in_page_cb))
546
+ cmd = FXMenuCommand.new(editmenu, "&Search Object in Map\tAlt-O\tFind Location with an Object in the Map")
547
+ cmd.connect(SEL_COMMAND, method(:find_object_in_map_cb))
548
+
549
+ # Complex Connection
550
+ FXMenuSeparator.new(editmenu)
551
+ cmd = FXMenuCommand.new(editmenu, "Comple&x Connection\tx\tCreate a complex connection.", nil)
552
+ cmd.connect( SEL_COMMAND, method(:complex_connection_cb))
553
+
554
+ FXMenuSeparator.new(editmenu)
555
+ cmd = FXMenuCommand.new(editmenu, "Delete\tDel\tDelete selected locations or connections.", nil)
556
+ cmd.connect( SEL_COMMAND, method(:delete_selected_cb) )
557
+
558
+ # Map menu
559
+ mapmenu = FXMenuPane.new(self)
560
+
561
+ cmd = FXMenuCommand.new(mapmenu, "Map Information\t\tChange Map Information.")
562
+ cmd.connect(SEL_COMMAND) { map_properties }
563
+
564
+ submenu = FXMenuPane.new(self)
565
+ cmd = FXMenuCommand.new(submenu, "Next Section\t\tGo to Next Map Section.")
566
+ cmd.connect(SEL_COMMAND) {
567
+ next_page
568
+ }
569
+ cmd = FXMenuCommand.new(submenu, "Previous Section\t\tGo to Previous Map Section.")
570
+ cmd.connect(SEL_COMMAND) {
571
+ previous_page
572
+ }
573
+ FXMenuSeparator.new(submenu)
574
+ cmd = FXMenuCommand.new(submenu, "Add Section\t\tAdd a New Section to Map.")
575
+ cmd.connect(SEL_COMMAND) {
576
+ map = current_map
577
+ if map
578
+ map.new_page
579
+ map.modified = true
580
+ update_page
581
+ end
582
+ }
583
+ cmd = FXMenuCommand.new(submenu, "Rename Section\t\tRename Current Section.")
584
+ cmd.connect(SEL_COMMAND) {
585
+ map = current_map
586
+ if map
587
+ map.rename_page
588
+ map.modified = true
589
+ end
590
+ }
591
+ FXMenuSeparator.new(submenu)
592
+ cmd = FXMenuCommand.new(submenu, "Delete Section\t\tDelete Current Section from Map.")
593
+ cmd.connect(SEL_COMMAND) {
594
+ map = current_map
595
+ if map
596
+ map.delete_page
597
+ map.modified = true
598
+ end
599
+ }
600
+ FXMenuCascade.new(mapmenu, "Sections", nil, submenu)
601
+
602
+
603
+ submenu = FXMenuPane.new(self)
604
+ [25, 50, 75, 100, 125].each { |v|
605
+ cmd = FXMenuCommand.new(submenu, v.to_s + "%\t\tZoom page to #{v}%.")
606
+ cmd.connect(SEL_COMMAND) {
607
+ map = current_map
608
+ if map
609
+ map.zoom = v / 100.0
610
+ map.draw
611
+ end
612
+ }
613
+ }
614
+ FXMenuCascade.new(mapmenu, "Zoom", nil, submenu)
615
+
616
+ submenu = FXMenuPane.new(self)
617
+
618
+ cmd = FXMenuCheck.new(submenu, "Edit on Creation\t\tEdit locations on creation.")
619
+ cmd.check = @@default_options['Edit on Creation']
620
+ cmd.connect(SEL_COMMAND) { |s, m, e|
621
+ map = current_map
622
+ if map
623
+ map.options['Edit on Creation'] = (s.check == true)
624
+ end
625
+ }
626
+ cmd.connect(SEL_UPDATE) { |s, m, e|
627
+ map = current_map
628
+ s.check = map.options['Edit on Creation'] if map
629
+ }
630
+
631
+ cmd = FXMenuCheck.new(submenu, "Automatic Connection\t\tConnect new location to adjacent one .")
632
+ cmd.check = @@default_options['Automatic Connection']
633
+ cmd.connect(SEL_COMMAND) { |s, m, e|
634
+ map = current_map
635
+ if map
636
+ map.options['Automatic Connection'] = (s.check == true)
637
+ end
638
+ }
639
+ cmd.connect(SEL_UPDATE) { |s, m, e|
640
+ map = current_map
641
+ s.check = map.options['Automatic Connection'] if map
642
+ }
643
+
644
+ cmd = FXMenuCheck.new(submenu, "Create on Connectionn\t\tCreate missing locations when creating a connection.")
645
+ cmd.check = @@default_options['Create on Connection']
646
+ cmd.connect(SEL_COMMAND) { |s, m, e|
647
+ map = current_map
648
+ map.options['Create on Connection'] = s.check if map
649
+ }
650
+ cmd.connect(SEL_UPDATE) { |s, m, e|
651
+ map = current_map
652
+ s.check = map.options['Create on Connection'] if map
653
+ }
654
+
655
+ FXMenuCascade.new(mapmenu, "Options", nil, submenu)
656
+
657
+ ##########################
658
+ # Display submenu
659
+ #########################
660
+ submenu = FXMenuPane.new(self)
661
+ cmd = FXMenuCheck.new(submenu, "Paths as Curves\t\tDraw complex paths as curves.")
662
+ cmd.check = @@default_options['Paths as Curves']
663
+ cmd.connect(SEL_COMMAND) { |s, m, e|
664
+ map = current_map
665
+ if map
666
+ map.options['Paths as Curves'] = (s.check == true)
667
+ map.draw
668
+ end
669
+ }
670
+ cmd.connect(SEL_UPDATE) { |s, m, e|
671
+ map = current_map
672
+ s.check = map.options['Paths as Curves'] if map
673
+ }
674
+ cmd = FXMenuCheck.new(submenu, "Location Numbers\t\tShow Location Numbers.")
675
+ cmd.check = @@default_options['Location Numbers']
676
+ cmd.connect(SEL_COMMAND) { |s, m, e|
677
+ map = current_map
678
+ if map
679
+ map.options['Location Numbers'] = (s.check == true)
680
+ map.draw
681
+ end
682
+ }
683
+ cmd.connect(SEL_UPDATE) { |s, m, e|
684
+ map = current_map
685
+ s.check = map.options['Location Numbers'] if map
686
+ }
687
+
688
+ cmd = FXMenuCheck.new(submenu, "Boxes\t\tDraw dashed box guidelines.")
689
+ cmd.check = @@default_options['Grid Boxes']
690
+ cmd.connect(SEL_COMMAND) { |s, m, e|
691
+ map = current_map
692
+ if map
693
+ map.options['Grid Boxes'] = (s.check == true)
694
+ map.draw
695
+ end
696
+ }
697
+ cmd.connect(SEL_UPDATE) { |s, m, e|
698
+ map = current_map
699
+ s.check = map.options['Grid Boxes'] if map
700
+ }
701
+
702
+ cmd = FXMenuCheck.new(submenu, "Straight Connections\t\tDraw dashed N/S/E/W guidelines")
703
+ cmd.check = @@default_options['Grid Straight Connections']
704
+ cmd.connect(SEL_COMMAND) { |s, m, e|
705
+ map = current_map
706
+ if map
707
+ map.options['Grid Straight Connections'] = (s.check == true)
708
+ map.draw
709
+ end
710
+ }
711
+ cmd.connect(SEL_UPDATE) { |s, m, e|
712
+ map = current_map
713
+ s.check = map.options['Grid Straight Connections'] if map
714
+ }
715
+
716
+ cmd = FXMenuCheck.new(submenu, "Diagonal Connections\t\tDraw dashed NW/NE/SW/SE guidelines")
717
+ cmd.check = @@default_options['Grid Diagonal Connections']
718
+ cmd.connect(SEL_COMMAND) { |s, m, e|
719
+ map = current_map
720
+ if map
721
+ map.options['Grid Diagonal Connections'] = s.check
722
+ map.draw
723
+ end
724
+ }
725
+ cmd.connect(SEL_UPDATE) { |s, m, e|
726
+ map = current_map
727
+ s.check = map.options['Grid Diagonal Connections'] if map
728
+ }
729
+
730
+ FXMenuCascade.new(mapmenu, 'Display', nil, submenu)
731
+
732
+ submenu = FXMenuPane.new(self)
733
+ cmd = FXMenuCommand.new(submenu, "Colors\t\tChange map colors.")
734
+ FXMenuSeparator.new(submenu)
735
+ cmd = FXMenuCommand.new(submenu, "Save Preferences\t\tSave Preferences for Startup")
736
+ cmd.connect(SEL_COMMAND) {
737
+ map = current_map
738
+ map.options.write if map
739
+ }
740
+ FXMenuCascade.new(mapmenu, 'Preferences', nil, submenu)
741
+
742
+ FXMenuTitle.new(@menubar,"&Map", nil, mapmenu)
743
+
744
+ # Window menu
745
+ windowmenu = FXMenuPane.new(self)
746
+ FXMenuCommand.new(windowmenu, "Tile &Horizontally", nil,
747
+ @mdiclient, FXMDIClient::ID_MDI_TILEHORIZONTAL)
748
+ FXMenuCommand.new(windowmenu, "Tile &Vertically", nil,
749
+ @mdiclient, FXMDIClient::ID_MDI_TILEVERTICAL)
750
+ FXMenuCommand.new(windowmenu, "C&ascade", nil,
751
+ @mdiclient, FXMDIClient::ID_MDI_CASCADE)
752
+ FXMenuCommand.new(windowmenu, "&Close", nil,
753
+ @mdiclient, FXMDIClient::ID_MDI_CLOSE)
754
+ sep1 = FXMenuSeparator.new(windowmenu)
755
+ sep1.setTarget(@mdiclient)
756
+ sep1.setSelector(FXMDIClient::ID_MDI_ANY)
757
+ FXMenuCommand.new(windowmenu, nil, nil, @mdiclient, FXMDIClient::ID_MDI_1)
758
+ FXMenuCommand.new(windowmenu, nil, nil, @mdiclient, FXMDIClient::ID_MDI_2)
759
+ FXMenuCommand.new(windowmenu, nil, nil, @mdiclient, FXMDIClient::ID_MDI_3)
760
+ FXMenuCommand.new(windowmenu, nil, nil, @mdiclient, FXMDIClient::ID_MDI_4)
761
+ FXMenuCommand.new(windowmenu, "&Others...", nil, @mdiclient, FXMDIClient::ID_MDI_OVER_5)
762
+ FXMenuTitle.new(@menubar,"&Window", nil, windowmenu)
763
+
764
+ # Help menu
765
+ helpmenu = FXMenuPane.new(self)
766
+ cmd = FXMenuCommand.new(helpmenu, "&Instructions", nil)
767
+ cmd.connect(SEL_COMMAND, method(:docs))
768
+ FXMenuSeparator.new(helpmenu)
769
+ cmd = FXMenuCommand.new(helpmenu, "&About", nil)
770
+ cmd.connect(SEL_COMMAND, method(:about_cb))
771
+
772
+ cmd = FXMenuCommand.new(helpmenu, "&Resource Code", nil)
773
+ cmd.connect(SEL_COMMAND) {
774
+ file = FXMapFileDialog.new(self, "Resource a Ruby File").filename
775
+ if file != ''
776
+ begin
777
+ Kernel.load file
778
+ rescue => e
779
+ p e
780
+ end
781
+ end
782
+ }
783
+ FXMenuTitle.new(@menubar,"&Help", nil, helpmenu)
784
+ end
785
+
786
+
787
+ def docs(*opts)
788
+ FXAboutDialogBox.new(self, "About This Software...", <<'EOF').show
789
+
790
+ LMB - Left Mouse Button
791
+ MMB - Middle Mouse Button
792
+
793
+ Mouse controls
794
+ --------------
795
+
796
+ Use LMB to add new locations or connections among rooms.
797
+
798
+ Click with LMB on any room or connection to select it.
799
+
800
+ Double click on rooms or connections to access their properties.
801
+
802
+ Click several times on an existing connecion to establish a one-way
803
+ connection.
804
+
805
+ Drag with MMB or ALT + LMB to scroll around the page.
806
+
807
+ Use mousewheel to zoom in and out.
808
+
809
+ Keyboard controls
810
+ -----------------
811
+
812
+ Use 'x' to start a new complex connection (ie. a connection across
813
+ rooms that are not neighbors), then click on each exit of each room.
814
+
815
+ Use 'Delete' or 'Backspace' to remove any selected connection or room.
816
+
817
+ Use arrow keys to move selected rooms around the page, one unit at a time.
818
+
819
+ Use CTRL + LMB when adding a connection to create a dead-end connection (one that loops
820
+ onto itself)
821
+
822
+ EOF
823
+ end
824
+
825
+ def create_toolbar(toolbar)
826
+
827
+ # Construct these icons
828
+ newdoc = load_icon("filenew")
829
+ opendoc = load_icon("fileopen")
830
+ savedoc = load_icon("filesave")
831
+ saveasdoc = load_icon("filesaveas")
832
+
833
+ # File manipulation
834
+ cmd = FXButton.new(toolbar, "\tNew\tCreate new document.", newdoc, nil, 0,
835
+ FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
836
+ cmd.connect(SEL_COMMAND, method(:new_map_cb))
837
+
838
+ cmd = FXButton.new(toolbar, "\tOpen\tOpen document file.", opendoc, nil, 0,
839
+ FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
840
+ cmd.connect(SEL_COMMAND, method(:open_cb))
841
+
842
+ cmd = FXButton.new(toolbar, "\tSave\tSave document.", savedoc, nil, 0,
843
+ FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
844
+ cmd.connect(SEL_COMMAND, method(:save_cb))
845
+ cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
846
+ map = current_map
847
+ message = map ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
848
+ sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
849
+ }
850
+ cmd = FXButton.new(toolbar, "\tSave As\tSave document to another file.",
851
+ saveasdoc, nil, 0, FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
852
+ cmd.connect(SEL_COMMAND, method(:save_as_cb))
853
+ cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
854
+ map = current_map
855
+ message = map ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
856
+ sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
857
+ }
858
+
859
+ # Print
860
+ FXFrame.new(toolbar,
861
+ LAYOUT_TOP|LAYOUT_LEFT|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT, 0, 0, 4, 20)
862
+ cmd = FXButton.new(toolbar, "\tPrint Map\tPrint map (as graph).",
863
+ load_icon("printicon"), @mdiclient, FXGLViewer::ID_PRINT_IMAGE,
864
+ BUTTON_AUTOGRAY|FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
865
+ cmd.connect(SEL_COMMAND, method(:print_cb))
866
+ cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
867
+ map = current_map
868
+ message = map ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
869
+ sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
870
+ }
871
+
872
+ # Editing
873
+ FXFrame.new(toolbar,
874
+ LAYOUT_TOP|LAYOUT_LEFT|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT, 0, 0, 4, 20)
875
+ cmd = FXButton.new(toolbar, "\tCut", load_icon("cut"), @mdiclient,
876
+ FXGLViewer::ID_CUT_SEL, (BUTTON_AUTOGRAY|FRAME_THICK|FRAME_RAISED|
877
+ LAYOUT_TOP|LAYOUT_LEFT))
878
+ cmd.connect(SEL_COMMAND, method(:cut_selected_cb))
879
+ cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
880
+ map = current_map
881
+ message = map ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
882
+ sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
883
+ }
884
+ cmd = FXButton.new(toolbar, "\tCopy", load_icon("copy"), @mdiclient,
885
+ FXGLViewer::ID_COPY_SEL, (BUTTON_AUTOGRAY|FRAME_THICK|FRAME_RAISED|
886
+ LAYOUT_TOP|LAYOUT_LEFT))
887
+ cmd.connect(SEL_COMMAND, method(:copy_selected_cb))
888
+ cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
889
+ map = current_map
890
+ message = map ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
891
+ sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
892
+ }
893
+ cmd = FXButton.new(toolbar, "\tPaste", load_icon("paste"), @mdiclient,
894
+ FXGLViewer::ID_PASTE_SEL, (BUTTON_AUTOGRAY|FRAME_THICK|FRAME_RAISED|
895
+ LAYOUT_TOP|LAYOUT_LEFT))
896
+ cmd.connect(SEL_COMMAND, method(:paste_selected_cb))
897
+ cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
898
+ map = current_map
899
+ message = (map and @@copy_buffer) ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
900
+ sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
901
+ }
902
+
903
+ # Zooming
904
+ FXFrame.new(toolbar,
905
+ LAYOUT_TOP|LAYOUT_LEFT|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT, 0, 0, 4, 20)
906
+ cmd = FXButton.new(toolbar, "+\tZoom In", load_icon("zoom"), @mdiclient,
907
+ 0, FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
908
+ cmd.connect(SEL_COMMAND) { zoom_in }
909
+
910
+ cmd = FXButton.new(toolbar, "-\tZoom Out", load_icon("zoom"), @mdiclient,
911
+ 0, FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
912
+ cmd.connect(SEL_COMMAND) { zoom_out }
913
+
914
+
915
+ # Page travel
916
+ frame = FXHorizontalFrame.new(toolbar,
917
+ LAYOUT_RIGHT|FRAME_THICK|FRAME_RAISED)
918
+ cmd = FXButton.new(frame, "\tPrevious Section", load_icon("prevpage"),
919
+ @mdiclient,
920
+ 0, FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
921
+ cmd.connect(SEL_COMMAND) { previous_page }
922
+
923
+ @page = FXTextField.new(frame, 5, nil, 0,
924
+ TEXTFIELD_INTEGER|LAYOUT_FILL_ROW)
925
+ @page.text = '1'
926
+ @page.connect(SEL_COMMAND) { |s,m,e|
927
+ v = s.text.to_i
928
+ map = current_map
929
+ if map
930
+ map.page = v - 1
931
+ map.draw
932
+ update_page
933
+ end
934
+ }
935
+ @page.connect(SEL_UPDATE) { |s,m,e|
936
+ v = s.text.to_i
937
+ map = current_map
938
+ update_page if map
939
+ }
940
+
941
+ cmd = FXButton.new(frame, "\tNext Section", load_icon("nextpage"),
942
+ @mdiclient,
943
+ 0, FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
944
+ cmd.connect(SEL_COMMAND) { next_page }
945
+ end
946
+
947
+ #
948
+ # Update page # in toolbar widget
949
+ #
950
+ def update_page
951
+ map = current_map
952
+ @page.text = (map.page + 1).to_s if map
953
+ end
954
+
955
+ #
956
+ # Go to next page in current map
957
+ #
958
+ def next_page
959
+ map = current_map
960
+ map.next_page if map
961
+ update_page
962
+ end
963
+
964
+ #
965
+ # Go to previous page in current map
966
+ #
967
+ def previous_page
968
+ map = current_map
969
+ map.previous_page if map
970
+ update_page
971
+ end
972
+
973
+ #
974
+ # Zoom in into current map
975
+ #
976
+ def zoom_in
977
+ map = current_map
978
+ if map
979
+ map.zoom_in
980
+ map.draw
981
+ end
982
+ end
983
+
984
+ #
985
+ # Zoom out from current map
986
+ #
987
+ def zoom_out
988
+ map = current_map
989
+ if map
990
+ map.zoom_out
991
+ map.draw
992
+ end
993
+ end
994
+
995
+ #
996
+ # Bring up the map property requester for current map
997
+ #
998
+ def map_properties
999
+ map = current_map
1000
+ map.properties if map
1001
+ end
1002
+
1003
+ #
1004
+ # In case of crash or runtime error, autosave all maps, so user
1005
+ # does not loose any data.
1006
+ #
1007
+ def autosave
1008
+ @maps.each { |m|
1009
+ m.save
1010
+ }
1011
+ end
1012
+
1013
+
1014
+ def initialize(app)
1015
+ super(app, TITLE, nil, nil, DECOR_ALL, 0, 0, 800, 600)
1016
+
1017
+ # Menubar
1018
+ @menubar = FXMenuBar.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X)
1019
+
1020
+ FXHorizontalSeparator.new(self,
1021
+ LAYOUT_SIDE_TOP|SEPARATOR_GROOVE|LAYOUT_FILL_X)
1022
+ toolbar = FXToolBar.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X,
1023
+ 0, 0, 0, 0, 4, 4, 0, 0, 0, 0)
1024
+
1025
+ # Status bar
1026
+ @statusbar = FXStatusBar.new(self,
1027
+ LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|
1028
+ STATUSBAR_WITH_DRAGCORNER)
1029
+
1030
+
1031
+ create_mdiclient
1032
+ create_menus
1033
+ create_toolbar(toolbar)
1034
+ new_map
1035
+
1036
+ self.connect(SEL_CLOSE) {
1037
+ exit = true
1038
+ @maps.each { |m|
1039
+ if not m.close_cb
1040
+ exit = false
1041
+ break
1042
+ else
1043
+ @maps.delete(m)
1044
+ end
1045
+ }
1046
+ self.close if exit
1047
+ }
1048
+
1049
+ show
1050
+ end
1051
+ end