ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/gde/GCE/MapEditor.pm
(Generate patch)

Comparing deliantra/gde/GCE/MapEditor.pm (file contents):
Revision 1.29 by elmex, Mon Mar 20 04:11:41 2006 UTC vs.
Revision 1.40 by elmex, Tue Aug 15 17:01:34 2006 UTC

19 19
20use Glib::Object::Subclass 20use Glib::Object::Subclass
21 Gtk2::Window; 21 Gtk2::Window;
22 22
23use strict; 23use strict;
24
25sub do_context_menu {
26 my ($self, $map, $event) = @_;
27
28 my ($x, $y) = $map->coord ($event->x, $event->y);
29
30 my $menu = Gtk2::Menu->new;
31 foreach my $cm (
32 [
33 Follow => sub {
34 $::MAINWIN->{edit_collection}{followexit}->edit ($map, $x, $y, $self)
35 },
36 ]
37 ) {
38 my $item = Gtk2::MenuItem->new ($cm->[0]);
39 $menu->append ($item);
40 $item->show;
41 $item->signal_connect (activate => $cm->[1]);
42 }
43
44 my $inv_item = Gtk2::MenuItem->new ("Add inventory");
45 $inv_item->set_submenu (my $smenu = new Gtk2::Menu);
46 $menu->append ($inv_item);
47
48 for my $sr (reverse $self->get_stack_refs ($map, $x, $y)) {
49 my $item = Gtk2::MenuItem->new ($sr->longname);
50 $smenu->append ($item);
51 $item->show;
52 $item->signal_connect (activate => sub {
53 $sr->add_inv ($::MAINWIN->get_pick);
54 });
55 }
56
57 $inv_item->show;
58 $menu->popup (undef, undef, undef, undef, $event->button, $event->time);
59}
24 60
25sub build_menu { 61sub build_menu {
26 my ($self) = @_; 62 my ($self) = @_;
27 63
28 my $menu_tree = [ 64 my $menu_tree = [
34 accelerator => '<ctrl>S' 70 accelerator => '<ctrl>S'
35 }, 71 },
36 "Save As" => { 72 "Save As" => {
37 callback => sub { $self->save_map_as }, 73 callback => sub { $self->save_map_as },
38 }, 74 },
75 "_Map Properties" => {
76 callback => sub { $self->open_map_prop },
77 accelerator => "<ctrl>P"
78 },
79 "_Map Resize" => {
80 callback => sub { $self->open_resize_map },
81 },
39 "Close" => { 82 "Close" => {
40 callback => sub { $self->delete; 1 }, 83 callback => sub { $self->destroy },
41 }, 84 },
42 ] 85 ]
43 }, 86 },
44 _Edit => { 87 _Edit => {
45 item_type => '<Branch>', 88 item_type => '<Branch>',
50 }, 93 },
51 "_Redo" => { 94 "_Redo" => {
52 callback => sub { $self->redo }, 95 callback => sub { $self->redo },
53 accelerator => "<ctrl>Y" 96 accelerator => "<ctrl>Y"
54 }, 97 },
55 "_Map Properties" => {
56 callback => sub { $self->open_map_prop },
57 accelerator => "<ctrl>P"
58 },
59 "_Map Resize" => {
60 callback => sub { $self->open_resize_map },
61 accelerator => "<ctrl>R"
62 },
63 ] 98 ]
64 }, 99 },
65 _Go => { 100 _Go => {
66 item_type => '<Branch>', 101 item_type => '<Branch>',
67 children => [ 102 children => [
68 "_Up" => { 103 "_Up" => {
69 callback => sub { $self->follow ('u') }, 104 callback => sub { $self->follow ('u') },
70 accelerator => "<ctrl>U" 105 accelerator => "<ctrl>Up"
71 }, 106 },
72 "_Down" => { 107 "_Down" => {
73 callback => sub { $self->follow ('d') }, 108 callback => sub { $self->follow ('d') },
74 accelerator => "<ctrl>D" 109 accelerator => "<ctrl>Down"
75 }, 110 },
76 "_Right" => { 111 "_Right" => {
77 callback => sub { $self->follow ('r') }, 112 callback => sub { $self->follow ('r') },
78 accelerator => "<ctrl>R" 113 accelerator => "<ctrl>Right"
79 }, 114 },
80 "_Left" => { 115 "_Left" => {
81 callback => sub { $self->follow ('l') }, 116 callback => sub { $self->follow ('l') },
82 accelerator => "<ctrl>L" 117 accelerator => "<ctrl>Left"
83 }, 118 },
84 ] 119 ]
85 }, 120 },
86 _Help => { 121 _Help => {
87 item_type => '<Branch>', 122 item_type => '<Branch>',
88 children => [ 123 children => [
89 _Help => { 124 _Manual => {
90 callback => sub { $::MAINWIN->show_help_window }, 125 callback => sub { $::MAINWIN->show_help_window },
91 accelerator => "<ctrl>H" 126 accelerator => "<ctrl>H"
92 }, 127 },
93 ] 128 ]
94 }, 129 },
104 [i => 'pick'], 139 [i => 'pick'],
105 [p => 'place'], 140 [p => 'place'],
106 [e => 'erase'], 141 [e => 'erase'],
107 [s => 'select'], 142 [s => 'select'],
108 [l => 'eval'], 143 [l => 'eval'],
109 [x => 'connectexit'], 144 [t => 'connect'],
110 [f => 'followexit'] 145 [f => 'followexit']
111 ) 146 )
112 { 147 {
113 my $tool = $_->[1]; 148 my $tool = $_->[1];
114 $men->{accel_group}->connect ($Gtk2::Gdk::Keysyms{$_->[0]}, [], 'visible', 149 $men->{accel_group}->connect ($Gtk2::Gdk::Keysyms{$_->[0]}, [], 'visible',
115 sub { $::MAINWIN->set_edit_tool ($tool) }); 150 sub { $::MAINWIN->set_edit_tool ($tool) });
116 } 151 }
117 152
153 $men->{accel_group}->connect ($Gtk2::Gdk::Keysyms{'r'}, ['control-mask'], 'visible',
154 sub { $self->redo });
155
118 $self->add_accel_group ($men->{accel_group}); 156 $self->add_accel_group ($men->{accel_group});
119 157
120 return $men->{widget}; 158 return $men->{widget};
121} 159}
122 160
161sub set_edit_tool {
162 my ($self, $tool) = @_;
163
164 $self->{etool} = $tool;
165
166 if ($self->ea->special_arrow) {
167 $self->{map}{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow));
168 } else {
169 # FIXME: Get the original cursor and insert it here
170 $self->{map}{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR'));
171 }
172}
173
123sub ea { 174sub ea {
124 my ($self) = @_; 175 my ($self) = @_;
125 $self->{ea_alt} || $::MAINWIN->{sel_editaction}; 176 $self->{ea_alt} || $self->{etool};
126} 177}
127 178
128sub INIT_INSTANCE { 179sub INIT_INSTANCE {
129 my ($self) = @_; 180 my ($self) = @_;
130 181
131 $self->set_title ('gce - map editor'); 182 $self->set_title ('gce - map editor');
132 $self->add (my $vb = Gtk2::VBox->new); 183 $self->add (my $vb = Gtk2::VBox->new);
133 184
134 $self->signal_connect (delete_event => sub { $self->delete; 1 });
135
136 $vb->pack_start (my $menu = $self->build_menu, 0, 1, 0); 185 $vb->pack_start (my $menu = $self->build_menu, 0, 1, 0);
137 186
138 $vb->pack_start (my $map = $self->{map} = Crossfire::MapWidget->new, 1, 1, 0); 187 $vb->pack_start (my $map = $self->{map} = Crossfire::MapWidget->new, 1, 1, 0);
139 188
140 $self->signal_connect (focus_in_event => sub {
141 my $ea = $::MAINWIN->{sel_editaction};
142 if ($ea->special_arrow) {
143 $self->{map}->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($ea->special_arrow));
144 } else {
145 $self->{map}->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR'));
146 }
147 delete $self->{ea_alt};
148 });
149
150 $map->signal_connect (key_press_event => sub { 189 $map->signal_connect_after (key_press_event => sub {
151 my ($map, $event) = @_; 190 my ($map, $event) = @_;
152 191
153 my $kv = $event->keyval; 192 my $kv = $event->keyval;
193
194 my $ret = 0;
154 195
155 my ($x, $y) = $map->coord ($map->get_pointer); 196 my ($x, $y) = $map->coord ($map->get_pointer);
156 for ([Control_L => sub { $self->{ea_alt} = $::MAINWIN->{edit_collection}{erase} }], 197 for ([Control_L => sub { $self->{ea_alt} = $::MAINWIN->{edit_collection}{erase} }],
157 [Alt_L => sub { $self->{ea_alt} = $::MAINWIN->{edit_collection}{pick} }], 198 [Alt_L => sub { $self->{ea_alt} = $::MAINWIN->{edit_collection}{pick} }],
158 [c => sub { $::MAINWIN->{edit_collection}{select}->copy }], 199 [c => sub { $::MAINWIN->{edit_collection}{select}->copy }],
159 [v => sub { $::MAINWIN->{edit_collection}{select}->paste ($map, $x, $y) }], 200 [v => sub { $::MAINWIN->{edit_collection}{select}->paste ($map, $x, $y) }],
160 [n => sub { $::MAINWIN->{edit_collection}{select}->invoke }], 201 [n => sub { $::MAINWIN->{edit_collection}{select}->invoke }],
161 ) 202 )
162 { 203 {
204 my $ed = $_;
205
163 if ($kv == $Gtk2::Gdk::Keysyms{$_->[0]}) { 206 if ($kv == $Gtk2::Gdk::Keysyms{$ed->[0]}) {
207 my $was_in_draw = defined $self->{draw_mode};
208
209 $self->stop_drawmode ($map)
210 if $was_in_draw && grep { $ed->[0] eq $_ } qw/Control_L Alt_L/;
211
164 $_->[1]->(); 212 $ed->[1]->();
213 $ret = 1;
214
215 $self->start_drawmode ($map)
216 if $was_in_draw && grep { $ed->[0] eq $_ } qw/Control_L Alt_L/;
165 } 217 }
166 } 218 }
167 219
168 if ($self->ea->special_arrow) { 220 if ($self->ea->special_arrow) {
169 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow)); 221 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow));
170 } 222 } else {
223 # FIXME: Get the original cursor and insert it here
224 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR'));
171 1; 225 }
226
227 $ret
172 }); 228 });
229
173 $map->signal_connect (key_release_event => sub { 230 $map->signal_connect_after (key_release_event => sub {
174 my ($map, $event) = @_; 231 my ($map, $event) = @_;
232
233 my $ret = 0;
175 234
176 if ($event->keyval == $Gtk2::Gdk::Keysyms{Control_L} 235 if ($event->keyval == $Gtk2::Gdk::Keysyms{Control_L}
177 or $event->keyval == $Gtk2::Gdk::Keysyms{Alt_L}) 236 or $event->keyval == $Gtk2::Gdk::Keysyms{Alt_L})
178 { 237 {
238 my $was_in_draw = defined $self->{draw_mode};
239
240 $self->stop_drawmode ($map)
241 if $was_in_draw;
242
179 delete $self->{ea_alt}; 243 delete $self->{ea_alt};
244 $ret = 1;
245
246 $self->start_drawmode ($map)
247 if $was_in_draw;
180 } 248 }
181 249
182 if ($self->ea->special_arrow) { 250 if ($self->ea->special_arrow) {
183 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow)); 251 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow));
184 } else { 252 } else {
185 # XXX: Get the original cursor and insert it here 253 # FIXME: Get the original cursor and insert it here
186 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR')); 254 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR'));
187 } 255 }
188 1; 256
257 $ret
189 }); 258 });
190 $map->signal_connect (button_press_event => sub { 259 $map->signal_connect_after (button_press_event => sub {
191 my ($map, $event) = @_; 260 my ($map, $event) = @_;
192 261
193 my ($x, $y) = $map->coord ($event->x, $event->y);
194 my $as = $map->get ($x, $y);
195
196 if ((not $self->{draw_mode}) and $event->button != 2) { 262 if ((not $self->{draw_mode}) and $event->button == 1) {
197
198 my $ea = $self->ea; 263 my $ea = $self->ea;
199 264
200 $ea->begin ($map, $x, $y, $self) 265 $self->start_drawmode ($map);
201 if $x >= 0 and $y >= 0 and $x < $map->{map}{width} and $y < $map->{map}{height};
202
203 $self->{draw_mode} = [$x, $y];
204 266
205 $ea->want_cursor 267 $ea->want_cursor
206 or $map->disable_tooltip; 268 or $map->disable_tooltip;
207 269
208 return 1; 270 return 1;
271 } elsif ($event->button == 3) {
272 $self->do_context_menu ($map, $event);
273 return 1;
209 } 274 }
275
210 0 276 0
211 }); 277 });
212 278
213 $map->signal_connect_after (motion_notify_event => sub { 279 $map->signal_connect_after (motion_notify_event => sub {
214 my ($map, $event) = @_; 280 my ($map, $event) = @_;
226 $X++ if $X < $x; 292 $X++ if $X < $x;
227 $X-- if $X > $x; 293 $X-- if $X > $x;
228 $Y++ if $Y < $y; 294 $Y++ if $Y < $y;
229 $Y-- if $Y > $y; 295 $Y-- if $Y > $y;
230 296
297 unless ($ea->only_on_click) {
231 $ea->edit ($map, $X, $Y, $self) 298 $ea->edit ($map, $X, $Y, $self)
232 if $X >= 0 and $Y >= 0 and $X < $map->{map}{width} and $Y < $map->{map}{height}; 299 if $X >= 0 and $Y >= 0 and $X < $map->{map}{width} and $Y < $map->{map}{height};
300 }
233 } 301 }
234 302
235 @{$self->{draw_mode}}[0,1] = ($X, $Y); 303 @{$self->{draw_mode}}[0,1] = ($X, $Y);
236 304
237 1 305 1
238 }); 306 });
239 307
240 $map->signal_connect (button_release_event => sub { 308 $map->signal_connect_after (button_release_event => sub {
241 my ($map, $event) = @_; 309 my ($map, $event) = @_;
242 310
243 if ($self->{draw_mode}) { 311 if ($self->{draw_mode} and $event->button == 1) {
244 my ($x, $y) = $map->coord ($map->get_pointer);
245
246 my $ea = $self->ea; 312 my $ea = $self->ea;
247 $ea->end ($map, $x, $y, $self);
248 313
249 delete $self->{draw_mode}; 314 $self->stop_drawmode ($map);
250 315
251 $ea->want_cursor 316 $ea->want_cursor
252 or $map->enable_tooltip; 317 or $map->enable_tooltip;
253 318
254 return 1; 319 return 1;
256 321
257 0 322 0
258 }); 323 });
259} 324}
260 325
326sub start_drawmode {
327 my ($self, $map) = @_;
328
329 $self->{draw_mode} and return;
330
331 # XXX: is this okay? my ($x, $y) = $map->coord ($event->x, $event->y);
332 my ($x, $y) = $map->coord ($map->get_pointer);
333
334 my $ea = $self->ea;
335
336 $ea->begin ($map, $x, $y, $self);
337
338 $ea->edit ($map, $x, $y, $self)
339 if $x >= 0 and $y >= 0 and $x < $map->{map}{width} and $y < $map->{map}{height};
340
341 $self->{draw_mode} = [$x, $y];
342}
343
344sub stop_drawmode {
345 my ($self, $map) = @_;
346
347 $self->{draw_mode} or return;
348
349 my ($x, $y) = $map->coord ($map->get_pointer);
350
351 my $ea = $self->ea;
352 $ea->end ($map, $x, $y, $self);
353
354 delete $self->{draw_mode};
355}
356
261# FIXME: Fix the automatic update of the attribute editor! and also the stack view! 357# FIXME: Fix the automatic update of the attribute editor! and also the stack view!
262sub undo { 358sub undo {
263 my ($self) = @_; 359 my ($self) = @_;
264 360
265 my $map = $self->{map}; # the Crossfire::MapWidget 361 my $map = $self->{map}; # the Crossfire::MapWidget
266 362
267 $map->{undo_stack_pos} 363 $map->{undo_stack_pos}
268 or return; 364 or return;
269 365
270 $map->change_swap ($map->{undo_stack}[--$map->{undo_stack_pos}]); 366 $map->change_swap ($map->{undo_stack}[--$map->{undo_stack_pos}]);
367}
368
369sub get_stack_refs {
370 my ($self, $map, $x, $y) = @_;
371
372 my $cstack = $map->get ($x, $y);
373
374 return [] unless @$cstack;
375
376 my @refs;
377
378 for my $arch (@$cstack) {
379 my ($ox, $oy) = ($x, $y);
380 if ($arch->{_virtual}) {
381 $ox = $arch->{virtual_x};
382 $oy = $arch->{virtual_y};
383 $arch = $arch->{_virtual};
384 $cstack = $map->get ($ox, $oy);
385 # XXX: This heavily blows up if $arch isn't on $cstack now.. and it actually really does :(
386 }
387
388 push @refs,
389 GCE::ArchRef->new (
390 arch => $arch,
391 cb => sub {
392 $map->change_begin ('attredit');
393 $map->change_stack ($ox, $oy, $cstack);
394
395 if (my $changeset = $map->change_end) {
396 splice @{ $map->{undo_stack} ||= [] },
397 $map->{undo_stack_pos}++, 1e6,
398 $changeset;
399 }
400 }
401 );
402 }
403
404 return @refs;
271} 405}
272 406
273sub redo { 407sub redo {
274 my ($self) = @_; 408 my ($self) = @_;
275 409
280 or return; 414 or return;
281 415
282 $map->change_swap ($map->{undo_stack}[$map->{undo_stack_pos}++]); 416 $map->change_swap ($map->{undo_stack}[$map->{undo_stack_pos}++]);
283} 417}
284 418
285sub delete {
286 my ($self) = @_;
287
288 # check and modla dialog if "dirty"
289
290 if ($self->{mapkey}) {
291 # XXX: This should be in a delete event handler in the MainWindow.pm... but it doesnt work
292 delete $::MAINWIN->{loaded_maps}->{$self->{mapkey}};
293 }
294
295 $self->destroy;
296}
297
298sub open_map { 419sub open_map {
299 my ($self, $path, $key) = @_; 420 my ($self, $path, $key) = @_;
300 421
301 $self->{mapkey} = $key; 422 $self->{mapkey} = $key;
302 423
303 if (ref $path) { 424 if (ref $path) {
304 $self->{map}->set_map ($path); 425 $self->{map}->set_map ($path);
426 $self->set_title ('<ram>');
305 427
306 } else { 428 } else {
307 $self->{path} = $path; 429 $self->{path} = $path;
308# print "OPENMAP $path\n"; 430# print "OPENMAP $path\n";
309 $self->{map}->set_map (my $m = new_from_file Crossfire::Map $path); 431 $self->{map}->set_map (my $m = new_from_file Crossfire::Map $path);
432 $self->set_title ("gce - map editor - $self->{path}");
310 require Data::Dumper; 433 require Data::Dumper;
311# print "FOO:" .Data::Dumper::Dumper ($m) . "\n"; 434# print "FOO:" .Data::Dumper::Dumper ($m) . "\n";
312 } 435 }
313} 436}
314 437
316 my ($self) = @_; 439 my ($self) = @_;
317 440
318 if ($self->{path}) { 441 if ($self->{path}) {
319 $self->{map}{map}->write_file ($self->{path}); 442 $self->{map}{map}->write_file ($self->{path});
320 quick_msg ($self, "saved to $self->{path}"); 443 quick_msg ($self, "saved to $self->{path}");
444 $self->set_title ("gce - map editor - $self->{path}");
321 } else { 445 } else {
322 $self->save_map_as; 446 $self->save_map_as;
323 } 447 }
324} 448}
325 449
333 $::MAINWIN->{fc_last_folder} = $fc->get_current_folder; 457 $::MAINWIN->{fc_last_folder} = $fc->get_current_folder;
334 $::MAINWIN->{fc_last_folders}->{$self->{fc_last_folder}}++; 458 $::MAINWIN->{fc_last_folders}->{$self->{fc_last_folder}}++;
335 459
336 $self->{map}{map}->write_file ($self->{path} = $fc->get_filename); 460 $self->{map}{map}->write_file ($self->{path} = $fc->get_filename);
337 quick_msg ($self, "saved to $self->{path}"); 461 quick_msg ($self, "saved to $self->{path}");
462 $self->set_title ("gce - map editor - $self->{path}");
338 } 463 }
339 464
340 $fc->destroy; 465 $fc->destroy;
341} 466}
342 467
473 [qw/tile_path_2 Eastpath string/], 598 [qw/tile_path_2 Eastpath string/],
474 [qw/tile_path_3 Southpath string/], 599 [qw/tile_path_3 Southpath string/],
475 [qw/tile_path_4 Westpath string/], 600 [qw/tile_path_4 Westpath string/],
476 [qw/tile_path_5 Toppath string/], 601 [qw/tile_path_5 Toppath string/],
477 [qw/tile_path_6 Bottompath string/], 602 [qw/tile_path_6 Bottompath string/],
603 [qw/x x sep/],
604 ['x', 'For shop description look in the manual', 'button', sub { $::MAINWIN->show_help_window }],
605 [qw/shopmin Shopmin string/],
606 [qw/shopmax Shopmax string/],
607 [qw/shoprace Shoprace string/],
608 [qw/shopgreed Shopgreed string/],
609 [qw/shopitems Shopitems string/],
610 [qw/x x sep/],
611 ['safe_map', 'Safe map (CF+)', 'check'],
478 ) 612 )
479 { 613 {
480 $self->_add_prop_entry ($t, $i++, @$_); 614 $self->_add_prop_entry ($t, $i++, @$_);
481 } 615 }
482 616

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines