ifmapper 1.0.0 → 1.0.6

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 (218) hide show
  1. data/HISTORY.txt +648 -627
  2. data/IFMapper.gemspec +29 -28
  3. data/IFMapper.rbw +31 -31
  4. data/TODO.txt +8 -7
  5. data/bin/IFMapper +31 -31
  6. data/docs/en/index.html +0 -0
  7. data/docs/en/start.html +3 -2
  8. data/docs/en/start.html~ +516 -0
  9. data/docs/es/index.html +0 -0
  10. data/docs/es/start.html +13 -14
  11. data/docs/es/start.html~ +1280 -0
  12. data/docs/images/IFMapper_main.gif +0 -0
  13. data/docs/images/automap.gif +0 -0
  14. data/docs/images/complex_connection.gif +0 -0
  15. data/docs/images/connection.gif +0 -0
  16. data/docs/images/connection_menu.gif +0 -0
  17. data/docs/images/room_description.gif +0 -0
  18. data/docs/images/room_small.gif +0 -0
  19. data/icons/copy.png +0 -0
  20. data/icons/cut.png +0 -0
  21. data/icons/filenew.png +0 -0
  22. data/icons/fileopen.png +0 -0
  23. data/icons/filesave.png +0 -0
  24. data/icons/filesaveas.png +0 -0
  25. data/icons/help.png +0 -0
  26. data/icons/kill.png +0 -0
  27. data/icons/nextpage.png +0 -0
  28. data/icons/paste.png +0 -0
  29. data/icons/prevpage.png +0 -0
  30. data/icons/printicon.png +0 -0
  31. data/icons/redo.png +0 -0
  32. data/icons/room_e.gif +0 -0
  33. data/icons/room_e.xpm +0 -0
  34. data/icons/room_n.gif +0 -0
  35. data/icons/room_n.xpm +0 -0
  36. data/icons/room_ne.gif +0 -0
  37. data/icons/room_ne.xpm +0 -0
  38. data/icons/room_nw.gif +0 -0
  39. data/icons/room_nw.xpm +0 -0
  40. data/icons/room_s.gif +0 -0
  41. data/icons/room_s.xpm +0 -0
  42. data/icons/room_se.gif +0 -0
  43. data/icons/room_se.xpm +0 -0
  44. data/icons/room_sw.gif +0 -0
  45. data/icons/room_sw.xpm +0 -0
  46. data/icons/room_w.gif +0 -0
  47. data/icons/room_w.xpm +0 -0
  48. data/icons/saveas.png +0 -0
  49. data/icons/undo.png +0 -0
  50. data/icons/winapp.png +0 -0
  51. data/icons/zoom.png +0 -0
  52. data/lib/IFMapper/AStar.rb +250 -250
  53. data/lib/IFMapper/Connection.rb +202 -202
  54. data/lib/IFMapper/FXAboutDialogBox.rb +32 -32
  55. data/lib/IFMapper/FXConnection.rb +364 -364
  56. data/lib/IFMapper/FXConnectionDialogBox.rb +124 -124
  57. data/lib/IFMapper/FXDCPostscript.rb +404 -404
  58. data/lib/IFMapper/FXDCPrint.rb +15 -15
  59. data/lib/IFMapper/FXItemList.rb +108 -0
  60. data/lib/IFMapper/FXMap.rb +2147 -2116
  61. data/lib/IFMapper/FXMapColorBox.rb +88 -88
  62. data/lib/IFMapper/FXMapDialogBox.rb +127 -127
  63. data/lib/IFMapper/FXMapFileDialog.rb +34 -34
  64. data/lib/IFMapper/FXMapperSettings.rb +206 -205
  65. data/lib/IFMapper/FXMapperWindow.rb +1592 -1571
  66. data/lib/IFMapper/FXPDFMapExporterOptionsDialogBox.rb +46 -0
  67. data/lib/IFMapper/FXRoom.rb +263 -263
  68. data/lib/IFMapper/FXRoomDialogBox.rb +159 -159
  69. data/lib/IFMapper/FXRoomList.rb +95 -95
  70. data/lib/IFMapper/FXSearchDialogBox.rb +51 -51
  71. data/lib/IFMapper/FXSection.rb +33 -33
  72. data/lib/IFMapper/FXSectionDialogBox.rb +38 -38
  73. data/lib/IFMapper/FXSpline.rb +52 -52
  74. data/lib/IFMapper/FXWarningBox.rb +51 -50
  75. data/lib/IFMapper/GUEReader.rb +445 -445
  76. data/lib/IFMapper/IFMReader.rb +584 -584
  77. data/lib/IFMapper/IFMWriter.rb +245 -227
  78. data/lib/IFMapper/Inform7Writer.rb +579 -573
  79. data/lib/IFMapper/InformReader.rb +478 -478
  80. data/lib/IFMapper/InformWriter.rb +364 -359
  81. data/lib/IFMapper/Map.rb +202 -200
  82. data/lib/IFMapper/MapPrinting.rb +162 -162
  83. data/lib/IFMapper/MapReader.rb +900 -900
  84. data/lib/IFMapper/PDFMapExporter.rb +526 -483
  85. data/lib/IFMapper/Room.rb +153 -151
  86. data/lib/IFMapper/Section.rb +234 -234
  87. data/lib/IFMapper/TADSReader.rb +474 -471
  88. data/lib/IFMapper/TADSWriter.rb +375 -370
  89. data/lib/IFMapper/TranscriptDialogBox.rb +0 -0
  90. data/lib/IFMapper/TranscriptReader.rb +1361 -1359
  91. data/lib/IFMapper/locales/en/Messages.rb +446 -435
  92. data/lib/IFMapper/locales/es/Messages.rb +451 -440
  93. data/lib/IFMapper/locales/es/Messages_iso-8859-1.rb +455 -440
  94. data/lib/IFMapper/locales/es/runme.sh +3 -3
  95. data/maps/A New Life.map b/data/maps/A New → Life.map +0 -0
  96. data/maps/AMFV.map +0 -0
  97. data/maps/AllRoads.map +0 -0
  98. data/maps/Aotearoa.map +0 -0
  99. data/maps/Bronze.map +0 -0
  100. data/maps/Bureaucracy.ifm +0 -0
  101. data/maps/Bureaucracy.map +0 -0
  102. data/maps/CityOfSecrets.map +0 -0
  103. data/maps/DDIV.map +0 -0
  104. data/maps/Following_A_Star.map +0 -0
  105. data/maps/Heated.map +0 -0
  106. data/maps/Heroine.map +0 -0
  107. data/maps/History Repeating.map b/data/maps/History → Repeating.map +0 -0
  108. data/maps/Hollywood_Hijinx.ifm +0 -0
  109. data/maps/Janitor.map +0 -0
  110. data/maps/Jigsaw.ifm +0 -0
  111. data/maps/Jigsaw.map +0 -0
  112. data/maps/LGOP.ifm +0 -0
  113. data/maps/Mercy.ifm +0 -0
  114. data/maps/Ninjas_Fate.map +0 -0
  115. data/maps/Pen_and_Paint.map +0 -0
  116. data/maps/Planetfall.ifm +0 -0
  117. data/maps/Planetfall.map +0 -0
  118. data/maps/Plundered_Hearts.ifm +0 -0
  119. data/maps/QuietEvening.map +0 -0
  120. data/maps/Ralph.ifm +0 -0
  121. data/maps/Reliques_of_Tolti_Alph.map +0 -0
  122. data/maps/Revolution.map +0 -0
  123. data/maps/Robots_of_Dawn.ifm +0 -0
  124. data/maps/SavoirFare.map +0 -0
  125. data/maps/Seastalker.ifm +0 -0
  126. data/maps/Seastalker.map +0 -0
  127. data/maps/Sherlock.ifm +0 -0
  128. data/maps/SoFar.ifm +0 -0
  129. data/maps/Starcross.ifm +0 -0
  130. data/maps/Suspended.ifm +0 -0
  131. data/maps/Tangle.map +0 -0
  132. data/maps/The_Lost_Sheep.map +0 -0
  133. data/maps/Unforgotten.map +0 -0
  134. data/maps/Warbler's Nest.map +0 -0
  135. data/maps/Warbler's_Nest.map +0 -0
  136. data/maps/Westminster_Abbey.map +0 -0
  137. data/maps/WinterWonderland.map +0 -0
  138. data/maps/Wishbringer.ifm +0 -0
  139. data/maps/Wishbringer2.ifm +0 -0
  140. data/maps/Zork1.ifm +0 -0
  141. data/maps/Zork2.ifm +0 -0
  142. data/maps/Zork3.ifm +0 -0
  143. data/maps/Zork_Zero.ifm +0 -0
  144. data/maps/anchor.ifm +0 -0
  145. data/maps/anchor.map +0 -0
  146. data/maps/atrox.ifm +0 -0
  147. data/maps/awaken.ifm +0 -0
  148. data/maps/babel.ifm +0 -0
  149. data/maps/balances.map +0 -0
  150. data/maps/ballerina.map +0 -0
  151. data/maps/bear.map +0 -0
  152. data/maps/bluechairs.map +0 -0
  153. data/maps/break_in.map +0 -0
  154. data/maps/bse.ifm +0 -0
  155. data/maps/building.map +0 -0
  156. data/maps/change.ifm +0 -0
  157. data/maps/christminster.map +0 -0
  158. data/maps/curses.ifm +0 -0
  159. data/maps/curves.ifm +0 -0
  160. data/maps/deadline.map +0 -0
  161. data/maps/delusions.map +0 -0
  162. data/maps/devours.map +0 -0
  163. data/maps/distress.map +0 -0
  164. data/maps/djinni.map +0 -0
  165. data/maps/dreamhold.map +0 -0
  166. data/maps/drift3.map +0 -0
  167. data/maps/eas.map +0 -0
  168. data/maps/eas2.map +0 -0
  169. data/maps/eas3.map +0 -0
  170. data/maps/edifice.ifm +0 -0
  171. data/maps/fallacy.map +0 -0
  172. data/maps/frozen.ifm +0 -0
  173. data/maps/gamlet.map +0 -0
  174. data/maps/glow.ifm +0 -0
  175. data/maps/guilty_bastards.map +0 -0
  176. data/maps/heist.map +0 -0
  177. data/maps/heroes.map +0 -0
  178. data/maps/inhumane.map +0 -0
  179. data/maps/kaged.map +0 -0
  180. data/maps/library.ifm +0 -0
  181. data/maps/lurkinghorror.map +0 -0
  182. data/maps/metamorphoses.map +0 -0
  183. data/maps/mindelec.ifm +0 -0
  184. data/maps/minster.ifm +0 -0
  185. data/maps/mite.map +0 -0
  186. data/maps/moonmist.map +0 -0
  187. data/maps/muldoon_legacy.map +0 -0
  188. data/maps/muse.ifm +0 -0
  189. data/maps/paperchase.ifm +0 -0
  190. data/maps/party.map +0 -0
  191. data/maps/pawn.map +0 -0
  192. data/maps/photograph.map +0 -0
  193. data/maps/pkgirl.map +0 -0
  194. data/maps/pytho.map +0 -0
  195. data/maps/risorgimento.map +0 -0
  196. data/maps/sherbet.map +0 -0
  197. data/maps/simple.map +0 -0
  198. data/maps/slouch.map +0 -0
  199. data/maps/space_st.ifm +0 -0
  200. data/maps/splashdown.map +0 -0
  201. data/maps/spring.map +0 -0
  202. data/maps/squarecircle.map +0 -0
  203. data/maps/stationfall.ifm +0 -0
  204. data/maps/theatre.ifm +0 -0
  205. data/maps/toonesia.ifm +0 -0
  206. data/maps/tortoise.ifm +0 -0
  207. data/maps/trinity.map +0 -0
  208. data/maps/vespers.map +0 -0
  209. data/maps/vgame.ifm +0 -0
  210. data/maps/wasp.map +0 -0
  211. data/maps/weather.ifm +0 -0
  212. data/maps/windhall.ifm +0 -0
  213. data/maps/worlds.map +0 -0
  214. data/maps/xtcontest.map +0 -0
  215. data/maps/zdungeon.map +0 -0
  216. data/maps/zebulon.ifm +0 -0
  217. data/maps/zerosum.map +0 -0
  218. metadata +226 -183
@@ -1,205 +1,206 @@
1
-
2
- begin
3
- require 'iconv'
4
- rescue LoadError
5
- $stderr.puts 'Iconv could not be loaded. Languages other than English will have problems'
6
- class Iconv
7
- def initialize(to, from)
8
- end
9
-
10
- def iconv(a)
11
- a
12
- end
13
- end
14
- end
15
-
16
-
17
- #
18
- # This class contains the applicatio and map's settings
19
- #
20
- class FXMapperSettings < Hash
21
-
22
- class UnknownParameter < StandardError; end;
23
-
24
- #
25
- # Return the home location
26
- #
27
- # Result: home location for user
28
- #
29
- def home
30
- if ENV['HOME']
31
- homedir = File.expand_path('~')
32
- # Ahh... you have to love windows...
33
- elsif ENV['USERPROFILE']
34
- homedir = ENV['USERPROFILE']
35
- elsif ENV['HOMEDRIVE'] and ENV['HOMEPATH']
36
- homedir = ENV['HOMEDRIVE'] + ENV['HOMEPATH']
37
- else
38
- homedir = '.'
39
- end
40
- return homedir
41
- end
42
-
43
-
44
- #
45
- # Write out the preferences to disk.
46
- #
47
- # Result: none
48
- #
49
- def write
50
- begin
51
- file = home + '/.ifmapper'
52
- f = File.open(file, 'w')
53
- f.puts YAML.dump(self)
54
- f.close
55
- rescue => e
56
- $stderr.puts "Preferences not saved:"
57
- $stderr.puts e
58
- end
59
- end
60
-
61
-
62
- if RUBY_PLATFORM =~ /mswin/
63
- require 'Win32API'
64
- LOCALE_USER_DEFAULT = 0x400
65
- LOCALE_SLANGUAGE = 0x1001
66
-
67
- FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
68
- FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000
69
- FormatMessage = Win32API.new("kernel32","FormatMessage","LPLLPLP","L")
70
- GetLastError = Win32API.new("kernel32","GetLastError",[],"L")
71
- GetLocaleInfo = Win32API.new('Kernel32.dll', 'GetLocaleInfo',
72
- %w{l l p i}, 'i')
73
-
74
- LANGUAGES = {
75
- /English/ => 'en',
76
- /Spanish/ => 'es',
77
- # todo1
78
- /German/ => 'de',
79
- /Italian/ => 'it',
80
- /French/ => 'fr',
81
- /Japanese/ => 'ja',
82
- /Chinese/ => 'ch',
83
- /Korean/ => 'ko',
84
- /Arabic/ => 'ar',
85
- /Hebrew/ => 'he',
86
- }
87
- end
88
-
89
-
90
- #
91
- # Restore user preferences
92
- #
93
- # Result:
94
- #
95
- def initialize
96
- has_yaml = true
97
- begin
98
- require 'yaml'
99
- f = home + '/.ifmapper'
100
- self.replace( YAML.load_file(f) )
101
- rescue LoadError
102
- has_yaml = false
103
- rescue
104
- end
105
-
106
- if RUBY_PLATFORM =~ /mswin/
107
- lcid = LOCALE_USER_DEFAULT
108
- lctype = LOCALE_SLANGUAGE
109
- slanguage = '\0' * 256
110
- len = 256
111
- ret = GetLocaleInfo.call lcid, lctype, slanguage, len
112
- if ret == 0
113
- error_code = GetLastError.call
114
- msg = " " * 255
115
- FormatMessage.call(
116
- FORMAT_MESSAGE_FROM_SYSTEM +
117
- FORMAT_MESSAGE_ARGUMENT_ARRAY,
118
- '',
119
- error_code,
120
- 0,
121
- msg,
122
- 255,
123
- ''
124
- )
125
- msg.gsub!(/\000/, '')
126
- msg.strip!
127
- $stderr.puts msg
128
- end
129
- language = 'en'
130
- LANGUAGES.each { |re, locale|
131
- if slanguage =~ re
132
- language = locale
133
- break
134
- end
135
- }
136
- else
137
- locale = ENV['LC_ALL'] || ENV['LC_CTYPE'] || 'en_US'
138
- language = locale[0,2]
139
- end
140
-
141
- while ARGV.size > 0
142
- param = ARGV.shift
143
- case param
144
- when /^-l(anguage)?$/
145
- language = ARGV.shift
146
- language = language[0,2].downcase
147
- self['Language'] = language
148
- else
149
- $stderr.puts "Unknown parameter '#{param}'."
150
- exit(1)
151
- end
152
- end
153
-
154
- defaults = {
155
- # Language
156
- 'Language' => language,
157
-
158
- # Colors
159
- 'BG Color' => 'dark grey',
160
- 'Arrow Color' => 'black',
161
- 'Box BG Color' => 'white',
162
- 'Box Darkness Color' => 'grey',
163
- 'Box Border Color' => 'black',
164
- 'Box Number Color' => 'grey',
165
-
166
- # Fonts
167
- 'Font Text' => 'Times',
168
- 'Font Objects' => 'Times',
169
-
170
- # Creation options
171
- 'Create on Connection' => true,
172
- 'Edit on Creation' => false,
173
- 'Automatic Connection' => true,
174
-
175
-
176
- # Display options
177
- 'Use Room Cursor' => false,
178
- 'Paths as Curves' => true,
179
-
180
- # Location options
181
- 'Location Tasks' => true,
182
- 'Location Description' => false,
183
- 'Location Numbers' => true,
184
-
185
- # Grid options
186
- 'Grid Boxes' => true,
187
- 'Grid Straight Connections' => true,
188
- 'Grid Diagonal Connections' => false,
189
-
190
- }
191
-
192
- self.replace( defaults.merge(self) )
193
- language = self['Language']
194
-
195
- begin
196
- require "IFMapper/locales/#{language}/Messages.rb"
197
- rescue LoadError => e
198
- puts "Language '#{language}' was not found."
199
- end
200
-
201
- unless has_yaml
202
- $stderr.puts ERR_NO_YAML
203
- end
204
- end
205
- end
1
+
2
+ begin
3
+ require 'iconv'
4
+ rescue LoadError
5
+ $stderr.puts 'Iconv could not be loaded. Languages other than English will have problems'
6
+ class Iconv
7
+ def initialize(to, from)
8
+ end
9
+
10
+ def iconv(a)
11
+ a
12
+ end
13
+ end
14
+ end
15
+
16
+
17
+ #
18
+ # This class contains the applicatio and map's settings
19
+ #
20
+ class FXMapperSettings < Hash
21
+
22
+ class UnknownParameter < StandardError; end;
23
+
24
+ #
25
+ # Return the home location
26
+ #
27
+ # Result: home location for user
28
+ #
29
+ def home
30
+ if ENV['HOME']
31
+ homedir = File.expand_path('~')
32
+ # Ahh... you have to love windows...
33
+ elsif ENV['USERPROFILE']
34
+ homedir = ENV['USERPROFILE']
35
+ elsif ENV['HOMEDRIVE'] and ENV['HOMEPATH']
36
+ homedir = ENV['HOMEDRIVE'] + ENV['HOMEPATH']
37
+ else
38
+ homedir = '.'
39
+ end
40
+ return homedir
41
+ end
42
+
43
+
44
+ #
45
+ # Write out the preferences to disk.
46
+ #
47
+ # Result: none
48
+ #
49
+ def write
50
+ begin
51
+ file = home + '/.ifmapper'
52
+ f = File.open(file, 'w')
53
+ f.puts YAML.dump(self)
54
+ f.close
55
+ rescue => e
56
+ $stderr.puts "Preferences not saved:"
57
+ $stderr.puts e
58
+ end
59
+ end
60
+
61
+
62
+ if RUBY_PLATFORM =~ /mswin/
63
+ require 'Win32API'
64
+ LOCALE_USER_DEFAULT = 0x400
65
+ LOCALE_SLANGUAGE = 0x1001
66
+
67
+ FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000
68
+ FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000
69
+ FormatMessage = Win32API.new("kernel32","FormatMessage","LPLLPLP","L")
70
+ GetLastError = Win32API.new("kernel32","GetLastError",[],"L")
71
+ GetLocaleInfo = Win32API.new('Kernel32.dll', 'GetLocaleInfo',
72
+ %w{l l p i}, 'i')
73
+
74
+ LANGUAGES = {
75
+ /English/ => 'en',
76
+ /Spanish/ => 'es',
77
+ # todo1
78
+ /German/ => 'de',
79
+ /Italian/ => 'it',
80
+ /French/ => 'fr',
81
+ /Japanese/ => 'ja',
82
+ /Chinese/ => 'ch',
83
+ /Korean/ => 'ko',
84
+ /Arabic/ => 'ar',
85
+ /Hebrew/ => 'he',
86
+ }
87
+ end
88
+
89
+
90
+ #
91
+ # Restore user preferences
92
+ #
93
+ # Result:
94
+ #
95
+ def initialize
96
+ has_yaml = true
97
+ begin
98
+ require 'yaml'
99
+ f = home + '/.ifmapper'
100
+ self.replace( YAML.load_file(f) )
101
+ rescue LoadError
102
+ has_yaml = false
103
+ rescue
104
+ end
105
+
106
+ if RUBY_PLATFORM =~ /mswin/
107
+ lcid = LOCALE_USER_DEFAULT
108
+ lctype = LOCALE_SLANGUAGE
109
+ slanguage = '\0' * 256
110
+ len = 256
111
+ ret = GetLocaleInfo.call lcid, lctype, slanguage, len
112
+ if ret == 0
113
+ error_code = GetLastError.call
114
+ msg = " " * 255
115
+ FormatMessage.call(
116
+ FORMAT_MESSAGE_FROM_SYSTEM +
117
+ FORMAT_MESSAGE_ARGUMENT_ARRAY,
118
+ '',
119
+ error_code,
120
+ 0,
121
+ msg,
122
+ 255,
123
+ ''
124
+ )
125
+ msg.gsub!(/\000/, '')
126
+ msg.strip!
127
+ $stderr.puts msg
128
+ end
129
+ language = 'en'
130
+ LANGUAGES.each { |re, locale|
131
+ if slanguage =~ re
132
+ language = locale
133
+ break
134
+ end
135
+ }
136
+ else
137
+ locale = ENV['LC_ALL'] || ENV['LC_CTYPE'] || 'en_US'
138
+ language = locale[0,2]
139
+ end
140
+
141
+ while ARGV.size > 0
142
+ param = ARGV.shift
143
+ case param
144
+ when /^-l(anguage)?$/
145
+ language = ARGV.shift
146
+ language = language[0,2].downcase
147
+ self['Language'] = language
148
+ else
149
+ $stderr.puts "Unknown parameter '#{param}'."
150
+ exit(1)
151
+ end
152
+ end
153
+
154
+ defaults = {
155
+ # Language
156
+ 'Language' => language,
157
+
158
+ # Colors
159
+ 'BG Color' => 'dark grey',
160
+ 'Arrow Color' => 'black',
161
+ 'Box BG Color' => 'white',
162
+ 'Box Darkness Color' => 'grey',
163
+ 'Box Border Color' => 'black',
164
+ 'Box Number Color' => 'grey',
165
+
166
+ # Fonts
167
+ 'Font Text' => 'Times',
168
+ 'Font Objects' => 'Times',
169
+
170
+ # Creation options
171
+ 'Create on Connection' => true,
172
+ 'Edit on Creation' => false,
173
+ 'Automatic Connection' => true,
174
+
175
+
176
+ # Display options
177
+ 'Use Room Cursor' => false,
178
+ 'Paths as Curves' => true,
179
+
180
+ # Location options
181
+ 'Location Tasks' => true,
182
+ 'Location Description' => false,
183
+ 'Location Numbers' => true,
184
+
185
+ # Grid options
186
+ 'Grid Boxes' => true,
187
+ 'Grid Straight Connections' => true,
188
+ 'Grid Diagonal Connections' => false,
189
+
190
+ }
191
+
192
+ self.replace( defaults.merge(self) )
193
+ language = self['Language']
194
+
195
+ begin
196
+ require "IFMapper/locales/#{language}/Messages.rb"
197
+ rescue LoadError => e
198
+ $stderr.puts "Language '#{language}' was not found. Using English."
199
+ require "IFMapper/locales/en/Messages.rb"
200
+ end
201
+
202
+ unless has_yaml
203
+ $stderr.puts ERR_NO_YAML
204
+ end
205
+ end
206
+ end
@@ -1,1571 +1,1592 @@
1
-
2
-
3
- begin
4
- $rubygems = false
5
- require 'rubygems'
6
- $rubygems = true
7
- rescue LoadError
8
- end
9
-
10
- def no_fox
11
- $stderr.puts ERR_NO_FOX
12
- if $rubygems
13
- $stderr.puts ERR_HAS_GEMS
14
- end
15
- exit(1)
16
- end
17
-
18
- def get_fox
19
- ##### ARRRGH!!!! Why does Lyle keep changing the fxruby name on each
20
- ##### release!
21
- foxes = [ 'fox16', 'fox14', 'fox12', 'fox' ]
22
- foxes.each { |fox|
23
- begin
24
- require "#{fox}"
25
- require "#{fox}/colors"
26
- break
27
- rescue LoadError
28
- no_fox if fox == foxes[-1]
29
- end
30
- }
31
-
32
- # verify fxruby version
33
- ver, rev, = Fox::fxrubyversion().split('.')
34
- no_fox if ver.to_i < 1 or rev.to_i < 2
35
- end
36
-
37
-
38
- get_fox
39
- include Fox
40
-
41
- require 'IFMapper/FXMap'
42
- require 'IFMapper/FXMapperSettings'
43
- require 'IFMapper/FXWarningBox'
44
-
45
-
46
- class FXMapperWindow < FXMainWindow
47
-
48
- PROGRAM_NAME = "Interactive Fiction Mapper"
49
- VERSION = '1.0.0'
50
- AUTHOR = "Gonzalo Garramuño"
51
-
52
- @@copy_buffer = nil
53
- @@default_options = FXMapperSettings.new
54
-
55
- LANGUAGES = {
56
- 'English' => 'en',
57
- 'Español' => 'es',
58
- # todo1
59
- 'Deutsch' => 'de',
60
- 'Italiano' => 'it',
61
- 'Fran�ais' => 'fr',
62
- # todo2
63
- 'Japanese' => 'ja',
64
- 'Chinese' => 'ch',
65
- 'Korean' => 'ko',
66
- 'Arabic' => 'ar',
67
- }
68
-
69
- def open_map(file)
70
- tmp = nil
71
- begin
72
- f = File.open(file, 'rb')
73
- tmp = Marshal.load(f)
74
- f.close
75
- tmp.filename = file
76
- rescue => e
77
- tmp = e
78
- end
79
- return tmp
80
- end
81
-
82
- def open_ifm(file, map)
83
- require 'IFMapper/IFMReader'
84
- begin
85
- IFMReader.new(file, map)
86
- rescue => e
87
- return "#{e} #{e.backtrace}"
88
- end
89
- return map
90
- end
91
-
92
- def open_tads(file, map)
93
- require 'IFMapper/TADSReader'
94
- begin
95
- TADSReader.new(file, map)
96
- rescue => e
97
- return "#{e}"
98
- end
99
- return map
100
- end
101
-
102
- def open_inform(file, map)
103
- require 'IFMapper/InformReader'
104
- begin
105
- InformReader.new(file, map)
106
- rescue => e
107
- return "#{e}"
108
- end
109
- return map
110
- end
111
-
112
- def open_guemap(file, map)
113
- require 'IFMapper/GUEReader'
114
- begin
115
- GUEReader.new(file, map)
116
- rescue => e
117
- return "#{e}"
118
- end
119
- return map
120
- end
121
-
122
- #
123
- # Start automapping from a transcript
124
- #
125
- def start_automap_cb(sender, sel, ptr)
126
- map = current_map
127
- return if not map
128
- map.start_automap
129
- end
130
-
131
- #
132
- # Properties of the automapper callback
133
- #
134
- def automap_properties_cb(sender, sel, ptr)
135
- map = current_map
136
- return if not map or not map.automap
137
- map.automap.properties(true)
138
- end
139
-
140
- #
141
- # Stop automapping from a transcript
142
- #
143
- def stop_automap_cb(sender, sel, ptr)
144
- map = current_map
145
- return if not map
146
- map.stop_automap
147
- end
148
-
149
-
150
- #
151
- # Callback to Open File
152
- #
153
- def open_cb(sender, sel, ptr)
154
- require 'IFMapper/FXMapFileDialog'
155
- file = FXMapFileDialog.new(self, MSG_LOAD_MAP).filename
156
- return if file == ''
157
-
158
- # First, make sure we don't have it loaded already...
159
- @maps.each { |m|
160
- if m.filename == file
161
- @mdiclient.setActiveChild(m.window)
162
- return
163
- end
164
- }
165
-
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
169
- make_new_map = false
170
- if @maps.size == 1
171
- @maps[0].sections.each { |p|
172
- if p.rooms.size != 0
173
- make_new_map = true
174
- break
175
- end
176
- }
177
- else
178
- make_new_map = true
179
- end
180
-
181
- if make_new_map
182
- map = new_map
183
- else
184
- map = @maps[0]
185
- end
186
- status "#{MSG_LOADING} '#{file}'..."
187
-
188
- tmp = nil
189
- if file =~ /\.ifm$/i
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)
197
- else
198
- tmp = open_map(file)
199
- end
200
-
201
- if not tmp.kind_of?(Map) and not tmp.kind_of?(FXMap)
202
- $stderr.puts tmp
203
- w = FXWarningBox.new( self,
204
- "#{tmp}")
205
- w.execute
206
- status "#{ERR_COULD_NOT_LOAD} '#{file}'."
207
- if make_new_map
208
- if map.close_cb
209
- @maps.delete(map)
210
- GC.start
211
- end
212
- end
213
- sleep 2
214
- return
215
- end
216
-
217
- map.copy(tmp)
218
- map.options.replace( @@default_options.merge(map.options) )
219
- map.verify_integrity
220
- map.fit
221
- map.window.create
222
- map.modified = false
223
- update_map
224
- status "#{MSG_LOADED} '#{file}'."
225
- end
226
-
227
- #
228
- # Write a message to the status bar
229
- #
230
- def status(msg)
231
- @statusbar.statusLine.text = msg
232
- end
233
-
234
- #
235
- # Returns current active map or nil if no maps
236
- #
237
- def current_map
238
- window = @mdiclient.activeChild
239
- return nil unless window
240
-
241
- @maps.each { |m|
242
- return m if m.window == window
243
- }
244
- return nil
245
- end
246
-
247
- #
248
- # Callback for Save
249
- #
250
- def save_cb(sender, sel, ptr)
251
- map = current_map
252
- return unless map
253
- map.save
254
- end
255
-
256
- #
257
- # Callback for Save As
258
- #
259
- def save_as_cb(sender, sel, ptr)
260
- map = current_map
261
- return unless map
262
- map.save_as
263
- end
264
-
265
- #
266
- # Callback used to create new map
267
- #
268
- def new_map_cb(*args)
269
- m = new_map
270
- m.window.create
271
- end
272
-
273
- #
274
- # Callback used to change language
275
- #
276
- def language_cb(sender, msg, opts)
277
- @@default_options['Language'] = LANGUAGES[sender.text]
278
-
279
- require "IFMapper/locales/#{language}/Messages.rb"
280
- recreate
281
- end
282
-
283
- #
284
- # Create a new map
285
- #
286
- def new_map
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) )
290
- map = @maps[-1]
291
- map.window.connect(SEL_PAINT) {
292
- map.draw
293
- }
294
- map.window.connect(SEL_CLOSE) {
295
- if map.close_cb
296
- @maps.delete(map)
297
- end
298
- if @maps[-1]
299
- @maps[-1].update_roomlist
300
- else
301
- FXMap::no_maps
302
- end
303
- }
304
-
305
- # Make it active
306
- @mdiclient.setActiveChild(map.window)
307
- map.update_roomlist
308
- return map
309
- end
310
-
311
- #
312
- # Load the named PNG icon from a file
313
- #
314
- def load_icon(filename)
315
- begin
316
- filename = File.join("icons", filename) + ".png"
317
- icon = nil
318
- File.open(filename, "rb") { |f|
319
- icon = FXPNGIcon.new(getApp(), f.read)
320
- }
321
- icon
322
- rescue
323
- raise RuntimeError, "#{ERR_NO_ICON} #{filename}"
324
- end
325
- end
326
-
327
- #
328
- # Start a complex connection
329
- #
330
- def complex_connection_cb(sender, sel, msg)
331
- map = current_map
332
- return unless map
333
- map.complex_connection
334
- end
335
-
336
- #
337
- # Delete selected elements in map
338
- #
339
- def delete_selected_cb(sender, sel, msg)
340
- map = current_map
341
- return unless map
342
- map.delete_selected
343
- end
344
-
345
- #
346
- # Popup a printer dialog and return the settings or false if user cancels
347
- #
348
- def printer_dialog(title = MSG_PRINT_MAP)
349
- map = current_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
353
- return false
354
- end
355
-
356
- #
357
- # Print out all the locations in map
358
- #
359
- def print_locations_cb(sender, sel, msg)
360
- map = current_map
361
- return unless map
362
-
363
- w = FXWarningBox.new( self, ERR_NO_PRINTING)
364
- w.execute
365
- return
366
-
367
- printer = printer_dialog MSG_PRINT_LOC
368
- map.print_locations( printer ) if printer
369
- end
370
-
371
- #
372
- # Export current map as an IFM file
373
- #
374
- def ifm_export_cb(sender, sel, msg)
375
- map = current_map
376
- return unless map
377
-
378
- require 'IFMapper/FXMapFileDialog'
379
- d = FXMapFileDialog.new(self, MSG_SAVE_MAP_AS_IFM,
380
- [
381
- FMT_IFM
382
- ])
383
- map.export_ifm(d.filename) if d.filename != ''
384
- end
385
-
386
- #
387
- # Export current map as an Inform source file
388
- #
389
- def inform_export_cb(sender, sel, msg)
390
- map = current_map
391
- return unless map
392
-
393
- require 'IFMapper/FXMapFileDialog'
394
- d = FXMapFileDialog.new(self, MSG_SAVE_MAP_AS_INFORM,
395
- [
396
- FMT_INFORM,
397
- ])
398
- map.export_inform( d.filename ) if d.filename != ''
399
- end
400
-
401
-
402
- #
403
- # Export current map as a TADs source file
404
- #
405
- def tads_export_cb(sender, sel, msg)
406
- map = current_map
407
- return unless map
408
-
409
- require 'IFMapper/TADSWriter'
410
- require 'IFMapper/FXMapFileDialog'
411
- d = FXMapFileDialog.new(self, MSG_SAVE_MAP_AS_TADS,
412
- [
413
- FMT_TADS
414
- ])
415
- map.export_tads( d.filename ) if d.filename != ''
416
- end
417
-
418
-
419
- #
420
- # Export current map as Acrobat PDF
421
- #
422
- def pdf_export_cb(sender, sel, msg)
423
- map = current_map
424
- return unless map
425
-
426
- begin
427
- require 'IFMapper/PDFMapExporter'
428
- rescue LoadError => e
429
- w = FXWarningBox.new( self, "#{e}")
430
- w.execute
431
- return
432
- end
433
-
434
- require 'IFMapper/FXMapFileDialog'
435
- d = FXMapFileDialog.new(self, MSG_SAVE_MAP_AS_PDF,
436
- [
437
- FMT_PDF
438
- ])
439
- if d.filename != ''
440
- map.pdf_export(d.filename)
441
- end
442
- end
443
-
444
- #
445
- # Print current map graphically
446
- #
447
- def print_cb(sender, sel, msg)
448
- map = current_map
449
- return unless map
450
-
451
- w = FXWarningBox.new( self, ERR_NO_PRINTING )
452
- w.execute
453
- return
454
- require 'IFMapper/MapPrinting'
455
-
456
- printer = printer_dialog
457
- map.print( printer ) if printer
458
- end
459
-
460
- def update_map
461
- map = current_map
462
- return unless map
463
- map.update_roomlist
464
- update_section
465
- end
466
-
467
- #
468
- # Creates the MDI (multi-document) client
469
- #
470
- def create_mdiclient
471
- # MDI Client
472
- @maps = []
473
-
474
- @mdiclient = FXMDIClient.new(self, LAYOUT_FILL_X|LAYOUT_FILL_Y)
475
- @mdiclient.connect(SEL_CHANGED) {
476
- update_map
477
- }
478
-
479
- # MDI buttons in menu:- note the message ID's!!!!!
480
- # Normally, MDI commands are simply sensitized or desensitized;
481
- # Under the @menubar, however, they're hidden if the MDI Client is
482
- # not maximized. To do this, they must have different ID's.
483
- FXMDIWindowButton.new(@menubar, @mdimenu, @mdiclient,
484
- FXMDIClient::ID_MDI_MENUWINDOW, LAYOUT_LEFT)
485
- FXMDIDeleteButton.new(@menubar, @mdiclient,
486
- FXMDIClient::ID_MDI_MENUCLOSE, FRAME_RAISED|LAYOUT_RIGHT)
487
- FXMDIRestoreButton.new(@menubar, @mdiclient,
488
- FXMDIClient::ID_MDI_MENURESTORE, FRAME_RAISED|LAYOUT_RIGHT)
489
- FXMDIMinimizeButton.new(@menubar, @mdiclient,
490
- FXMDIClient::ID_MDI_MENUMINIMIZE, FRAME_RAISED|LAYOUT_RIGHT)
491
-
492
- # Icon for MDI Child
493
- @mdiicon = load_icon("winapp")
494
-
495
- # Make MDI Window Menu
496
- @mdimenu = FXMDIMenu.new(self, @mdiclient)
497
- end
498
-
499
- #
500
- # Return the copied elements
501
- #
502
- def self.copy_buffer
503
- return @@copy_buffer
504
- end
505
-
506
- #
507
- # Copy selected elements
508
- #
509
- def self.copy_selected(map)
510
- sect = map.sections[map.section]
511
-
512
- # Get all selected rooms
513
- rooms = sect.rooms.find_all { |r| r.selected }
514
- return if rooms.size < 1
515
-
516
- # Get all selected connections
517
- links = sect.connections.find_all { |c| c.selected }
518
-
519
- # Make sure we store only those connections for
520
- # those rooms we selected
521
- delete = []
522
- links.each { |c|
523
- if not rooms.include?(c.roomA) or
524
- (c.roomB and not rooms.include?(c.roomB))
525
- delete << c
526
- end
527
- }
528
- links -= delete
529
-
530
- selection = [ rooms, links ]
531
-
532
- @@copy_buffer = selection
533
- end
534
-
535
- #
536
- # Cut selected elements
537
- #
538
- def self.cut_selected(map)
539
- FXMapperWindow::copy_selected(map)
540
- map.cut_selected
541
- end
542
-
543
- #
544
- # Paste selected elements
545
- #
546
- def self.paste_selected(map)
547
- return if not @@copy_buffer
548
- return map.navigation_warning if map.navigation
549
-
550
- sel = @@copy_buffer
551
- pos = map.find_empty_area( sel[0] )
552
- if not pos
553
- w = FXWarningBox.new( map.window, ERR_NO_FREE_ROOM)
554
- w.execute
555
- else
556
- map.clear_selection
557
-
558
- # Add rooms
559
- r_to_nr = {} # orig room to new room hash
560
- rooms = sel[0]
561
- rooms.each { |r|
562
- nr = map.new_room(r.x + pos[0], r.y + pos[1])
563
- nr.selected = true
564
- nr.copy(r) # copy the room data
565
- r_to_nr[r] = nr
566
- }
567
-
568
- if rooms.empty?
569
- # Add connections only (no rooms copied)
570
- sel[1].each { |c|
571
- exitA, exitB = c.dirs
572
- roomA = c.roomA
573
- roomB = c.roomB
574
- sect = map.sections[map.section]
575
- if not sect.rooms.include?(roomA) or
576
- (roomB and not sect.rooms.include?(roomB))
577
- next
578
- end
579
- begin
580
- nc = map.new_connection(roomA, exitA, roomB, exitB)
581
- nc.selected = true
582
- nc.dir = c.dir
583
- nc.type = c.type
584
- rescue
585
- end
586
- }
587
- else
588
- # Add connections
589
- sel[1].each { |c|
590
- exitA, exitB = c.dirs
591
- roomA = r_to_nr[c.roomA]
592
- if c.roomB
593
- roomB = r_to_nr[c.roomB]
594
- else
595
- roomB = nil
596
- end
597
- next if not roomA
598
- begin
599
- nc = map.new_connection(roomA, exitA, roomB, exitB)
600
- nc.selected = true
601
- nc.dir = c.dir
602
- nc.type = c.type
603
- rescue Section::ConnectionError => e
604
- puts c
605
- puts e
606
- end
607
- }
608
- end
609
-
610
- map.create_pathmap
611
- map.draw
612
- end
613
- end
614
-
615
- def cut_selected_cb(*o)
616
- map = current_map
617
- return unless map
618
- FXMapperWindow::cut_selected(map)
619
- end
620
-
621
- def copy_selected_cb(*o)
622
- map = current_map
623
- return unless map
624
- FXMapperWindow::copy_selected(map)
625
- end
626
-
627
- def paste_selected_cb(*o)
628
- map = current_map
629
- return if not map or not @@copy_buffer
630
- FXMapperWindow::paste_selected(map)
631
- end
632
-
633
-
634
- #
635
- # Hilite matches after a search
636
- #
637
- def hilite_matches(map, matches, re, idx = 0 )
638
- if matches.size == 0
639
- status "#{ERR_NO_MATCHES} '#{re}'."
640
- return
641
- end
642
-
643
- # sort matches by section
644
- matches.sort_by { |a| a[0] }
645
-
646
- # Jump to first section of match
647
- map.section = matches[idx][0]
648
- map.sections.each { |s|
649
- s.rooms.each { |r| r.selected = false }
650
- }
651
-
652
- matches.each { |p, r|
653
- next if p != map.section
654
- r.selected = true
655
- }
656
-
657
- num = matches.find_all { |p, r| p == matches[idx][0] }
658
- room = matches[idx][1]
659
- map.center_view_on_room( room )
660
- update_section
661
-
662
- status "'#{room.name}' #{MSG_MATCHES}. #{matches.size} #{MSG_MATCHES_IN_MAP}, #{num.size} #{MSG_MATCHES_IN_SECTION}."
663
- map.draw
664
- end
665
-
666
- #
667
- # Find location in map
668
- #
669
- def find_in_map(s, m, e)
670
- map = current_map
671
- return unless map
672
-
673
- re = /#{s.text}/
674
- matches = []
675
- (0...map.sections.size).each { |p|
676
- map.sections[p].rooms.each { |r|
677
- next unless r.name =~ re
678
- matches.push( [p, r] )
679
- }
680
- }
681
- idx = @search.index
682
- @search.index = matches.size-1 if idx >= matches.size
683
- hilite_matches(map, matches, re, @search.index)
684
- end
685
-
686
- def find_in_map_cb(s, m, e)
687
- map = current_map
688
- return unless map
689
-
690
- title = MSG_FIND_LOCATION_IN_MAP
691
- if not @search
692
- require 'IFMapper/FXSearchDialogBox'
693
- @search = FXSearchDialogBox.new(self)
694
- end
695
- @search.proc = method(:find_in_map)
696
- @search.title = title
697
- @search.text = ''
698
- @search.show
699
- end
700
-
701
- #
702
- # Find location in section
703
- #
704
- def find_in_section(s, m, e)
705
- map = current_map
706
- return unless map
707
-
708
- re = /#{s.text}/
709
- matches = []
710
- map.sections[map.section].rooms.each { |r|
711
- next unless r.name =~ re
712
- matches.push( [ map.section, r] )
713
- }
714
- hilite_matches(map, matches, re)
715
- end
716
-
717
- #
718
- # Callback
719
- #
720
- def find_in_section_cb(s, m, e)
721
- map = current_map
722
- return unless map
723
-
724
- title = MSG_FIND_LOCATION_IN_SECTION
725
- if not @search
726
- require 'IFMapper/FXSearchDialogBox'
727
- @search = FXSearchDialogBox.new(self)
728
- end
729
- @search.proc = method(:find_in_section)
730
- @search.title = title
731
- @search.text = ''
732
- @search.show
733
- end
734
-
735
- #
736
- # Find object in map
737
- #
738
- def find_object_in_map(s, m, e)
739
- map = current_map
740
- return unless map
741
-
742
- re = /#{s.text}/
743
- matches = []
744
- (0...map.sections.size).each { |p|
745
- map.sections[p].rooms.each { |r|
746
- next unless r.objects =~ re
747
- matches.push( [p, r] )
748
- }
749
- }
750
- idx = @search.index
751
- @search.index = matches.size-1 if idx >= matches.size
752
- hilite_matches(map, matches, re, @search.index)
753
- end
754
-
755
- #
756
- # Find task in map
757
- #
758
- def find_task_in_map(s, m, e)
759
- map = current_map
760
- return unless map
761
-
762
- re = /#{s.text}/
763
- matches = []
764
- (0...map.sections.size).each { |p|
765
- map.sections[p].rooms.each { |r|
766
- next unless r.tasks =~ re
767
- matches.push( [p, r] )
768
- }
769
- }
770
- idx = @search.index
771
- @search.index = matches.size-1 if idx >= matches.size
772
- hilite_matches(map, matches, re, @search.index)
773
- end
774
-
775
- #
776
- # Find object in map
777
- #
778
- def find_object_in_map_cb(s, m, e)
779
- map = current_map
780
- return unless map
781
-
782
- title = MSG_FIND_OBJECT_IN_MAP
783
- if not @search
784
- require 'IFMapper/FXSearchDialogBox'
785
- @search = FXSearchDialogBox.new(self)
786
- end
787
- @search.proc = method(:find_object_in_map)
788
- @search.title = title
789
- @search.text = ''
790
- @search.show
791
- end
792
-
793
- #
794
- # Find task in map
795
- #
796
- def find_task_in_map_cb(s, m, e)
797
- map = current_map
798
- return unless map
799
-
800
- title = MSG_FIND_TASK_IN_MAP
801
- if not @search
802
- require 'IFMapper/FXSearchDialogBox'
803
- @search = FXSearchDialogBox.new(self)
804
- end
805
- @search.proc = method(:find_task_in_map)
806
- @search.title = title
807
- @search.text = ''
808
- @search.show
809
- end
810
-
811
- #
812
- # Find task in map
813
- #
814
- def find_desc_in_map(s, m, e)
815
- map = current_map
816
- return unless map
817
-
818
- re = /#{s.text}/
819
- matches = []
820
- (0...map.sections.size).each { |p|
821
- map.sections[p].rooms.each { |r|
822
- next unless r.desc =~ re
823
- matches.push( [p, r] )
824
- }
825
- }
826
- idx = @search.index
827
- @search.index = matches.size-1 if idx >= matches.size
828
- hilite_matches(map, matches, re, @search.index)
829
- end
830
-
831
- #
832
- # Find description in map
833
- #
834
- def find_desc_in_map_cb(s, m, e)
835
- map = current_map
836
- return unless map
837
-
838
- title = MSG_FIND_DESCRIPTION_IN_MAP
839
- if not @search
840
- require 'IFMapper/FXSearchDialogBox'
841
- @search = FXSearchDialogBox.new(self)
842
- end
843
- @search.proc = method(:find_desc_in_map)
844
- @search.title = title
845
- @search.text = ''
846
- @search.show
847
- end
848
-
849
-
850
- #
851
- # Pop-up color preferences
852
- #
853
- def colors_cb(sender, id, msg)
854
- map = current_map
855
- return if not map
856
-
857
- if not @colors
858
- require 'IFMapper/FXMapColorBox'
859
- @colors = FXMapColorBox.new(self)
860
- else
861
- @colors.show
862
- end
863
- @colors.copy_from(map)
864
- end
865
-
866
- #
867
- # Unselect all
868
- #
869
- def select_none_cb( sender, id, event )
870
- map = current_map
871
- return if not map
872
- map.clear_selection
873
- end
874
-
875
- #
876
- # Select all
877
- #
878
- def select_all_cb( sender, id, event )
879
- map = current_map
880
- return if not map
881
- sect = map.sections[map.section]
882
- sect.rooms.each { |r|
883
- r.selected = true
884
- }
885
- sect.connections.each { |c|
886
- c.selected = true
887
- }
888
- end
889
-
890
- def roomlist(sender, sel, event)
891
- map = current_map
892
- return unless map
893
- map.show_roomlist
894
- end
895
-
896
-
897
- def about_cb(sender, id, event )
898
- require 'IFMapper/FXAboutDialogBox'
899
- FXAboutDialogBox.new(self, MSG_ABOUT_SOFTWARE,
900
- eval("\"#{MSG_ABOUT}\"")).execute
901
- end
902
-
903
-
904
- def create_menus
905
- # Construct these icons
906
- newdoc = load_icon("filenew")
907
- opendoc = load_icon("fileopen")
908
- savedoc = load_icon("filesave")
909
- saveasdoc = load_icon("filesaveas")
910
-
911
- # File Menu
912
- filemenu = FXMenuPane.new(self)
913
- FXMenuTitle.new(@menubar, MENU_FILE, nil, filemenu)
914
- cmd = FXMenuCommand.new(filemenu, MENU_NEW, newdoc)
915
- cmd.connect(SEL_COMMAND, method(:new_map_cb))
916
-
917
- cmd = FXMenuCommand.new(filemenu, MENU_OPEN, opendoc)
918
- cmd.connect(SEL_COMMAND, method(:open_cb))
919
- cmd = FXMenuCommand.new(filemenu, MENU_SAVE, savedoc)
920
- cmd.connect(SEL_COMMAND, method(:save_cb))
921
- cmd = FXMenuCommand.new(filemenu, MENU_SAVE_AS,
922
- saveasdoc)
923
- cmd.connect(SEL_COMMAND, method(:save_as_cb))
924
-
925
- # Export submenu
926
- submenu = FXMenuPane.new(self)
927
-
928
- cmd = FXMenuCommand.new(submenu, MENU_EXPORT_PDF, nil)
929
- cmd.connect(SEL_COMMAND, method(:pdf_export_cb))
930
-
931
- cmd = FXMenuCommand.new(submenu, MENU_EXPORT_IFM, nil)
932
- cmd.connect(SEL_COMMAND, method(:ifm_export_cb))
933
-
934
- cmd = FXMenuCommand.new(submenu, MENU_EXPORT_INFORM, nil)
935
- cmd.connect(SEL_COMMAND, method(:inform_export_cb))
936
-
937
- cmd = FXMenuCommand.new(submenu, MENU_EXPORT_TADS, nil)
938
- cmd.connect(SEL_COMMAND, method(:tads_export_cb))
939
-
940
- FXMenuCascade.new(filemenu, MENU_EXPORT, nil, submenu)
941
-
942
-
943
- submenu = FXMenuPane.new(self)
944
- cmd = FXMenuCommand.new(submenu, MENU_PRINT_MAP, nil)
945
- cmd.connect(SEL_COMMAND, method(:print_cb))
946
-
947
- cmd = FXMenuCommand.new(submenu, MENU_PRINT_LOCATIONS, nil)
948
- cmd.connect(SEL_COMMAND, method(:print_locations_cb))
949
- FXMenuCascade.new(filemenu, MENU_PRINT, nil, submenu)
950
-
951
- cmd = FXMenuCommand.new(filemenu, MENU_QUIT, nil)
952
- cmd.connect( SEL_COMMAND, method(:close_cb) )
953
-
954
- # Edit Menu
955
- editmenu = FXMenuPane.new(self)
956
- FXMenuTitle.new(@menubar, MENU_EDIT, nil, editmenu)
957
- cmd = FXMenuCommand.new(editmenu, MENU_COPY, nil)
958
- cmd.connect(SEL_COMMAND, method(:copy_selected_cb))
959
- cmd = FXMenuCommand.new(editmenu, MENU_CUT, nil)
960
- cmd.connect(SEL_COMMAND, method(:cut_selected_cb))
961
- cmd = FXMenuCommand.new(editmenu, MENU_PASTE, nil)
962
- cmd.connect(SEL_COMMAND, method(:paste_selected_cb))
963
-
964
- # Select submenu
965
- FXMenuSeparator.new(editmenu)
966
- submenu = FXMenuPane.new(self)
967
- cmd = FXMenuCommand.new( submenu, MENU_SELECT_ALL )
968
- cmd.connect(SEL_COMMAND, method(:select_all_cb))
969
- cmd = FXMenuCommand.new( submenu, MENU_SELECT_NONE )
970
- cmd.connect(SEL_COMMAND, method(:select_none_cb))
971
- FXMenuCascade.new( editmenu, MENU_SELECT, nil, submenu )
972
-
973
- # Searching submenu
974
- FXMenuSeparator.new(editmenu)
975
- submenu = FXMenuPane.new(self)
976
- cmd = FXMenuCommand.new(submenu, MENU_SEARCH_MAP)
977
- cmd.connect(SEL_COMMAND, method(:find_in_map_cb))
978
- cmd = FXMenuCommand.new(submenu, MENU_SEARCH_SECTION)
979
- cmd.connect(SEL_COMMAND, method(:find_in_section_cb))
980
- cmd = FXMenuCommand.new(submenu, MENU_SEARCH_OBJECT)
981
- cmd.connect(SEL_COMMAND, method(:find_object_in_map_cb))
982
- cmd = FXMenuCommand.new(submenu, MENU_SEARCH_TASK)
983
- cmd.connect(SEL_COMMAND, method(:find_task_in_map_cb))
984
- cmd = FXMenuCommand.new(submenu, MENU_SEARCH_DESCRIPTION)
985
- cmd.connect(SEL_COMMAND, method(:find_desc_in_map_cb))
986
- FXMenuCascade.new(editmenu, MENU_SEARCH, nil, submenu)
987
-
988
- # Complex Connection
989
- FXMenuSeparator.new(editmenu)
990
- cmd = FXMenuCommand.new(editmenu, MENU_COMPLEX_CONNECTION, nil)
991
- cmd.connect( SEL_COMMAND, method(:complex_connection_cb) )
992
-
993
- FXMenuSeparator.new(editmenu)
994
- cmd = FXMenuCommand.new(editmenu, MENU_DELETE, nil)
995
- cmd.connect( SEL_COMMAND, method(:delete_selected_cb) )
996
-
997
- # Map menu
998
- mapmenu = FXMenuPane.new(self)
999
-
1000
- cmd = FXMenuCommand.new(mapmenu, MENU_MAP_INFO)
1001
- cmd.connect(SEL_COMMAND) { map_properties }
1002
-
1003
- cmd = FXMenuCommand.new(mapmenu, MENU_ROOM_LIST)
1004
- cmd.connect(SEL_COMMAND, method(:roomlist) )
1005
-
1006
- # Automap submenu
1007
- #
1008
- submenu = FXMenuPane.new(self)
1009
- cmd = FXMenuCommand.new(submenu, MENU_AUTOMAP_START)
1010
- cmd.connect(SEL_COMMAND, method(:start_automap_cb))
1011
- cmd = FXMenuCommand.new(submenu, MENU_AUTOMAP_STOP)
1012
- cmd.connect(SEL_COMMAND, method(:stop_automap_cb))
1013
- FXMenuSeparator.new(submenu)
1014
- cmd = FXMenuCommand.new(submenu, MENU_AUTOMAP_PROPERTIES)
1015
- cmd.connect(SEL_COMMAND, method(:automap_properties_cb))
1016
- FXMenuCascade.new(mapmenu, MENU_AUTOMAP, nil, submenu)
1017
-
1018
- # Sections submenu
1019
- submenu = FXMenuPane.new(self)
1020
- cmd = FXMenuCommand.new(submenu, MENU_NEXT_SECTION)
1021
- cmd.connect(SEL_COMMAND) {
1022
- next_section
1023
- }
1024
- cmd = FXMenuCommand.new(submenu, MENU_PREVIOUS_SECTION)
1025
- cmd.connect(SEL_COMMAND) {
1026
- previous_section
1027
- }
1028
- FXMenuSeparator.new(submenu)
1029
- cmd = FXMenuCommand.new(submenu, MENU_ADD_SECTION)
1030
- cmd.connect(SEL_COMMAND) {
1031
- map = current_map
1032
- if map
1033
- map.new_section
1034
- map.modified = true
1035
- update_section
1036
- end
1037
- }
1038
- cmd = FXMenuCommand.new(submenu, MENU_RENAME_SECTION)
1039
- cmd.connect(SEL_COMMAND) {
1040
- map = current_map
1041
- if map
1042
- map.rename_section
1043
- map.modified = true
1044
- end
1045
- }
1046
- FXMenuSeparator.new(submenu)
1047
- cmd = FXMenuCommand.new(submenu, MENU_DELETE_SECTION)
1048
- cmd.connect(SEL_COMMAND) {
1049
- map = current_map
1050
- if map
1051
- map.delete_section
1052
- map.modified = true
1053
- update_section
1054
- end
1055
- }
1056
- FXMenuCascade.new(mapmenu, MENU_SECTIONS, nil, submenu)
1057
-
1058
- #
1059
- # Zoom submenu
1060
- #
1061
- submenu = FXMenuPane.new(self)
1062
- [25, 50, 75, 100, 125].each { |v|
1063
- cmd = FXMenuCommand.new(submenu, eval("\"#{MENU_ZOOM_PERCENT}\""))
1064
- cmd.connect(SEL_COMMAND) {
1065
- map = current_map
1066
- if map
1067
- map.zoom = v / 100.0
1068
- map.draw
1069
- end
1070
- }
1071
- }
1072
- FXMenuCascade.new(mapmenu, MENU_ZOOM, nil, submenu)
1073
-
1074
- submenu = FXMenuPane.new(self)
1075
-
1076
- cmd = FXMenuCheck.new(submenu, MENU_EDIT_ON_CREATION)
1077
- cmd.check = @@default_options['Edit on Creation']
1078
- cmd.connect(SEL_COMMAND) { |s, m, e|
1079
- map = current_map
1080
- if map
1081
- map.options['Edit on Creation'] = (s.check == true)
1082
- end
1083
- }
1084
- cmd.connect(SEL_UPDATE) { |s, m, e|
1085
- map = current_map
1086
- s.check = map.options['Edit on Creation'] if map
1087
- }
1088
-
1089
- cmd = FXMenuCheck.new(submenu, MENU_AUTOMATIC_CONNECTION)
1090
- cmd.check = @@default_options['Automatic Connection']
1091
- cmd.connect(SEL_COMMAND) { |s, m, e|
1092
- map = current_map
1093
- if map
1094
- map.options['Automatic Connection'] = (s.check == true)
1095
- end
1096
- }
1097
- cmd.connect(SEL_UPDATE) { |s, m, e|
1098
- map = current_map
1099
- s.check = map.options['Automatic Connection'] if map
1100
- }
1101
-
1102
- cmd = FXMenuCheck.new(submenu, MENU_CREATE_ON_CONNECTION)
1103
- cmd.check = @@default_options['Create on Connection']
1104
- cmd.connect(SEL_COMMAND) { |s, m, e|
1105
- map = current_map
1106
- map.options['Create on Connection'] = s.check if map
1107
- }
1108
- cmd.connect(SEL_UPDATE) { |s, m, e|
1109
- map = current_map
1110
- s.check = map.options['Create on Connection'] if map
1111
- }
1112
-
1113
- FXMenuCascade.new(mapmenu, MENU_OPTIONS, nil, submenu)
1114
-
1115
- ##########################
1116
- # Display submenu
1117
- #########################
1118
- submenu = FXMenuPane.new(self)
1119
- cmd = FXMenuCheck.new(submenu, MENU_USE_ROOM_CURSOR)
1120
- cmd.check = @@default_options['Use Room Cursor']
1121
- cmd.connect(SEL_COMMAND) { |s, m, e|
1122
- map = current_map
1123
- if map
1124
- map.options['Use Room Cursor'] = (s.check == true)
1125
- map.draw
1126
- end
1127
- }
1128
- cmd.connect(SEL_UPDATE) { |s, m, e|
1129
- map = current_map
1130
- s.check = map.options['Use Room Cursor'] if map
1131
- }
1132
-
1133
- cmd = FXMenuCheck.new(submenu, MENU_PATHS_AS_CURVES)
1134
- cmd.check = @@default_options['Paths as Curves']
1135
- cmd.connect(SEL_COMMAND) { |s, m, e|
1136
- map = current_map
1137
- if map
1138
- map.options['Paths as Curves'] = (s.check == true)
1139
- map.draw
1140
- end
1141
- }
1142
- cmd.connect(SEL_UPDATE) { |s, m, e|
1143
- map = current_map
1144
- s.check = map.options['Paths as Curves'] if map
1145
- }
1146
- cmd = FXMenuCheck.new(submenu, MENU_LOCATION_NUMBERS)
1147
- cmd.check = @@default_options['Location Numbers']
1148
- cmd.connect(SEL_COMMAND) { |s, m, e|
1149
- map = current_map
1150
- if map
1151
- map.options['Location Numbers'] = (s.check == true)
1152
- map.draw
1153
- end
1154
- }
1155
- cmd.connect(SEL_UPDATE) { |s, m, e|
1156
- map = current_map
1157
- s.check = map.options['Location Numbers'] if map
1158
- }
1159
-
1160
- cmd = FXMenuCheck.new(submenu, MENU_LOCATION_TASKS)
1161
- cmd.check = @@default_options['Location Tasks']
1162
- cmd.connect(SEL_COMMAND) { |s, m, e|
1163
- map = current_map
1164
- if map
1165
- map.options['Location Tasks'] = (s.check == true)
1166
- map.draw
1167
- end
1168
- }
1169
- cmd.connect(SEL_UPDATE) { |s, m, e|
1170
- map = current_map
1171
- s.check = map.options['Location Tasks'] if map
1172
- }
1173
-
1174
- cmd = FXMenuCheck.new(submenu, MENU_LOCATION_DESC)
1175
- cmd.check = @@default_options['Location Description']
1176
- cmd.connect(SEL_COMMAND) { |s, m, e|
1177
- map = current_map
1178
- if map
1179
- map.options['Location Description'] = (s.check == true)
1180
- map.draw
1181
- end
1182
- }
1183
- cmd.connect(SEL_UPDATE) { |s, m, e|
1184
- map = current_map
1185
- s.check = map.options['Location Description'] if map
1186
- }
1187
-
1188
- cmd = FXMenuCheck.new(submenu, MENU_BOXES)
1189
- cmd.check = @@default_options['Grid Boxes']
1190
- cmd.connect(SEL_COMMAND) { |s, m, e|
1191
- map = current_map
1192
- if map
1193
- map.options['Grid Boxes'] = (s.check == true)
1194
- map.draw
1195
- end
1196
- }
1197
- cmd.connect(SEL_UPDATE) { |s, m, e|
1198
- map = current_map
1199
- s.check = map.options['Grid Boxes'] if map
1200
- }
1201
-
1202
- cmd = FXMenuCheck.new(submenu, MENU_STRAIGHT_CONN)
1203
- cmd.check = @@default_options['Grid Straight Connections']
1204
- cmd.connect(SEL_COMMAND) { |s, m, e|
1205
- map = current_map
1206
- if map
1207
- map.options['Grid Straight Connections'] = (s.check == true)
1208
- map.draw
1209
- end
1210
- }
1211
- cmd.connect(SEL_UPDATE) { |s, m, e|
1212
- map = current_map
1213
- s.check = map.options['Grid Straight Connections'] if map
1214
- }
1215
-
1216
- cmd = FXMenuCheck.new(submenu, MENU_DIAGONAL_CONN)
1217
- cmd.check = @@default_options['Grid Diagonal Connections']
1218
- cmd.connect(SEL_COMMAND) { |s, m, e|
1219
- map = current_map
1220
- if map
1221
- map.options['Grid Diagonal Connections'] = s.check
1222
- map.draw
1223
- end
1224
- }
1225
- cmd.connect(SEL_UPDATE) { |s, m, e|
1226
- map = current_map
1227
- s.check = map.options['Grid Diagonal Connections'] if map
1228
- }
1229
-
1230
- FXMenuCascade.new(mapmenu, MENU_DISPLAY, nil, submenu)
1231
-
1232
- submenu = FXMenuPane.new(self)
1233
-
1234
- cmd = FXMenuCommand.new(submenu, MENU_COLORS)
1235
- cmd.connect(SEL_COMMAND, method(:colors_cb))
1236
-
1237
- # langmenu = FXMenuPane.new(self)
1238
- # LANGUAGES.each { |k, v|
1239
- # next unless File.exists?( "lib/IFMapper/locales/#{v}/Messages.rb" )
1240
- # cmd = FXMenuCheck.new(langmenu, k)
1241
- # cmd.connect(SEL_COMMAND, method(:language_cb))
1242
- # }
1243
-
1244
- # FXMenuCascade.new(mapmenu, MENU_LANGUAGE, nil, langmenu)
1245
-
1246
-
1247
- FXMenuSeparator.new(submenu)
1248
- cmd = FXMenuCommand.new(submenu, MENU_SAVE_PREFS)
1249
- cmd.connect(SEL_COMMAND) {
1250
- map = current_map
1251
- map.options.write if map
1252
- }
1253
- FXMenuCascade.new(mapmenu, MENU_PREFS, nil, submenu)
1254
-
1255
- FXMenuTitle.new(@menubar, MENU_MAP, nil, mapmenu)
1256
-
1257
- # Window menu
1258
- windowmenu = FXMenuPane.new(self)
1259
- FXMenuCommand.new(windowmenu, MENU_TILE_HORIZONTALLY, nil,
1260
- @mdiclient, FXMDIClient::ID_MDI_TILEHORIZONTAL)
1261
- FXMenuCommand.new(windowmenu, MENU_TILE_VERTICALLY, nil,
1262
- @mdiclient, FXMDIClient::ID_MDI_TILEVERTICAL)
1263
- FXMenuCommand.new(windowmenu, MENU_CASCADE, nil,
1264
- @mdiclient, FXMDIClient::ID_MDI_CASCADE)
1265
- FXMenuCommand.new(windowmenu, MENU_CLOSE, nil,
1266
- @mdiclient, FXMDIClient::ID_MDI_CLOSE)
1267
- sep1 = FXMenuSeparator.new(windowmenu)
1268
- sep1.setTarget(@mdiclient)
1269
- sep1.setSelector(FXMDIClient::ID_MDI_ANY)
1270
- FXMenuCommand.new(windowmenu, nil, nil, @mdiclient, FXMDIClient::ID_MDI_1)
1271
- FXMenuCommand.new(windowmenu, nil, nil, @mdiclient, FXMDIClient::ID_MDI_2)
1272
- FXMenuCommand.new(windowmenu, nil, nil, @mdiclient, FXMDIClient::ID_MDI_3)
1273
- FXMenuCommand.new(windowmenu, nil, nil, @mdiclient, FXMDIClient::ID_MDI_4)
1274
- FXMenuCommand.new(windowmenu, MENU_OTHERS, nil, @mdiclient,
1275
- FXMDIClient::ID_MDI_OVER_5)
1276
- FXMenuTitle.new(@menubar, MENU_WINDOW, nil, windowmenu)
1277
-
1278
- # Help menu
1279
- helpmenu = FXMenuPane.new(self)
1280
- cmd = FXMenuCommand.new(helpmenu, MENU_HOTKEYS, nil)
1281
- cmd.connect(SEL_COMMAND, method(:hotkeys))
1282
- cmd = FXMenuCommand.new(helpmenu, MENU_INSTRUCTIONS, nil)
1283
- cmd.connect(SEL_COMMAND, method(:docs))
1284
- FXMenuSeparator.new(helpmenu)
1285
- cmd = FXMenuCommand.new(helpmenu, MENU_ABOUT, nil)
1286
- cmd.connect(SEL_COMMAND, method(:about_cb))
1287
-
1288
- cmd = FXMenuCommand.new(helpmenu, MENU_RESOURCE, nil)
1289
- cmd.connect(SEL_COMMAND) {
1290
- require 'IFMapper/FXMapFileDialog'
1291
- file = FXMapFileDialog.new(self, "Resource a Ruby File",
1292
- ['Ruby File (*.rb)']).filename
1293
- if file != ''
1294
- begin
1295
- Kernel.load file
1296
- rescue => e
1297
- p e
1298
- end
1299
- end
1300
- }
1301
- FXMenuTitle.new(@menubar, MENU_HELP, nil, helpmenu)
1302
- end
1303
-
1304
- def language
1305
- return @@default_options['Language']
1306
- end
1307
-
1308
- def docs(*opts)
1309
- browsers = [ 'firefox', 'opera', 'explorer' ]
1310
- address = 'docs/' + language + '/start.html'
1311
- status "#{MSG_OPENING_WEB_PAGE} #{address}..."
1312
- ok = false
1313
- browsers.each { |cmd|
1314
- if RUBY_PLATFORM =~ /mswin/
1315
- ok = system("start #{cmd} #{address}")
1316
- else
1317
- ok = system("#{cmd} #{address} &")
1318
- end
1319
- break if ok
1320
- }
1321
- if not ok
1322
- status ERR_COULD_NOT_OPEN_WEB_BROWSER
1323
- end
1324
- end
1325
-
1326
-
1327
- def hotkeys(*opts)
1328
- require 'IFMapper/FXAboutDialogBox'
1329
- FXAboutDialogBox.new(self, BOX_HOTKEYS, MSG_HOTKEYS).show
1330
- end
1331
-
1332
- def create_toolbar(toolbar)
1333
-
1334
- # Construct these icons
1335
- newdoc = load_icon("filenew")
1336
- opendoc = load_icon("fileopen")
1337
- savedoc = load_icon("filesave")
1338
- saveasdoc = load_icon("filesaveas")
1339
-
1340
- # File manipulation
1341
- cmd = FXButton.new(toolbar, ICON_NEW, newdoc, nil, 0,
1342
- FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
1343
- cmd.connect(SEL_COMMAND, method(:new_map_cb))
1344
-
1345
- cmd = FXButton.new(toolbar, ICON_OPEN, opendoc, nil, 0,
1346
- FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
1347
- cmd.connect(SEL_COMMAND, method(:open_cb))
1348
-
1349
- cmd = FXButton.new(toolbar, ICON_SAVE, savedoc, nil, 0,
1350
- FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
1351
- cmd.connect(SEL_COMMAND, method(:save_cb))
1352
- cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
1353
- map = current_map
1354
- message = map ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
1355
- sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
1356
- }
1357
- cmd = FXButton.new(toolbar, ICON_SAVE_AS,
1358
- saveasdoc, nil, 0, FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
1359
- cmd.connect(SEL_COMMAND, method(:save_as_cb))
1360
- cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
1361
- map = current_map
1362
- message = map ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
1363
- sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
1364
- }
1365
-
1366
- # Print
1367
- FXFrame.new(toolbar,
1368
- LAYOUT_TOP|LAYOUT_LEFT|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT, 0, 0, 4, 20)
1369
- cmd = FXButton.new(toolbar, ICON_PRINT,
1370
- load_icon("printicon"), @mdiclient, FXGLViewer::ID_PRINT_IMAGE,
1371
- BUTTON_AUTOGRAY|FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
1372
- cmd.connect(SEL_COMMAND, method(:print_cb))
1373
- cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
1374
- map = current_map
1375
- message = map ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
1376
- sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
1377
- }
1378
-
1379
- # Editing
1380
- FXFrame.new(toolbar,
1381
- LAYOUT_TOP|LAYOUT_LEFT|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT, 0, 0, 4, 20)
1382
- cmd = FXButton.new(toolbar, ICON_CUT, load_icon("cut"), @mdiclient,
1383
- FXGLViewer::ID_CUT_SEL, (BUTTON_AUTOGRAY|FRAME_THICK|FRAME_RAISED|
1384
- LAYOUT_TOP|LAYOUT_LEFT))
1385
- cmd.connect(SEL_COMMAND, method(:cut_selected_cb))
1386
- cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
1387
- map = current_map
1388
- message = map ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
1389
- sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
1390
- }
1391
- cmd = FXButton.new(toolbar, ICON_COPY, load_icon("copy"), @mdiclient,
1392
- FXGLViewer::ID_COPY_SEL, (BUTTON_AUTOGRAY|FRAME_THICK|FRAME_RAISED|
1393
- LAYOUT_TOP|LAYOUT_LEFT))
1394
- cmd.connect(SEL_COMMAND, method(:copy_selected_cb))
1395
- cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
1396
- map = current_map
1397
- message = map ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
1398
- sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
1399
- }
1400
- cmd = FXButton.new(toolbar, ICON_PASTE, load_icon("paste"), @mdiclient,
1401
- FXGLViewer::ID_PASTE_SEL, (BUTTON_AUTOGRAY|FRAME_THICK|FRAME_RAISED|
1402
- LAYOUT_TOP|LAYOUT_LEFT))
1403
- cmd.connect(SEL_COMMAND, method(:paste_selected_cb))
1404
- cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
1405
- map = current_map
1406
- message = (map and @@copy_buffer) ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
1407
- sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
1408
- }
1409
-
1410
- # Zooming
1411
- FXFrame.new(toolbar,
1412
- LAYOUT_TOP|LAYOUT_LEFT|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT, 0, 0, 4, 20)
1413
- cmd = FXButton.new(toolbar, ICON_ZOOM_IN, load_icon("zoom"), @mdiclient,
1414
- 0, FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
1415
- cmd.connect(SEL_COMMAND) { zoom_in }
1416
-
1417
- cmd = FXButton.new(toolbar, ICON_ZOOM_OUT, load_icon("zoom"), @mdiclient,
1418
- 0, FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
1419
- cmd.connect(SEL_COMMAND) { zoom_out }
1420
-
1421
-
1422
- # Section travel
1423
- frame = FXHorizontalFrame.new(toolbar,
1424
- LAYOUT_RIGHT|FRAME_THICK|FRAME_RAISED)
1425
- cmd = FXButton.new(frame, ICON_PREV_SECTION, load_icon("prevpage"),
1426
- @mdiclient,
1427
- 0, FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
1428
- cmd.connect(SEL_COMMAND) { previous_section }
1429
-
1430
- @section = FXTextField.new(frame, 5, nil, 0,
1431
- TEXTFIELD_INTEGER|LAYOUT_FILL_ROW)
1432
- @section.text = '1'
1433
- @section.connect(SEL_COMMAND) { |s,m,e|
1434
- v = s.text.to_i
1435
- map = current_map
1436
- if map
1437
- map.section = v - 1
1438
- map.draw
1439
- update_section
1440
- end
1441
- }
1442
- @section.connect(SEL_UPDATE) { |s,m,e|
1443
- v = s.text.to_i
1444
- map = current_map
1445
- update_section if map
1446
- }
1447
-
1448
- cmd = FXButton.new(frame, ICON_NEXT_SECTION, load_icon("nextpage"),
1449
- @mdiclient,
1450
- 0, FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
1451
- cmd.connect(SEL_COMMAND) { next_section }
1452
- end
1453
-
1454
- #
1455
- # Update section # in toolbar widget
1456
- #
1457
- def update_section
1458
- map = current_map
1459
- return unless map
1460
- @section.text = (map.section + 1).to_s
1461
- end
1462
-
1463
- #
1464
- # Go to next section in current map
1465
- #
1466
- def next_section
1467
- map = current_map
1468
- map.next_section if map
1469
- update_section
1470
- end
1471
-
1472
- #
1473
- # Go to previous section in current map
1474
- #
1475
- def previous_section
1476
- map = current_map
1477
- map.previous_section if map
1478
- update_section
1479
- end
1480
-
1481
- #
1482
- # Zoom in into current map
1483
- #
1484
- def zoom_in
1485
- map = current_map
1486
- if map
1487
- map.zoom_in
1488
- map.draw
1489
- end
1490
- end
1491
-
1492
- #
1493
- # Zoom out from current map
1494
- #
1495
- def zoom_out
1496
- map = current_map
1497
- if map
1498
- map.zoom_out
1499
- map.draw
1500
- end
1501
- end
1502
-
1503
- #
1504
- # Bring up the map property requester for current map
1505
- #
1506
- def map_properties
1507
- map = current_map
1508
- map.properties if map
1509
- end
1510
-
1511
- #
1512
- # In case of crash or runtime error, autosave all maps, so user
1513
- # does not loose any data.
1514
- #
1515
- def autosave
1516
- @maps.each { |m|
1517
- m.save
1518
- }
1519
- end
1520
-
1521
-
1522
- def create_widgets
1523
- # Menubar
1524
- @menubar = FXMenuBar.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X)
1525
-
1526
- FXHorizontalSeparator.new(self,
1527
- LAYOUT_SIDE_TOP|SEPARATOR_GROOVE|LAYOUT_FILL_X)
1528
- toolbar = FXToolBar.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X,
1529
- 0, 0, 0, 0, 4, 4, 0, 0, 0, 0)
1530
-
1531
- # Status bar
1532
- @statusbar = FXStatusBar.new(self,
1533
- LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|
1534
- STATUSBAR_WITH_DRAGCORNER)
1535
-
1536
-
1537
- create_mdiclient
1538
- create_menus
1539
- create_toolbar(toolbar)
1540
- new_map
1541
-
1542
- self.connect(SEL_CLOSE, method(:close_cb))
1543
- show
1544
- end
1545
-
1546
- def initialize(app)
1547
- super(app, eval("\"#{TITLE}\""), nil, nil, DECOR_ALL, 0, 0, 800, 600)
1548
-
1549
- create_widgets
1550
-
1551
- # Trap CTRL-C signals and exit nicely
1552
- trap('SIGINT') {
1553
- close_cb
1554
- exit(0)
1555
- }
1556
- end
1557
-
1558
- def close_cb(*args)
1559
- exit = true
1560
- @maps.each { |m|
1561
- if not m.close_cb
1562
- exit = false
1563
- break
1564
- else
1565
- @maps.delete(m)
1566
- end
1567
- }
1568
- self.close if exit
1569
- end
1570
-
1571
- end
1
+
2
+
3
+ begin
4
+ $rubygems = false
5
+ require 'rubygems'
6
+ $rubygems = true
7
+ rescue LoadError
8
+ end
9
+
10
+ def no_fox
11
+ $stderr.puts ERR_NO_FOX
12
+ if $rubygems
13
+ $stderr.puts ERR_HAS_GEMS
14
+ end
15
+ exit(1)
16
+ end
17
+
18
+ def get_fox
19
+ ##### ARRRGH!!!! Why does Lyle keep changing the fxruby name on each
20
+ ##### release!
21
+ foxes = [ 'fox16', 'fox14', 'fox12', 'fox' ]
22
+ foxes.each { |fox|
23
+ begin
24
+ require "#{fox}"
25
+ require "#{fox}/colors"
26
+ break
27
+ rescue LoadError
28
+ no_fox if fox == foxes[-1]
29
+ end
30
+ }
31
+
32
+ # verify fxruby version
33
+ ver, rev, = Fox::fxrubyversion().split('.')
34
+ no_fox if ver.to_i < 1 or rev.to_i < 2
35
+ end
36
+
37
+
38
+ get_fox
39
+ include Fox
40
+
41
+ require 'IFMapper/FXMap'
42
+ require 'IFMapper/FXMapperSettings'
43
+ require 'IFMapper/FXWarningBox'
44
+
45
+
46
+ class FXMapperWindow < FXMainWindow
47
+
48
+ PROGRAM_NAME = "Interactive Fiction Mapper"
49
+ VERSION = '1.0.6'
50
+ AUTHOR = "Gonzalo Garramuño"
51
+
52
+ @@copy_buffer = nil
53
+ @@default_options = FXMapperSettings.new
54
+
55
+ LANGUAGES = {
56
+ 'English' => 'en',
57
+ 'Español' => 'es',
58
+ # todo1
59
+ 'Deutsch' => 'de',
60
+ 'Italiano' => 'it',
61
+ 'Fran�ais' => 'fr',
62
+ # todo2
63
+ 'Japanese' => 'ja',
64
+ 'Chinese' => 'ch',
65
+ 'Korean' => 'ko',
66
+ 'Arabic' => 'ar',
67
+ }
68
+
69
+ def open_map(file)
70
+ tmp = nil
71
+ begin
72
+ f = File.open(file, 'rb')
73
+ tmp = Marshal.load(f)
74
+ f.close
75
+ tmp.filename = file
76
+ rescue => e
77
+ tmp = e
78
+ end
79
+ return tmp
80
+ end
81
+
82
+ def open_ifm(file, map)
83
+ require 'IFMapper/IFMReader'
84
+ begin
85
+ IFMReader.new(file, map)
86
+ rescue => e
87
+ return "#{e} #{e.backtrace}"
88
+ end
89
+ return map
90
+ end
91
+
92
+ def open_tads(file, map)
93
+ require 'IFMapper/TADSReader'
94
+ begin
95
+ TADSReader.new(file, map)
96
+ rescue => e
97
+ return "#{e}"
98
+ end
99
+ return map
100
+ end
101
+
102
+ def open_inform(file, map)
103
+ require 'IFMapper/InformReader'
104
+ begin
105
+ InformReader.new(file, map)
106
+ rescue => e
107
+ return "#{e}"
108
+ end
109
+ return map
110
+ end
111
+
112
+ def open_guemap(file, map)
113
+ require 'IFMapper/GUEReader'
114
+ begin
115
+ GUEReader.new(file, map)
116
+ rescue => e
117
+ return "#{e}"
118
+ end
119
+ return map
120
+ end
121
+
122
+ #
123
+ # Start automapping from a transcript
124
+ #
125
+ def start_automap_cb(sender, sel, ptr)
126
+ map = current_map
127
+ return if not map
128
+ map.start_automap
129
+ end
130
+
131
+ #
132
+ # Properties of the automapper callback
133
+ #
134
+ def automap_properties_cb(sender, sel, ptr)
135
+ map = current_map
136
+ return if not map or not map.automap
137
+ map.automap.properties(true)
138
+ end
139
+
140
+ #
141
+ # Stop automapping from a transcript
142
+ #
143
+ def stop_automap_cb(sender, sel, ptr)
144
+ map = current_map
145
+ return if not map
146
+ map.stop_automap
147
+ end
148
+
149
+
150
+ #
151
+ # Callback to Open File
152
+ #
153
+ def open_cb(sender, sel, ptr)
154
+ require 'IFMapper/FXMapFileDialog'
155
+ file = FXMapFileDialog.new(self, MSG_LOAD_MAP).filename
156
+ return if file == ''
157
+
158
+ # First, make sure we don't have it loaded already...
159
+ @maps.each { |m|
160
+ if m.filename == file
161
+ @mdiclient.setActiveChild(m.window)
162
+ return
163
+ end
164
+ }
165
+
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
169
+ make_new_map = false
170
+ if @maps.size == 1
171
+ @maps[0].sections.each { |p|
172
+ if p.rooms.size != 0
173
+ make_new_map = true
174
+ break
175
+ end
176
+ }
177
+ else
178
+ make_new_map = true
179
+ end
180
+
181
+ if make_new_map
182
+ map = new_map
183
+ else
184
+ map = @maps[0]
185
+ end
186
+ status "#{MSG_LOADING} '#{file}'..."
187
+
188
+ tmp = nil
189
+ if file =~ /\.ifm$/i
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)
197
+ else
198
+ tmp = open_map(file)
199
+ end
200
+
201
+ if not tmp.kind_of?(Map) and not tmp.kind_of?(FXMap)
202
+ $stderr.puts tmp
203
+ w = FXWarningBox.new( self,
204
+ "#{tmp}")
205
+ w.execute
206
+ status "#{ERR_COULD_NOT_LOAD} '#{file}'."
207
+ if make_new_map
208
+ if map.close_cb
209
+ @maps.delete(map)
210
+ GC.start
211
+ end
212
+ end
213
+ sleep 2
214
+ return
215
+ end
216
+
217
+ map.copy(tmp)
218
+ map.options.replace( @@default_options.merge(map.options) )
219
+ map.verify_integrity
220
+ map.fit
221
+ map.window.create
222
+ map.modified = false
223
+ update_map
224
+ status "#{MSG_LOADED} '#{file}'."
225
+ end
226
+
227
+ #
228
+ # Write a message to the status bar
229
+ #
230
+ def status(msg)
231
+ @statusbar.statusLine.text = msg
232
+ end
233
+
234
+ #
235
+ # Returns current active map or nil if no maps
236
+ #
237
+ def current_map
238
+ window = @mdiclient.activeChild
239
+ return nil unless window
240
+
241
+ @maps.each { |m|
242
+ return m if m.window == window
243
+ }
244
+ return nil
245
+ end
246
+
247
+ #
248
+ # Callback for Save
249
+ #
250
+ def save_cb(sender, sel, ptr)
251
+ map = current_map
252
+ return unless map
253
+ map.save
254
+ end
255
+
256
+ #
257
+ # Callback for Save As
258
+ #
259
+ def save_as_cb(sender, sel, ptr)
260
+ map = current_map
261
+ return unless map
262
+ map.save_as
263
+ end
264
+
265
+ #
266
+ # Callback used to create new map
267
+ #
268
+ def new_map_cb(*args)
269
+ m = new_map
270
+ m.window.create
271
+ end
272
+
273
+ #
274
+ # Callback used to change language
275
+ #
276
+ def language_cb(sender, msg, opts)
277
+ @@default_options['Language'] = LANGUAGES[sender.text]
278
+
279
+ require "IFMapper/locales/#{language}/Messages.rb"
280
+ recreate
281
+ end
282
+
283
+ #
284
+ # Create a new map
285
+ #
286
+ def new_map
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) )
290
+ map = @maps[-1]
291
+ map.window.connect(SEL_PAINT) {
292
+ map.draw
293
+ }
294
+ map.window.connect(SEL_CLOSE) {
295
+ if map.close_cb
296
+ @maps.delete(map)
297
+ end
298
+ if @maps[-1]
299
+ @maps[-1].update_roomlist
300
+ else
301
+ FXMap::no_maps
302
+ end
303
+ }
304
+
305
+ # Make it active
306
+ @mdiclient.setActiveChild(map.window)
307
+ map.update_roomlist
308
+ return map
309
+ end
310
+
311
+ #
312
+ # Load the named PNG icon from a file
313
+ #
314
+ def load_icon(filename)
315
+ begin
316
+ filename = File.join("icons", filename) + ".png"
317
+ icon = nil
318
+ File.open(filename, "rb") { |f|
319
+ icon = FXPNGIcon.new(getApp(), f.read)
320
+ }
321
+ icon
322
+ rescue
323
+ raise RuntimeError, "#{ERR_NO_ICON} #{filename}"
324
+ end
325
+ end
326
+
327
+ #
328
+ # Start a complex connection
329
+ #
330
+ def complex_connection_cb(sender, sel, msg)
331
+ map = current_map
332
+ return unless map
333
+ map.complex_connection
334
+ end
335
+
336
+ #
337
+ # Delete selected elements in map
338
+ #
339
+ def delete_selected_cb(sender, sel, msg)
340
+ map = current_map
341
+ return unless map
342
+ map.delete_selected
343
+ end
344
+
345
+ #
346
+ # Popup a printer dialog and return the settings or false if user cancels
347
+ #
348
+ def printer_dialog(title = MSG_PRINT_MAP)
349
+ map = current_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
353
+ return false
354
+ end
355
+
356
+ #
357
+ # Print out all the locations in map
358
+ #
359
+ def print_locations_cb(sender, sel, msg)
360
+ map = current_map
361
+ return unless map
362
+
363
+ w = FXWarningBox.new( self, ERR_NO_PRINTING)
364
+ w.execute
365
+ return
366
+
367
+ printer = printer_dialog MSG_PRINT_LOC
368
+ map.print_locations( printer ) if printer
369
+ end
370
+
371
+ #
372
+ # Export current map as an IFM file
373
+ #
374
+ def ifm_export_cb(sender, sel, msg)
375
+ map = current_map
376
+ return unless map
377
+
378
+ require 'IFMapper/FXMapFileDialog'
379
+ d = FXMapFileDialog.new(self, MSG_SAVE_MAP_AS_IFM,
380
+ [
381
+ FMT_IFM
382
+ ])
383
+ map.export_ifm(d.filename) if d.filename != ''
384
+ end
385
+
386
+ #
387
+ # Export current map as an Inform source file
388
+ #
389
+ def inform_export_cb(sender, sel, msg)
390
+ map = current_map
391
+ return unless map
392
+
393
+ require 'IFMapper/FXMapFileDialog'
394
+ d = FXMapFileDialog.new(self, MSG_SAVE_MAP_AS_INFORM,
395
+ [
396
+ FMT_INFORM,
397
+ ])
398
+ map.export_inform( d.filename ) if d.filename != ''
399
+ end
400
+
401
+
402
+ #
403
+ # Export current map as a TADs source file
404
+ #
405
+ def tads_export_cb(sender, sel, msg)
406
+ map = current_map
407
+ return unless map
408
+
409
+ require 'IFMapper/TADSWriter'
410
+ require 'IFMapper/FXMapFileDialog'
411
+ d = FXMapFileDialog.new(self, MSG_SAVE_MAP_AS_TADS,
412
+ [
413
+ FMT_TADS
414
+ ])
415
+ map.export_tads( d.filename ) if d.filename != ''
416
+ end
417
+
418
+
419
+ #
420
+ # Export current map as Acrobat PDF
421
+ #
422
+ def pdf_export_cb(sender, sel, msg)
423
+ map = current_map
424
+ return unless map
425
+
426
+ begin
427
+ require 'IFMapper/PDFMapExporter'
428
+ rescue LoadError => e
429
+ w = FXWarningBox.new( self, "#{e}")
430
+ w.execute
431
+ return
432
+ end
433
+
434
+ # PRE: Let's ask for a page size and orientation for the PDF
435
+ # and whether the user wants to include location numbers
436
+ map.pdfpapersize = 0
437
+ map.pdflocationnos = 1
438
+ require 'IFMapper/FXPDFMapExporterOptionsDialogBox'
439
+ cmd = FXPDFMapExporterOptionsDialogBox.new(self, MSG_SAVE_MAP_AS_PDF,
440
+ map).execute
441
+
442
+ return if cmd == 0
443
+
444
+ require 'IFMapper/FXMapFileDialog'
445
+ d = FXMapFileDialog.new(self, MSG_SAVE_MAP_AS_PDF,
446
+ [
447
+ FMT_PDF
448
+ ])
449
+ if d.filename != ''
450
+ map.pdf_export(d.filename)
451
+ end
452
+ end
453
+
454
+ #
455
+ # Print current map graphically
456
+ #
457
+ def print_cb(sender, sel, msg)
458
+ map = current_map
459
+ return unless map
460
+
461
+ w = FXWarningBox.new( self, ERR_NO_PRINTING )
462
+ w.execute
463
+ return
464
+ require 'IFMapper/MapPrinting'
465
+
466
+ printer = printer_dialog
467
+ map.print( printer ) if printer
468
+ end
469
+
470
+ def update_map
471
+ map = current_map
472
+ return unless map
473
+ map.update_roomlist
474
+ update_section
475
+ end
476
+
477
+ #
478
+ # Creates the MDI (multi-document) client
479
+ #
480
+ def create_mdiclient
481
+ # MDI Client
482
+ @maps = []
483
+
484
+ @mdiclient = FXMDIClient.new(self, LAYOUT_FILL_X|LAYOUT_FILL_Y)
485
+ @mdiclient.connect(SEL_CHANGED) {
486
+ update_map
487
+ }
488
+
489
+ # MDI buttons in menu:- note the message ID's!!!!!
490
+ # Normally, MDI commands are simply sensitized or desensitized;
491
+ # Under the @menubar, however, they're hidden if the MDI Client is
492
+ # not maximized. To do this, they must have different ID's.
493
+ FXMDIWindowButton.new(@menubar, @mdimenu, @mdiclient,
494
+ FXMDIClient::ID_MDI_MENUWINDOW, LAYOUT_LEFT)
495
+ FXMDIDeleteButton.new(@menubar, @mdiclient,
496
+ FXMDIClient::ID_MDI_MENUCLOSE, FRAME_RAISED|LAYOUT_RIGHT)
497
+ FXMDIRestoreButton.new(@menubar, @mdiclient,
498
+ FXMDIClient::ID_MDI_MENURESTORE, FRAME_RAISED|LAYOUT_RIGHT)
499
+ FXMDIMinimizeButton.new(@menubar, @mdiclient,
500
+ FXMDIClient::ID_MDI_MENUMINIMIZE, FRAME_RAISED|LAYOUT_RIGHT)
501
+
502
+ # Icon for MDI Child
503
+ @mdiicon = load_icon("winapp")
504
+
505
+ # Make MDI Window Menu
506
+ @mdimenu = FXMDIMenu.new(self, @mdiclient)
507
+ end
508
+
509
+ #
510
+ # Return the copied elements
511
+ #
512
+ def self.copy_buffer
513
+ return @@copy_buffer
514
+ end
515
+
516
+ #
517
+ # Copy selected elements
518
+ #
519
+ def self.copy_selected(map)
520
+ sect = map.sections[map.section]
521
+
522
+ # Get all selected rooms
523
+ rooms = sect.rooms.find_all { |r| r.selected }
524
+ return if rooms.size < 1
525
+
526
+ # Get all selected connections
527
+ links = sect.connections.find_all { |c| c.selected }
528
+
529
+ # Make sure we store only those connections for
530
+ # those rooms we selected
531
+ delete = []
532
+ links.each { |c|
533
+ if not rooms.include?(c.roomA) or
534
+ (c.roomB and not rooms.include?(c.roomB))
535
+ delete << c
536
+ end
537
+ }
538
+ links -= delete
539
+
540
+ selection = [ rooms, links ]
541
+
542
+ @@copy_buffer = selection
543
+ end
544
+
545
+ #
546
+ # Cut selected elements
547
+ #
548
+ def self.cut_selected(map)
549
+ FXMapperWindow::copy_selected(map)
550
+ map.cut_selected
551
+ end
552
+
553
+ #
554
+ # Paste selected elements
555
+ #
556
+ def self.paste_selected(map)
557
+ return if not @@copy_buffer
558
+ return map.navigation_warning if map.navigation
559
+
560
+ sel = @@copy_buffer
561
+ pos = map.find_empty_area( sel[0] )
562
+ if not pos
563
+ w = FXWarningBox.new( map.window, ERR_NO_FREE_ROOM)
564
+ w.execute
565
+ else
566
+ map.clear_selection
567
+
568
+ # Add rooms
569
+ r_to_nr = {} # orig room to new room hash
570
+ rooms = sel[0]
571
+ rooms.each { |r|
572
+ nr = map.new_room(r.x + pos[0], r.y + pos[1])
573
+ nr.selected = true
574
+ nr.copy(r) # copy the room data
575
+ r_to_nr[r] = nr
576
+ }
577
+
578
+ if rooms.empty?
579
+ # Add connections only (no rooms copied)
580
+ sel[1].each { |c|
581
+ exitA, exitB = c.dirs
582
+ roomA = c.roomA
583
+ roomB = c.roomB
584
+ sect = map.sections[map.section]
585
+ if not sect.rooms.include?(roomA) or
586
+ (roomB and not sect.rooms.include?(roomB))
587
+ next
588
+ end
589
+ begin
590
+ nc = map.new_connection(roomA, exitA, roomB, exitB)
591
+ nc.selected = true
592
+ nc.dir = c.dir
593
+ nc.type = c.type
594
+ rescue
595
+ end
596
+ }
597
+ else
598
+ # Add connections
599
+ sel[1].each { |c|
600
+ exitA, exitB = c.dirs
601
+ roomA = r_to_nr[c.roomA]
602
+ if c.roomB
603
+ roomB = r_to_nr[c.roomB]
604
+ else
605
+ roomB = nil
606
+ end
607
+ next if not roomA
608
+ begin
609
+ nc = map.new_connection(roomA, exitA, roomB, exitB)
610
+ nc.selected = true
611
+ nc.dir = c.dir
612
+ nc.type = c.type
613
+ rescue Section::ConnectionError => e
614
+ puts c
615
+ puts e
616
+ end
617
+ }
618
+ end
619
+
620
+ map.create_pathmap
621
+ map.draw
622
+ end
623
+ end
624
+
625
+ def cut_selected_cb(*o)
626
+ map = current_map
627
+ return unless map
628
+ FXMapperWindow::cut_selected(map)
629
+ end
630
+
631
+ def copy_selected_cb(*o)
632
+ map = current_map
633
+ return unless map
634
+ FXMapperWindow::copy_selected(map)
635
+ end
636
+
637
+ def paste_selected_cb(*o)
638
+ map = current_map
639
+ return if not map or not @@copy_buffer
640
+ FXMapperWindow::paste_selected(map)
641
+ end
642
+
643
+
644
+ #
645
+ # Hilite matches after a search
646
+ #
647
+ def hilite_matches(map, matches, re, idx = 0 )
648
+ if matches.size == 0
649
+ status "#{ERR_NO_MATCHES} '#{re}'."
650
+ return
651
+ end
652
+
653
+ # sort matches by section
654
+ matches.sort_by { |a| a[0] }
655
+
656
+ # Jump to first section of match
657
+ map.section = matches[idx][0]
658
+ map.sections.each { |s|
659
+ s.rooms.each { |r| r.selected = false }
660
+ }
661
+
662
+ matches.each { |p, r|
663
+ next if p != map.section
664
+ r.selected = true
665
+ }
666
+
667
+ num = matches.find_all { |p, r| p == matches[idx][0] }
668
+ room = matches[idx][1]
669
+ map.center_view_on_room( room )
670
+ update_section
671
+
672
+ status "'#{room.name}' #{MSG_MATCHES}. #{matches.size} #{MSG_MATCHES_IN_MAP}, #{num.size} #{MSG_MATCHES_IN_SECTION}."
673
+ map.draw
674
+ end
675
+
676
+ #
677
+ # Find location in map
678
+ #
679
+ def find_in_map(s, m, e)
680
+ map = current_map
681
+ return unless map
682
+
683
+ re = /#{s.text}/
684
+ matches = []
685
+ (0...map.sections.size).each { |p|
686
+ map.sections[p].rooms.each { |r|
687
+ next unless r.name =~ re
688
+ matches.push( [p, r] )
689
+ }
690
+ }
691
+ idx = @search.index
692
+ @search.index = matches.size-1 if idx >= matches.size
693
+ hilite_matches(map, matches, re, @search.index)
694
+ end
695
+
696
+ def find_in_map_cb(s, m, e)
697
+ map = current_map
698
+ return unless map
699
+
700
+ title = MSG_FIND_LOCATION_IN_MAP
701
+ if not @search
702
+ require 'IFMapper/FXSearchDialogBox'
703
+ @search = FXSearchDialogBox.new(self)
704
+ end
705
+ @search.proc = method(:find_in_map)
706
+ @search.title = title
707
+ @search.text = ''
708
+ @search.show
709
+ end
710
+
711
+ #
712
+ # Find location in section
713
+ #
714
+ def find_in_section(s, m, e)
715
+ map = current_map
716
+ return unless map
717
+
718
+ re = /#{s.text}/
719
+ matches = []
720
+ map.sections[map.section].rooms.each { |r|
721
+ next unless r.name =~ re
722
+ matches.push( [ map.section, r] )
723
+ }
724
+ hilite_matches(map, matches, re)
725
+ end
726
+
727
+ #
728
+ # Callback
729
+ #
730
+ def find_in_section_cb(s, m, e)
731
+ map = current_map
732
+ return unless map
733
+
734
+ title = MSG_FIND_LOCATION_IN_SECTION
735
+ if not @search
736
+ require 'IFMapper/FXSearchDialogBox'
737
+ @search = FXSearchDialogBox.new(self)
738
+ end
739
+ @search.proc = method(:find_in_section)
740
+ @search.title = title
741
+ @search.text = ''
742
+ @search.show
743
+ end
744
+
745
+ #
746
+ # Find object in map
747
+ #
748
+ def find_object_in_map(s, m, e)
749
+ map = current_map
750
+ return unless map
751
+
752
+ re = /#{s.text}/
753
+ matches = []
754
+ (0...map.sections.size).each { |p|
755
+ map.sections[p].rooms.each { |r|
756
+ next unless r.objects =~ re
757
+ matches.push( [p, r] )
758
+ }
759
+ }
760
+ idx = @search.index
761
+ @search.index = matches.size-1 if idx >= matches.size
762
+ hilite_matches(map, matches, re, @search.index)
763
+ end
764
+
765
+ #
766
+ # Find task in map
767
+ #
768
+ def find_task_in_map(s, m, e)
769
+ map = current_map
770
+ return unless map
771
+
772
+ re = /#{s.text}/
773
+ matches = []
774
+ (0...map.sections.size).each { |p|
775
+ map.sections[p].rooms.each { |r|
776
+ next unless r.tasks =~ re
777
+ matches.push( [p, r] )
778
+ }
779
+ }
780
+ idx = @search.index
781
+ @search.index = matches.size-1 if idx >= matches.size
782
+ hilite_matches(map, matches, re, @search.index)
783
+ end
784
+
785
+ #
786
+ # Find object in map
787
+ #
788
+ def find_object_in_map_cb(s, m, e)
789
+ map = current_map
790
+ return unless map
791
+
792
+ title = MSG_FIND_OBJECT_IN_MAP
793
+ if not @search
794
+ require 'IFMapper/FXSearchDialogBox'
795
+ @search = FXSearchDialogBox.new(self)
796
+ end
797
+ @search.proc = method(:find_object_in_map)
798
+ @search.title = title
799
+ @search.text = ''
800
+ @search.show
801
+ end
802
+
803
+ #
804
+ # Find task in map
805
+ #
806
+ def find_task_in_map_cb(s, m, e)
807
+ map = current_map
808
+ return unless map
809
+
810
+ title = MSG_FIND_TASK_IN_MAP
811
+ if not @search
812
+ require 'IFMapper/FXSearchDialogBox'
813
+ @search = FXSearchDialogBox.new(self)
814
+ end
815
+ @search.proc = method(:find_task_in_map)
816
+ @search.title = title
817
+ @search.text = ''
818
+ @search.show
819
+ end
820
+
821
+ #
822
+ # Find task in map
823
+ #
824
+ def find_desc_in_map(s, m, e)
825
+ map = current_map
826
+ return unless map
827
+
828
+ re = /#{s.text}/
829
+ matches = []
830
+ (0...map.sections.size).each { |p|
831
+ map.sections[p].rooms.each { |r|
832
+ next unless r.desc =~ re
833
+ matches.push( [p, r] )
834
+ }
835
+ }
836
+ idx = @search.index
837
+ @search.index = matches.size-1 if idx >= matches.size
838
+ hilite_matches(map, matches, re, @search.index)
839
+ end
840
+
841
+ #
842
+ # Find description in map
843
+ #
844
+ def find_desc_in_map_cb(s, m, e)
845
+ map = current_map
846
+ return unless map
847
+
848
+ title = MSG_FIND_DESCRIPTION_IN_MAP
849
+ if not @search
850
+ require 'IFMapper/FXSearchDialogBox'
851
+ @search = FXSearchDialogBox.new(self)
852
+ end
853
+ @search.proc = method(:find_desc_in_map)
854
+ @search.title = title
855
+ @search.text = ''
856
+ @search.show
857
+ end
858
+
859
+
860
+ #
861
+ # Pop-up color preferences
862
+ #
863
+ def colors_cb(sender, id, msg)
864
+ map = current_map
865
+ return if not map
866
+
867
+ if not @colors
868
+ require 'IFMapper/FXMapColorBox'
869
+ @colors = FXMapColorBox.new(self)
870
+ else
871
+ @colors.show
872
+ end
873
+ @colors.copy_from(map)
874
+ end
875
+
876
+ #
877
+ # Unselect all
878
+ #
879
+ def select_none_cb( sender, id, event )
880
+ map = current_map
881
+ return if not map
882
+ map.clear_selection
883
+ end
884
+
885
+ #
886
+ # Select all
887
+ #
888
+ def select_all_cb( sender, id, event )
889
+ map = current_map
890
+ return if not map
891
+ sect = map.sections[map.section]
892
+ sect.rooms.each { |r|
893
+ r.selected = true
894
+ }
895
+ sect.connections.each { |c|
896
+ c.selected = true
897
+ }
898
+ end
899
+
900
+ def roomlist(sender, sel, event)
901
+ map = current_map
902
+ return unless map
903
+ map.show_roomlist
904
+ end
905
+
906
+ def itemlist(sender, sel, event)
907
+ map = current_map
908
+ return unless map
909
+ map.show_itemlist
910
+ end
911
+
912
+ def about_cb(sender, id, event )
913
+ require 'IFMapper/FXAboutDialogBox'
914
+ FXAboutDialogBox.new(self, MSG_ABOUT_SOFTWARE,
915
+ eval("\"#{MSG_ABOUT}\"")).execute
916
+ end
917
+
918
+
919
+ def create_menus
920
+ # Construct these icons
921
+ newdoc = load_icon("filenew")
922
+ opendoc = load_icon("fileopen")
923
+ savedoc = load_icon("filesave")
924
+ saveasdoc = load_icon("filesaveas")
925
+
926
+ # File Menu
927
+ filemenu = FXMenuPane.new(self)
928
+ FXMenuTitle.new(@menubar, MENU_FILE, nil, filemenu)
929
+ cmd = FXMenuCommand.new(filemenu, MENU_NEW, newdoc)
930
+ cmd.connect(SEL_COMMAND, method(:new_map_cb))
931
+
932
+ cmd = FXMenuCommand.new(filemenu, MENU_OPEN, opendoc)
933
+ cmd.connect(SEL_COMMAND, method(:open_cb))
934
+ cmd = FXMenuCommand.new(filemenu, MENU_SAVE, savedoc)
935
+ cmd.connect(SEL_COMMAND, method(:save_cb))
936
+ cmd = FXMenuCommand.new(filemenu, MENU_SAVE_AS,
937
+ saveasdoc)
938
+ cmd.connect(SEL_COMMAND, method(:save_as_cb))
939
+
940
+ # Export submenu
941
+ submenu = FXMenuPane.new(self)
942
+
943
+ cmd = FXMenuCommand.new(submenu, MENU_EXPORT_PDF, nil)
944
+ cmd.connect(SEL_COMMAND, method(:pdf_export_cb))
945
+
946
+ cmd = FXMenuCommand.new(submenu, MENU_EXPORT_IFM, nil)
947
+ cmd.connect(SEL_COMMAND, method(:ifm_export_cb))
948
+
949
+ cmd = FXMenuCommand.new(submenu, MENU_EXPORT_INFORM, nil)
950
+ cmd.connect(SEL_COMMAND, method(:inform_export_cb))
951
+
952
+ cmd = FXMenuCommand.new(submenu, MENU_EXPORT_TADS, nil)
953
+ cmd.connect(SEL_COMMAND, method(:tads_export_cb))
954
+
955
+ FXMenuCascade.new(filemenu, MENU_EXPORT, nil, submenu)
956
+
957
+
958
+ submenu = FXMenuPane.new(self)
959
+ cmd = FXMenuCommand.new(submenu, MENU_PRINT_MAP, nil)
960
+ cmd.connect(SEL_COMMAND, method(:print_cb))
961
+
962
+ cmd = FXMenuCommand.new(submenu, MENU_PRINT_LOCATIONS, nil)
963
+ cmd.connect(SEL_COMMAND, method(:print_locations_cb))
964
+ FXMenuCascade.new(filemenu, MENU_PRINT, nil, submenu)
965
+
966
+ cmd = FXMenuCommand.new(filemenu, MENU_QUIT, nil)
967
+ cmd.connect( SEL_COMMAND, method(:close_cb) )
968
+
969
+ # Edit Menu
970
+ editmenu = FXMenuPane.new(self)
971
+ FXMenuTitle.new(@menubar, MENU_EDIT, nil, editmenu)
972
+ cmd = FXMenuCommand.new(editmenu, MENU_COPY, nil)
973
+ cmd.connect(SEL_COMMAND, method(:copy_selected_cb))
974
+ cmd = FXMenuCommand.new(editmenu, MENU_CUT, nil)
975
+ cmd.connect(SEL_COMMAND, method(:cut_selected_cb))
976
+ cmd = FXMenuCommand.new(editmenu, MENU_PASTE, nil)
977
+ cmd.connect(SEL_COMMAND, method(:paste_selected_cb))
978
+
979
+ # Select submenu
980
+ FXMenuSeparator.new(editmenu)
981
+ submenu = FXMenuPane.new(self)
982
+ cmd = FXMenuCommand.new( submenu, MENU_SELECT_ALL )
983
+ cmd.connect(SEL_COMMAND, method(:select_all_cb))
984
+ cmd = FXMenuCommand.new( submenu, MENU_SELECT_NONE )
985
+ cmd.connect(SEL_COMMAND, method(:select_none_cb))
986
+ FXMenuCascade.new( editmenu, MENU_SELECT, nil, submenu )
987
+
988
+ # Searching submenu
989
+ FXMenuSeparator.new(editmenu)
990
+ submenu = FXMenuPane.new(self)
991
+ cmd = FXMenuCommand.new(submenu, MENU_SEARCH_MAP)
992
+ cmd.connect(SEL_COMMAND, method(:find_in_map_cb))
993
+ cmd = FXMenuCommand.new(submenu, MENU_SEARCH_SECTION)
994
+ cmd.connect(SEL_COMMAND, method(:find_in_section_cb))
995
+ cmd = FXMenuCommand.new(submenu, MENU_SEARCH_OBJECT)
996
+ cmd.connect(SEL_COMMAND, method(:find_object_in_map_cb))
997
+ cmd = FXMenuCommand.new(submenu, MENU_SEARCH_TASK)
998
+ cmd.connect(SEL_COMMAND, method(:find_task_in_map_cb))
999
+ cmd = FXMenuCommand.new(submenu, MENU_SEARCH_DESCRIPTION)
1000
+ cmd.connect(SEL_COMMAND, method(:find_desc_in_map_cb))
1001
+ FXMenuCascade.new(editmenu, MENU_SEARCH, nil, submenu)
1002
+
1003
+ # Complex Connection
1004
+ FXMenuSeparator.new(editmenu)
1005
+ cmd = FXMenuCommand.new(editmenu, MENU_COMPLEX_CONNECTION, nil)
1006
+ cmd.connect( SEL_COMMAND, method(:complex_connection_cb) )
1007
+
1008
+ FXMenuSeparator.new(editmenu)
1009
+ cmd = FXMenuCommand.new(editmenu, MENU_DELETE, nil)
1010
+ cmd.connect( SEL_COMMAND, method(:delete_selected_cb) )
1011
+
1012
+ # Map menu
1013
+ mapmenu = FXMenuPane.new(self)
1014
+
1015
+ cmd = FXMenuCommand.new(mapmenu, MENU_MAP_INFO)
1016
+ cmd.connect(SEL_COMMAND) { map_properties }
1017
+
1018
+ cmd = FXMenuCommand.new(mapmenu, MENU_ROOM_LIST)
1019
+ cmd.connect(SEL_COMMAND, method(:roomlist) )
1020
+
1021
+ cmd = FXMenuCommand.new(mapmenu, MENU_ITEM_LIST)
1022
+ cmd.connect(SEL_COMMAND, method(:itemlist) )
1023
+
1024
+ # Automap submenu
1025
+ #
1026
+ submenu = FXMenuPane.new(self)
1027
+ cmd = FXMenuCommand.new(submenu, MENU_AUTOMAP_START)
1028
+ cmd.connect(SEL_COMMAND, method(:start_automap_cb))
1029
+ cmd = FXMenuCommand.new(submenu, MENU_AUTOMAP_STOP)
1030
+ cmd.connect(SEL_COMMAND, method(:stop_automap_cb))
1031
+ FXMenuSeparator.new(submenu)
1032
+ cmd = FXMenuCommand.new(submenu, MENU_AUTOMAP_PROPERTIES)
1033
+ cmd.connect(SEL_COMMAND, method(:automap_properties_cb))
1034
+ FXMenuCascade.new(mapmenu, MENU_AUTOMAP, nil, submenu)
1035
+
1036
+ # Sections submenu
1037
+ submenu = FXMenuPane.new(self)
1038
+ cmd = FXMenuCommand.new(submenu, MENU_NEXT_SECTION)
1039
+ cmd.connect(SEL_COMMAND) {
1040
+ next_section
1041
+ }
1042
+ cmd = FXMenuCommand.new(submenu, MENU_PREVIOUS_SECTION)
1043
+ cmd.connect(SEL_COMMAND) {
1044
+ previous_section
1045
+ }
1046
+ FXMenuSeparator.new(submenu)
1047
+ cmd = FXMenuCommand.new(submenu, MENU_ADD_SECTION)
1048
+ cmd.connect(SEL_COMMAND) {
1049
+ map = current_map
1050
+ if map
1051
+ map.new_section
1052
+ map.modified = true
1053
+ update_section
1054
+ end
1055
+ }
1056
+ cmd = FXMenuCommand.new(submenu, MENU_RENAME_SECTION)
1057
+ cmd.connect(SEL_COMMAND) {
1058
+ map = current_map
1059
+ if map
1060
+ map.rename_section
1061
+ map.modified = true
1062
+ end
1063
+ }
1064
+ FXMenuSeparator.new(submenu)
1065
+ cmd = FXMenuCommand.new(submenu, MENU_DELETE_SECTION)
1066
+ cmd.connect(SEL_COMMAND) {
1067
+ map = current_map
1068
+ if map
1069
+ map.delete_section
1070
+ map.modified = true
1071
+ update_section
1072
+ end
1073
+ }
1074
+ FXMenuCascade.new(mapmenu, MENU_SECTIONS, nil, submenu)
1075
+
1076
+ #
1077
+ # Zoom submenu
1078
+ #
1079
+ submenu = FXMenuPane.new(self)
1080
+ [25, 50, 75, 100, 125].each { |v|
1081
+ cmd = FXMenuCommand.new(submenu, eval("\"#{MENU_ZOOM_PERCENT}\""))
1082
+ cmd.connect(SEL_COMMAND) {
1083
+ map = current_map
1084
+ if map
1085
+ map.zoom = v / 100.0
1086
+ map.draw
1087
+ end
1088
+ }
1089
+ }
1090
+ FXMenuCascade.new(mapmenu, MENU_ZOOM, nil, submenu)
1091
+
1092
+ submenu = FXMenuPane.new(self)
1093
+
1094
+ cmd = FXMenuCheck.new(submenu, MENU_EDIT_ON_CREATION)
1095
+ cmd.check = @@default_options['Edit on Creation']
1096
+ cmd.connect(SEL_COMMAND) { |s, m, e|
1097
+ map = current_map
1098
+ if map
1099
+ map.options['Edit on Creation'] = (s.check == true)
1100
+ end
1101
+ }
1102
+ cmd.connect(SEL_UPDATE) { |s, m, e|
1103
+ map = current_map
1104
+ s.check = map.options['Edit on Creation'] if map
1105
+ }
1106
+
1107
+ cmd = FXMenuCheck.new(submenu, MENU_AUTOMATIC_CONNECTION)
1108
+ cmd.check = @@default_options['Automatic Connection']
1109
+ cmd.connect(SEL_COMMAND) { |s, m, e|
1110
+ map = current_map
1111
+ if map
1112
+ map.options['Automatic Connection'] = (s.check == true)
1113
+ end
1114
+ }
1115
+ cmd.connect(SEL_UPDATE) { |s, m, e|
1116
+ map = current_map
1117
+ s.check = map.options['Automatic Connection'] if map
1118
+ }
1119
+
1120
+ cmd = FXMenuCheck.new(submenu, MENU_CREATE_ON_CONNECTION)
1121
+ cmd.check = @@default_options['Create on Connection']
1122
+ cmd.connect(SEL_COMMAND) { |s, m, e|
1123
+ map = current_map
1124
+ map.options['Create on Connection'] = s.check if map
1125
+ }
1126
+ cmd.connect(SEL_UPDATE) { |s, m, e|
1127
+ map = current_map
1128
+ s.check = map.options['Create on Connection'] if map
1129
+ }
1130
+
1131
+ FXMenuCascade.new(mapmenu, MENU_OPTIONS, nil, submenu)
1132
+
1133
+ ##########################
1134
+ # Display submenu
1135
+ #########################
1136
+ submenu = FXMenuPane.new(self)
1137
+ cmd = FXMenuCheck.new(submenu, MENU_USE_ROOM_CURSOR)
1138
+ cmd.check = @@default_options['Use Room Cursor']
1139
+ cmd.connect(SEL_COMMAND) { |s, m, e|
1140
+ map = current_map
1141
+ if map
1142
+ map.options['Use Room Cursor'] = (s.check == true)
1143
+ map.draw
1144
+ end
1145
+ }
1146
+ cmd.connect(SEL_UPDATE) { |s, m, e|
1147
+ map = current_map
1148
+ s.check = map.options['Use Room Cursor'] if map
1149
+ }
1150
+
1151
+ cmd = FXMenuCheck.new(submenu, MENU_PATHS_AS_CURVES)
1152
+ cmd.check = @@default_options['Paths as Curves']
1153
+ cmd.connect(SEL_COMMAND) { |s, m, e|
1154
+ map = current_map
1155
+ if map
1156
+ map.options['Paths as Curves'] = (s.check == true)
1157
+ map.draw
1158
+ end
1159
+ }
1160
+ cmd.connect(SEL_UPDATE) { |s, m, e|
1161
+ map = current_map
1162
+ s.check = map.options['Paths as Curves'] if map
1163
+ }
1164
+ cmd = FXMenuCheck.new(submenu, MENU_LOCATION_NUMBERS)
1165
+ cmd.check = @@default_options['Location Numbers']
1166
+ cmd.connect(SEL_COMMAND) { |s, m, e|
1167
+ map = current_map
1168
+ if map
1169
+ map.options['Location Numbers'] = (s.check == true)
1170
+ map.draw
1171
+ end
1172
+ }
1173
+ cmd.connect(SEL_UPDATE) { |s, m, e|
1174
+ map = current_map
1175
+ s.check = map.options['Location Numbers'] if map
1176
+ }
1177
+
1178
+ cmd = FXMenuCheck.new(submenu, MENU_LOCATION_TASKS)
1179
+ cmd.check = @@default_options['Location Tasks']
1180
+ cmd.connect(SEL_COMMAND) { |s, m, e|
1181
+ map = current_map
1182
+ if map
1183
+ map.options['Location Tasks'] = (s.check == true)
1184
+ map.draw
1185
+ end
1186
+ }
1187
+ cmd.connect(SEL_UPDATE) { |s, m, e|
1188
+ map = current_map
1189
+ s.check = map.options['Location Tasks'] if map
1190
+ }
1191
+
1192
+ cmd = FXMenuCheck.new(submenu, MENU_LOCATION_DESC)
1193
+ cmd.check = @@default_options['Location Description']
1194
+ cmd.connect(SEL_COMMAND) { |s, m, e|
1195
+ map = current_map
1196
+ if map
1197
+ map.options['Location Description'] = (s.check == true)
1198
+ map.draw
1199
+ end
1200
+ }
1201
+ cmd.connect(SEL_UPDATE) { |s, m, e|
1202
+ map = current_map
1203
+ s.check = map.options['Location Description'] if map
1204
+ }
1205
+
1206
+ cmd = FXMenuCheck.new(submenu, MENU_BOXES)
1207
+ cmd.check = @@default_options['Grid Boxes']
1208
+ cmd.connect(SEL_COMMAND) { |s, m, e|
1209
+ map = current_map
1210
+ if map
1211
+ map.options['Grid Boxes'] = (s.check == true)
1212
+ map.draw
1213
+ end
1214
+ }
1215
+ cmd.connect(SEL_UPDATE) { |s, m, e|
1216
+ map = current_map
1217
+ s.check = map.options['Grid Boxes'] if map
1218
+ }
1219
+
1220
+ cmd = FXMenuCheck.new(submenu, MENU_STRAIGHT_CONN)
1221
+ cmd.check = @@default_options['Grid Straight Connections']
1222
+ cmd.connect(SEL_COMMAND) { |s, m, e|
1223
+ map = current_map
1224
+ if map
1225
+ map.options['Grid Straight Connections'] = (s.check == true)
1226
+ map.draw
1227
+ end
1228
+ }
1229
+ cmd.connect(SEL_UPDATE) { |s, m, e|
1230
+ map = current_map
1231
+ s.check = map.options['Grid Straight Connections'] if map
1232
+ }
1233
+
1234
+ cmd = FXMenuCheck.new(submenu, MENU_DIAGONAL_CONN)
1235
+ cmd.check = @@default_options['Grid Diagonal Connections']
1236
+ cmd.connect(SEL_COMMAND) { |s, m, e|
1237
+ map = current_map
1238
+ if map
1239
+ map.options['Grid Diagonal Connections'] = s.check
1240
+ map.draw
1241
+ end
1242
+ }
1243
+ cmd.connect(SEL_UPDATE) { |s, m, e|
1244
+ map = current_map
1245
+ s.check = map.options['Grid Diagonal Connections'] if map
1246
+ }
1247
+
1248
+ FXMenuCascade.new(mapmenu, MENU_DISPLAY, nil, submenu)
1249
+
1250
+ submenu = FXMenuPane.new(self)
1251
+
1252
+ cmd = FXMenuCommand.new(submenu, MENU_COLORS)
1253
+ cmd.connect(SEL_COMMAND, method(:colors_cb))
1254
+
1255
+ # langmenu = FXMenuPane.new(self)
1256
+ # LANGUAGES.each { |k, v|
1257
+ # next unless File.exists?( "lib/IFMapper/locales/#{v}/Messages.rb" )
1258
+ # cmd = FXMenuCheck.new(langmenu, k)
1259
+ # cmd.connect(SEL_COMMAND, method(:language_cb))
1260
+ # }
1261
+
1262
+ # FXMenuCascade.new(mapmenu, MENU_LANGUAGE, nil, langmenu)
1263
+
1264
+
1265
+ FXMenuSeparator.new(submenu)
1266
+ cmd = FXMenuCommand.new(submenu, MENU_SAVE_PREFS)
1267
+ cmd.connect(SEL_COMMAND) {
1268
+ map = current_map
1269
+ map.options.write if map
1270
+ }
1271
+ FXMenuCascade.new(mapmenu, MENU_PREFS, nil, submenu)
1272
+
1273
+ FXMenuTitle.new(@menubar, MENU_MAP, nil, mapmenu)
1274
+
1275
+ # Window menu
1276
+ windowmenu = FXMenuPane.new(self)
1277
+ FXMenuCommand.new(windowmenu, MENU_TILE_HORIZONTALLY, nil,
1278
+ @mdiclient, FXMDIClient::ID_MDI_TILEHORIZONTAL)
1279
+ FXMenuCommand.new(windowmenu, MENU_TILE_VERTICALLY, nil,
1280
+ @mdiclient, FXMDIClient::ID_MDI_TILEVERTICAL)
1281
+ FXMenuCommand.new(windowmenu, MENU_CASCADE, nil,
1282
+ @mdiclient, FXMDIClient::ID_MDI_CASCADE)
1283
+ FXMenuCommand.new(windowmenu, MENU_CLOSE, nil,
1284
+ @mdiclient, FXMDIClient::ID_MDI_CLOSE)
1285
+ sep1 = FXMenuSeparator.new(windowmenu)
1286
+ sep1.setTarget(@mdiclient)
1287
+ sep1.setSelector(FXMDIClient::ID_MDI_ANY)
1288
+ FXMenuCommand.new(windowmenu, nil, nil, @mdiclient, FXMDIClient::ID_MDI_1)
1289
+ FXMenuCommand.new(windowmenu, nil, nil, @mdiclient, FXMDIClient::ID_MDI_2)
1290
+ FXMenuCommand.new(windowmenu, nil, nil, @mdiclient, FXMDIClient::ID_MDI_3)
1291
+ FXMenuCommand.new(windowmenu, nil, nil, @mdiclient, FXMDIClient::ID_MDI_4)
1292
+ FXMenuCommand.new(windowmenu, MENU_OTHERS, nil, @mdiclient,
1293
+ FXMDIClient::ID_MDI_OVER_5)
1294
+ FXMenuTitle.new(@menubar, MENU_WINDOW, nil, windowmenu)
1295
+
1296
+ # Help menu
1297
+ helpmenu = FXMenuPane.new(self)
1298
+ cmd = FXMenuCommand.new(helpmenu, MENU_HOTKEYS, nil)
1299
+ cmd.connect(SEL_COMMAND, method(:hotkeys))
1300
+ cmd = FXMenuCommand.new(helpmenu, MENU_INSTRUCTIONS, nil)
1301
+ cmd.connect(SEL_COMMAND, method(:docs))
1302
+ FXMenuSeparator.new(helpmenu)
1303
+ cmd = FXMenuCommand.new(helpmenu, MENU_ABOUT, nil)
1304
+ cmd.connect(SEL_COMMAND, method(:about_cb))
1305
+
1306
+ cmd = FXMenuCommand.new(helpmenu, MENU_RESOURCE, nil)
1307
+ cmd.connect(SEL_COMMAND) {
1308
+ require 'IFMapper/FXMapFileDialog'
1309
+ file = FXMapFileDialog.new(self, "Resource a Ruby File",
1310
+ ['Ruby File (*.rb)']).filename
1311
+ if file != ''
1312
+ begin
1313
+ Kernel.load file
1314
+ rescue => e
1315
+ p e
1316
+ end
1317
+ end
1318
+ }
1319
+ FXMenuTitle.new(@menubar, MENU_HELP, nil, helpmenu)
1320
+ end
1321
+
1322
+ def language
1323
+ return @@default_options['Language']
1324
+ end
1325
+
1326
+ def docs(*opts)
1327
+ browsers = [ 'firefox', 'opera', 'explorer' ]
1328
+ address = 'docs/' + language + '/start.html'
1329
+ status "#{MSG_OPENING_WEB_PAGE} #{address}..."
1330
+ ok = false
1331
+ browsers.each { |cmd|
1332
+ if RUBY_PLATFORM =~ /mswin/
1333
+ ok = system("start #{cmd} #{address}")
1334
+ else
1335
+ ok = system("#{cmd} #{address} &")
1336
+ end
1337
+ break if ok
1338
+ }
1339
+ if not ok
1340
+ status ERR_COULD_NOT_OPEN_WEB_BROWSER
1341
+ end
1342
+ end
1343
+
1344
+
1345
+ def hotkeys(*opts)
1346
+ require 'IFMapper/FXAboutDialogBox'
1347
+ FXAboutDialogBox.new(self, BOX_HOTKEYS, MSG_HOTKEYS).show
1348
+ end
1349
+
1350
+ def create_toolbar(toolbar)
1351
+
1352
+ # Construct these icons
1353
+ newdoc = load_icon("filenew")
1354
+ opendoc = load_icon("fileopen")
1355
+ savedoc = load_icon("filesave")
1356
+ saveasdoc = load_icon("filesaveas")
1357
+
1358
+ # File manipulation
1359
+ cmd = FXButton.new(toolbar, ICON_NEW, newdoc, nil, 0,
1360
+ FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
1361
+ cmd.connect(SEL_COMMAND, method(:new_map_cb))
1362
+
1363
+ cmd = FXButton.new(toolbar, ICON_OPEN, opendoc, nil, 0,
1364
+ FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
1365
+ cmd.connect(SEL_COMMAND, method(:open_cb))
1366
+
1367
+ cmd = FXButton.new(toolbar, ICON_SAVE, savedoc, nil, 0,
1368
+ FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
1369
+ cmd.connect(SEL_COMMAND, method(:save_cb))
1370
+ cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
1371
+ map = current_map
1372
+ message = map ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
1373
+ sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
1374
+ }
1375
+ cmd = FXButton.new(toolbar, ICON_SAVE_AS,
1376
+ saveasdoc, nil, 0, FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
1377
+ cmd.connect(SEL_COMMAND, method(:save_as_cb))
1378
+ cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
1379
+ map = current_map
1380
+ message = map ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
1381
+ sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
1382
+ }
1383
+
1384
+ # Print
1385
+ FXFrame.new(toolbar,
1386
+ LAYOUT_TOP|LAYOUT_LEFT|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT, 0, 0, 4, 20)
1387
+ cmd = FXButton.new(toolbar, ICON_PRINT,
1388
+ load_icon("printicon"), @mdiclient, FXGLViewer::ID_PRINT_IMAGE,
1389
+ BUTTON_AUTOGRAY|FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
1390
+ cmd.connect(SEL_COMMAND, method(:print_cb))
1391
+ cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
1392
+ map = current_map
1393
+ message = map ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
1394
+ sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
1395
+ }
1396
+
1397
+ # Editing
1398
+ FXFrame.new(toolbar,
1399
+ LAYOUT_TOP|LAYOUT_LEFT|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT, 0, 0, 4, 20)
1400
+ cmd = FXButton.new(toolbar, ICON_CUT, load_icon("cut"), @mdiclient,
1401
+ FXGLViewer::ID_CUT_SEL, (BUTTON_AUTOGRAY|FRAME_THICK|FRAME_RAISED|
1402
+ LAYOUT_TOP|LAYOUT_LEFT))
1403
+ cmd.connect(SEL_COMMAND, method(:cut_selected_cb))
1404
+ cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
1405
+ map = current_map
1406
+ message = map ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
1407
+ sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
1408
+ }
1409
+ cmd = FXButton.new(toolbar, ICON_COPY, load_icon("copy"), @mdiclient,
1410
+ FXGLViewer::ID_COPY_SEL, (BUTTON_AUTOGRAY|FRAME_THICK|FRAME_RAISED|
1411
+ LAYOUT_TOP|LAYOUT_LEFT))
1412
+ cmd.connect(SEL_COMMAND, method(:copy_selected_cb))
1413
+ cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
1414
+ map = current_map
1415
+ message = map ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
1416
+ sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
1417
+ }
1418
+ cmd = FXButton.new(toolbar, ICON_PASTE, load_icon("paste"), @mdiclient,
1419
+ FXGLViewer::ID_PASTE_SEL, (BUTTON_AUTOGRAY|FRAME_THICK|FRAME_RAISED|
1420
+ LAYOUT_TOP|LAYOUT_LEFT))
1421
+ cmd.connect(SEL_COMMAND, method(:paste_selected_cb))
1422
+ cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
1423
+ map = current_map
1424
+ message = (map and @@copy_buffer) ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
1425
+ sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
1426
+ }
1427
+
1428
+ # Zooming
1429
+ FXFrame.new(toolbar,
1430
+ LAYOUT_TOP|LAYOUT_LEFT|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT, 0, 0, 4, 20)
1431
+ cmd = FXButton.new(toolbar, ICON_ZOOM_IN, load_icon("zoom"), @mdiclient,
1432
+ 0, FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
1433
+ cmd.connect(SEL_COMMAND) { zoom_in }
1434
+
1435
+ cmd = FXButton.new(toolbar, ICON_ZOOM_OUT, load_icon("zoom"), @mdiclient,
1436
+ 0, FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
1437
+ cmd.connect(SEL_COMMAND) { zoom_out }
1438
+
1439
+
1440
+ # Section travel
1441
+ frame = FXHorizontalFrame.new(toolbar,
1442
+ LAYOUT_RIGHT|FRAME_THICK|FRAME_RAISED)
1443
+ cmd = FXButton.new(frame, ICON_PREV_SECTION, load_icon("prevpage"),
1444
+ @mdiclient,
1445
+ 0, FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
1446
+ cmd.connect(SEL_COMMAND) { previous_section }
1447
+
1448
+ @section = FXTextField.new(frame, 5, nil, 0,
1449
+ TEXTFIELD_INTEGER|LAYOUT_FILL_ROW)
1450
+ @section.text = '1'
1451
+ @section.connect(SEL_COMMAND) { |s,m,e|
1452
+ v = s.text.to_i
1453
+ map = current_map
1454
+ if map
1455
+ map.section = v - 1
1456
+ map.draw
1457
+ update_section
1458
+ end
1459
+ }
1460
+ @section.connect(SEL_UPDATE) { |s,m,e|
1461
+ v = s.text.to_i
1462
+ map = current_map
1463
+ update_section if map
1464
+ }
1465
+
1466
+ cmd = FXButton.new(frame, ICON_NEXT_SECTION, load_icon("nextpage"),
1467
+ @mdiclient,
1468
+ 0, FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
1469
+ cmd.connect(SEL_COMMAND) { next_section }
1470
+ end
1471
+
1472
+ #
1473
+ # Update section # in toolbar widget
1474
+ #
1475
+ def update_section
1476
+ map = current_map
1477
+ return unless map
1478
+ @section.text = (map.section + 1).to_s
1479
+ end
1480
+
1481
+ #
1482
+ # Go to next section in current map
1483
+ #
1484
+ def next_section
1485
+ map = current_map
1486
+ map.next_section if map
1487
+ update_section
1488
+ end
1489
+
1490
+ #
1491
+ # Go to previous section in current map
1492
+ #
1493
+ def previous_section
1494
+ map = current_map
1495
+ map.previous_section if map
1496
+ update_section
1497
+ end
1498
+
1499
+ #
1500
+ # Zoom in into current map
1501
+ #
1502
+ def zoom_in
1503
+ map = current_map
1504
+ if map
1505
+ map.zoom_in
1506
+ map.draw
1507
+ end
1508
+ end
1509
+
1510
+ #
1511
+ # Zoom out from current map
1512
+ #
1513
+ def zoom_out
1514
+ map = current_map
1515
+ if map
1516
+ map.zoom_out
1517
+ map.draw
1518
+ end
1519
+ end
1520
+
1521
+ #
1522
+ # Bring up the map property requester for current map
1523
+ #
1524
+ def map_properties
1525
+ map = current_map
1526
+ map.properties if map
1527
+ end
1528
+
1529
+ #
1530
+ # In case of crash or runtime error, autosave all maps, so user
1531
+ # does not loose any data.
1532
+ #
1533
+ def autosave
1534
+ @maps.each { |m|
1535
+ m.save
1536
+ }
1537
+ end
1538
+
1539
+
1540
+ def create_widgets
1541
+ # Menubar
1542
+ @menubar = FXMenuBar.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X)
1543
+
1544
+ FXHorizontalSeparator.new(self,
1545
+ LAYOUT_SIDE_TOP|SEPARATOR_GROOVE|LAYOUT_FILL_X)
1546
+ toolbar = FXToolBar.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X,
1547
+ 0, 0, 0, 0, 4, 4, 0, 0, 0, 0)
1548
+
1549
+ # Status bar
1550
+ @statusbar = FXStatusBar.new(self,
1551
+ LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|
1552
+ STATUSBAR_WITH_DRAGCORNER)
1553
+
1554
+
1555
+ create_mdiclient
1556
+ create_menus
1557
+ create_toolbar(toolbar)
1558
+ new_map
1559
+
1560
+ self.connect(SEL_CLOSE, method(:close_cb))
1561
+ show
1562
+ end
1563
+
1564
+ def initialize(app)
1565
+ super(app, eval("\"#{TITLE}\""), nil, nil, DECOR_ALL, 0, 0, 800, 600)
1566
+
1567
+ @colors = nil
1568
+ @mdimenu = nil
1569
+
1570
+ create_widgets
1571
+
1572
+ # Trap CTRL-C signals and exit nicely
1573
+ trap('SIGINT') {
1574
+ close_cb
1575
+ exit(0)
1576
+ }
1577
+ end
1578
+
1579
+ def close_cb(*args)
1580
+ exit = true
1581
+ @maps.each { |m|
1582
+ if not m.close_cb
1583
+ exit = false
1584
+ break
1585
+ else
1586
+ @maps.delete(m)
1587
+ end
1588
+ }
1589
+ self.close if exit
1590
+ end
1591
+
1592
+ end