ifmapper 0.6 → 0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,16 +3,35 @@
3
3
  # This class contains the applicatio and map's settings
4
4
  #
5
5
  class FXMapperSettings < Hash
6
+ def home
7
+ if ENV['HOME']
8
+ homedir = File.expand_path('~')
9
+ # Ahh... you have to love windows...
10
+ elsif ENV['USERPROFILE']
11
+ homedir = ENV['USERPROFILE']
12
+ elsif ENV['HOMEDRIVE'] and ENV['HOMEPATH']
13
+ homedir = ENV['HOMEDRIVE'] + ENV['HOMEPATH']
14
+ else
15
+ homedir = '.'
16
+ end
17
+ return homedir
18
+ end
19
+
6
20
  def write
7
- file = File.expand_path('~/.ifmapper')
8
- f = File.open(file, 'w')
9
- f.puts YAML.dump(self)
10
- f.close
21
+ begin
22
+ file = home + '/.ifmapper'
23
+ f = File.open(file, 'w')
24
+ f.puts YAML.dump(self)
25
+ f.close
26
+ rescue => e
27
+ $stderr.puts "Preferences not saved:"
28
+ $stderr.puts e
29
+ end
11
30
  end
12
31
 
13
32
  def initialize
14
- f = File.expand_path('~/.ifmapper')
15
33
  begin
34
+ f = home + '/.ifmapper'
16
35
  self.replace( YAML.load_file(f) )
17
36
  rescue
18
37
  self.replace( {
@@ -25,8 +44,8 @@ class FXMapperSettings < Hash
25
44
  'Box Number Color' => 'grey',
26
45
 
27
46
  # Fonts
28
- 'Font Text' => 'times new roman',
29
- 'Font Objects' => 'times new roman',
47
+ 'Font Text' => 'Times',
48
+ 'Font Objects' => 'Times',
30
49
 
31
50
  # Creation options
32
51
  'Create on Connection' => true,
@@ -1,20 +1,26 @@
1
1
 
2
+ begin
3
+ $rubygems = false
4
+ require 'rubygems'
5
+ $rubygems = true
6
+ rescue LoadError
7
+ end
8
+
2
9
  begin
3
10
  require 'fox12'
4
- rescue => e
5
- $stderr.puts "Please install the fox library v1.2 or later."
11
+ rescue LoadError => e
12
+ $stderr.puts "Please install the FXRuby (FOX) library v1.2 or later."
6
13
  if $rubygems
7
- $stderr.puts "You can usually do so if you do 'gem install fox'."
14
+ $stderr.puts "You can usually do so if you do 'gem install fxruby'."
8
15
  end
9
16
  exit(1)
10
17
  end
11
18
 
12
19
  begin
13
20
  require 'yaml'
14
- rescue => e
21
+ rescue LoadError => e
15
22
  $stderr.puts "Please install the yaml library."
16
- $stderr.puts "Without it, preferences cannot be saved."
17
- exit(1)
23
+ $stderr.puts "Without it, preferences cannot be loaded or saved."
18
24
  end
19
25
 
20
26
 
@@ -27,13 +33,13 @@ require 'IFMapper/FXAboutDialogBox'
27
33
  require 'IFMapper/FXSearchDialogBox'
28
34
  require 'IFMapper/FXMapFileDialog'
29
35
  require 'IFMapper/FXMapColorBox'
30
- require 'IFMapper/IFMReader'
36
+
31
37
 
32
38
 
33
39
  class FXMapperWindow < FXMainWindow
34
40
 
35
41
  PROGRAM_NAME = "Interactive Fiction Mapper"
36
- VERSION = 0.6
42
+ VERSION = 0.7
37
43
  AUTHOR = "Gonzalo Garramuno"
38
44
  TITLE = "#{PROGRAM_NAME} v#{VERSION} - Written by #{AUTHOR}"
39
45
 
@@ -54,6 +60,7 @@ class FXMapperWindow < FXMainWindow
54
60
  end
55
61
 
56
62
  def open_ifm(file, map)
63
+ require 'IFMapper/IFMReader'
57
64
  IFMReader.new(file, map)
58
65
  return map
59
66
  end
@@ -110,8 +117,10 @@ class FXMapperWindow < FXMainWindow
110
117
 
111
118
  map.copy(tmp)
112
119
  map.create_pathmap
120
+ map.verify_integrity
113
121
  map.update_title
114
122
  map.window.create
123
+ map.draw
115
124
  update_section
116
125
  status "Loaded '#{file}'."
117
126
  end
@@ -193,34 +202,13 @@ class FXMapperWindow < FXMainWindow
193
202
  map.delete_selected
194
203
  end
195
204
 
196
- #
197
- # Export current map as a Postscript file
198
- #
199
- def ps_export
200
- map = current_map
201
- return unless map
202
- raise "Postscript Exporting not yet supported."
203
-
204
- begin
205
- require "IFMapper/PSMapExporter"
206
- rescue => e
207
- # bring up fox warning dialog box
208
- return
209
- end
210
-
211
- printer = printer_dialog
212
- map.ps_export(printer) if printer
213
- end
214
-
215
-
216
205
  #
217
206
  # Popup a printer dialog and return the settings or false if user cancels
218
207
  #
219
- def printer_dialog
208
+ def printer_dialog(title = 'Print Map')
220
209
  map = current_map
221
- dlg = FXPrintDialog.new(self, "Print Map #{map.name}")
222
- # Set printer to always use Landscape printing
223
- dlg.printer.flags |= PRINT_DEST_PAPER | PRINT_LANDSCAPE | PRINT_NOBOUNDS
210
+ dlg = FXPrintDialog.new(self, title + " for #{map.name}")
211
+ dlg.printer.flags |= PRINT_DEST_PAPER
224
212
  return dlg.printer if dlg.execute != 0
225
213
  return false
226
214
  end
@@ -234,7 +222,7 @@ class FXMapperWindow < FXMainWindow
234
222
  w.execute
235
223
  return
236
224
 
237
- printer = printer_dialog
225
+ printer = printer_dialog "Print Locations"
238
226
  map.print_locations( printer ) if printer
239
227
  end
240
228
 
@@ -242,20 +230,44 @@ class FXMapperWindow < FXMainWindow
242
230
  map = current_map
243
231
  return unless map
244
232
 
245
- w = FXWarningBox.new( self, "Sorry, but IFM exporting is not yet implemented\n" +
246
- "in this version.")
247
- w.execute
248
- return
233
+ require 'IFMapper/IFMWriter'
234
+
235
+ d = FXMapFileDialog.new(self, "Save Map as IFM File",
236
+ [
237
+ "IFM Map (*.ifm)"
238
+ ])
239
+ if d.filename != ''
240
+ file = d.filename
241
+ file += '.ifm' if file !~ /\.ifm$/
242
+ IFMWriter.new(map, file)
243
+ end
249
244
  end
250
245
 
246
+ #
247
+ # Export current map as a Postscript file
248
+ #
251
249
  def ps_export_cb(sender, sel, msg)
252
250
  map = current_map
253
251
  return unless map
254
252
 
255
- w = FXWarningBox.new( self, "Sorry, but Postscript exporting is not implemented\n" +
256
- "in this version.")
257
- w.execute
258
- return
253
+ require 'IFMapper/MapPrinting'
254
+
255
+ d = FXMapFileDialog.new(self, "Save Postscript File",
256
+ [
257
+ "Postscript (*.ps)"
258
+ ])
259
+ if d.filename != ''
260
+ printer = FXPrinter.new
261
+ printer.firstpage = 1
262
+ printer.lastpage = map.sections.size
263
+ printer.mediawidth = 612.0
264
+ printer.mediaheight = 792.0
265
+ printer.leftmargin = printer.rightmargin =
266
+ printer.topmargin = printer.bottommargin = 72.0
267
+ printer.flags |= PRINT_DEST_FILE
268
+ printer.name = d.filename
269
+ map.print(printer)
270
+ end
259
271
  end
260
272
 
261
273
  def pdf_export_cb(sender, sel, msg)
@@ -264,13 +276,14 @@ class FXMapperWindow < FXMainWindow
264
276
 
265
277
  begin
266
278
  require 'IFMapper/PDFMapExporter'
267
- rescue => e
279
+ rescue LoadError => e
268
280
  w = FXWarningBox.new( self, "#{e}")
269
281
  w.execute
270
282
  return
271
283
  end
272
284
 
273
285
 
286
+
274
287
  d = FXMapFileDialog.new(self, "Save Acrobat PDF File",
275
288
  [
276
289
  "Acrobat PDF (*.pdf)"
@@ -288,6 +301,7 @@ class FXMapperWindow < FXMainWindow
288
301
  "in this version.")
289
302
  w.execute
290
303
  return
304
+ require 'IFMapper/MapPrinting'
291
305
 
292
306
  printer = printer_dialog
293
307
  map.print( printer ) if printer
@@ -324,9 +338,26 @@ class FXMapperWindow < FXMainWindow
324
338
  end
325
339
 
326
340
  def self.copy_selected(map)
327
- selection = map.sections[map.section].rooms.find_all { |r| r.selected }
328
- return if selection.size > 1
329
- @@copy_buffer = selection[0]
341
+ sect = map.sections[map.section]
342
+
343
+ # Get all selected rooms
344
+ rooms = sect.rooms.find_all { |r| r.selected }
345
+ return if rooms.size < 1
346
+
347
+ # Get all selected connections
348
+ links = sect.connections.find_all { |c| c.selected }
349
+
350
+ # Make sure we store only those connections for
351
+ # those rooms we selected
352
+ delete = []
353
+ links.each { |c|
354
+ delete << c if not rooms.include?(c.roomA) or not rooms.include?(c.roomB)
355
+ }
356
+ links -= delete
357
+
358
+ selection = [ rooms, links ]
359
+
360
+ @@copy_buffer = selection
330
361
  end
331
362
 
332
363
  def self.cut_selected(map)
@@ -335,8 +366,37 @@ class FXMapperWindow < FXMainWindow
335
366
  end
336
367
 
337
368
  def self.paste_selected(map)
338
- selection = map.sections[map.section].rooms.find_all { |r| r.selected }
339
- selection.each { |r| r.copy(@@copy_buffer) }
369
+ return if not @@copy_buffer
370
+ return map.navigation_warning if map.navigation
371
+
372
+ sel = @@copy_buffer
373
+ pos = map.find_empty_area( sel[0] )
374
+ if not pos
375
+ w = FXWarningBox.new( map.window,
376
+ "Sorry. No free room in map to paste the nodes.")
377
+ w.execute
378
+ else
379
+ map.clear_selection
380
+
381
+ # Add rooms
382
+ r_to_nr = {} # orig room to new room hash
383
+ sel[0].each { |r|
384
+ nr = map.new_room(r.x + pos[0], r.y + pos[1])
385
+ nr.copy(r) # copy the room data
386
+ r_to_nr[r] = nr
387
+ }
388
+
389
+ # Add connections
390
+ sel[1].each { |c|
391
+ exitA = c.roomA.exits.index(c)
392
+ exitB = c.roomB.exits.rindex(c)
393
+ roomA = r_to_nr[c.roomA]
394
+ roomB = r_to_nr[c.roomB]
395
+ c = map.new_connection(roomA, exitA, roomB, exitB)
396
+ map.path_find(c)
397
+ }
398
+ map.draw
399
+ end
340
400
  end
341
401
 
342
402
  def cut_selected_cb(*o)
@@ -357,9 +417,10 @@ class FXMapperWindow < FXMainWindow
357
417
  FXMapperWindow::paste_selected(map)
358
418
  end
359
419
 
420
+
360
421
  def hilite_matches(map, matches, re, idx = 0 )
361
422
  if matches.size == 0
362
- status "No objects match regex '#{re}'."
423
+ status "No matches for regex '#{re}'."
363
424
  return
364
425
  end
365
426
 
@@ -368,13 +429,21 @@ class FXMapperWindow < FXMainWindow
368
429
 
369
430
  # Jump to first section of match
370
431
  map.section = matches[idx][0]
371
- map.sections[map.section].rooms.each { |r| r.selected = false }
432
+ map.sections.each { |s|
433
+ s.rooms.each { |r| r.selected = false }
434
+ }
372
435
 
373
- matches.each { |p, r| r.selected = true }
436
+ matches.each { |p, r|
437
+ next if p != map.section
438
+ r.selected = true
439
+ }
374
440
 
375
441
  num = matches.find_all { |p, r| p == matches[idx][0] }
442
+ room = matches[idx][1]
443
+ map.center_view_on_room( room )
444
+ update_section
376
445
 
377
- status "#{matches.size} in map, #{num.size} in section."
446
+ status "'#{room.name}' matches. #{matches.size} matches in map, #{num.size} in section."
378
447
  map.draw
379
448
  end
380
449
 
@@ -453,6 +522,23 @@ class FXMapperWindow < FXMainWindow
453
522
  hilite_matches(map, matches, re, @search.index)
454
523
  end
455
524
 
525
+ def find_task_in_map(s, m, e)
526
+ map = current_map
527
+ return unless map
528
+
529
+ re = /#{s.text}/
530
+ matches = []
531
+ (0...map.sections.size).each { |p|
532
+ map.sections[p].rooms.each { |r|
533
+ next unless r.tasks =~ re
534
+ matches.push( [p, r] )
535
+ }
536
+ }
537
+ idx = @search.index
538
+ @search.index = matches.size-1 if idx >= matches.size
539
+ hilite_matches(map, matches, re, @search.index)
540
+ end
541
+
456
542
  def find_object_in_map_cb(s, m, e)
457
543
  map = current_map
458
544
  return unless map
@@ -467,6 +553,20 @@ class FXMapperWindow < FXMainWindow
467
553
  @search.show
468
554
  end
469
555
 
556
+ def find_task_in_map_cb(s, m, e)
557
+ map = current_map
558
+ return unless map
559
+
560
+ title = "Find Task In Map"
561
+ if not @search
562
+ @search = FXSearchDialogBox.new(self)
563
+ end
564
+ @search.proc = method(:find_task_in_map)
565
+ @search.title = title
566
+ @search.text = ''
567
+ @search.show
568
+ end
569
+
470
570
  def colors_cb(sender, id, msg)
471
571
  map = current_map
472
572
  return if not map
@@ -479,7 +579,33 @@ class FXMapperWindow < FXMainWindow
479
579
  @colors.copy_from(map)
480
580
  end
481
581
 
482
- def about_cb(sender, id, msg)
582
+
583
+ def select_none_cb( sender, id, event )
584
+ map = current_map
585
+ return if not map
586
+ sect = map.sections[map.section]
587
+ sect.rooms.each { |r|
588
+ r.selected = false
589
+ }
590
+ sect.connections.each { |c|
591
+ c.selected = false
592
+ }
593
+ end
594
+
595
+
596
+ def select_all_cb( sender, id, event )
597
+ map = current_map
598
+ return if not map
599
+ sect = map.sections[map.section]
600
+ sect.rooms.each { |r|
601
+ r.selected = true
602
+ }
603
+ sect.connections.each { |c|
604
+ c.selected = true
605
+ }
606
+ end
607
+
608
+ def about_cb(sender, id, event )
483
609
  FXAboutDialogBox.new(self, "About This Software...", <<"EOF").execute
484
610
  #{TITLE} - #{VERSION}
485
611
 
@@ -487,10 +613,10 @@ A WYSIWYG mapping tool for interactive fiction.
487
613
  Written by Gonzalo Garramuno.
488
614
 
489
615
  ggarra@advancedsl.com.ar
490
- GGarramuno@aol.com
491
616
  EOF
492
617
  end
493
618
 
619
+
494
620
  def create_menus
495
621
  # Construct these icons
496
622
  newdoc = load_icon("filenew")
@@ -536,8 +662,8 @@ EOF
536
662
  FXMenuCascade.new(filemenu, "Print", nil, submenu)
537
663
 
538
664
 
539
- FXMenuCommand.new(filemenu, "&Quit\tCtl-Q\tQuit the application.", nil,
540
- getApp(), FXApp::ID_QUIT)
665
+ cmd = FXMenuCommand.new(filemenu, "&Quit\tCtl-Q\tQuit the application.", nil)
666
+ cmd.connect( SEL_COMMAND, method(:close_cb) )
541
667
 
542
668
  # Edit Menu
543
669
  editmenu = FXMenuPane.new(self)
@@ -550,14 +676,27 @@ EOF
550
676
  nil)
551
677
  cmd.connect(SEL_COMMAND, method(:paste_selected_cb))
552
678
 
553
- # Searching menu
679
+ # Select submenu
554
680
  FXMenuSeparator.new(editmenu)
555
- cmd = FXMenuCommand.new(editmenu, "&Search Location in Map\tCtl-F\tFind a Location Name in Map")
681
+ submenu = FXMenuPane.new(self)
682
+ cmd = FXMenuCommand.new(submenu, "&All Locations\tAlt-A\tSelect all locations in section")
683
+ cmd.connect(SEL_COMMAND, method(:select_all_cb))
684
+ cmd = FXMenuCommand.new(submenu, "&None\tAlt-N\tClear selection in section.")
685
+ cmd.connect(SEL_COMMAND, method(:select_none_cb))
686
+ FXMenuCascade.new(editmenu, "Select", nil, submenu)
687
+
688
+ # Searching submenu
689
+ FXMenuSeparator.new(editmenu)
690
+ submenu = FXMenuPane.new(self)
691
+ cmd = FXMenuCommand.new(submenu, "&Location in Map\tCtl-F\tFind a Location Name in Map")
556
692
  cmd.connect(SEL_COMMAND, method(:find_in_map_cb))
557
- cmd = FXMenuCommand.new(editmenu, "&Search Location in Section\tAlt-F\tFind a Location Name in Current Section")
693
+ cmd = FXMenuCommand.new(submenu, "Location in &Section\tAlt-F\tFind a Location Name in Current Section")
558
694
  cmd.connect(SEL_COMMAND, method(:find_in_section_cb))
559
- cmd = FXMenuCommand.new(editmenu, "&Search Object in Map\tAlt-O\tFind Location with an Object in the Map")
695
+ cmd = FXMenuCommand.new(submenu, "&Object in Map\tAlt-O\tFind Location with an Object in the Map")
560
696
  cmd.connect(SEL_COMMAND, method(:find_object_in_map_cb))
697
+ cmd = FXMenuCommand.new(submenu, "&Task in Map\tAlt-T\tFind Location with a Task in the Map")
698
+ cmd.connect(SEL_COMMAND, method(:find_task_in_map_cb))
699
+ FXMenuCascade.new(editmenu, "Search", nil, submenu)
561
700
 
562
701
  # Complex Connection
563
702
  FXMenuSeparator.new(editmenu)
@@ -1062,7 +1201,17 @@ EOF
1062
1201
  create_toolbar(toolbar)
1063
1202
  new_map
1064
1203
 
1065
- self.connect(SEL_CLOSE) {
1204
+ self.connect(SEL_CLOSE, method(:close_cb))
1205
+ show
1206
+
1207
+ # Trap CTRL-C signals and exit nicely
1208
+ trap('SIGINT') {
1209
+ close_cb
1210
+ exit(0)
1211
+ }
1212
+ end
1213
+
1214
+ def close_cb(*args)
1066
1215
  exit = true
1067
1216
  @maps.each { |m|
1068
1217
  if not m.close_cb
@@ -1073,13 +1222,5 @@ EOF
1073
1222
  end
1074
1223
  }
1075
1224
  self.close if exit
1076
- }
1077
-
1078
- show
1079
-
1080
- # Trap CTRL-C signals and exit nicely
1081
- trap('SIGINT') {
1082
- exit(0)
1083
- }
1084
1225
  end
1085
1226
  end