ifmapper 0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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