ifmapper 0.6 → 0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/HISTORY.txt +68 -1
- data/IFMapper.gemspec +2 -2
- data/IFMapper.rb +5 -1
- data/lib/IFMapper/AStar.rb +7 -7
- data/lib/IFMapper/Connection.rb +2 -2
- data/lib/IFMapper/FXConnection.rb +7 -4
- data/lib/IFMapper/FXDCPostscript.rb +404 -0
- data/lib/IFMapper/FXDCPrint.rb +15 -0
- data/lib/IFMapper/FXMap.rb +271 -60
- data/lib/IFMapper/FXMapperSettings.rb +26 -7
- data/lib/IFMapper/FXMapperWindow.rb +209 -68
- data/lib/IFMapper/FXRoom.rb +8 -5
- data/lib/IFMapper/IFMReader.rb +4 -3
- data/lib/IFMapper/IFMWriter.rb +219 -0
- data/lib/IFMapper/Map.rb +23 -5
- data/lib/IFMapper/MapPrinting.rb +1 -0
- data/lib/IFMapper/PDFMapExporter.rb +11 -9
- data/lib/IFMapper/Room.rb +13 -9
- data/lib/IFMapper/Section.rb +2 -1
- metadata +17 -14
@@ -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
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
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' => '
|
29
|
-
'Font Objects' => '
|
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
|
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
|
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
|
-
|
36
|
+
|
31
37
|
|
32
38
|
|
33
39
|
class FXMapperWindow < FXMainWindow
|
34
40
|
|
35
41
|
PROGRAM_NAME = "Interactive Fiction Mapper"
|
36
|
-
VERSION = 0.
|
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, "
|
222
|
-
|
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
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
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
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
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
|
-
|
328
|
-
|
329
|
-
|
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
|
-
|
339
|
-
|
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
|
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
|
432
|
+
map.sections.each { |s|
|
433
|
+
s.rooms.each { |r| r.selected = false }
|
434
|
+
}
|
372
435
|
|
373
|
-
matches.each { |p, r|
|
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
|
-
|
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
|
-
|
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
|
-
#
|
679
|
+
# Select submenu
|
554
680
|
FXMenuSeparator.new(editmenu)
|
555
|
-
|
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(
|
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(
|
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
|