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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines