ifmapper 0.6 → 0.7
Sign up to get free protection for your applications and to get access to all the features.
- 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
|