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.4 by elmex, Sat Feb 11 01:23:07 2006 UTC vs.
Revision 1.57 by elmex, Fri Aug 10 12:20:51 2007 UTC

13use Crossfire; 13use Crossfire;
14use Crossfire::Map; 14use Crossfire::Map;
15use Crossfire::MapWidget; 15use Crossfire::MapWidget;
16 16
17use GCE::AttrEdit; 17use GCE::AttrEdit;
18use GCE::Util;
19use GCE::HashDialog;
20
21use POSIX qw/strftime/;
18 22
19use Glib::Object::Subclass 23use Glib::Object::Subclass
20 Gtk2::VBox, 24 Gtk2::Window;
21 ; 25
26use Storable qw/dclone/;
22 27
23use strict; 28use strict;
24 29
30#################################################################
31###### WINDOW MANAGEMENT ########################################
32#################################################################
33
34sub save_layout {
35 my ($self) = @_;
36
37 $self->{attach_editor}->save_layout if $self->{attach_editor};
38 $self->{map_properties}->save_layout if $self->{map_properties};
39 $self->{meta_info_win}->save_layout if $self->{meta_info_win};
40
41 $main::CFG->{map_info} = main::get_pos_and_size ($self->{map_info})
42 if $self->{map_info};
43}
44
45sub close_windows {
46 my ($self) = @_;
47
48 $self->{attach_editor}->destroy if $self->{attach_editor};
49 $self->{map_properties}->destroy if $self->{map_properties};
50 $self->{meta_info_win}->destroy if $self->{meta_info_win};
51}
52
53#################################################################
54###### MENU MANAGEMENT ##########################################
55#################################################################
56
57sub do_context_menu {
58 my ($self, $map, $event) = @_;
59
60 my ($x, $y) = $map->coord ($event->x, $event->y);
61
62 my $menu = Gtk2::Menu->new;
63 foreach my $cm (
64 [
65 Follow => sub {
66 $::MAINWIN->{edit_collection}{followexit}->edit ($map, $x, $y, $self)
67 },
68 ]
69 ) {
70 my $item = Gtk2::MenuItem->new ($cm->[0]);
71 $menu->append ($item);
72 $item->show;
73 $item->signal_connect (activate => $cm->[1]);
74 }
75
76 $menu->append (my $sep = new Gtk2::SeparatorMenuItem);
77 $sep->show;
78
79 for my $sr (reverse $self->get_stack_refs ($map, $x, $y)) {
80 my $item = Gtk2::MenuItem->new ($sr->longname);
81 $menu->append ($item);
82 $item->set_submenu (my $smenu = new Gtk2::Menu);
83
84 for my $act (
85 [ 'Add inventory' => sub { $_[0]->add_inv ($::MAINWIN->get_pick) } ],
86 [ 'Find in picker' => sub { $::MAINWIN->open_pick_window ({ selection => $sr->picker_folder }) } ],
87 ) {
88 my $sitem = Gtk2::MenuItem->new ($act->[0]);
89 $smenu->append ($sitem);
90 $sitem->signal_connect (activate => sub { $act->[1]->($sr) });
91 $sitem->show;
92 }
93
94 $item->show;
95 }
96
97 $menu->popup (undef, undef, undef, undef, $event->button, $event->time);
98}
99
100sub build_menu {
101 my ($self) = @_;
102
103 my $menu_tree = [
104 _File => {
105 item_type => '<Branch>',
106 children => [
107 "_Save" => {
108 callback => sub { $self->save_map },
109 accelerator => '<ctrl>S'
110 },
111 "Save As" => {
112 callback => sub { $self->save_map_as },
113 },
114 "Map _Info" => {
115 callback => sub { $self->open_map_info },
116 accelerator => "<ctrl>I",
117 },
118 "Map _Properties" => {
119 callback => sub { $self->open_map_prop },
120 accelerator => "<ctrl>P"
121 },
122 "Map _Attachments" => {
123 callback => sub { $self->open_attach_edit },
124 accelerator => "<ctrl>A"
125 },
126 "Map Meta _Info" => {
127 callback => sub { $self->open_meta_info },
128 },
129 Upload => {
130 item_type => '<Branch>',
131 children => [
132 "Upload for testing" => {
133 callback => sub { $self->upload_map_test },
134 },
135 "Upload for inclusion" => {
136 callback => sub { $self->upload_map_incl },
137 },
138 ]
139 },
140 "_Map Resize" => {
141 callback => sub { $self->open_resize_map },
142 },
143 "Close" => {
144 callback => sub { $self->destroy },
145 },
146 ]
147 },
148 _Edit => {
149 item_type => '<Branch>',
150 children => [
151 "_Undo" => {
152 callback => sub { $self->undo },
153 accelerator => "<ctrl>Z"
154 },
155 "_Redo" => {
156 callback => sub { $self->redo },
157 accelerator => "<ctrl>Y"
158 },
159 ]
160 },
161 _Go => {
162 item_type => '<Branch>',
163 children => [
164 "_Up" => {
165 callback => sub { $self->follow ('u') },
166 accelerator => "<ctrl>Up"
167 },
168 "_Down" => {
169 callback => sub { $self->follow ('d') },
170 accelerator => "<ctrl>Down"
171 },
172 "_Right" => {
173 callback => sub { $self->follow ('r') },
174 accelerator => "<ctrl>Right"
175 },
176 "_Left" => {
177 callback => sub { $self->follow ('l') },
178 accelerator => "<ctrl>Left"
179 },
180 ]
181 },
182 _Help => {
183 item_type => '<Branch>',
184 children => [
185 _Manual => {
186 callback => sub { $::MAINWIN->show_help_window },
187 accelerator => "<ctrl>H"
188 },
189 ]
190 },
191 ];
192
193 my $men =
194 Gtk2::SimpleMenu->new (
195 menu_tree => $menu_tree,
196 default_callback => \&default_cb,
197 );
198
199 for (
200 [i => 'pick'],
201 [p => 'place'],
202 [e => 'erase'],
203 [s => 'select'],
204 [l => 'eval'],
205 [t => 'connect'],
206 [f => 'followexit']
207 )
208 {
209 my $tool = $_->[1];
210 $men->{accel_group}->connect ($Gtk2::Gdk::Keysyms{$_->[0]}, [], 'visible',
211 sub { $::MAINWIN->set_edit_tool ($tool) });
212 }
213
214 $men->{accel_group}->connect ($Gtk2::Gdk::Keysyms{'r'}, ['control-mask'], 'visible',
215 sub { $self->redo });
216
217 $self->add_accel_group ($men->{accel_group});
218
219 return $men->{widget};
220}
221
222#################################################################
223###### EDIT TOOL STUFF ##########################################
224#################################################################
225
226sub set_edit_tool {
227 my ($self, $tool) = @_;
228
229 $self->{etool} = $tool;
230
231 if ($self->ea->special_arrow) {
232 $self->{map}{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow));
233 } else {
234 # FIXME: Get the original cursor and insert it here
235 $self->{map}{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR'));
236 }
237}
238
239sub ea {
240 my ($self) = @_;
241 $self->{ea_alt} || $self->{etool};
242}
243
244sub start_drawmode {
245 my ($self, $map) = @_;
246
247 $self->{draw_mode} and return;
248
249 # XXX: is this okay? my ($x, $y) = $map->coord ($event->x, $event->y);
250 my ($x, $y) = $map->coord ($map->get_pointer);
251
252 my $ea = $self->ea;
253
254 $ea->begin ($map, $x, $y, $self);
255
256 $ea->edit ($map, $x, $y, $self)
257 if $x >= 0 and $y >= 0 and $x < $map->{map}{width} and $y < $map->{map}{height};
258
259 $self->{draw_mode} = [$x, $y];
260}
261
262sub stop_drawmode {
263 my ($self, $map) = @_;
264
265 $self->{draw_mode} or return;
266
267 my ($x, $y) = $map->coord ($map->get_pointer);
268
269 my $ea = $self->ea;
270 $ea->end ($map, $x, $y, $self);
271
272 delete $self->{draw_mode};
273}
274
275#################################################################
276###### UTILITY FUNCTIONS ########################################
277#################################################################
278
279sub follow {
280 my ($self, $dir) = @_;
281
282 my %dir_to_path = (
283 u => 'tile_path_1',
284 d => 'tile_path_3',
285 r => 'tile_path_2',
286 l => 'tile_path_4',
287 );
288
289 defined $dir_to_path{$dir}
290 or return;
291 my $map = $self->{map}{map}{info}{$dir_to_path{$dir}}
292 or return;
293
294 $map = map2abs ($map, $self);
295 $::MAINWIN->open_map_editor ($map);
296}
297
298# FIXME: Fix the automatic update of the attribute editor! and also the stack view!
299sub undo {
300 my ($self) = @_;
301
302 my $map = $self->{map}; # the Crossfire::MapWidget
303
304 $map->{undo_stack_pos}
305 or return;
306
307 $map->change_swap ($map->{undo_stack}[--$map->{undo_stack_pos}]);
308}
309
310sub get_stack_refs {
311 my ($self, $map, $x, $y) = @_;
312
313 my $cstack = $map->get ($x, $y);
314
315 return [] unless @$cstack;
316
317 my @refs;
318
319 for my $arch (@$cstack) {
320 my ($ox, $oy) = ($x, $y);
321 if ($arch->{_virtual}) {
322 $ox = $arch->{virtual_x};
323 $oy = $arch->{virtual_y};
324 $arch = $arch->{_virtual};
325 $cstack = $map->get ($ox, $oy);
326 # XXX: This heavily blows up if $arch isn't on $cstack now.. and it actually really does :(
327 }
328
329 push @refs,
330 GCE::ArchRef->new (
331 arch => $arch,
332 source => 'map',
333 cb => sub {
334 $map->change_begin ('attredit');
335 $map->change_stack ($ox, $oy, $cstack);
336
337 if (my $changeset = $map->change_end) {
338 splice @{ $map->{undo_stack} ||= [] },
339 $map->{undo_stack_pos}++, 1e6,
340 $changeset;
341 }
342 }
343 );
344 }
345
346 return @refs;
347}
348
349sub redo {
350 my ($self) = @_;
351
352 my $map = $self->{map}; # the Crossfire::MapWidget
353
354 $map->{undo_stack}
355 and $map->{undo_stack_pos} < @{$map->{undo_stack}}
356 or return;
357
358 $map->change_swap ($map->{undo_stack}[$map->{undo_stack_pos}++]);
359}
360
361sub load_meta_info {
362 my ($mapfile) = @_;
363 if (-e "$mapfile.meta") {
364 open my $metafh, "<", "$mapfile.meta"
365 or warn "Couldn't open meta file $mapfile.meta: $!";
366 my $metadata = do { local $/; <$metafh> };
367 return Crossfire::from_json ($metadata);
368 }
369}
370
371sub save_meta_info {
372 my ($mapfile, $metainfo) = @_;
373 open my $metafh, ">", "$mapfile.meta"
374 or warn "Couldn't write meta file $mapfile.meta: $!";
375 print $metafh Crossfire::to_json ($metainfo);
376}
377
378sub open_map {
379 my ($self, $path, $key) = @_;
380
381 $self->{mapkey} = $key;
382
383 if (ref $path) {
384 $self->{map}->set_map ($path);
385 delete $self->{meta_info};
386 $self->set_title ('<ram>');
387
388 } else {
389 my $ok = 0;
390 if (-e $path && -f $path) {
391 $ok = 1;
392 } else {
393 unless ($path =~ m/\.map$/) { # yuck
394 my $p = $path . '.map';
395 if ($ok = -e $p && -f $p) {
396 $path = $p;
397 }
398 }
399 }
400 unless ($ok) {
401 die "Couldn't open '$path' or find '$path.map': No such file or it is not a file.\n";
402 }
403 $self->{path} = $path;
404 $self->{map}->set_map (my $m = new_from_file Crossfire::Map $path);
405 $self->{meta_info} = load_meta_info ($path);
406 $self->set_title ("gce - map editor - $self->{path}");
407 }
408 $self->close_windows;
409}
410
411sub save_map {
412 my ($self) = @_;
413
414 if ($self->{path}) {
415 $self->{map}{map}->write_file ($self->{path});
416 if ($self->{meta_info}) {
417 save_meta_info ($self->{path}, $self->{meta_info});
418 }
419 quick_msg ($self, "saved to $self->{path}");
420 $self->set_title ("gce - map editor - $self->{path}");
421 } else {
422 $self->save_map_as;
423 }
424}
425
426sub save_map_as {
427 my ($self) = @_;
428
429 my $fc = $::MAINWIN->new_filechooser ('gce - save map', 1, $self->{path});
430
431 if ('ok' eq $fc->run) {
432
433 $::MAINWIN->{fc_last_folder} = $fc->get_current_folder;
434 $::MAINWIN->{fc_last_folders}->{$self->{fc_last_folder}}++;
435
436 $self->{map}{map}->write_file ($self->{path} = $fc->get_filename);
437 if ($self->{meta_info}) {
438 save_meta_info ($self->{path}, $self->{meta_info});
439 }
440 quick_msg ($self, "saved to $self->{path}");
441 $self->set_title ("gce - map editor - $self->{path}");
442 }
443
444 $fc->destroy;
445}
446
447#################################################################
448###### DIALOGOUES ###############################################
449#################################################################
450
451sub open_resize_map {
452 my ($self) = @_;
453
454 return if $self->{meta_info_win};
455
456 my $w = $self->{meta_info_win} = GCE::HashDialogue->new ();
457
458 $w->init (
459 dialog_default_size => [500, 200, 220, 20],
460 layout_name => 'resize_win',
461 title => 'resize map',
462 ref_hash => $self->{map}{map}{info},
463 dialog => [
464 [width => 'Width' => 'string'],
465 [height => 'Height' => 'string'],
466 ],
467 close_on_save => 1,
468 save_cb => sub {
469 my ($info) = @_;
470 $self->{map}{map}->resize ($info->{width}, $info->{height});
471 $self->{map}->invalidate_all;
472 }
473 );
474
475 $w->signal_connect (destroy => sub { delete $self->{meta_info_win} });
476
477 $w->show_all;
478}
479
480sub open_attach_edit {
481 my ($self) = @_;
482
483 my $w = GCE::AttachEditor->new;
484 $w->set_attachment (
485 $self->{map}{map}{info}{attach},
486 sub {
487 if (@{$_[0]}) {
488 $self->{map}{map}{info}{attach} = $_[0]
489 } else {
490 delete $self->{map}{map}{info}{attach};
491 }
492 }
493 );
494 $self->{attach_editor} = $w;
495 $w->signal_connect (destroy => sub { delete $self->{attach_editor} });
496 $w->show_all;
497}
498
499sub upload_map_incl {
500 my ($self) = @_;
501
502 my $meta = dclone $self->{meta_info};
503
504 my $w = $self->{meta_info_win} = GCE::HashDialogue->new ();
505
506 $w->init (
507 dialog_default_size => [500, 300, 220, 20],
508 layout_name => 'map_upload_incl',
509 title => 'gce - map inclusion upload',
510 ref_hash => $meta,
511 text_entry => { key => 'changes', label => 'Changes (required for inclusion):' },
512 dialog => [
513 [gameserver => 'Game server' => 'label'],
514 [testserver => 'Test server' => 'label'],
515 [undef => x => 'sep' ],
516 [cf_login => 'Server login name' => 'string'],
517 [cf_password=> 'Password' => 'password'],
518 [path => 'Map path' => 'string'],
519 ],
520 close_on_save => 1,
521 save_cb => sub {
522 my ($meta) = @_;
523 warn "UPLOAD[".Crossfire::to_json ($meta)."]\n";
524 }
525 );
526
527 $w->signal_connect (destroy => sub { delete $self->{meta_info_win} });
528
529 $w->show_all;
530}
531
532sub upload_map_test {
533 my ($self) = @_;
534
535 my $meta = dclone $self->{meta_info};
536
537 my $w = $self->{meta_info_win} = GCE::HashDialogue->new ();
538
539 $w->init (
540 dialog_default_size => [500, 300, 220, 20],
541 layout_name => 'map_upload_test',
542 title => 'gce - map test upload',
543 ref_hash => $meta,
544 dialog => [
545 [gameserver => 'Game server' => 'string'],
546 [testserver => 'Test server' => 'string'],
547 [undef => x => 'sep' ],
548 [cf_login => 'Server login name' => 'string'],
549 [cf_password=> 'Password' => 'password'],
550 [path => 'Map path' => 'string'],
551 ],
552 save_cb => sub {
553 my ($meta) = @_;
554 warn "UPLOAD[".Crossfire::to_json ($meta)."]\n";
555 }
556 );
557
558 $w->signal_connect (destroy => sub { delete $self->{meta_info_win} });
559
560 $w->show_all;
561
562
563}
564
565sub open_meta_info {
566 my ($self) = @_;
567
568 return if $self->{meta_info_win};
569
570 my $w = $self->{meta_info_win} = GCE::HashDialogue->new ();
571
572 $w->init (
573 dialog_default_size => [500, 300, 220, 20],
574 layout_name => 'meta_info_win',
575 title => 'meta info',
576 ref_hash => $self->{meta_info},
577 dialog => [
578 [path => 'Map path' => 'string'],
579 [cf_login => 'Login name' => 'string'],
580 [revision => 'CVS Revision' => 'label'],
581 [cvs_root => 'CVS Root' => 'label'],
582 [lib_root => 'LIB Root' => 'label'],
583 [testserver => 'Test server' => 'label'],
584 [gameserver => 'Game server' => 'label'],
585 ],
586 );
587
588 $w->signal_connect (destroy => sub { delete $self->{meta_info_win} });
589
590 $w->show_all;
591}
592
593sub open_map_info {
594 my ($self) = @_;
595 return if $self->{map_info};
596
597 my $w = $self->{map_info} = Gtk2::Window->new ('toplevel');
598 $w->set_title ("gcrossedit - map info");
599
600 $w->add (my $vb = Gtk2::VBox->new);
601 $vb->add (my $sw = Gtk2::ScrolledWindow->new);
602 $sw->set_policy ('automatic', 'automatic');
603 $sw->add (my $txt = Gtk2::TextView->new);
604 $vb->pack_start (my $hb = Gtk2::HBox->new (1, 1), 0, 1, 0);
605 $hb->pack_start (my $svbtn = Gtk2::Button->new ("save"), 1, 1, 0);
606 $hb->pack_start (my $logbtn = Gtk2::Button->new ("add log"), 1, 1, 0);
607 $hb->pack_start (my $closebtn = Gtk2::Button->new ("close"), 1, 1, 0);
608
609 my $buf = $txt->get_buffer ();
610 $buf->set_text ($self->{map}{map}{info}{msg});
611
612 $svbtn->signal_connect (clicked => sub {
613 my $buf = $txt->get_buffer ();
614 my $txt = $buf->get_text ($buf->get_start_iter, $buf->get_end_iter, 0);
615 $self->{map}{map}{info}{msg} = $txt;
616 });
617
618 $logbtn->signal_connect (clicked => sub {
619 my $buf = $txt->get_buffer ();
620 $buf->insert ($buf->get_start_iter, "- " . strftime ("%F %T %Z", localtime (time)) . " by " . ($main::CFG->{username} || $ENV{USER}) . ":\n");
621 $txt->set_buffer ($buf);
622 });
623
624 $closebtn->signal_connect (clicked => sub {
625 $w->destroy;
626 });
627
628 ::set_pos_and_size ($w, $main::CFG->{map_info}, 400, 400, 220, 20);
629 $w->signal_connect (destroy => sub {
630 delete $self->{map_info};
631 });
632 $w->show_all;
633}
634
635sub open_map_prop {
636 my ($self) = @_;
637
638 return if $self->{map_properties};
639
640 my $w = $self->{map_properties} = GCE::HashDialogue->new ();
641
642 $w->init (
643 dialog_default_size => [500, 500, 220, 20],
644 layout_name => 'map_prop_win',
645 title => 'map properties',
646 ref_hash => $self->{map}{map}{info},
647 close_on_save => 1,
648 dialog => [
649 [qw/name Name string/],
650 [qw/region Region string/],
651 [qw/enter_x Enter-x string/],
652 [qw/enter_y Enter-y string/],
653 [qw/reset_timeout Reset-timeout string/],
654 [qw/swap_time Swap-timeout string/],
655 [undef, qw/x sep/],
656 [qw/difficulty Difficulty string/],
657 [qw/windspeed Windspeed string/],
658 [qw/pressure Pressure string/],
659 [qw/humid Humid string/],
660 [qw/temp Temp string/],
661 [qw/darkness Darkness string/],
662 [qw/sky Sky string/],
663 [qw/winddir Winddir string/],
664 [undef, qw/x sep/],
665 [qw/width Width label/], # sub { $self->{map}{map}->resize ($_[0], $self->{map}{map}{height}) }],
666 [qw/height Height label/], # sub { $self->{map}{map}->resize ($self->{map}{map}{width}, $_[0]) }],
667 [undef, qw/x sep/],
668 # [qw/msg Text text/],
669# [qw/maplore Maplore text/],
670 [qw/outdoor Outdoor check/],
671 [qw/unique Unique check/],
672 [qw/fixed_resettime Fixed-resettime check/],
673 [per_player => 'Per player' => 'check'],
674 [per_party => 'Per party' => 'check'],
675 [no_reset => 'No reset' => 'check'],
676 [music => 'Map Music' => 'string'],
677 [undef, qw/x sep/],
678 [qw/tile_path_1 Northpath string/],
679 [qw/tile_path_2 Eastpath string/],
680 [qw/tile_path_3 Southpath string/],
681 [qw/tile_path_4 Westpath string/],
682 [qw/tile_path_5 Toppath string/],
683 [qw/tile_path_6 Bottompath string/],
684 [undef, qw/x sep/],
685 [undef, 'For shop description look in the manual',
686 'button', sub { $::MAINWIN->show_help_window }],
687 [qw/shopmin Shopmin string/],
688 [qw/shopmax Shopmax string/],
689 [qw/shoprace Shoprace string/],
690 [qw/shopgreed Shopgreed string/],
691 [qw/shopitems Shopitems string/],
692 ]
693 );
694
695 $w->signal_connect (destroy => sub { delete $self->{map_properties} });
696 $w->show_all;
697}
698
699#################################################################
700###### MAP EDITOR INIT ##########################################
701#################################################################
702
25sub INIT_INSTANCE { 703sub INIT_INSTANCE {
26 my ($self) = @_; 704 my ($self) = @_;
27 705
28 my $map = new Crossfire::MapWidget; 706 $self->set_title ('gce - map editor');
707 $self->add (my $vb = Gtk2::VBox->new);
29 708
30 # XXX: Make a nicer pick-view (with picture) 709 $vb->pack_start (my $menu = $self->build_menu, 0, 1, 0);
31 $self->pack_start (my $lbl = Gtk2::Label->new ("no selection"), 0, 1, 0);
32 $self->{pick_view} = $lbl;
33 710
34 $self->pack_start ($map, 1, 1, 0); 711 $vb->pack_start (my $map = $self->{map} = Crossfire::MapWidget->new, 1, 1, 0);
35 $self->{map} = $map;
36 712
37 $map->signal_connect (key_press_event => sub { 713 $map->signal_connect_after (key_press_event => sub {
38 my ($map, $event) = @_; 714 my ($map, $event) = @_;
39 715
40 $event->keyval == $Gtk2::Gdk::Keysyms{Shift_L} 716 my $kv = $event->keyval;
41 and $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_QUESTION_ARROW'));
42 717
43 $event->keyval == $Gtk2::Gdk::Keysyms{Control_L} 718 my $ret = 0;
719
720 my ($x, $y) = $map->coord ($map->get_pointer);
721 for ([Control_L => sub { $self->{ea_alt} = $::MAINWIN->{edit_collection}{erase} }],
722 [Alt_L => sub { $self->{ea_alt} = $::MAINWIN->{edit_collection}{pick} }],
723 [c => sub { $::MAINWIN->{edit_collection}{select}->copy }],
724 [v => sub { $::MAINWIN->{edit_collection}{select}->paste ($map, $x, $y) }],
725 [n => sub { $::MAINWIN->{edit_collection}{select}->invoke }],
726 )
727 {
728 my $ed = $_;
729
730 if ($kv == $Gtk2::Gdk::Keysyms{$ed->[0]}) {
731 my $was_in_draw = defined $self->{draw_mode};
732
733 $self->stop_drawmode ($map)
734 if $was_in_draw && grep { $ed->[0] eq $_ } qw/Control_L Alt_L/;
735
736 $ed->[1]->();
737 $ret = 1;
738
739 $self->start_drawmode ($map)
740 if $was_in_draw && grep { $ed->[0] eq $_ } qw/Control_L Alt_L/;
741 }
742 }
743
744 if ($self->ea->special_arrow) {
745 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow));
746 } else {
747 # FIXME: Get the original cursor and insert it here
44 and $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_HAND2')); 748 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR'));
749 }
45 750
751 $ret
46 }); 752 });
47 753
48 # XXX: What happens if the release got lost?
49 $map->signal_connect (key_release_event => sub { 754 $map->signal_connect_after (key_release_event => sub {
50 my ($map, $event) = @_; 755 my ($map, $event) = @_;
51 756
757 my $ret = 0;
758
759 if ($event->keyval == $Gtk2::Gdk::Keysyms{Control_L}
760 or $event->keyval == $Gtk2::Gdk::Keysyms{Alt_L})
761 {
762 my $was_in_draw = defined $self->{draw_mode};
763
764 $self->stop_drawmode ($map)
765 if $was_in_draw;
766
767 delete $self->{ea_alt};
768 $ret = 1;
769
770 $self->start_drawmode ($map)
771 if $was_in_draw;
772 }
773
774 if ($self->ea->special_arrow) {
775 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow));
776 } else {
777 # FIXME: Get the original cursor and insert it here
52 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR')); 778 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR'));
779 }
53 780
781 $ret
54 }); 782 });
55 $map->signal_connect (button_press_event => sub { 783 $map->signal_connect_after (button_press_event => sub {
56 my ($map, $event) = @_; 784 my ($map, $event) = @_;
57 785
58 my ($x, $y) = $map->coord ($event->x, $event->y); 786 if ((not $self->{draw_mode}) and $event->button == 1) {
787 my $ea = $self->ea;
59 788
60 if ($event->button == 1) { 789 $self->start_drawmode ($map);
61 790
62 my $s = map_get_tile_stack ($self->{map}, $x, $y); 791 $ea->want_cursor
792 or $map->disable_tooltip;
63 793
64 if ($event->state * "control-mask") {
65
66 $self->update_pick ($s->[-1]) if @$s;
67 }
68
69 unless ($event->state * "shift-mask") {
70
71 $map->disable_tooltip unless $self->{draw_mode};
72 $self->{draw_mode} = [1, $x, $y];
73 $self->place_pick ($x, $y);
74 $s = map_get_tile_stack ($self->{map}, $x, $y);
75 }
76
77 $GCE::MainWindow::MAINWIN->update_attr_editor ($s->[-1]) if @$s;
78 $GCE::MainWindow::MAINWIN->update_stack_view ($self, $s, $x, $y);
79
80 return 1; 794 return 1;
81
82 } elsif ($event->button == 3) { 795 } elsif ($event->button == 3) {
83 796 $self->do_context_menu ($map, $event);
84 $map->disable_tooltip unless $self->{draw_mode};
85 $self->{draw_mode} = [2, $x, $y];
86 $self->delete_arch ($x, $y);
87
88 my $s = map_get_tile_stack ($self->{map}, $x, $y);
89 $GCE::MainWindow::MAINWIN->update_attr_editor ($s->[-1]) if @$s;
90 $GCE::MainWindow::MAINWIN->update_stack_view ($self, $s, $x, $y);
91
92 return 1; 797 return 1;
93 } 798 }
94 799
95 0 800 0
96 }); 801 });
97 802
98 $map->signal_connect_after (motion_notify_event => sub { 803 $map->signal_connect_after (motion_notify_event => sub {
99 my ($map, $event) = @_; 804 my ($map, $event) = @_;
100 805
101 $self->{draw_mode} 806 $self->{draw_mode}
102 or return; 807 or return;
103 808
809 my $ea = $self->ea;
810
104 my ($X, $Y) = @{$self->{draw_mode}}[1,2]; 811 my ($X, $Y) = @{$self->{draw_mode}}[0,1];
105 my ($x, $y) = $map->coord ($map->get_pointer); 812 my ($x, $y) = $map->coord ($map->get_pointer);
106 813
107 while ($x != $X || $y != $Y) { 814 while ($x != $X || $y != $Y) {
108 815
109 $X++ if $X < $x; 816 $X++ if $X < $x;
110 $X-- if $X > $x; 817 $X-- if $X > $x;
111 $Y++ if $Y < $y; 818 $Y++ if $Y < $y;
112 $Y-- if $Y > $y; 819 $Y-- if $Y > $y;
113 820
114 if ($self->{draw_mode}[0] == 1) { 821 unless ($ea->only_on_click) {
115 822 $ea->edit ($map, $X, $Y, $self)
116 $self->place_pick ($X, $Y); 823 if $X >= 0 and $Y >= 0 and $X < $map->{map}{width} and $Y < $map->{map}{height};
117
118 } elsif ($self->{draw_mode}[0] == 2) {
119
120 $self->delete_arch ($X, $Y);
121 } 824 }
122 } 825 }
123 826
124 @{$self->{draw_mode}}[1,2] = ($X, $Y); 827 @{$self->{draw_mode}}[0,1] = ($X, $Y);
125 828
126 1 829 1
127 }); 830 });
128 831
129 $map->signal_connect (button_release_event => sub { 832 $map->signal_connect_after (button_release_event => sub {
130 my ($map, $event) = @_; 833 my ($map, $event) = @_;
131 834
132 $map->enable_tooltip if delete $self->{draw_mode}; 835 if ($self->{draw_mode} and $event->button == 1) {
836 my $ea = $self->ea;
837
838 $self->stop_drawmode ($map);
839
840 $ea->want_cursor
841 or $map->enable_tooltip;
842
843 return 1;
844 }
133 845
134 0 846 0
135 }); 847 });
136}
137 848
138sub set_place_arch { 849 ::set_pos_and_size ($self, $main::CFG->{map_window}, 500, 500, 200, 0);
139 my ($self, $arch) = @_;
140
141 $self->{pick_arch} = $arch;
142}
143
144sub delete_arch {
145 my ($self, $x, $y) = @_;
146
147 defined $self->{map}
148 or return 0;
149
150 my $s = map_pop_tile_stack ($self->{map}, $x, $y);
151 $self->{map}->update_map ($x, $y, 1, 1);
152}
153
154sub place_pick {
155 my ($self, $x, $y) = @_;
156
157 defined $self->{map}
158 and defined $self->{pick_arch}
159 or return 0;
160
161 my $s = map_get_tile_stack ($self->{map}, $x, $y);
162
163 # XXX: Do a deep clone?
164 my $arch = { _name => $self->{pick_arch}->{_name} };
165
166 if (not defined ($s->[-1]) or $s->[-1]->{_name} ne $arch->{_name}) {
167
168 map_push_tile_stack ($self->{map}, $x, $y, $arch);
169 }
170
171 $self->{map}->update_map ($x, $y, 1, 1);
172}
173
174sub open_map {
175 my ($self, $path) = @_;
176
177 $self->{map}->set_map (new_from_file Crossfire::Map $path);
178}
179
180sub update_pick {
181 my ($self, $arch) = @_;
182
183 $self->{pick_arch} = $arch;
184 $self->{pick_view}->set_text ($arch->{_name});
185} 850}
186 851
187=head1 AUTHOR 852=head1 AUTHOR
188 853
189 Marc Lehmann <schmorp@schmorp.de> 854 Marc Lehmann <schmorp@schmorp.de>
191 856
192 Robin Redeker <elmex@ta-sa.org> 857 Robin Redeker <elmex@ta-sa.org>
193 http://www.ta-sa.org/ 858 http://www.ta-sa.org/
194 859
195=cut 860=cut
1961;
197 861
8621
863

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines