… | |
… | |
13 | use Crossfire; |
13 | use Crossfire; |
14 | use Crossfire::Map; |
14 | use Crossfire::Map; |
15 | use Crossfire::MapWidget; |
15 | use Crossfire::MapWidget; |
16 | |
16 | |
17 | use GCE::AttrEdit; |
17 | use GCE::AttrEdit; |
|
|
18 | use GCE::Util; |
18 | |
19 | |
19 | use Glib::Object::Subclass |
20 | use Glib::Object::Subclass |
20 | Gtk2::Window; |
21 | Gtk2::Window; |
21 | |
22 | |
22 | use strict; |
23 | use strict; |
… | |
… | |
68 | Gtk2::SimpleMenu->new ( |
69 | Gtk2::SimpleMenu->new ( |
69 | menu_tree => $menu_tree, |
70 | menu_tree => $menu_tree, |
70 | default_callback => \&default_cb, |
71 | default_callback => \&default_cb, |
71 | ); |
72 | ); |
72 | |
73 | |
|
|
74 | for ( |
|
|
75 | [i => 'pick'], |
|
|
76 | [p => 'place'], |
|
|
77 | [e => 'erase'], |
|
|
78 | [s => 'select'], |
|
|
79 | [l => 'eval'], |
|
|
80 | [x => 'connectexit'], |
|
|
81 | [f => 'followexit'] |
|
|
82 | ) |
|
|
83 | { |
|
|
84 | my $tool = $_->[1]; |
|
|
85 | $men->{accel_group}->connect ($Gtk2::Gdk::Keysyms{$_->[0]}, [], 'visible', |
|
|
86 | sub { $::MAINWIN->set_edit_tool ($tool) }); |
|
|
87 | } |
|
|
88 | |
73 | $self->add_accel_group ($men->{accel_group}); |
89 | $self->add_accel_group ($men->{accel_group}); |
74 | |
90 | |
75 | return $men->{widget}; |
91 | return $men->{widget}; |
|
|
92 | } |
|
|
93 | |
|
|
94 | sub ea { |
|
|
95 | my ($self) = @_; |
|
|
96 | $self->{ea_alt} || $::MAINWIN->{sel_editaction}; |
76 | } |
97 | } |
77 | |
98 | |
78 | sub INIT_INSTANCE { |
99 | sub INIT_INSTANCE { |
79 | my ($self) = @_; |
100 | my ($self) = @_; |
80 | |
101 | |
81 | $self->set_title ('gce - map editor'); |
102 | $self->set_title ('gce - map editor'); |
82 | $self->add (my $vb = Gtk2::VBox->new); |
103 | $self->add (my $vb = Gtk2::VBox->new); |
83 | |
104 | |
84 | $self->signal_connect (delete_event => sub { $self->delete }); |
105 | $self->signal_connect (delete_event => sub { $self->delete; 1 }); |
85 | |
106 | |
86 | $vb->pack_start (my $menu = $self->build_menu, 0, 1, 0); |
107 | $vb->pack_start (my $menu = $self->build_menu, 0, 1, 0); |
87 | |
108 | |
88 | $vb->pack_start (my $map = $self->{map} = Crossfire::MapWidget->new, 1, 1, 0); |
109 | $vb->pack_start (my $map = $self->{map} = Crossfire::MapWidget->new, 1, 1, 0); |
89 | |
110 | |
|
|
111 | $self->signal_connect (focus_in_event => sub { |
|
|
112 | my $ea = $::MAINWIN->{sel_editaction}; |
|
|
113 | if ($ea->special_arrow) { |
|
|
114 | $self->{map}->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($ea->special_arrow)); |
|
|
115 | } else { |
|
|
116 | $self->{map}->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR')); |
|
|
117 | } |
|
|
118 | delete $self->{ea_alt}; |
|
|
119 | }); |
|
|
120 | |
|
|
121 | $map->signal_connect (key_press_event => sub { |
|
|
122 | my ($map, $event) = @_; |
|
|
123 | |
|
|
124 | my $kv = $event->keyval; |
|
|
125 | |
|
|
126 | my ($x, $y) = $map->coord ($map->get_pointer); |
|
|
127 | for ([Control_L => sub { $self->{ea_alt} = $::MAINWIN->{edit_collection}{erase} }], |
|
|
128 | [Alt_L => sub { $self->{ea_alt} = $::MAINWIN->{edit_collection}{pick} }], |
|
|
129 | [c => sub { $::MAINWIN->{edit_collection}{select}->copy }], |
|
|
130 | [v => sub { $::MAINWIN->{edit_collection}{select}->paste ($x, $y) }], |
|
|
131 | [n => sub { $::MAINWIN->{edit_collection}{select}->invoke }], |
|
|
132 | ) |
|
|
133 | { |
|
|
134 | if ($kv == $Gtk2::Gdk::Keysyms{$_->[0]}) { |
|
|
135 | $_->[1]->(); |
|
|
136 | } |
|
|
137 | } |
|
|
138 | |
|
|
139 | if ($self->ea->special_arrow) { |
|
|
140 | $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow)); |
|
|
141 | } |
|
|
142 | 1; |
|
|
143 | }); |
|
|
144 | $map->signal_connect (key_release_event => sub { |
|
|
145 | my ($map, $event) = @_; |
|
|
146 | |
|
|
147 | if ($event->keyval == $Gtk2::Gdk::Keysyms{Control_L} |
|
|
148 | or $event->keyval == $Gtk2::Gdk::Keysyms{Alt_L}) |
|
|
149 | { |
|
|
150 | delete $self->{ea_alt}; |
|
|
151 | } |
|
|
152 | |
|
|
153 | if ($self->ea->special_arrow) { |
|
|
154 | $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow)); |
|
|
155 | } else { |
|
|
156 | # XXX: Get the original cursor and insert it here |
|
|
157 | $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR')); |
|
|
158 | } |
|
|
159 | 1; |
|
|
160 | }); |
90 | $map->signal_connect (button_press_event => sub { |
161 | $map->signal_connect (button_press_event => sub { |
91 | my ($map, $event) = @_; |
162 | my ($map, $event) = @_; |
92 | |
163 | |
93 | my ($x, $y) = $map->coord ($event->x, $event->y); |
164 | my ($x, $y) = $map->coord ($event->x, $event->y); |
94 | my $as = $map->get ($x, $y); |
165 | my $as = $map->get ($x, $y); |
95 | |
166 | |
96 | my $btn = $event->button; |
167 | if ((not $self->{draw_mode}) and $event->button != 2) { |
97 | |
168 | |
98 | if ((not $self->{draw_mode}) |
169 | my $ea = $self->ea; |
99 | and $btn != 2 |
|
|
100 | and my $ea = $::MAINWIN->{sel_editaction}) { |
|
|
101 | |
170 | |
102 | $ea->begin ($map, $x, $y, $btn) |
171 | $ea->begin ($map, $x, $y, $self) |
103 | if $x >= 0 and $y >= 0 and $x < $map->{map}{width} and $y < $map->{map}{height}; |
172 | if $x >= 0 and $y >= 0 and $x < $map->{map}{width} and $y < $map->{map}{height}; |
104 | |
173 | |
105 | $self->{draw_mode} = [$ea, $x, $y, $btn]; |
174 | $self->{draw_mode} = [$x, $y]; |
106 | |
175 | |
107 | $ea->want_cursor |
176 | $ea->want_cursor |
108 | or $map->disable_tooltip; |
177 | or $map->disable_tooltip; |
109 | |
178 | |
110 | if ($ea->special_arrow) { |
|
|
111 | |
|
|
112 | $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($ea->special_arrow)); |
|
|
113 | #'GDK_QUESTION_ARROW')); |
|
|
114 | } |
|
|
115 | |
|
|
116 | return 1; |
179 | return 1; |
117 | } |
180 | } |
118 | |
|
|
119 | 0 |
181 | 0 |
120 | }); |
182 | }); |
121 | |
183 | |
122 | $map->signal_connect_after (motion_notify_event => sub { |
184 | $map->signal_connect_after (motion_notify_event => sub { |
123 | my ($map, $event) = @_; |
185 | my ($map, $event) = @_; |
124 | |
186 | |
125 | $self->{draw_mode} |
187 | $self->{draw_mode} |
126 | or return; |
188 | or return; |
127 | |
189 | |
|
|
190 | my $ea = $self->ea; |
|
|
191 | |
128 | my ($X, $Y) = @{$self->{draw_mode}}[1,2]; |
192 | my ($X, $Y) = @{$self->{draw_mode}}[0,1]; |
129 | my ($x, $y) = $map->coord ($map->get_pointer); |
193 | my ($x, $y) = $map->coord ($map->get_pointer); |
130 | |
194 | |
131 | while ($x != $X || $y != $Y) { |
195 | while ($x != $X || $y != $Y) { |
132 | |
196 | |
133 | $X++ if $X < $x; |
197 | $X++ if $X < $x; |
134 | $X-- if $X > $x; |
198 | $X-- if $X > $x; |
135 | $Y++ if $Y < $y; |
199 | $Y++ if $Y < $y; |
136 | $Y-- if $Y > $y; |
200 | $Y-- if $Y > $y; |
137 | |
201 | |
138 | $self->{draw_mode}[0]->edit ($map, $X, $Y, $self->{draw_mode}[3]) |
202 | $ea->edit ($map, $X, $Y, $self) |
139 | if $X >= 0 and $Y >= 0 and $X < $map->{map}{width} and $Y < $map->{map}{height}; |
203 | if $X >= 0 and $Y >= 0 and $X < $map->{map}{width} and $Y < $map->{map}{height}; |
140 | } |
204 | } |
141 | |
205 | |
142 | @{$self->{draw_mode}}[1,2] = ($X, $Y); |
206 | @{$self->{draw_mode}}[0,1] = ($X, $Y); |
143 | |
207 | |
144 | 1 |
208 | 1 |
145 | }); |
209 | }); |
146 | |
210 | |
147 | $map->signal_connect (button_release_event => sub { |
211 | $map->signal_connect (button_release_event => sub { |
148 | my ($map, $event) = @_; |
212 | my ($map, $event) = @_; |
149 | |
213 | |
150 | if ($self->{draw_mode} |
214 | if ($self->{draw_mode}) { |
151 | and my $ea = $self->{draw_mode}[0] |
|
|
152 | and $self->{draw_mode}[3] == $event->button) { |
|
|
153 | |
|
|
154 | my ($x, $y) = $map->coord ($map->get_pointer); |
215 | my ($x, $y) = $map->coord ($map->get_pointer); |
155 | |
216 | |
|
|
217 | my $ea = $self->ea; |
156 | $ea->end ($map, $x, $y, $event->button); |
218 | $ea->end ($map, $x, $y, $self); |
157 | |
|
|
158 | if ($ea->special_arrow) { |
|
|
159 | |
|
|
160 | # XXX: Get the original cursor and insert it here |
|
|
161 | $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR')); |
|
|
162 | } |
|
|
163 | |
219 | |
164 | delete $self->{draw_mode}; |
220 | delete $self->{draw_mode}; |
165 | |
221 | |
166 | $ea->want_cursor |
222 | $ea->want_cursor |
167 | or $map->enable_tooltip; |
223 | or $map->enable_tooltip; |
… | |
… | |
195 | or return; |
251 | or return; |
196 | |
252 | |
197 | $map->change_swap ($map->{undo_stack}[$map->{undo_stack_pos}++]); |
253 | $map->change_swap ($map->{undo_stack}[$map->{undo_stack_pos}++]); |
198 | } |
254 | } |
199 | |
255 | |
200 | sub delete_arch { |
|
|
201 | my ($self, $x, $y) = @_; |
|
|
202 | |
|
|
203 | defined $self->{map} |
|
|
204 | or return 0; |
|
|
205 | |
|
|
206 | my $as = $self->{map}->get ($x, $y); |
|
|
207 | pop @$as; |
|
|
208 | $self->{map}->set ($x, $y, $as); |
|
|
209 | } |
|
|
210 | |
|
|
211 | sub place_pick { |
|
|
212 | my ($self, $x, $y) = @_; |
|
|
213 | |
|
|
214 | my $pick = $::MAINWIN->get_pick; |
|
|
215 | my $as = $self->{map}->get ($x, $y); |
|
|
216 | |
|
|
217 | my $arch = { _name => $pick->{_name} }; |
|
|
218 | |
|
|
219 | push @$as, $arch |
|
|
220 | unless @$as && $as->[-1]->{_name} eq $arch->{_name}; |
|
|
221 | |
|
|
222 | $self->{map}->set ($x, $y, $as); |
|
|
223 | } |
|
|
224 | |
|
|
225 | sub delete { |
256 | sub delete { |
226 | my ($self) = @_; |
257 | my ($self) = @_; |
227 | |
258 | |
228 | # check and modla dialog if "dirty" |
259 | # check and modla dialog if "dirty" |
|
|
260 | |
|
|
261 | if ($self->{path}) { |
|
|
262 | # XXX: This should be in a delete event handler in the MainWindow.pm... but it doesnt work |
|
|
263 | delete $::MAINWIN->{loaded_maps}->{$self->{path}}; |
|
|
264 | } |
229 | |
265 | |
230 | $self->destroy; |
266 | $self->destroy; |
231 | } |
267 | } |
232 | |
268 | |
233 | sub open_map { |
269 | sub open_map { |
234 | my ($self, $path) = @_; |
270 | my ($self, $path) = @_; |
235 | |
271 | |
|
|
272 | if (ref $path) { |
|
|
273 | $self->{map}->set_map ($path); |
|
|
274 | |
|
|
275 | } else { |
236 | $self->{path} = $path; |
276 | $self->{path} = $path; |
237 | # print "OPENMAP $path\n"; |
277 | # print "OPENMAP $path\n"; |
238 | $self->{map}->set_map (my $m = new_from_file Crossfire::Map $path); |
278 | $self->{map}->set_map (my $m = new_from_file Crossfire::Map $path); |
239 | require Data::Dumper; |
279 | require Data::Dumper; |
240 | print "FOO:" .Data::Dumper::Dumper ($m) . "\n"; |
280 | # print "FOO:" .Data::Dumper::Dumper ($m) . "\n"; |
|
|
281 | } |
241 | } |
282 | } |
242 | |
283 | |
243 | sub save_map { |
284 | sub save_map { |
244 | my ($self) = @_; |
285 | my ($self) = @_; |
245 | |
286 | |
|
|
287 | if ($self->{path}) { |
246 | $self->{map}{map}->write_file ($self->{path}); |
288 | $self->{map}{map}->write_file ($self->{path}); |
|
|
289 | quick_msg ($self, "saved to $self->{path}"); |
|
|
290 | } else { |
|
|
291 | $self->save_map_as; |
|
|
292 | } |
247 | } |
293 | } |
248 | |
294 | |
249 | sub save_map_as { |
295 | sub save_map_as { |
250 | my ($self) = @_; |
296 | my ($self) = @_; |
251 | warn "save_map_as nyi\n"; |
297 | |
|
|
298 | my $fc = $::MAINWIN->new_filechooser ('gce - save map', 1, $self->{path}); |
|
|
299 | |
|
|
300 | if ('ok' eq $fc->run) { |
|
|
301 | |
|
|
302 | $::MAINWIN->{fc_last_folder} = $fc->get_current_folder; |
|
|
303 | $::MAINWIN->{fc_last_folders}->{$self->{fc_last_folder}}++; |
|
|
304 | |
|
|
305 | $self->{map}{map}->write_file ($self->{path} = $fc->get_filename); |
|
|
306 | quick_msg ($self, "saved to $self->{path}"); |
|
|
307 | } |
|
|
308 | |
|
|
309 | $fc->destroy; |
252 | } |
310 | } |
253 | |
311 | |
254 | sub _add_prop_entry { |
312 | sub _add_prop_entry { |
255 | my ($self, $table, $idx, $key, $desc, $type, $changecb) = @_; |
313 | my ($self, $table, $idx, $key, $desc, $type, $changecb) = @_; |
256 | |
|
|
257 | |
314 | |
258 | my $edwid; |
315 | my $edwid; |
259 | |
316 | |
260 | if ($type eq 'string') { |
317 | if ($type eq 'string') { |
261 | $table->attach_defaults (my $lbl = Gtk2::Label->new ($desc), 0, 1, $idx, $idx + 1); |
318 | $table->attach_defaults (my $lbl = Gtk2::Label->new ($desc), 0, 1, $idx, $idx + 1); |
262 | $edwid = Gtk2::Entry->new; |
319 | $edwid = Gtk2::Entry->new; |
263 | $edwid->set_text ($self->{map}{map}{info}{$key}); |
320 | $edwid->set_text ($self->{map}{map}{info}{$key}); |
264 | $edwid->signal_connect (changed => sub { |
321 | $edwid->signal_connect (changed => sub { |
265 | $self->{map}{map}{info}{$key} = $_[0]->get_text; |
322 | $self->{map}{map}{info}{$key} = $_[0]->get_text; |
266 | if ($changecb) { |
323 | if ($changecb) { |
267 | print "CHANGECB " . $_[0]->get_text . "\n"; |
|
|
268 | $changecb->($_[0]->get_text); |
324 | $changecb->($_[0]->get_text); |
269 | } |
325 | } |
270 | }); |
326 | }); |
271 | $table->attach_defaults ($edwid, 1, 2, $idx, $idx + 1); |
327 | $table->attach_defaults ($edwid, 1, 2, $idx, $idx + 1); |
272 | |
328 | |
… | |
… | |
299 | [qw/width Width string/], |
355 | [qw/width Width string/], |
300 | [qw/height Height string/], |
356 | [qw/height Height string/], |
301 | [qw/save Save button/, |
357 | [qw/save Save button/, |
302 | sub { |
358 | sub { |
303 | $self->{map}{map}->resize ($self->{map}{map}{info}{width}, $self->{map}{map}{info}{height}); |
359 | $self->{map}{map}->resize ($self->{map}{map}{info}{width}, $self->{map}{map}{info}{height}); |
304 | $self->{map}->invalidate_all |
360 | $self->{map}->invalidate_all; |
|
|
361 | $w->destroy; |
305 | } |
362 | } |
306 | ], |
363 | ], |
307 | ) |
364 | ) |
308 | { |
365 | { |
309 | $self->_add_prop_entry ($t, $i++, @$_); |
366 | $self->_add_prop_entry ($t, $i++, @$_); |