ifmapper 0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/HISTORY.txt +2 -0
- data/IFMapper.gemspec +21 -0
- data/IFMapper.rb +27 -0
- data/icons/copy.png +0 -0
- data/icons/cut.png +0 -0
- data/icons/filenew.png +0 -0
- data/icons/fileopen.png +0 -0
- data/icons/filesave.png +0 -0
- data/icons/filesaveas.png +0 -0
- data/icons/help.png +0 -0
- data/icons/kill.png +0 -0
- data/icons/nextpage.png +0 -0
- data/icons/paste.png +0 -0
- data/icons/prevpage.png +0 -0
- data/icons/printicon.png +0 -0
- data/icons/redo.png +0 -0
- data/icons/saveas.png +0 -0
- data/icons/undo.png +0 -0
- data/icons/winapp.png +0 -0
- data/icons/zoom.png +0 -0
- data/lib/IFMapper/Connection.rb +63 -0
- data/lib/IFMapper/FXAboutDialogBox.rb +32 -0
- data/lib/IFMapper/FXConnection.rb +283 -0
- data/lib/IFMapper/FXConnectionDialogBox.rb +126 -0
- data/lib/IFMapper/FXMap.rb +1614 -0
- data/lib/IFMapper/FXMapDialogBox.rb +51 -0
- data/lib/IFMapper/FXMapFileDialog.rb +29 -0
- data/lib/IFMapper/FXMapperSettings.rb +45 -0
- data/lib/IFMapper/FXMapperWindow.rb +1051 -0
- data/lib/IFMapper/FXPage.rb +24 -0
- data/lib/IFMapper/FXPageDialogBox.rb +38 -0
- data/lib/IFMapper/FXRoom.rb +218 -0
- data/lib/IFMapper/FXRoomDialogBox.rb +119 -0
- data/lib/IFMapper/FXSearchDialogBox.rb +51 -0
- data/lib/IFMapper/FXSpline.rb +54 -0
- data/lib/IFMapper/FXWarningBox.rb +45 -0
- data/lib/IFMapper/IFMReader.rb +613 -0
- data/lib/IFMapper/Map.rb +110 -0
- data/lib/IFMapper/PDFMapExporter.rb +315 -0
- data/lib/IFMapper/Page.rb +158 -0
- data/lib/IFMapper/Room.rb +104 -0
- data/maps/Bureaucracy.ifm +75 -0
- data/maps/Hollywood_Hijinx.ifm +149 -0
- data/maps/Jigsaw.ifm +806 -0
- data/maps/LGOP.ifm +705 -0
- data/maps/Mercy.ifm +76 -0
- data/maps/Planetfall.ifm +186 -0
- data/maps/Plundered_Hearts.ifm +251 -0
- data/maps/Ralph.ifm +50 -0
- data/maps/Robots_of_Dawn.ifm +224 -0
- data/maps/Seastalker.ifm +149 -0
- data/maps/Sherlock.ifm +209 -0
- data/maps/SoFar.ifm +72 -0
- data/maps/Starcross.ifm +170 -0
- data/maps/Suspended.ifm +82 -0
- data/maps/Wishbringer.ifm +277 -0
- data/maps/Wishbringer2.ifm +246 -0
- data/maps/Zork1.ifm +410 -0
- data/maps/Zork2.ifm +150 -0
- data/maps/Zork3.ifm +136 -0
- data/maps/Zork_Zero.ifm +557 -0
- data/maps/anchor.ifm +645 -0
- data/maps/atrox.ifm +134 -0
- data/maps/awaken.ifm +116 -0
- data/maps/babel.ifm +279 -0
- data/maps/bse.ifm +150 -0
- data/maps/change.ifm +128 -0
- data/maps/curses.ifm +307 -0
- data/maps/curves.ifm +529 -0
- data/maps/edifice.ifm +158 -0
- data/maps/frozen.ifm +126 -0
- data/maps/glow.ifm +101 -0
- data/maps/library.ifm +93 -0
- data/maps/mindelec.ifm +89 -0
- data/maps/minster.ifm +234 -0
- data/maps/muse.ifm +154 -0
- data/maps/paperchase.ifm +110 -0
- data/maps/space_st.ifm +104 -0
- data/maps/stationfall.ifm +320 -0
- data/maps/theatre.ifm +182 -0
- data/maps/toonesia.ifm +54 -0
- data/maps/tortoise.ifm +72 -0
- data/maps/vgame.ifm +219 -0
- data/maps/weather.ifm +98 -0
- data/maps/windhall.ifm +154 -0
- data/maps/zebulon.ifm +68 -0
- metadata +144 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
class FXMapDialogBox < FXDialogBox
|
4
|
+
|
5
|
+
def copy_to()
|
6
|
+
@map.name = @name.text
|
7
|
+
@map.creator = @creator.text
|
8
|
+
@map.navigation = (@read.checkState == 1)
|
9
|
+
@map.update_title
|
10
|
+
end
|
11
|
+
|
12
|
+
def copy_from(map)
|
13
|
+
@name.text = map.name
|
14
|
+
@creator.text = map.creator
|
15
|
+
@read.checkState = map.navigation
|
16
|
+
@map = map
|
17
|
+
end
|
18
|
+
|
19
|
+
def initialize(parent)
|
20
|
+
decor = DECOR_TITLE|DECOR_BORDER|DECOR_CLOSE
|
21
|
+
|
22
|
+
super( parent, "Map Information", decor, 40, 40, 0, 0 )
|
23
|
+
mainFrame = FXVerticalFrame.new(self,
|
24
|
+
FRAME_SUNKEN|FRAME_THICK|
|
25
|
+
LAYOUT_FILL_X|LAYOUT_FILL_Y)
|
26
|
+
|
27
|
+
frame = FXHorizontalFrame.new(mainFrame, LAYOUT_SIDE_TOP|LAYOUT_FILL_X)
|
28
|
+
|
29
|
+
FXLabel.new(frame, "Name: ", nil, 0, LAYOUT_FILL_X)
|
30
|
+
@name = FXTextField.new(frame, 40, nil, 0, LAYOUT_FILL_ROW)
|
31
|
+
|
32
|
+
frame = FXVerticalFrame.new(mainFrame,
|
33
|
+
LAYOUT_SIDE_TOP|LAYOUT_FILL_X|LAYOUT_FILL_Y)
|
34
|
+
|
35
|
+
FXLabel.new(frame, "Creator: ", nil, 0, LAYOUT_FILL_X)
|
36
|
+
@creator = FXTextField.new(frame, 40, nil, 0, LAYOUT_FILL_ROW)
|
37
|
+
|
38
|
+
@read = FXCheckButton.new(frame, "Read Only Map", nil, 0,
|
39
|
+
ICON_BEFORE_TEXT|LAYOUT_LEFT|
|
40
|
+
LAYOUT_SIDE_RIGHT)
|
41
|
+
|
42
|
+
@name.connect(SEL_CHANGED) { copy_to() }
|
43
|
+
@creator.connect(SEL_CHANGED) { copy_to() }
|
44
|
+
@read.connect(SEL_COMMAND) { copy_to() }
|
45
|
+
|
46
|
+
@parent = parent
|
47
|
+
|
48
|
+
# We need to create the dialog box first, so we can use select text.
|
49
|
+
create
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
#
|
4
|
+
# A simple class to show a filerequester that remembers the
|
5
|
+
# last directory visited.
|
6
|
+
#
|
7
|
+
class FXMapFileDialog < FXFileDialog
|
8
|
+
@@last_path = nil
|
9
|
+
|
10
|
+
KNOWN_EXTENSIONS = [
|
11
|
+
"Map Files (*.map,*.ifm)",
|
12
|
+
"All Files (*)",
|
13
|
+
]
|
14
|
+
|
15
|
+
|
16
|
+
def initialize(parent, title, patterns = KNOWN_EXTENSIONS)
|
17
|
+
opts = 0
|
18
|
+
if RUBY_PLATFORM =~ /mswin/
|
19
|
+
opts |= FILEMATCH_NOESCAPE
|
20
|
+
end
|
21
|
+
super(parent, title, opts)
|
22
|
+
setPatternList(patterns)
|
23
|
+
self.directory = @@last_path if @@last_path
|
24
|
+
if execute != 0
|
25
|
+
@@last_path = filename.sub(/[\\\/][^\\\/]+$/, '')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# This class contains the applicatio and map's settings
|
4
|
+
#
|
5
|
+
class FXMapperSettings < Hash
|
6
|
+
def write
|
7
|
+
file = File.expand_path('~/.ifmapper')
|
8
|
+
f = File.open(file, 'w')
|
9
|
+
f.puts YAML.dump(self)
|
10
|
+
f.close
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
f = File.expand_path('~/.ifmapper')
|
15
|
+
begin
|
16
|
+
self.replace( YAML.load_file(f) )
|
17
|
+
rescue
|
18
|
+
self.replace( {
|
19
|
+
# Colors
|
20
|
+
'BG Color' => 'dark grey',
|
21
|
+
'Arrow Color' => 'black',
|
22
|
+
'Box BG Color' => 'white',
|
23
|
+
'Box Darkness Color' => 'grey',
|
24
|
+
'Box Border Color' => 'black',
|
25
|
+
'Box Number Color' => 'grey',
|
26
|
+
|
27
|
+
# Fonts
|
28
|
+
'Font Text' => 'times new roman',
|
29
|
+
'Font Objects' => 'times new roman',
|
30
|
+
|
31
|
+
# Creation options
|
32
|
+
'Create on Connection' => true,
|
33
|
+
'Edit on Creation' => false,
|
34
|
+
'Automatic Connection' => true,
|
35
|
+
|
36
|
+
# Display options
|
37
|
+
'Paths as Curves' => true,
|
38
|
+
'Location Numbers' => true,
|
39
|
+
'Grid Boxes' => true,
|
40
|
+
'Grid Straight Connections' => true,
|
41
|
+
'Grid Diagonal Connections' => false,
|
42
|
+
} )
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,1051 @@
|
|
1
|
+
|
2
|
+
begin
|
3
|
+
require 'fox12'
|
4
|
+
rescue => e
|
5
|
+
$stderr.puts "Please install the fox library v1.2 or later."
|
6
|
+
if $rubygems
|
7
|
+
$stderr.puts "You can usually do so if you do 'gem install fox'."
|
8
|
+
end
|
9
|
+
exit(1)
|
10
|
+
end
|
11
|
+
|
12
|
+
begin
|
13
|
+
require 'yaml'
|
14
|
+
rescue => e
|
15
|
+
$stderr.puts "Please install the yaml library."
|
16
|
+
$stderr.puts "Without it, preferences cannot be saved."
|
17
|
+
exit(1)
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
include Fox
|
22
|
+
|
23
|
+
require 'IFMapper/FXMap'
|
24
|
+
require 'IFMapper/FXMapperSettings'
|
25
|
+
require 'IFMapper/FXWarningBox'
|
26
|
+
require 'IFMapper/FXAboutDialogBox'
|
27
|
+
require 'IFMapper/FXSearchDialogBox'
|
28
|
+
require 'IFMapper/FXMapFileDialog'
|
29
|
+
require 'IFMapper/IFMReader'
|
30
|
+
|
31
|
+
|
32
|
+
class FXMapperWindow < FXMainWindow
|
33
|
+
|
34
|
+
PROGRAM_NAME = "Interactive Fiction Mapper"
|
35
|
+
VERSION = 0.5
|
36
|
+
AUTHOR = "Gonzalo Garramuno"
|
37
|
+
TITLE = "#{PROGRAM_NAME} v#{VERSION} - Written by #{AUTHOR}"
|
38
|
+
|
39
|
+
@@copy_buffer = nil
|
40
|
+
@@default_options = FXMapperSettings.new
|
41
|
+
|
42
|
+
def open_map(file)
|
43
|
+
tmp = nil
|
44
|
+
begin
|
45
|
+
f = File.open(file, 'rb')
|
46
|
+
tmp = Marshal.load(f)
|
47
|
+
f.close
|
48
|
+
tmp.filename = file
|
49
|
+
rescue => e
|
50
|
+
tmp = e
|
51
|
+
end
|
52
|
+
return tmp
|
53
|
+
end
|
54
|
+
|
55
|
+
def open_ifm(file, map)
|
56
|
+
IFMReader.new(file, map)
|
57
|
+
return map
|
58
|
+
end
|
59
|
+
|
60
|
+
def open_cb(sender, sel, ptr)
|
61
|
+
file = FXMapFileDialog.new(self, "Load New Map").filename
|
62
|
+
return if file == ''
|
63
|
+
|
64
|
+
# First, make sure we don't have it loaded already...
|
65
|
+
@maps.each { |m|
|
66
|
+
if m.filename == file
|
67
|
+
@mdiclient.setActiveChild(m.window)
|
68
|
+
return
|
69
|
+
end
|
70
|
+
}
|
71
|
+
|
72
|
+
# Then, check if we have a single and empty map.
|
73
|
+
# If so, we can just use that one to load the file in.
|
74
|
+
# If not, we need to create a new map
|
75
|
+
make_new_map = false
|
76
|
+
if @maps.size == 1
|
77
|
+
@maps[0].pages.each { |p|
|
78
|
+
if p.rooms.size != 0
|
79
|
+
make_new_map = true
|
80
|
+
break
|
81
|
+
end
|
82
|
+
}
|
83
|
+
else
|
84
|
+
make_new_map = true
|
85
|
+
end
|
86
|
+
|
87
|
+
if make_new_map
|
88
|
+
map = new_map
|
89
|
+
else
|
90
|
+
map = @maps[0]
|
91
|
+
end
|
92
|
+
status "Loading '#{file}'..."
|
93
|
+
|
94
|
+
tmp = nil
|
95
|
+
if file =~ /\.ifm$/i
|
96
|
+
tmp = open_ifm(file, map)
|
97
|
+
else
|
98
|
+
tmp = open_map(file)
|
99
|
+
end
|
100
|
+
|
101
|
+
if not tmp.kind_of?(Map) and not tmp.kind_of?(FXMap)
|
102
|
+
status "Could not load '#{file}'. Error: #{tmp}."
|
103
|
+
if map.close_cb
|
104
|
+
@maps.delete(map) if make_new_map
|
105
|
+
end
|
106
|
+
sleep 2
|
107
|
+
return
|
108
|
+
end
|
109
|
+
|
110
|
+
map.copy(tmp)
|
111
|
+
map.create_pathmap
|
112
|
+
map.update_title
|
113
|
+
map.window.create
|
114
|
+
update_page
|
115
|
+
status "Loaded '#{file}'."
|
116
|
+
end
|
117
|
+
|
118
|
+
def status(msg)
|
119
|
+
@statusbar.statusLine.text = msg
|
120
|
+
end
|
121
|
+
|
122
|
+
def current_map
|
123
|
+
window = @mdiclient.activeChild
|
124
|
+
return nil unless window
|
125
|
+
|
126
|
+
@maps.each { |m|
|
127
|
+
return m if m.window == window
|
128
|
+
}
|
129
|
+
return nil
|
130
|
+
end
|
131
|
+
|
132
|
+
def save_cb(sender, sel, ptr)
|
133
|
+
map = current_map
|
134
|
+
return unless map
|
135
|
+
map.save
|
136
|
+
end
|
137
|
+
|
138
|
+
def save_as_cb(sender, sel, ptr)
|
139
|
+
map = current_map
|
140
|
+
return unless map
|
141
|
+
map.save_as
|
142
|
+
end
|
143
|
+
|
144
|
+
def new_map_cb(*args)
|
145
|
+
m = new_map
|
146
|
+
m.window.create
|
147
|
+
end
|
148
|
+
|
149
|
+
def new_map
|
150
|
+
mapname = "Empty Map \##{@maps.size+1}"
|
151
|
+
@maps.push( FXMap.new(mapname, @mdiclient, @@default_options.dup,
|
152
|
+
@mdiicon, @mdimenu, MDI_NORMAL, 0, 0, 790, 500) )
|
153
|
+
@maps[-1].window.connect(SEL_PAINT) { |sender, sel, event|
|
154
|
+
map = current_map
|
155
|
+
map.draw
|
156
|
+
}
|
157
|
+
@maps[-1].window.connect(SEL_CLOSE) { |sender, sel, event|
|
158
|
+
map = current_map
|
159
|
+
if map.close_cb
|
160
|
+
@maps.delete(map)
|
161
|
+
end
|
162
|
+
}
|
163
|
+
|
164
|
+
# Make it active
|
165
|
+
@mdiclient.setActiveChild(@maps[-1].window)
|
166
|
+
return @maps[-1]
|
167
|
+
end
|
168
|
+
|
169
|
+
# Load the named PNG icon from a file
|
170
|
+
def load_icon(filename)
|
171
|
+
begin
|
172
|
+
filename = File.join("icons", filename) + ".png"
|
173
|
+
icon = nil
|
174
|
+
File.open(filename, "rb") { |f|
|
175
|
+
icon = FXPNGIcon.new(getApp(), f.read)
|
176
|
+
}
|
177
|
+
icon
|
178
|
+
rescue
|
179
|
+
raise RuntimeError, "Couldn't load icon: #{filename}"
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def complex_connection_cb(sender, sel, msg)
|
184
|
+
map = current_map
|
185
|
+
return unless map
|
186
|
+
map.complex_connection
|
187
|
+
end
|
188
|
+
|
189
|
+
def delete_selected_cb(sender, sel, msg)
|
190
|
+
map = current_map
|
191
|
+
return unless map
|
192
|
+
map.delete_selected
|
193
|
+
end
|
194
|
+
|
195
|
+
#
|
196
|
+
# Export current map as a Postscript file
|
197
|
+
#
|
198
|
+
def ps_export
|
199
|
+
map = current_map
|
200
|
+
return unless map
|
201
|
+
raise "Postscript Exporting not yet supported."
|
202
|
+
|
203
|
+
begin
|
204
|
+
require "IFMapper/PSMapExporter"
|
205
|
+
rescue => e
|
206
|
+
# bring up fox warning dialog box
|
207
|
+
return
|
208
|
+
end
|
209
|
+
|
210
|
+
printer = printer_dialog
|
211
|
+
map.ps_export(printer) if printer
|
212
|
+
end
|
213
|
+
|
214
|
+
|
215
|
+
#
|
216
|
+
# Popup a printer dialog and return the settings or false if user cancels
|
217
|
+
#
|
218
|
+
def printer_dialog
|
219
|
+
map = current_map
|
220
|
+
dlg = FXPrintDialog.new(self, "Print Map #{map.name}")
|
221
|
+
# Set printer to always use Landscape printing
|
222
|
+
dlg.printer.flags |= PRINT_DEST_PAPER | PRINT_LANDSCAPE | PRINT_NOBOUNDS
|
223
|
+
return dlg.printer if dlg.execute != 0
|
224
|
+
return false
|
225
|
+
end
|
226
|
+
|
227
|
+
def print_locations_cb(sender, sel, msg)
|
228
|
+
map = current_map
|
229
|
+
return unless map
|
230
|
+
|
231
|
+
w = FXWarningBox.new( self, "Sorry, but printing is not yet implemented\n" +
|
232
|
+
"in this version.")
|
233
|
+
w.execute
|
234
|
+
return
|
235
|
+
|
236
|
+
printer = printer_dialog
|
237
|
+
map.print_locations( printer ) if printer
|
238
|
+
end
|
239
|
+
|
240
|
+
def ifm_export_cb(sender, sel, msg)
|
241
|
+
map = current_map
|
242
|
+
return unless map
|
243
|
+
|
244
|
+
w = FXWarningBox.new( self, "Sorry, but IFM exporting is not yet implemented\n" +
|
245
|
+
"in this version.")
|
246
|
+
w.execute
|
247
|
+
return
|
248
|
+
end
|
249
|
+
|
250
|
+
def ps_export_cb(sender, sel, msg)
|
251
|
+
map = current_map
|
252
|
+
return unless map
|
253
|
+
|
254
|
+
w = FXWarningBox.new( self, "Sorry, but Postscript exporting is not implemented\n" +
|
255
|
+
"in this version.")
|
256
|
+
w.execute
|
257
|
+
return
|
258
|
+
end
|
259
|
+
|
260
|
+
def pdf_export_cb(sender, sel, msg)
|
261
|
+
map = current_map
|
262
|
+
return unless map
|
263
|
+
|
264
|
+
begin
|
265
|
+
require 'IFMapper/PDFMapExporter'
|
266
|
+
rescue => e
|
267
|
+
w = FXWarningBox.new( self, "#{e}")
|
268
|
+
w.execute
|
269
|
+
return
|
270
|
+
end
|
271
|
+
|
272
|
+
|
273
|
+
d = FXMapFileDialog.new(self, "Save Acrobat PDF File",
|
274
|
+
[
|
275
|
+
"Acrobat PDF (*.pdf)"
|
276
|
+
])
|
277
|
+
if d.filename != ''
|
278
|
+
map.pdf_export(d.filename)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
def print_cb(sender, sel, msg)
|
283
|
+
map = current_map
|
284
|
+
return unless map
|
285
|
+
|
286
|
+
w = FXWarningBox.new( self, "Sorry, but printing is not yet implemented\n" +
|
287
|
+
"in this version.")
|
288
|
+
w.execute
|
289
|
+
return
|
290
|
+
|
291
|
+
printer = printer_dialog
|
292
|
+
map.print( printer ) if printer
|
293
|
+
end
|
294
|
+
|
295
|
+
def create_mdiclient
|
296
|
+
# MDI Client
|
297
|
+
@maps = []
|
298
|
+
|
299
|
+
@mdiclient = FXMDIClient.new(self, LAYOUT_FILL_X|LAYOUT_FILL_Y)
|
300
|
+
|
301
|
+
# MDI buttons in menu:- note the message ID's!!!!!
|
302
|
+
# Normally, MDI commands are simply sensitized or desensitized;
|
303
|
+
# Under the @menubar, however, they're hidden if the MDI Client is
|
304
|
+
# not maximized. To do this, they must have different ID's.
|
305
|
+
FXMDIWindowButton.new(@menubar, @mdimenu, @mdiclient,
|
306
|
+
FXMDIClient::ID_MDI_MENUWINDOW, LAYOUT_LEFT)
|
307
|
+
FXMDIDeleteButton.new(@menubar, @mdiclient,
|
308
|
+
FXMDIClient::ID_MDI_MENUCLOSE, FRAME_RAISED|LAYOUT_RIGHT)
|
309
|
+
FXMDIRestoreButton.new(@menubar, @mdiclient,
|
310
|
+
FXMDIClient::ID_MDI_MENURESTORE, FRAME_RAISED|LAYOUT_RIGHT)
|
311
|
+
FXMDIMinimizeButton.new(@menubar, @mdiclient,
|
312
|
+
FXMDIClient::ID_MDI_MENUMINIMIZE, FRAME_RAISED|LAYOUT_RIGHT)
|
313
|
+
|
314
|
+
# Icon for MDI Child
|
315
|
+
@mdiicon = load_icon("winapp")
|
316
|
+
|
317
|
+
# Make MDI Window Menu
|
318
|
+
@mdimenu = FXMDIMenu.new(self, @mdiclient)
|
319
|
+
end
|
320
|
+
|
321
|
+
def self.copy_buffer
|
322
|
+
return @@copy_buffer
|
323
|
+
end
|
324
|
+
|
325
|
+
def self.copy_selected(map)
|
326
|
+
selection = map.pages[map.page].rooms.find_all { |r| r.selected }
|
327
|
+
return if selection.size > 1
|
328
|
+
@@copy_buffer = selection[0]
|
329
|
+
end
|
330
|
+
|
331
|
+
def self.cut_selected(map)
|
332
|
+
FXMapperWindow::copy_selected(map)
|
333
|
+
map.delete_selected
|
334
|
+
end
|
335
|
+
|
336
|
+
def self.paste_selected(map)
|
337
|
+
selection = map.pages[map.page].rooms.find_all { |r| r.selected }
|
338
|
+
selection.each { |r| r.copy(@@copy_buffer) }
|
339
|
+
end
|
340
|
+
|
341
|
+
def cut_selected_cb(*o)
|
342
|
+
map = current_map
|
343
|
+
return unless map
|
344
|
+
FXMapperWindow::cut_selected(map)
|
345
|
+
end
|
346
|
+
|
347
|
+
def copy_selected_cb(*o)
|
348
|
+
map = current_map
|
349
|
+
return unless map
|
350
|
+
FXMapperWindow::copy_selected(map)
|
351
|
+
end
|
352
|
+
|
353
|
+
def paste_selected_cb(*o)
|
354
|
+
map = current_map
|
355
|
+
return if not map or not @@copy_buffer
|
356
|
+
FXMapperWindow::paste_selected(map)
|
357
|
+
end
|
358
|
+
|
359
|
+
def hilite_matches(map, matches, re, idx = 0 )
|
360
|
+
if matches.size == 0
|
361
|
+
status "No objects match regex '#{re}'."
|
362
|
+
return
|
363
|
+
end
|
364
|
+
|
365
|
+
# sort matches by page
|
366
|
+
matches.sort_by { |a| a[0] }
|
367
|
+
|
368
|
+
# Jump to first page of match
|
369
|
+
map.page = matches[idx][0]
|
370
|
+
map.pages[map.page].rooms.each { |r| r.selected = false }
|
371
|
+
|
372
|
+
matches.each { |p, r| r.selected = true }
|
373
|
+
|
374
|
+
num = matches.find_all { |p, r| p == matches[idx][0] }
|
375
|
+
|
376
|
+
status "#{matches.size} in map, #{num.size} in page."
|
377
|
+
map.draw
|
378
|
+
end
|
379
|
+
|
380
|
+
def find_in_map(s, m, e)
|
381
|
+
map = current_map
|
382
|
+
return unless map
|
383
|
+
|
384
|
+
re = /#{s.text}/
|
385
|
+
matches = []
|
386
|
+
(0...map.pages.size).each { |p|
|
387
|
+
map.pages[p].rooms.each { |r|
|
388
|
+
next unless r.name =~ re
|
389
|
+
matches.push( [p, r] )
|
390
|
+
}
|
391
|
+
}
|
392
|
+
idx = @search.index
|
393
|
+
@search.index = matches.size-1 if idx >= matches.size
|
394
|
+
hilite_matches(map, matches, re, @search.index)
|
395
|
+
end
|
396
|
+
|
397
|
+
def find_in_map_cb(s, m, e)
|
398
|
+
map = current_map
|
399
|
+
return unless map
|
400
|
+
|
401
|
+
title = "Find Location In Map"
|
402
|
+
if not @search
|
403
|
+
@search = FXSearchDialogBox.new(self)
|
404
|
+
end
|
405
|
+
@search.proc = method(:find_in_map)
|
406
|
+
@search.title = title
|
407
|
+
@search.text = ''
|
408
|
+
@search.show
|
409
|
+
end
|
410
|
+
|
411
|
+
def find_in_page(s, m, e)
|
412
|
+
map = current_map
|
413
|
+
return unless map
|
414
|
+
|
415
|
+
re = /#{s.text}/
|
416
|
+
matches = []
|
417
|
+
map.pages[map.page].rooms.each { |r|
|
418
|
+
next unless r.name =~ re
|
419
|
+
matches.push( [ map.page, r] )
|
420
|
+
}
|
421
|
+
hilite_matches(map, matches, re)
|
422
|
+
end
|
423
|
+
|
424
|
+
def find_in_page_cb(s, m, e)
|
425
|
+
map = current_map
|
426
|
+
return unless map
|
427
|
+
|
428
|
+
title = "Find Location in this Section"
|
429
|
+
if not @search
|
430
|
+
@search = FXSearchDialogBox.new(self)
|
431
|
+
end
|
432
|
+
@search.proc = method(:find_in_page)
|
433
|
+
@search.title = title
|
434
|
+
@search.text = ''
|
435
|
+
@search.show
|
436
|
+
end
|
437
|
+
|
438
|
+
def find_object_in_map(s, m, e)
|
439
|
+
map = current_map
|
440
|
+
return unless map
|
441
|
+
|
442
|
+
re = /#{s.text}/
|
443
|
+
matches = []
|
444
|
+
(0...map.pages.size).each { |p|
|
445
|
+
map.pages[p].rooms.each { |r|
|
446
|
+
next unless r.objects =~ re
|
447
|
+
matches.push( [p, r] )
|
448
|
+
}
|
449
|
+
}
|
450
|
+
idx = @search.index
|
451
|
+
@search.index = matches.size-1 if idx >= matches.size
|
452
|
+
hilite_matches(map, matches, re, @search.index)
|
453
|
+
end
|
454
|
+
|
455
|
+
def find_object_in_map_cb(s, m, e)
|
456
|
+
map = current_map
|
457
|
+
return unless map
|
458
|
+
|
459
|
+
title = "Find Object In Map"
|
460
|
+
if not @search
|
461
|
+
@search = FXSearchDialogBox.new(self)
|
462
|
+
end
|
463
|
+
@search.proc = method(:find_object_in_map)
|
464
|
+
@search.title = title
|
465
|
+
@search.text = ''
|
466
|
+
@search.show
|
467
|
+
end
|
468
|
+
|
469
|
+
def about_cb(sender, id, msg)
|
470
|
+
FXAboutDialogBox.new(self, "About This Software...", <<"EOF").execute
|
471
|
+
#{TITLE} - #{VERSION}
|
472
|
+
|
473
|
+
A WYSIWYG mapping tool for interactive fiction.
|
474
|
+
Written by Gonzalo Garramuno.
|
475
|
+
|
476
|
+
ggarra@advancedsl.com.ar
|
477
|
+
GGarramuno@aol.com
|
478
|
+
EOF
|
479
|
+
end
|
480
|
+
|
481
|
+
def create_menus
|
482
|
+
# Construct these icons
|
483
|
+
newdoc = load_icon("filenew")
|
484
|
+
opendoc = load_icon("fileopen")
|
485
|
+
savedoc = load_icon("filesave")
|
486
|
+
saveasdoc = load_icon("filesaveas")
|
487
|
+
|
488
|
+
# File Menu
|
489
|
+
filemenu = FXMenuPane.new(self)
|
490
|
+
FXMenuTitle.new(@menubar, "&File", nil, filemenu)
|
491
|
+
cmd = FXMenuCommand.new(filemenu, "&New...\tCtl-N\tCreate new document.", newdoc)
|
492
|
+
cmd.connect(SEL_COMMAND, method(:new_map_cb))
|
493
|
+
|
494
|
+
cmd = FXMenuCommand.new(filemenu, "&Open...\tCtl-O\tOpen document file.", opendoc)
|
495
|
+
cmd.connect(SEL_COMMAND, method(:open_cb))
|
496
|
+
cmd = FXMenuCommand.new(filemenu, "&Save\tCtl-S\tSave document.", savedoc)
|
497
|
+
cmd.connect(SEL_COMMAND, method(:save_cb))
|
498
|
+
cmd = FXMenuCommand.new(filemenu, "Save &As...\t\tSave document to another file.",
|
499
|
+
saveasdoc)
|
500
|
+
cmd.connect(SEL_COMMAND, method(:save_as_cb))
|
501
|
+
|
502
|
+
# Export submenu
|
503
|
+
submenu = FXMenuPane.new(self)
|
504
|
+
|
505
|
+
cmd = FXMenuCommand.new(submenu, "&Export as PDF...\t\tExport map as Acrobat PDF document.", nil)
|
506
|
+
cmd.connect(SEL_COMMAND, method(:pdf_export_cb))
|
507
|
+
|
508
|
+
cmd = FXMenuCommand.new(submenu, "&Export as Postscript...\t\tExport map as Postscript document.", nil)
|
509
|
+
cmd.connect(SEL_COMMAND, method(:ps_export_cb))
|
510
|
+
|
511
|
+
cmd = FXMenuCommand.new(submenu, "&Export as IFM...\t\tExport map as an IFM map.", nil)
|
512
|
+
cmd.connect(SEL_COMMAND, method(:ifm_export_cb))
|
513
|
+
FXMenuCascade.new(filemenu, "Export", nil, submenu)
|
514
|
+
|
515
|
+
|
516
|
+
submenu = FXMenuPane.new(self)
|
517
|
+
cmd = FXMenuCommand.new(submenu, "&Map...\t\tPrint map (as graph).",
|
518
|
+
nil)
|
519
|
+
cmd.connect(SEL_COMMAND, method(:print_cb))
|
520
|
+
|
521
|
+
cmd = FXMenuCommand.new(submenu, "Map &Locations...\t\tPrint map locations as text list.", nil)
|
522
|
+
cmd.connect(SEL_COMMAND, method(:print_locations_cb))
|
523
|
+
FXMenuCascade.new(filemenu, "Print", nil, submenu)
|
524
|
+
|
525
|
+
|
526
|
+
FXMenuCommand.new(filemenu, "&Quit\tCtl-Q\tQuit the application.", nil,
|
527
|
+
getApp(), FXApp::ID_QUIT)
|
528
|
+
|
529
|
+
# Edit Menu
|
530
|
+
editmenu = FXMenuPane.new(self)
|
531
|
+
FXMenuTitle.new(@menubar, "&Edit", nil, editmenu)
|
532
|
+
cmd = FXMenuCommand.new(editmenu, "&Copy\tCtl-C\tCopy Location Data", nil)
|
533
|
+
cmd.connect(SEL_COMMAND, method(:copy_selected_cb))
|
534
|
+
cmd = FXMenuCommand.new(editmenu, "Cu&t\tCtl-X\tCut Location", nil)
|
535
|
+
cmd.connect(SEL_COMMAND, method(:cut_selected_cb))
|
536
|
+
cmd = FXMenuCommand.new(editmenu, "&Paste\tCtl-V\tPaste Location Data",
|
537
|
+
nil)
|
538
|
+
cmd.connect(SEL_COMMAND, method(:paste_selected_cb))
|
539
|
+
|
540
|
+
# Searching menu
|
541
|
+
FXMenuSeparator.new(editmenu)
|
542
|
+
cmd = FXMenuCommand.new(editmenu, "&Search Location in Map\tCtl-F\tFind a Location Name in Map")
|
543
|
+
cmd.connect(SEL_COMMAND, method(:find_in_map_cb))
|
544
|
+
cmd = FXMenuCommand.new(editmenu, "&Search Location in Section\tAlt-F\tFind a Location Name in Current Page")
|
545
|
+
cmd.connect(SEL_COMMAND, method(:find_in_page_cb))
|
546
|
+
cmd = FXMenuCommand.new(editmenu, "&Search Object in Map\tAlt-O\tFind Location with an Object in the Map")
|
547
|
+
cmd.connect(SEL_COMMAND, method(:find_object_in_map_cb))
|
548
|
+
|
549
|
+
# Complex Connection
|
550
|
+
FXMenuSeparator.new(editmenu)
|
551
|
+
cmd = FXMenuCommand.new(editmenu, "Comple&x Connection\tx\tCreate a complex connection.", nil)
|
552
|
+
cmd.connect( SEL_COMMAND, method(:complex_connection_cb))
|
553
|
+
|
554
|
+
FXMenuSeparator.new(editmenu)
|
555
|
+
cmd = FXMenuCommand.new(editmenu, "Delete\tDel\tDelete selected locations or connections.", nil)
|
556
|
+
cmd.connect( SEL_COMMAND, method(:delete_selected_cb) )
|
557
|
+
|
558
|
+
# Map menu
|
559
|
+
mapmenu = FXMenuPane.new(self)
|
560
|
+
|
561
|
+
cmd = FXMenuCommand.new(mapmenu, "Map Information\t\tChange Map Information.")
|
562
|
+
cmd.connect(SEL_COMMAND) { map_properties }
|
563
|
+
|
564
|
+
submenu = FXMenuPane.new(self)
|
565
|
+
cmd = FXMenuCommand.new(submenu, "Next Section\t\tGo to Next Map Section.")
|
566
|
+
cmd.connect(SEL_COMMAND) {
|
567
|
+
next_page
|
568
|
+
}
|
569
|
+
cmd = FXMenuCommand.new(submenu, "Previous Section\t\tGo to Previous Map Section.")
|
570
|
+
cmd.connect(SEL_COMMAND) {
|
571
|
+
previous_page
|
572
|
+
}
|
573
|
+
FXMenuSeparator.new(submenu)
|
574
|
+
cmd = FXMenuCommand.new(submenu, "Add Section\t\tAdd a New Section to Map.")
|
575
|
+
cmd.connect(SEL_COMMAND) {
|
576
|
+
map = current_map
|
577
|
+
if map
|
578
|
+
map.new_page
|
579
|
+
map.modified = true
|
580
|
+
update_page
|
581
|
+
end
|
582
|
+
}
|
583
|
+
cmd = FXMenuCommand.new(submenu, "Rename Section\t\tRename Current Section.")
|
584
|
+
cmd.connect(SEL_COMMAND) {
|
585
|
+
map = current_map
|
586
|
+
if map
|
587
|
+
map.rename_page
|
588
|
+
map.modified = true
|
589
|
+
end
|
590
|
+
}
|
591
|
+
FXMenuSeparator.new(submenu)
|
592
|
+
cmd = FXMenuCommand.new(submenu, "Delete Section\t\tDelete Current Section from Map.")
|
593
|
+
cmd.connect(SEL_COMMAND) {
|
594
|
+
map = current_map
|
595
|
+
if map
|
596
|
+
map.delete_page
|
597
|
+
map.modified = true
|
598
|
+
end
|
599
|
+
}
|
600
|
+
FXMenuCascade.new(mapmenu, "Sections", nil, submenu)
|
601
|
+
|
602
|
+
|
603
|
+
submenu = FXMenuPane.new(self)
|
604
|
+
[25, 50, 75, 100, 125].each { |v|
|
605
|
+
cmd = FXMenuCommand.new(submenu, v.to_s + "%\t\tZoom page to #{v}%.")
|
606
|
+
cmd.connect(SEL_COMMAND) {
|
607
|
+
map = current_map
|
608
|
+
if map
|
609
|
+
map.zoom = v / 100.0
|
610
|
+
map.draw
|
611
|
+
end
|
612
|
+
}
|
613
|
+
}
|
614
|
+
FXMenuCascade.new(mapmenu, "Zoom", nil, submenu)
|
615
|
+
|
616
|
+
submenu = FXMenuPane.new(self)
|
617
|
+
|
618
|
+
cmd = FXMenuCheck.new(submenu, "Edit on Creation\t\tEdit locations on creation.")
|
619
|
+
cmd.check = @@default_options['Edit on Creation']
|
620
|
+
cmd.connect(SEL_COMMAND) { |s, m, e|
|
621
|
+
map = current_map
|
622
|
+
if map
|
623
|
+
map.options['Edit on Creation'] = (s.check == true)
|
624
|
+
end
|
625
|
+
}
|
626
|
+
cmd.connect(SEL_UPDATE) { |s, m, e|
|
627
|
+
map = current_map
|
628
|
+
s.check = map.options['Edit on Creation'] if map
|
629
|
+
}
|
630
|
+
|
631
|
+
cmd = FXMenuCheck.new(submenu, "Automatic Connection\t\tConnect new location to adjacent one .")
|
632
|
+
cmd.check = @@default_options['Automatic Connection']
|
633
|
+
cmd.connect(SEL_COMMAND) { |s, m, e|
|
634
|
+
map = current_map
|
635
|
+
if map
|
636
|
+
map.options['Automatic Connection'] = (s.check == true)
|
637
|
+
end
|
638
|
+
}
|
639
|
+
cmd.connect(SEL_UPDATE) { |s, m, e|
|
640
|
+
map = current_map
|
641
|
+
s.check = map.options['Automatic Connection'] if map
|
642
|
+
}
|
643
|
+
|
644
|
+
cmd = FXMenuCheck.new(submenu, "Create on Connectionn\t\tCreate missing locations when creating a connection.")
|
645
|
+
cmd.check = @@default_options['Create on Connection']
|
646
|
+
cmd.connect(SEL_COMMAND) { |s, m, e|
|
647
|
+
map = current_map
|
648
|
+
map.options['Create on Connection'] = s.check if map
|
649
|
+
}
|
650
|
+
cmd.connect(SEL_UPDATE) { |s, m, e|
|
651
|
+
map = current_map
|
652
|
+
s.check = map.options['Create on Connection'] if map
|
653
|
+
}
|
654
|
+
|
655
|
+
FXMenuCascade.new(mapmenu, "Options", nil, submenu)
|
656
|
+
|
657
|
+
##########################
|
658
|
+
# Display submenu
|
659
|
+
#########################
|
660
|
+
submenu = FXMenuPane.new(self)
|
661
|
+
cmd = FXMenuCheck.new(submenu, "Paths as Curves\t\tDraw complex paths as curves.")
|
662
|
+
cmd.check = @@default_options['Paths as Curves']
|
663
|
+
cmd.connect(SEL_COMMAND) { |s, m, e|
|
664
|
+
map = current_map
|
665
|
+
if map
|
666
|
+
map.options['Paths as Curves'] = (s.check == true)
|
667
|
+
map.draw
|
668
|
+
end
|
669
|
+
}
|
670
|
+
cmd.connect(SEL_UPDATE) { |s, m, e|
|
671
|
+
map = current_map
|
672
|
+
s.check = map.options['Paths as Curves'] if map
|
673
|
+
}
|
674
|
+
cmd = FXMenuCheck.new(submenu, "Location Numbers\t\tShow Location Numbers.")
|
675
|
+
cmd.check = @@default_options['Location Numbers']
|
676
|
+
cmd.connect(SEL_COMMAND) { |s, m, e|
|
677
|
+
map = current_map
|
678
|
+
if map
|
679
|
+
map.options['Location Numbers'] = (s.check == true)
|
680
|
+
map.draw
|
681
|
+
end
|
682
|
+
}
|
683
|
+
cmd.connect(SEL_UPDATE) { |s, m, e|
|
684
|
+
map = current_map
|
685
|
+
s.check = map.options['Location Numbers'] if map
|
686
|
+
}
|
687
|
+
|
688
|
+
cmd = FXMenuCheck.new(submenu, "Boxes\t\tDraw dashed box guidelines.")
|
689
|
+
cmd.check = @@default_options['Grid Boxes']
|
690
|
+
cmd.connect(SEL_COMMAND) { |s, m, e|
|
691
|
+
map = current_map
|
692
|
+
if map
|
693
|
+
map.options['Grid Boxes'] = (s.check == true)
|
694
|
+
map.draw
|
695
|
+
end
|
696
|
+
}
|
697
|
+
cmd.connect(SEL_UPDATE) { |s, m, e|
|
698
|
+
map = current_map
|
699
|
+
s.check = map.options['Grid Boxes'] if map
|
700
|
+
}
|
701
|
+
|
702
|
+
cmd = FXMenuCheck.new(submenu, "Straight Connections\t\tDraw dashed N/S/E/W guidelines")
|
703
|
+
cmd.check = @@default_options['Grid Straight Connections']
|
704
|
+
cmd.connect(SEL_COMMAND) { |s, m, e|
|
705
|
+
map = current_map
|
706
|
+
if map
|
707
|
+
map.options['Grid Straight Connections'] = (s.check == true)
|
708
|
+
map.draw
|
709
|
+
end
|
710
|
+
}
|
711
|
+
cmd.connect(SEL_UPDATE) { |s, m, e|
|
712
|
+
map = current_map
|
713
|
+
s.check = map.options['Grid Straight Connections'] if map
|
714
|
+
}
|
715
|
+
|
716
|
+
cmd = FXMenuCheck.new(submenu, "Diagonal Connections\t\tDraw dashed NW/NE/SW/SE guidelines")
|
717
|
+
cmd.check = @@default_options['Grid Diagonal Connections']
|
718
|
+
cmd.connect(SEL_COMMAND) { |s, m, e|
|
719
|
+
map = current_map
|
720
|
+
if map
|
721
|
+
map.options['Grid Diagonal Connections'] = s.check
|
722
|
+
map.draw
|
723
|
+
end
|
724
|
+
}
|
725
|
+
cmd.connect(SEL_UPDATE) { |s, m, e|
|
726
|
+
map = current_map
|
727
|
+
s.check = map.options['Grid Diagonal Connections'] if map
|
728
|
+
}
|
729
|
+
|
730
|
+
FXMenuCascade.new(mapmenu, 'Display', nil, submenu)
|
731
|
+
|
732
|
+
submenu = FXMenuPane.new(self)
|
733
|
+
cmd = FXMenuCommand.new(submenu, "Colors\t\tChange map colors.")
|
734
|
+
FXMenuSeparator.new(submenu)
|
735
|
+
cmd = FXMenuCommand.new(submenu, "Save Preferences\t\tSave Preferences for Startup")
|
736
|
+
cmd.connect(SEL_COMMAND) {
|
737
|
+
map = current_map
|
738
|
+
map.options.write if map
|
739
|
+
}
|
740
|
+
FXMenuCascade.new(mapmenu, 'Preferences', nil, submenu)
|
741
|
+
|
742
|
+
FXMenuTitle.new(@menubar,"&Map", nil, mapmenu)
|
743
|
+
|
744
|
+
# Window menu
|
745
|
+
windowmenu = FXMenuPane.new(self)
|
746
|
+
FXMenuCommand.new(windowmenu, "Tile &Horizontally", nil,
|
747
|
+
@mdiclient, FXMDIClient::ID_MDI_TILEHORIZONTAL)
|
748
|
+
FXMenuCommand.new(windowmenu, "Tile &Vertically", nil,
|
749
|
+
@mdiclient, FXMDIClient::ID_MDI_TILEVERTICAL)
|
750
|
+
FXMenuCommand.new(windowmenu, "C&ascade", nil,
|
751
|
+
@mdiclient, FXMDIClient::ID_MDI_CASCADE)
|
752
|
+
FXMenuCommand.new(windowmenu, "&Close", nil,
|
753
|
+
@mdiclient, FXMDIClient::ID_MDI_CLOSE)
|
754
|
+
sep1 = FXMenuSeparator.new(windowmenu)
|
755
|
+
sep1.setTarget(@mdiclient)
|
756
|
+
sep1.setSelector(FXMDIClient::ID_MDI_ANY)
|
757
|
+
FXMenuCommand.new(windowmenu, nil, nil, @mdiclient, FXMDIClient::ID_MDI_1)
|
758
|
+
FXMenuCommand.new(windowmenu, nil, nil, @mdiclient, FXMDIClient::ID_MDI_2)
|
759
|
+
FXMenuCommand.new(windowmenu, nil, nil, @mdiclient, FXMDIClient::ID_MDI_3)
|
760
|
+
FXMenuCommand.new(windowmenu, nil, nil, @mdiclient, FXMDIClient::ID_MDI_4)
|
761
|
+
FXMenuCommand.new(windowmenu, "&Others...", nil, @mdiclient, FXMDIClient::ID_MDI_OVER_5)
|
762
|
+
FXMenuTitle.new(@menubar,"&Window", nil, windowmenu)
|
763
|
+
|
764
|
+
# Help menu
|
765
|
+
helpmenu = FXMenuPane.new(self)
|
766
|
+
cmd = FXMenuCommand.new(helpmenu, "&Instructions", nil)
|
767
|
+
cmd.connect(SEL_COMMAND, method(:docs))
|
768
|
+
FXMenuSeparator.new(helpmenu)
|
769
|
+
cmd = FXMenuCommand.new(helpmenu, "&About", nil)
|
770
|
+
cmd.connect(SEL_COMMAND, method(:about_cb))
|
771
|
+
|
772
|
+
cmd = FXMenuCommand.new(helpmenu, "&Resource Code", nil)
|
773
|
+
cmd.connect(SEL_COMMAND) {
|
774
|
+
file = FXMapFileDialog.new(self, "Resource a Ruby File").filename
|
775
|
+
if file != ''
|
776
|
+
begin
|
777
|
+
Kernel.load file
|
778
|
+
rescue => e
|
779
|
+
p e
|
780
|
+
end
|
781
|
+
end
|
782
|
+
}
|
783
|
+
FXMenuTitle.new(@menubar,"&Help", nil, helpmenu)
|
784
|
+
end
|
785
|
+
|
786
|
+
|
787
|
+
def docs(*opts)
|
788
|
+
FXAboutDialogBox.new(self, "About This Software...", <<'EOF').show
|
789
|
+
|
790
|
+
LMB - Left Mouse Button
|
791
|
+
MMB - Middle Mouse Button
|
792
|
+
|
793
|
+
Mouse controls
|
794
|
+
--------------
|
795
|
+
|
796
|
+
Use LMB to add new locations or connections among rooms.
|
797
|
+
|
798
|
+
Click with LMB on any room or connection to select it.
|
799
|
+
|
800
|
+
Double click on rooms or connections to access their properties.
|
801
|
+
|
802
|
+
Click several times on an existing connecion to establish a one-way
|
803
|
+
connection.
|
804
|
+
|
805
|
+
Drag with MMB or ALT + LMB to scroll around the page.
|
806
|
+
|
807
|
+
Use mousewheel to zoom in and out.
|
808
|
+
|
809
|
+
Keyboard controls
|
810
|
+
-----------------
|
811
|
+
|
812
|
+
Use 'x' to start a new complex connection (ie. a connection across
|
813
|
+
rooms that are not neighbors), then click on each exit of each room.
|
814
|
+
|
815
|
+
Use 'Delete' or 'Backspace' to remove any selected connection or room.
|
816
|
+
|
817
|
+
Use arrow keys to move selected rooms around the page, one unit at a time.
|
818
|
+
|
819
|
+
Use CTRL + LMB when adding a connection to create a dead-end connection (one that loops
|
820
|
+
onto itself)
|
821
|
+
|
822
|
+
EOF
|
823
|
+
end
|
824
|
+
|
825
|
+
def create_toolbar(toolbar)
|
826
|
+
|
827
|
+
# Construct these icons
|
828
|
+
newdoc = load_icon("filenew")
|
829
|
+
opendoc = load_icon("fileopen")
|
830
|
+
savedoc = load_icon("filesave")
|
831
|
+
saveasdoc = load_icon("filesaveas")
|
832
|
+
|
833
|
+
# File manipulation
|
834
|
+
cmd = FXButton.new(toolbar, "\tNew\tCreate new document.", newdoc, nil, 0,
|
835
|
+
FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
|
836
|
+
cmd.connect(SEL_COMMAND, method(:new_map_cb))
|
837
|
+
|
838
|
+
cmd = FXButton.new(toolbar, "\tOpen\tOpen document file.", opendoc, nil, 0,
|
839
|
+
FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
|
840
|
+
cmd.connect(SEL_COMMAND, method(:open_cb))
|
841
|
+
|
842
|
+
cmd = FXButton.new(toolbar, "\tSave\tSave document.", savedoc, nil, 0,
|
843
|
+
FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
|
844
|
+
cmd.connect(SEL_COMMAND, method(:save_cb))
|
845
|
+
cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
|
846
|
+
map = current_map
|
847
|
+
message = map ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
|
848
|
+
sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
|
849
|
+
}
|
850
|
+
cmd = FXButton.new(toolbar, "\tSave As\tSave document to another file.",
|
851
|
+
saveasdoc, nil, 0, FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
|
852
|
+
cmd.connect(SEL_COMMAND, method(:save_as_cb))
|
853
|
+
cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
|
854
|
+
map = current_map
|
855
|
+
message = map ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
|
856
|
+
sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
|
857
|
+
}
|
858
|
+
|
859
|
+
# Print
|
860
|
+
FXFrame.new(toolbar,
|
861
|
+
LAYOUT_TOP|LAYOUT_LEFT|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT, 0, 0, 4, 20)
|
862
|
+
cmd = FXButton.new(toolbar, "\tPrint Map\tPrint map (as graph).",
|
863
|
+
load_icon("printicon"), @mdiclient, FXGLViewer::ID_PRINT_IMAGE,
|
864
|
+
BUTTON_AUTOGRAY|FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
|
865
|
+
cmd.connect(SEL_COMMAND, method(:print_cb))
|
866
|
+
cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
|
867
|
+
map = current_map
|
868
|
+
message = map ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
|
869
|
+
sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
|
870
|
+
}
|
871
|
+
|
872
|
+
# Editing
|
873
|
+
FXFrame.new(toolbar,
|
874
|
+
LAYOUT_TOP|LAYOUT_LEFT|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT, 0, 0, 4, 20)
|
875
|
+
cmd = FXButton.new(toolbar, "\tCut", load_icon("cut"), @mdiclient,
|
876
|
+
FXGLViewer::ID_CUT_SEL, (BUTTON_AUTOGRAY|FRAME_THICK|FRAME_RAISED|
|
877
|
+
LAYOUT_TOP|LAYOUT_LEFT))
|
878
|
+
cmd.connect(SEL_COMMAND, method(:cut_selected_cb))
|
879
|
+
cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
|
880
|
+
map = current_map
|
881
|
+
message = map ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
|
882
|
+
sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
|
883
|
+
}
|
884
|
+
cmd = FXButton.new(toolbar, "\tCopy", load_icon("copy"), @mdiclient,
|
885
|
+
FXGLViewer::ID_COPY_SEL, (BUTTON_AUTOGRAY|FRAME_THICK|FRAME_RAISED|
|
886
|
+
LAYOUT_TOP|LAYOUT_LEFT))
|
887
|
+
cmd.connect(SEL_COMMAND, method(:copy_selected_cb))
|
888
|
+
cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
|
889
|
+
map = current_map
|
890
|
+
message = map ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
|
891
|
+
sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
|
892
|
+
}
|
893
|
+
cmd = FXButton.new(toolbar, "\tPaste", load_icon("paste"), @mdiclient,
|
894
|
+
FXGLViewer::ID_PASTE_SEL, (BUTTON_AUTOGRAY|FRAME_THICK|FRAME_RAISED|
|
895
|
+
LAYOUT_TOP|LAYOUT_LEFT))
|
896
|
+
cmd.connect(SEL_COMMAND, method(:paste_selected_cb))
|
897
|
+
cmd.connect(SEL_UPDATE) { |sender, sel, ptr|
|
898
|
+
map = current_map
|
899
|
+
message = (map and @@copy_buffer) ? FXWindow::ID_ENABLE : FXWindow::ID_DISABLE
|
900
|
+
sender.handle(self, MKUINT(message, SEL_COMMAND), nil)
|
901
|
+
}
|
902
|
+
|
903
|
+
# Zooming
|
904
|
+
FXFrame.new(toolbar,
|
905
|
+
LAYOUT_TOP|LAYOUT_LEFT|LAYOUT_FIX_WIDTH|LAYOUT_FIX_HEIGHT, 0, 0, 4, 20)
|
906
|
+
cmd = FXButton.new(toolbar, "+\tZoom In", load_icon("zoom"), @mdiclient,
|
907
|
+
0, FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
|
908
|
+
cmd.connect(SEL_COMMAND) { zoom_in }
|
909
|
+
|
910
|
+
cmd = FXButton.new(toolbar, "-\tZoom Out", load_icon("zoom"), @mdiclient,
|
911
|
+
0, FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
|
912
|
+
cmd.connect(SEL_COMMAND) { zoom_out }
|
913
|
+
|
914
|
+
|
915
|
+
# Page travel
|
916
|
+
frame = FXHorizontalFrame.new(toolbar,
|
917
|
+
LAYOUT_RIGHT|FRAME_THICK|FRAME_RAISED)
|
918
|
+
cmd = FXButton.new(frame, "\tPrevious Section", load_icon("prevpage"),
|
919
|
+
@mdiclient,
|
920
|
+
0, FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
|
921
|
+
cmd.connect(SEL_COMMAND) { previous_page }
|
922
|
+
|
923
|
+
@page = FXTextField.new(frame, 5, nil, 0,
|
924
|
+
TEXTFIELD_INTEGER|LAYOUT_FILL_ROW)
|
925
|
+
@page.text = '1'
|
926
|
+
@page.connect(SEL_COMMAND) { |s,m,e|
|
927
|
+
v = s.text.to_i
|
928
|
+
map = current_map
|
929
|
+
if map
|
930
|
+
map.page = v - 1
|
931
|
+
map.draw
|
932
|
+
update_page
|
933
|
+
end
|
934
|
+
}
|
935
|
+
@page.connect(SEL_UPDATE) { |s,m,e|
|
936
|
+
v = s.text.to_i
|
937
|
+
map = current_map
|
938
|
+
update_page if map
|
939
|
+
}
|
940
|
+
|
941
|
+
cmd = FXButton.new(frame, "\tNext Section", load_icon("nextpage"),
|
942
|
+
@mdiclient,
|
943
|
+
0, FRAME_THICK|FRAME_RAISED|LAYOUT_TOP|LAYOUT_LEFT)
|
944
|
+
cmd.connect(SEL_COMMAND) { next_page }
|
945
|
+
end
|
946
|
+
|
947
|
+
#
|
948
|
+
# Update page # in toolbar widget
|
949
|
+
#
|
950
|
+
def update_page
|
951
|
+
map = current_map
|
952
|
+
@page.text = (map.page + 1).to_s if map
|
953
|
+
end
|
954
|
+
|
955
|
+
#
|
956
|
+
# Go to next page in current map
|
957
|
+
#
|
958
|
+
def next_page
|
959
|
+
map = current_map
|
960
|
+
map.next_page if map
|
961
|
+
update_page
|
962
|
+
end
|
963
|
+
|
964
|
+
#
|
965
|
+
# Go to previous page in current map
|
966
|
+
#
|
967
|
+
def previous_page
|
968
|
+
map = current_map
|
969
|
+
map.previous_page if map
|
970
|
+
update_page
|
971
|
+
end
|
972
|
+
|
973
|
+
#
|
974
|
+
# Zoom in into current map
|
975
|
+
#
|
976
|
+
def zoom_in
|
977
|
+
map = current_map
|
978
|
+
if map
|
979
|
+
map.zoom_in
|
980
|
+
map.draw
|
981
|
+
end
|
982
|
+
end
|
983
|
+
|
984
|
+
#
|
985
|
+
# Zoom out from current map
|
986
|
+
#
|
987
|
+
def zoom_out
|
988
|
+
map = current_map
|
989
|
+
if map
|
990
|
+
map.zoom_out
|
991
|
+
map.draw
|
992
|
+
end
|
993
|
+
end
|
994
|
+
|
995
|
+
#
|
996
|
+
# Bring up the map property requester for current map
|
997
|
+
#
|
998
|
+
def map_properties
|
999
|
+
map = current_map
|
1000
|
+
map.properties if map
|
1001
|
+
end
|
1002
|
+
|
1003
|
+
#
|
1004
|
+
# In case of crash or runtime error, autosave all maps, so user
|
1005
|
+
# does not loose any data.
|
1006
|
+
#
|
1007
|
+
def autosave
|
1008
|
+
@maps.each { |m|
|
1009
|
+
m.save
|
1010
|
+
}
|
1011
|
+
end
|
1012
|
+
|
1013
|
+
|
1014
|
+
def initialize(app)
|
1015
|
+
super(app, TITLE, nil, nil, DECOR_ALL, 0, 0, 800, 600)
|
1016
|
+
|
1017
|
+
# Menubar
|
1018
|
+
@menubar = FXMenuBar.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X)
|
1019
|
+
|
1020
|
+
FXHorizontalSeparator.new(self,
|
1021
|
+
LAYOUT_SIDE_TOP|SEPARATOR_GROOVE|LAYOUT_FILL_X)
|
1022
|
+
toolbar = FXToolBar.new(self, LAYOUT_SIDE_TOP|LAYOUT_FILL_X,
|
1023
|
+
0, 0, 0, 0, 4, 4, 0, 0, 0, 0)
|
1024
|
+
|
1025
|
+
# Status bar
|
1026
|
+
@statusbar = FXStatusBar.new(self,
|
1027
|
+
LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|
|
1028
|
+
STATUSBAR_WITH_DRAGCORNER)
|
1029
|
+
|
1030
|
+
|
1031
|
+
create_mdiclient
|
1032
|
+
create_menus
|
1033
|
+
create_toolbar(toolbar)
|
1034
|
+
new_map
|
1035
|
+
|
1036
|
+
self.connect(SEL_CLOSE) {
|
1037
|
+
exit = true
|
1038
|
+
@maps.each { |m|
|
1039
|
+
if not m.close_cb
|
1040
|
+
exit = false
|
1041
|
+
break
|
1042
|
+
else
|
1043
|
+
@maps.delete(m)
|
1044
|
+
end
|
1045
|
+
}
|
1046
|
+
self.close if exit
|
1047
|
+
}
|
1048
|
+
|
1049
|
+
show
|
1050
|
+
end
|
1051
|
+
end
|