11 $stderr.puts ERR_NO_FOX
13 $stderr.puts ERR_HAS_GEMS
19 ##### ARRRGH!!!! Why does Lyle keep changing the fxruby name on each
21 foxes = [ 'fox16', 'fox14', 'fox12', 'fox' ]
25 require "#{fox}/colors"
28 no_fox if fox == foxes[-1]
32 # verify fxruby version
33 ver, rev, = Fox::fxrubyversion().split('.')
34 no_fox if ver.to_i < 1 or rev.to_i < 2
41 require 'IFMapper/FXMap'
42 require 'IFMapper/FXMapperSettings'
43 require 'IFMapper/FXWarningBox'
46 class FXMapperWindow < FXMainWindow
48 PROGRAM_NAME = "Interactive Fiction Mapper"
50 AUTHOR = "Gonzalo Garramuno"
53 @@default_options = FXMapperSettings.new
72 f = File.open(file, 'rb')
82 def open_ifm(file, map)
83 require 'IFMapper/IFMReader'
85 IFMReader.new(file, map)
87 return "#{e} #{e.backtrace}"
92 def open_tads(file, map)
93 require 'IFMapper/TADSReader'
95 TADSReader.new(file, map)
102 def open_inform(file, map)
103 require 'IFMapper/InformReader'
105 InformReader.new(file, map)
112 def open_guemap(file, map)
113 require 'IFMapper/GUEReader'
115 GUEReader.new(file, map)
123 # Start automapping from a transcript
125 def start_automap_cb(sender, sel, ptr)
132 # Properties of the automapper callback
134 def automap_properties_cb(sender, sel, ptr)
136 return if not map or not map.automap
137 map.automap.properties(true)
141 # Stop automapping from a transcript
143 def stop_automap_cb(sender, sel, ptr)
151 # Callback to Open File
153 def open_cb(sender, sel, ptr)
154 require 'IFMapper/FXMapFileDialog'
155 file = FXMapFileDialog.new(self, MSG_LOAD_MAP).filename
158 # First, make sure we don't have it loaded already...
160 if m.filename == file
161 @mdiclient.setActiveChild(m.window)
166 # Then, check if we have a single and empty map.
167 # If so, we can just use that one to load the file in.
168 # If not, we need to create a new map
171 @maps[0].sections.each { |p|
186 status "#{MSG_LOADING} '#{file}'..."
190 tmp = open_ifm(file, map)
191 elsif file =~ /\.inf$/i
192 tmp = open_inform(file, map)
193 elsif file =~ /\.t$/i or file =~ /\.t3m$/
194 tmp = open_tads(file, map)
195 elsif file =~ /\.gmp$/
196 tmp = open_guemap(file, map)
201 if not tmp.kind_of?(Map) and not tmp.kind_of?(FXMap)
203 w = FXWarningBox.new( self,
206 status "#{ERR_COULD_NOT_LOAD} '#{file}'."
218 map.options.replace( @@default_options.merge(map.options) )
224 status "#{MSG_LOADED} '#{file}'."
228 # Write a message to the status bar
231 @statusbar.statusLine.text = msg
235 # Returns current active map or nil if no maps
238 window = @mdiclient.activeChild
239 return nil unless window
242 return m if m.window == window
250 def save_cb(sender, sel, ptr)
257 # Callback for Save As
259 def save_as_cb(sender, sel, ptr)
266 # Callback used to create new map
268 def new_map_cb(*args)
274 # Callback used to change language
276 def language_cb(sender, msg, opts)
277 @@default_options['Language'] = LANGUAGES[sender.text]
279 require "IFMapper/locales/#{language}/Messages.rb"
287 mapname = "#{MSG_EMPTY_MAP} \##{@maps.size+1}"
288 @maps.push( FXMap.new(mapname, @mdiclient, @@default_options.dup,
289 @mdiicon, @mdimenu, MDI_NORMAL, 0, 0, 790, 500) )
291 map.window.connect(SEL_PAINT) {
294 map.window.connect(SEL_CLOSE) {
299 @maps[-1].update_roomlist
306 @mdiclient.setActiveChild(map.window)
312 # Load the named PNG icon from a file
314 def load_icon(filename)
316 filename = File.join("icons", filename) + ".png"
318 File.open(filename, "rb") { |f|
319 icon = FXPNGIcon.new(getApp(), f.read)
323 raise RuntimeError, "#{ERR_NO_ICON} #{filename}"
328 # Start a complex connection
330 def complex_connection_cb(sender, sel, msg)
333 map.complex_connection
337 # Delete selected elements in map
339 def delete_selected_cb(sender, sel, msg)
346 # Popup a printer dialog and return the settings or false if user cancels
348 def printer_dialog(title = MSG_PRINT_MAP)
350 dlg = FXPrintDialog.new(self, title + " for #{map.name}")
351 dlg.printer.flags |= PRINT_DEST_PAPER
352 return dlg.printer if dlg.execute != 0
357 # Print out all the locations in map
359 def print_locations_cb(sender, sel, msg)
363 w = FXWarningBox.new( self, ERR_NO_PRINTING)
367 printer = printer_dialog MSG_PRINT_LOC
368 map.print_locations( printer ) if printer
372 # Export current map as an IFM file
374 def ifm_export_cb(sender, sel, msg)
378 require 'IFMapper/FXMapFileDialog'
379 d = FXMapFileDialog.new(self, MSG_SAVE_MAP_AS_IFM,
383 map.export_ifm(d.filename) if d.filename != ''
387 # Export current map as an Inform source file
389 def inform_export_cb(sender, sel, msg)
393 require 'IFMapper/FXMapFileDialog'
394 d = FXMapFileDialog.new(self, MSG_SAVE_MAP_AS_INFORM,
398 map.export_inform( d.filename ) if d.filename != ''
403 # Export current map as a TADs source file
405 def tads_export_cb(sender, sel, msg)
409 require 'IFMapper/TADSWriter'
410 require 'IFMapper/FXMapFileDialog'
411 d = FXMapFileDialog.new(self, MSG_SAVE_MAP_AS_TADS,
415 map.export_tads( d.filename ) if d.filename != ''
420 # Export current map as Acrobat PDF
422 def pdf_export_cb(sender, sel, msg)
427 require 'IFMapper/PDFMapExporter'
428 rescue LoadError => e
429 w = FXWarningBox.new( self, "#{e}")
434 # PRE: Let's ask for a page size and orientation for the PDF
435 # and whether the user wants to include location numbers
437 map.pdflocationnos = 1
438 require 'IFMapper/FXPDFMapExporterOptionsDialogBox'
439 FXPDFMapExporterOptionsDialogBox.new(self, MSG_SAVE_MAP_AS_PDF, map).execute
441 require 'IFMapper/FXMapFileDialog'
442 d = FXMapFileDialog.new(self, MSG_SAVE_MAP_AS_PDF,
447 map.pdf_export(d.filename)
452 # Print current map graphically
454 def print_cb(sender, sel, msg)
458 w = FXWarningBox.new( self, ERR_NO_PRINTING )
461 require 'IFMapper/MapPrinting'
463 printer = printer_dialog
464 map.print( printer ) if printer
475 # Creates the MDI (multi-document) client
481 @mdiclient = FXMDIClient.new(self, LAYOUT_FILL_X|LAYOUT_FILL_Y)
482 @mdiclient.connect(SEL_CHANGED) {
486 # MDI buttons in menu:- note the message ID's!!!!!
487 # Normally, MDI commands are simply sensitized or desensitized;
488 # Under the @menubar, however, they're hidden if the MDI Client is
489 # not maximized. To do this, they must have different ID's.
490 FXMDIWindowButton.new(@menubar, @mdimenu, @mdiclient,
491 FXMDIClient::ID_MDI_MENUWINDOW, LAYOUT_LEFT)
492 FXMDIDeleteButton.new(@menubar, @mdiclient,
493 FXMDIClient::ID_MDI_MENUCLOSE, FRAME_RAISED|LAYOUT_RIGHT)
494 FXMDIRestoreButton.new(@menubar, @mdiclient,
495 FXMDIClient::ID_MDI_MENURESTORE, FRAME_RAISED|LAYOUT_RIGHT)
496 FXMDIMinimizeButton.new(@menubar, @mdiclient,
497 FXMDIClient::ID_MDI_MENUMINIMIZE, FRAME_RAISED|LAYOUT_RIGHT)
500 @mdiicon = load_icon("winapp")
502 # Make MDI Window Menu
503 @mdimenu = FXMDIMenu.new(self, @mdiclient)
507 # Return the copied elements
514 # Copy selected elements
516 def self.copy_selected(map)
517 sect = map.sections[map.section]
519 # Get all selected rooms
520 rooms = sect.rooms.find_all { |r| r.selected }
521 return if rooms.size < 1
523 # Get all selected connections
524 links = sect.connections.find_all { |c| c.selected }
526 # Make sure we store only those connections for
527 # those rooms we selected
530 if not rooms.include?(c.roomA) or
531 (c.roomB and not rooms.include?(c.roomB))
537 selection = [ rooms, links ]
539 @@copy_buffer = selection
543 # Cut selected elements
545 def self.cut_selected(map)
546 FXMapperWindow::copy_selected(map)
551 # Paste selected elements
553 def self.paste_selected(map)
554 return if not @@copy_buffer
555 return map.navigation_warning if map.navigation
558 pos = map.find_empty_area( sel[0] )
560 w = FXWarningBox.new( map.window, ERR_NO_FREE_ROOM)
566 r_to_nr = {} # orig room to new room hash
569 nr = map.new_room(r.x + pos[0], r.y + pos[1])
571 nr.copy(r) # copy the room data
576 # Add connections only (no rooms copied)
578 exitA, exitB = c.dirs
581 sect = map.sections[map.section]
582 if not sect.rooms.include?(roomA) or
583 (roomB and not sect.rooms.include?(roomB))
587 nc = map.new_connection(roomA, exitA, roomB, exitB)
597 exitA, exitB = c.dirs
598 roomA = r_to_nr[c.roomA]
600 roomB = r_to_nr[c.roomB]
606 nc = map.new_connection(roomA, exitA, roomB, exitB)
610 rescue Section::ConnectionError => e
622 def cut_selected_cb(*o)
625 FXMapperWindow::cut_selected(map)
628 def copy_selected_cb(*o)
631 FXMapperWindow::copy_selected(map)
634 def paste_selected_cb(*o)
636 return if not map or not @@copy_buffer
637 FXMapperWindow::paste_selected(map)
642 # Hilite matches after a search
644 def hilite_matches(map, matches, re, idx = 0 )
646 status "#{ERR_NO_MATCHES} '#{re}'."
650 # sort matches by section
651 matches.sort_by { |a| a[0] }
653 # Jump to first section of match
654 map.section = matches[idx][0]
655 map.sections.each { |s|
656 s.rooms.each { |r| r.selected = false }
659 matches.each { |p, r|
660 next if p != map.section
664 num = matches.find_all { |p, r| p == matches[idx][0] }
665 room = matches[idx][1]
666 map.center_view_on_room( room )
669 status "'#{room.name}' #{MSG_MATCHES}. #{matches.size} #{MSG_MATCHES_IN_MAP}, #{num.size} #{MSG_MATCHES_IN_SECTION}."
674 # Find location in map
676 def find_in_map(s, m, e)
682 (0...map.sections.size).each { |p|
683 map.sections[p].rooms.each { |r|
684 next unless r.name =~ re
685 matches.push( [p, r] )
689 @search.index = matches.size-1 if idx >= matches.size
690 hilite_matches(map, matches, re, @search.index)
693 def find_in_map_cb(s, m, e)
697 title = MSG_FIND_LOCATION_IN_MAP
699 require 'IFMapper/FXSearchDialogBox'
700 @search = FXSearchDialogBox.new(self)
702 @search.proc = method(:find_in_map)
703 @search.title = title
709 # Find location in section
711 def find_in_section(s, m, e)
717 map.sections[map.section].rooms.each { |r|
718 next unless r.name =~ re
719 matches.push( [ map.section, r] )
721 hilite_matches(map, matches, re)
727 def find_in_section_cb(s, m, e)
731 title = MSG_FIND_LOCATION_IN_SECTION
733 require 'IFMapper/FXSearchDialogBox'
734 @search = FXSearchDialogBox.new(self)
736 @search.proc = method(:find_in_section)
737 @search.title = title
745 def find_object_in_map(s, m, e)
751 (0...map.sections.size).each { |p|
752 map.sections[p].rooms.each { |r|
753 next unless r.objects =~ re
754 matches.push( [p, r] )
758 @search.index = matches.size-1 if idx >= matches.size
759 hilite_matches(map, matches, re, @search.index)
765 def find_task_in_map(s, m, e)
771 (0...map.sections.size).each { |p|
772 map.sections[p].rooms.each { |r|
773 next unless r.tasks =~ re
774 matches.push( [p, r] )
778 @search.index = matches.size-1 if idx >= matches.size
779 hilite_matches(map, matches, re, @search.index)
785 def find_object_in_map_cb(s, m, e)
789 title = MSG_FIND_OBJECT_IN_MAP
791 require 'IFMapper/FXSearchDialogBox'
792 @search = FXSearchDialogBox.new(self)
794 @search.proc = method(:find_object_in_map)
795 @search.title = title
803 def find_task_in_map_cb(s, m, e)
807 title = MSG_FIND_TASK_IN_MAP
809 require 'IFMapper/FXSearchDialogBox'
810 @search = FXSearchDialogBox.new(self)
812 @search.proc = method(:find_task_in_map)
813 @search.title = title
821 def find_desc_in_map(s, m, e)
827 (0...map.sections.size).each { |p|
828 map.sections[p].rooms.each { |r|
829 next unless r.desc =~ re
830 matches.push( [p, r] )
834 @search.index = matches.size-1 if idx >= matches.size
835 hilite_matches(map, matches, re, @search.index)
839 # Find description in map
841 def find_desc_in_map_cb(s, m, e)
845 title = MSG_FIND_DESCRIPTION_IN_MAP
847 require 'IFMapper/FXSearchDialogBox'
848 @search = FXSearchDialogBox.new(self)
850 @search.proc = method(:find_desc_in_map)
851 @search.title = title
858 # Pop-up color preferences
860 def colors_cb(sender, id, msg)
865 require 'IFMapper/FXMapColorBox'
866 @colors = FXMapColorBox.new(self)
870 @colors.copy_from(map)
876 def select_none_cb( sender, id, event )
885 def select_all_cb( sender, id, event )
888 sect = map.sections[map.section]
889 sect.rooms.each { |r|
892 sect.connections.each { |c|
897 def roomlist(sender, sel, event)
904 def about_cb(sender, id, event )
905 require 'IFMapper/FXAboutDialogBox'
906 FXAboutDialogBox.new(self, MSG_ABOUT_SOFTWARE,
907 eval("\"#{MSG_ABOUT}\"")).execute
912 # Construct these icons
913 newdoc = load_icon("filenew")
914 opendoc = load_icon("fileopen")
915 savedoc = load_icon("filesave")
916 saveasdoc = load_icon("filesaveas")
919 filemenu = FXMenuPane.new(self)
920 FXMenuTitle.new(@menubar, MENU_FILE, nil, filemenu)
921 cmd = FXMenuCommand.new(filemenu, MENU_NEW, newdoc)
922 cmd.connect(SEL_COMMAND, method(:new_map_cb))
924 cmd = FXMenuCommand.new(filemenu, MENU_OPEN, opendoc)
925 cmd.connect(SEL_COMMAND, method(:open_cb))
926 cmd = FXMenuCommand.new(filemenu, MENU_SAVE, savedoc)
927 cmd.connect(SEL_COMMAND, method(:save_cb))
928 cmd = FXMenuCommand.new(filemenu, MENU_SAVE_AS,
930 cmd.connect(SEL_COMMAND, method(:save_as_cb))
933 submenu = FXMenuPane.new(self)
935 cmd = FXMenuCommand.new(submenu, MENU_EXPORT_PDF, nil)
936 cmd.connect(SEL_COMMAND, method(:pdf_export_cb))
938 cmd = FXMenuCommand.new(submenu, MENU_EXPORT_IFM, nil)
939 cmd.connect(SEL_COMMAND, method(:ifm_export_cb))
941 cmd = FXMenuCommand.new(submenu, MENU_EXPORT_INFORM, nil)
942 cmd.connect(SEL_COMMAND, method(:inform_export_cb))
944 cmd = FXMenuCommand.new(submenu, MENU_EXPORT_TADS, nil)
945 cmd.connect(SEL_COMMAND, method(:tads_export_cb))
947 FXMenuCascade.new(filemenu, MENU_EXPORT, nil, submenu)
950 submenu = FXMenuPane.new(self)
951 cmd = FXMenuCommand.new(submenu, MENU_PRINT_MAP, nil)
952 cmd.connect(SEL_COMMAND, method(:print_cb))
954 cmd = FXMenuCommand.new(submenu, MENU_PRINT_LOCATIONS, nil)
955 cmd.connect(SEL_COMMAND, method(:print_locations_cb))
956 FXMenuCascade.new(filemenu, MENU_PRINT, nil, submenu)
958 cmd = FXMenuCommand.new(filemenu, MENU_QUIT, nil)
959 cmd.connect( SEL_COMMAND, method(:close_cb) )
962 editmenu = FXMenuPane.new(self)
963 FXMenuTitle.new(@menubar, MENU_EDIT, nil, editmenu)
964 cmd = FXMenuCommand.new(editmenu, MENU_COPY, nil)
965 cmd.connect(SEL_COMMAND, method(:copy_selected_cb))
966 cmd = FXMenuCommand.new(editmenu, MENU_CUT, nil)
967 cmd.connect(SEL_COMMAND, method(:cut_selected_cb))
968 cmd = FXMenuCommand.new(editmenu, MENU_PASTE, nil)
969 cmd.connect(SEL_COMMAND, method(:paste_selected_cb))
972 FXMenuSeparator.new(editmenu)
973 submenu = FXMenuPane.new(self)
974 cmd = FXMenuCommand.new( submenu, MENU_SELECT_ALL )
975 cmd.connect(SEL_COMMAND, method(:select_all_cb))
976 cmd = FXMenuCommand.new( submenu, MENU_SELECT_NONE )
977 cmd.connect(SEL_COMMAND, method(:select_none_cb))
978 FXMenuCascade.new( editmenu, MENU_SELECT, nil, submenu )
981 FXMenuSeparator.new(editmenu)
982 submenu = FXMenuPane.new(self)
983 cmd = FXMenuCommand.new(submenu, MENU_SEARCH_MAP)
984 cmd.connect(SEL_COMMAND, method(:find_in_map_cb))
985 cmd = FXMenuCommand.new(submenu, MENU_SEARCH_SECTION)
986 cmd.connect(SEL_COMMAND, method(:find_in_section_cb))
987 cmd = FXMenuCommand.new(submenu, MENU_SEARCH_OBJECT)
988 cmd.connect(SEL_COMMAND, method(:find_object_in_map_cb))
989 cmd = FXMenuCommand.new(submenu, MENU_SEARCH_TASK)
990 cmd.connect(SEL_COMMAND, method(:find_task_in_map_cb))
991 cmd = FXMenuCommand.new(submenu, MENU_SEARCH_DESCRIPTION)
992 cmd.connect(SEL_COMMAND, method(:find_desc_in_map_cb))
993 FXMenuCascade.new(editmenu, MENU_SEARCH, nil, submenu)
996 FXMenuSeparator.new(editmenu)
997 cmd = FXMenuCommand.new(editmenu, MENU_COMPLEX_CONNECTION, nil)
998 cmd.connect( SEL_COMMAND, method(:complex_connection_cb) )
1000 FXMenuSeparator.new(editmenu)
1001 cmd = FXMenuCommand.new(editmenu, MENU_DELETE, nil)
1002 cmd.connect( SEL_COMMAND, method(:delete_selected_cb) )
1005 mapmenu = FXMenuPane.new(self)
1007 cmd = FXMenuCommand.new(mapmenu, MENU_MAP_INFO)
1008 cmd.connect(SEL_COMMAND) { map_properties }
1010 cmd = FXMenuCommand.new(mapmenu, MENU_ROOM_LIST)
1011 cmd.connect(SEL_COMMAND, method(:roomlist) )
1015 submenu = FXMenuPane.new(self)
1016 cmd = FXMenuCommand.new(submenu, MENU_AUTOMAP_START)
1017 cmd.connect(SEL_COMMAND, method(:start_automap_cb))
1018 cmd = FXMenuCommand.new(submenu, MENU_AUTOMAP_STOP)
1019 cmd.connect(SEL_COMMAND, method(:stop_automap_cb))
1020 FXMenuSeparator.new(submenu)
1021 cmd = FXMenuCommand.new(submenu, MENU_AUTOMAP_PROPERTIES)
1022 cmd.connect(SEL_COMMAND, method(:automap_properties_cb))
1023 FXMenuCascade.new(mapmenu, MENU_AUTOMAP, nil, submenu)
1026 submenu = FXMenuPane.new(self)
1027 cmd = FXMenuCommand.new(submenu, MENU_NEXT_SECTION)
1028 cmd.connect(SEL_COMMAND) {
1031 cmd = FXMenuCommand.new(submenu, MENU_PREVIOUS_SECTION)
1032 cmd.connect(SEL_COMMAND) {
1035 FXMenuSeparator.new(submenu)
1036 cmd = FXMenuCommand.new(submenu, MENU_ADD_SECTION)
1037 cmd.connect(SEL_COMMAND) {
1045 cmd = FXMenuCommand.new(submenu, MENU_RENAME_SECTION)
1046 cmd.connect(SEL_COMMAND) {
1053 FXMenuSeparator.new(submenu)
1054 cmd = FXMenuCommand.new(submenu, MENU_DELETE_SECTION)
1055 cmd.connect(SEL_COMMAND) {
1063 FXMenuCascade.new(mapmenu, MENU_SECTIONS, nil, submenu)
1068 submenu = FXMenuPane.new(self)
1069 [25, 50, 75, 100, 125].each { |v|
1070 cmd = FXMenuCommand.new(submenu, eval("\"#{MENU_ZOOM_PERCENT}\""))
1071 cmd.connect(SEL_COMMAND) {
1074 map.zoom = v / 100.0
1079 FXMenuCascade.new(mapmenu, MENU_ZOOM, nil, submenu)
1081 submenu = FXMenuPane.new(self)
1083 cmd = FXMenuCheck.new(submenu, MENU_EDIT_ON_CREATION)
1084 cmd.check = @@default_options['Edit on Creation']
1085 cmd.connect(SEL_COMMAND) { |s, m, e|
1088 map.options['Edit on Creation'] = (s.check == true)
1091 cmd.connect(SEL_UPDATE) { |s, m, e|
1093 s.check = map.options['Edit on Creation'] if map
1096 cmd = FXMenuCheck.new(submenu, MENU_AUTOMATIC_CONNECTION)
1097 cmd.check = @@default_options['Automatic Connection']
1098 cmd.connect(SEL_COMMAND) { |s, m, e|
1101 map.options['Automatic Connection'] = (s.check == true)
1104 cmd.connect(SEL_UPDATE) { |s, m, e|
1106 s.check = map.options['Automatic Connection'] if map
1109 cmd = FXMenuCheck.new(submenu, MENU_CREATE_ON_CONNECTION)
1110 cmd.check = @@default_options['Create on Connection']
1111 cmd.connect(SEL_COMMAND) { |s, m, e|
1113 map.options['Create on Connection'] = s.check if map
1115 cmd.connect(SEL_UPDATE) { |s, m, e|
1117 s.check = map.options['Create on Connection'] if map
1120 FXMenuCascade.new(mapmenu, MENU_OPTIONS, nil, submenu)
1122 ##########################
1124 #########################
1125 submenu = FXMenuPane.new(self)
1126 cmd = FXMenuCheck.new(submenu, MENU_USE_ROOM_CURSOR)
1127 cmd.check = @@default_options['Use Room Cursor']
1128 cmd.connect(SEL_COMMAND) { |s, m, e|
1131 map.options['Use Room Cursor'] = (s.check == true)
1135 cmd.connect(SEL_UPDATE) { |s, m, e|
1137 s.check = map.options['Use Room Cursor'] if map
1140 cmd = FXMenuCheck.new(submenu, MENU_PATHS_AS_CURVES)
1141 cmd.check = @@default_options['Paths as Curves']
1142 cmd.connect(SEL_COMMAND) { |s, m, e|
1145 map.options['Paths as Curves'] = (s.check == true)
1149 cmd.connect(SEL_UPDATE) { |s, m, e|
1151 s.check = map.options['Paths as Curves'] if map
1153 cmd = FXMenuCheck.new(submenu, MENU_LOCATION_NUMBERS)
1154 cmd.check = @@default_options['Location Numbers']
1155 cmd.connect(SEL_COMMAND) { |s, m, e|
1158 map.options['Location Numbers'] = (s.check == true)
1162 cmd.connect(SEL_UPDATE) { |s, m, e|
1164 s.check = map.options['Location Numbers'] if map
1167 cmd = FXMenuCheck.new(submenu, MENU_LOCATION_TASKS)
1168 cmd.check = @@default_options['Location Tasks']
1169 cmd.connect(SEL_COMMAND) { |s, m, e|
1172 map.options['Location Tasks'] = (s.check == true)
1176 cmd.connect(SEL_UPDATE) { |s, m, e|
1178 s.check = map.options['Location Tasks'] if map
1181 cmd = FXMenuCheck.new(submenu, MENU_LOCATION_DESC)
1182 cmd.check = @@default_options['Location Description']
1183 cmd.connect(SEL_COMMAND) { |s, m, e|
1186 map.options['Location Description'] = (s.check == true)
1190 cmd.connect(SEL_UPDATE) { |s, m, e|
1192 s.check = map.options['Location Description'] if map
1195 cmd = FXMenuCheck.new(submenu, MENU_BOXES)
1196 cmd.check = @@default_options['Grid Boxes']
1197 cmd.connect(SEL_COMMAND) { |s, m, e|
1200 map.options['Grid Boxes'] = (s.check == true)
1204 cmd.connect(SEL_UPDATE) { |s, m, e|
1206 s.check = map.options['Grid Boxes'] if map
1209 cmd = FXMenuCheck.new(submenu, MENU_STRAIGHT_CONN)
1210 cmd.check = @@default_options['Grid Straight Connections']
1211 cmd.connect(SEL_COMMAND) { |s, m, e|
1214 map.options['Grid Straight Connections'] = (s.check == true)
1218 cmd.connect(SEL_UPDATE) { |s, m, e|
1220 s.check = map.options['Grid Straight Connections'] if map
1223 cmd = FXMenuCheck.new(submenu, MENU_DIAGONAL_CONN)
1224 cmd.check = @@default_options['Grid Diagonal Connections']
1225 cmd.connect(SEL_COMMAND) { |s, m, e|
1228 map.options['Grid Diagonal Connections'] = s.check
1232 cmd.connect(SEL_UPDATE) { |s, m, e|
1234 s.check = map.options['Grid Diagonal Connections'] if map
1237 FXMenuCascade.new(mapmenu, MENU_DISPLAY, nil, submenu)
1239 submenu = FXMenuPane.new(self)
1241 cmd = FXMenuCommand.new(submenu, MENU_COLORS)
1242 cmd.connect(SEL_COMMAND, method(:colors_cb))
1244 # langmenu = FXMenuPane.new(self)
1245 # LANGUAGES.each { |k, v|
1246 # next unless File.exists?( "lib/IFMapper/locales/#{v}/Messages.rb" )
1247 # cmd = FXMenuCheck.new(langmenu, k)
1248 # cmd.connect(SEL_COMMAND, method(:language_cb))
1251 # FXMenuCascade.new(mapmenu, MENU_LANGUAGE, nil, langmenu)
1254 FXMenuSeparator.new(submenu)
1255 cmd = FXMenuCommand.new(submenu, MENU_SAVE_PREFS)
1256 cmd.connect(SEL_COMMAND) {
1258 map.options.write if map
1260 FXMenuCascade.new(mapmenu, MENU_PREFS, nil, submenu)
1262 FXMenuTitle.new(@menubar, MENU_MAP, nil, mapmenu)
1265 windowmenu = FXMenuPane.new(self)
1266 FXMenuCommand.new(windowmenu, MENU_TILE_HORIZONTALLY, nil,
1267 @mdiclient, FXMDIClient::ID_MDI_TILEHORIZONTAL)
1268 FXMenuCommand.new(windowmenu, MENU_TILE_VERTICALLY, nil,
1269 @mdiclient, FXMDIClient::ID_MDI_TILEVERTICAL)
1270 FXMenuCommand.new(windowmenu, MENU_CASCADE, nil,
1271 @mdiclient, FXMDIClient::ID_MDI_CASCADE)
1272 FXMenuCommand.new(windowmenu, MENU_CLOSE, nil,
1273 @mdiclient, FXMDIClient::ID_MDI_CLOSE)
1274 sep1 = FXMenuSeparator.new(windowmenu)
1275 sep1.setTarget(@mdiclient)
1276 sep1.setSelector(FXMDIClient::ID_MDI_ANY)
1277 FXMenuCommand.new(windowmenu, nil, nil, @mdiclient, FXMDIClient::ID_MDI_1)
1278 FXMenuCommand.new(windowmenu, nil, nil, @mdiclient, FXMDIClient::ID_MDI_2)
1279 FXMenuCommand.new(windowmenu, nil, nil, @mdiclient, FXMDIClient::ID_MDI_3)
1280 FXMenuCommand.new(windowmenu, nil, nil, @mdiclient, FXMDIClient::ID_MDI_4)
1281 FXMenuCommand.new(windowmenu, MENU_OTHERS, nil, @mdiclient,
1282 FXMDIClient::ID_MDI_OVER_5)
1283 FXMenuTitle.new(@menubar, MENU_WINDOW, nil, windowmenu)
1286 helpmenu = FXMenuPane.new(self)
1287 cmd = FXMenuCommand.new(helpmenu, MENU_HOTKEYS, nil)
1288 cmd.connect(SEL_COMMAND, method(:hotkeys))
1289 cmd = FXMenuCommand.new(helpmenu, MENU_INSTRUCTIONS, nil)
1290 cmd.connect(SEL_COMMAND, method(:docs))
1291 FXMenuSeparator.new(helpmenu)
1292 cmd = FXMenuCommand.new(helpmenu, MENU_ABOUT, nil)
1293 cmd.connect(SEL_COMMAND, method(:about_cb))
1295 cmd = FXMenuCommand.new(helpmenu, MENU_RESOURCE, nil)
1296 cmd.connect(SEL_COMMAND) {
1297 require 'IFMapper/FXMapFileDialog'
1298 file = FXMapFileDialog.new(self, "Resource a Ruby File",
1299 ['Ruby File (*.rb)']).filename
1308 FXMenuTitle.new(@menubar, MENU_HELP, nil, helpmenu)
1312 return @@default_options['Language']
1316 browsers = [ 'firefox', 'opera', 'explorer' ]
1317 address = 'docs/' + language + '/start.html'
1318 status "#{MSG_OPENING_WEB_PAGE} #{address}..."
1320 browsers.each { |cmd|
1321 if RUBY_PLATFORM =~ /mswin/
1322 ok = system("start #{cmd} #{address}")
1324 ok = system("#{cmd} #{address} &")
1329 status ERR_COULD_NOT_OPEN_WEB_BROWSER
1335 require 'IFMapper/FXAboutDialogBox'
1336 FXAboutDialogBox.new(self, BOX_HOTKEYS, MSG_HOTKEYS).show
1339 def create_toolbar(toolbar)
1341 # Construct these icons
1342 newdoc = load_icon("filenew")
1343 opendoc = load_icon("fileopen")
1344 savedoc = load_icon("filesave")
1345 saveasdoc = load_icon("filesaveas")
1348 cmd = FXButton.new(toolbar, ICON_NEW, newdoc, nil, 0,
1349 FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
1350 cmd.connect(SEL_COMMAND, method(:new_map_cb))
1352 cmd = FXButton.new(toolbar, ICON_OPEN, opendoc, nil, 0,
1353 FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
1354 cmd.connect(SEL_COMMAND, method(:open_cb))
1356 cmd = FXButton.new(toolbar, ICON_SAVE, savedoc, nil, 0,
1357 FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
1358 cmd.connect(SEL_COMMAND, method(:save_cb))
1359 cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
1361 message = map ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
1362 sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
1364 cmd = FXButton.new(toolbar, ICON_SAVE_AS,
1365 saveasdoc, nil, 0, FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
1366 cmd.connect(SEL_COMMAND, method(:save_as_cb))
1367 cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
1369 message = map ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
1370 sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
1374 FXFrame.new(toolbar,
1375 LAYOUT_TOP|LAYOUT_LEFT|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT, 0, 0, 4, 20)
1376 cmd = FXButton.new(toolbar, ICON_PRINT,
1377 load_icon("printicon"), @mdiclient, FXGLViewer::ID_PRINT_IMAGE,
1378 BUTTON_AUTOGRAY|FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
1379 cmd.connect(SEL_COMMAND, method(:print_cb))
1380 cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
1382 message = map ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
1383 sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
1387 FXFrame.new(toolbar,
1388 LAYOUT_TOP|LAYOUT_LEFT|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT, 0, 0, 4, 20)
1389 cmd = FXButton.new(toolbar, ICON_CUT, load_icon("cut"), @mdiclient,
1390 FXGLViewer::ID_CUT_SEL, (BUTTON_AUTOGRAY|FRAME_THICK|FRAME_RAISED|
1391 LAYOUT_TOP|LAYOUT_LEFT))
1392 cmd.connect(SEL_COMMAND, method(:cut_selected_cb))
1393 cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
1395 message = map ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
1396 sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
1398 cmd = FXButton.new(toolbar, ICON_COPY, load_icon("copy"), @mdiclient,
1399 FXGLViewer::ID_COPY_SEL, (BUTTON_AUTOGRAY|FRAME_THICK|FRAME_RAISED|
1400 LAYOUT_TOP|LAYOUT_LEFT))
1401 cmd.connect(SEL_COMMAND, method(:copy_selected_cb))
1402 cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
1404 message = map ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
1405 sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
1407 cmd = FXButton.new(toolbar, ICON_PASTE, load_icon("paste"), @mdiclient,
1408 FXGLViewer::ID_PASTE_SEL, (BUTTON_AUTOGRAY|FRAME_THICK|FRAME_RAISED|
1409 LAYOUT_TOP|LAYOUT_LEFT))
1410 cmd.connect(SEL_COMMAND, method(:paste_selected_cb))
1411 cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
1413 message = (map and @@copy_buffer) ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
1414 sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
1418 FXFrame.new(toolbar,
1419 LAYOUT_TOP|LAYOUT_LEFT|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT, 0, 0, 4, 20)
1420 cmd = FXButton.new(toolbar, ICON_ZOOM_IN, load_icon("zoom"), @mdiclient,
1421 0, FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
1422 cmd.connect(SEL_COMMAND) { zoom_in }
1424 cmd = FXButton.new(toolbar, ICON_ZOOM_OUT, load_icon("zoom"), @mdiclient,
1425 0, FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
1426 cmd.connect(SEL_COMMAND) { zoom_out }
1430 frame = FXHorizontalFrame.new(toolbar,
1431 LAYOUT_RIGHT|FRAME_THICK|FRAME_RAISED)
1432 cmd = FXButton.new(frame, ICON_PREV_SECTION, load_icon("prevpage"),
1434 0, FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
1435 cmd.connect(SEL_COMMAND) { previous_section }
1437 @section = FXTextField.new(frame, 5, nil, 0,
1438 TEXTFIELD_INTEGER|LAYOUT_FILL_ROW)
1440 @section.connect(SEL_COMMAND) { |s,m,e|
1449 @section.connect(SEL_UPDATE) { |s,m,e|
1452 update_section if map
1455 cmd = FXButton.new(frame, ICON_NEXT_SECTION, load_icon("nextpage"),
1457 0, FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
1458 cmd.connect(SEL_COMMAND) { next_section }
1462 # Update section # in toolbar widget
1467 @section.text = (map.section + 1).to_s
1471 # Go to next section in current map
1475 map.next_section if map
1480 # Go to previous section in current map
1482 def previous_section
1484 map.previous_section if map
1489 # Zoom in into current map
1500 # Zoom out from current map
1511 # Bring up the map property requester for current map
1515 map.properties if map
1519 # In case of crash or runtime error, autosave all maps, so user
1520 # does not loose any data.
1531 @menubar = FXMenuBar.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X)
1533 FXHorizontalSeparator.new(self,
1534 LAYOUT_SIDE_TOP|SEPARATOR_GROOVE|LAYOUT_FILL_X)
1535 toolbar = FXToolBar.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X,
1536 0, 0, 0, 0, 4, 4, 0, 0, 0, 0)
1539 @statusbar = FXStatusBar.new(self,
1540 LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|
1541 STATUSBAR_WITH_DRAGCORNER)
1546 create_toolbar(toolbar)
1549 self.connect(SEL_CLOSE, method(:close_cb))
1554 super(app, eval("\"#{TITLE}\""), nil, nil, DECOR_ALL, 0, 0, 800, 600)
1561 # Trap CTRL-C signals and exit nicely