ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/gde/GCE/MapEditor.pm
Revision: 1.69
Committed: Fri Sep 21 07:19:21 2007 UTC (16 years, 8 months ago) by elmex
Branch: MAIN
Changes since 1.68: +14 -14 lines
Log Message:
removed the upload stuff for now

File Contents

# User Rev Content
1 elmex 1.1 package GCE::MapEditor;
2    
3     =head1 NAME
4    
5     GCE::MapEditor - the map editing widget
6    
7     =cut
8    
9     use Gtk2;
10     use Gtk2::Gdk::Keysyms;
11     use Gtk2::SimpleMenu;
12    
13     use Crossfire;
14 root 1.3 use Crossfire::Map;
15 elmex 1.1 use Crossfire::MapWidget;
16    
17     use GCE::AttrEdit;
18 elmex 1.63 use GCE::Util;
19 elmex 1.44 use GCE::HashDialog;
20 elmex 1.1
21 elmex 1.49 use POSIX qw/strftime/;
22    
23 elmex 1.4 use Glib::Object::Subclass
24 elmex 1.6 Gtk2::Window;
25 elmex 1.1
26 elmex 1.45 use Storable qw/dclone/;
27    
28 elmex 1.1 use strict;
29    
30 elmex 1.44 #################################################################
31     ###### WINDOW MANAGEMENT ########################################
32     #################################################################
33    
34     sub 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 elmex 1.49
41     $main::CFG->{map_info} = main::get_pos_and_size ($self->{map_info})
42     if $self->{map_info};
43 elmex 1.44 }
44    
45     sub 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    
57 elmex 1.35 sub 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 elmex 1.36 $::MAINWIN->{edit_collection}{followexit}->edit ($map, $x, $y, $self)
67 elmex 1.38 },
68 elmex 1.35 ]
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 elmex 1.38
76 elmex 1.47 $menu->append (my $sep = new Gtk2::SeparatorMenuItem);
77     $sep->show;
78 elmex 1.38
79     for my $sr (reverse $self->get_stack_refs ($map, $x, $y)) {
80     my $item = Gtk2::MenuItem->new ($sr->longname);
81 elmex 1.47 $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 elmex 1.38 $item->show;
95     }
96    
97 elmex 1.35 $menu->popup (undef, undef, undef, undef, $event->button, $event->time);
98     }
99    
100 root 1.9 sub build_menu {
101     my ($self) = @_;
102    
103     my $menu_tree = [
104     _File => {
105     item_type => '<Branch>',
106     children => [
107     "_Save" => {
108 root 1.10 callback => sub { $self->save_map },
109 root 1.9 accelerator => '<ctrl>S'
110     },
111 root 1.10 "Save As" => {
112     callback => sub { $self->save_map_as },
113     },
114 elmex 1.49 "Map _Info" => {
115     callback => sub { $self->open_map_info },
116     accelerator => "<ctrl>I",
117     },
118 elmex 1.42 "Map _Properties" => {
119 elmex 1.32 callback => sub { $self->open_map_prop },
120     accelerator => "<ctrl>P"
121     },
122 elmex 1.42 "Map _Attachments" => {
123     callback => sub { $self->open_attach_edit },
124     accelerator => "<ctrl>A"
125     },
126 elmex 1.69 # "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 elmex 1.32 "_Map Resize" => {
141     callback => sub { $self->open_resize_map },
142     },
143 root 1.13 "Close" => {
144 elmex 1.31 callback => sub { $self->destroy },
145 elmex 1.60 accelerator => "<ctrl>W",
146 root 1.13 },
147 root 1.10 ]
148 root 1.9 },
149     _Edit => {
150     item_type => '<Branch>',
151     children => [
152     "_Undo" => {
153     callback => sub { $self->undo },
154 elmex 1.17 accelerator => "<ctrl>Z"
155 root 1.9 },
156     "_Redo" => {
157     callback => sub { $self->redo },
158 elmex 1.17 accelerator => "<ctrl>Y"
159     },
160 root 1.9 ]
161     },
162 elmex 1.27 _Go => {
163     item_type => '<Branch>',
164     children => [
165     "_Up" => {
166     callback => sub { $self->follow ('u') },
167 elmex 1.33 accelerator => "<ctrl>Up"
168 elmex 1.27 },
169     "_Down" => {
170     callback => sub { $self->follow ('d') },
171 elmex 1.33 accelerator => "<ctrl>Down"
172 elmex 1.27 },
173     "_Right" => {
174     callback => sub { $self->follow ('r') },
175 elmex 1.33 accelerator => "<ctrl>Right"
176 elmex 1.27 },
177     "_Left" => {
178     callback => sub { $self->follow ('l') },
179 elmex 1.33 accelerator => "<ctrl>Left"
180 elmex 1.27 },
181     ]
182     },
183 elmex 1.29 _Help => {
184     item_type => '<Branch>',
185     children => [
186 elmex 1.30 _Manual => {
187 elmex 1.29 callback => sub { $::MAINWIN->show_help_window },
188     accelerator => "<ctrl>H"
189     },
190     ]
191     },
192 root 1.9 ];
193    
194     my $men =
195     Gtk2::SimpleMenu->new (
196     menu_tree => $menu_tree,
197     default_callback => \&default_cb,
198     );
199    
200 elmex 1.23 for (
201     [i => 'pick'],
202     [p => 'place'],
203 elmex 1.24 [e => 'erase'],
204     [s => 'select'],
205     [l => 'eval'],
206 elmex 1.37 [t => 'connect'],
207 elmex 1.24 [f => 'followexit']
208 elmex 1.23 )
209     {
210 elmex 1.24 my $tool = $_->[1];
211 elmex 1.23 $men->{accel_group}->connect ($Gtk2::Gdk::Keysyms{$_->[0]}, [], 'visible',
212 elmex 1.24 sub { $::MAINWIN->set_edit_tool ($tool) });
213 elmex 1.23 }
214    
215 elmex 1.39 $men->{accel_group}->connect ($Gtk2::Gdk::Keysyms{'r'}, ['control-mask'], 'visible',
216     sub { $self->redo });
217    
218 root 1.9 $self->add_accel_group ($men->{accel_group});
219    
220     return $men->{widget};
221     }
222    
223 elmex 1.44 #################################################################
224     ###### EDIT TOOL STUFF ##########################################
225     #################################################################
226    
227 elmex 1.31 sub set_edit_tool {
228     my ($self, $tool) = @_;
229    
230     $self->{etool} = $tool;
231    
232     if ($self->ea->special_arrow) {
233     $self->{map}{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow));
234 elmex 1.34 } else {
235     # FIXME: Get the original cursor and insert it here
236     $self->{map}{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR'));
237 elmex 1.31 }
238     }
239    
240 elmex 1.19 sub ea {
241     my ($self) = @_;
242 elmex 1.31 $self->{ea_alt} || $self->{etool};
243 elmex 1.19 }
244    
245 elmex 1.34 sub start_drawmode {
246     my ($self, $map) = @_;
247    
248     $self->{draw_mode} and return;
249    
250     # XXX: is this okay? my ($x, $y) = $map->coord ($event->x, $event->y);
251     my ($x, $y) = $map->coord ($map->get_pointer);
252    
253     my $ea = $self->ea;
254    
255 elmex 1.36 $ea->begin ($map, $x, $y, $self);
256    
257     $ea->edit ($map, $x, $y, $self)
258 elmex 1.34 if $x >= 0 and $y >= 0 and $x < $map->{map}{width} and $y < $map->{map}{height};
259    
260     $self->{draw_mode} = [$x, $y];
261     }
262    
263     sub stop_drawmode {
264     my ($self, $map) = @_;
265    
266     $self->{draw_mode} or return;
267    
268     my ($x, $y) = $map->coord ($map->get_pointer);
269    
270     my $ea = $self->ea;
271     $ea->end ($map, $x, $y, $self);
272    
273     delete $self->{draw_mode};
274     }
275    
276 elmex 1.44 #################################################################
277     ###### UTILITY FUNCTIONS ########################################
278     #################################################################
279    
280     sub follow {
281     my ($self, $dir) = @_;
282    
283     my %dir_to_path = (
284     u => 'tile_path_1',
285     d => 'tile_path_3',
286     r => 'tile_path_2',
287     l => 'tile_path_4',
288     );
289    
290     defined $dir_to_path{$dir}
291     or return;
292     my $map = $self->{map}{map}{info}{$dir_to_path{$dir}}
293     or return;
294    
295     $map = map2abs ($map, $self);
296     $::MAINWIN->open_map_editor ($map);
297     }
298    
299 elmex 1.16 # FIXME: Fix the automatic update of the attribute editor! and also the stack view!
300 root 1.9 sub undo {
301     my ($self) = @_;
302 elmex 1.8
303 root 1.9 my $map = $self->{map}; # the Crossfire::MapWidget
304 elmex 1.1
305 root 1.9 $map->{undo_stack_pos}
306     or return;
307 elmex 1.1
308 root 1.9 $map->change_swap ($map->{undo_stack}[--$map->{undo_stack_pos}]);
309 elmex 1.68
310     $::MAINWIN->update_stack_view ();
311 root 1.9 }
312 elmex 1.1
313 elmex 1.38 sub get_stack_refs {
314     my ($self, $map, $x, $y) = @_;
315    
316     my $cstack = $map->get ($x, $y);
317    
318     return [] unless @$cstack;
319    
320     my @refs;
321    
322     for my $arch (@$cstack) {
323 elmex 1.64 my ($ix, $iy, $iarch, $istack) = devirtualize ($map, $x, $y, $arch, $cstack);
324 elmex 1.38 push @refs,
325     GCE::ArchRef->new (
326 elmex 1.64 arch => $iarch,
327 elmex 1.56 source => 'map',
328 elmex 1.38 cb => sub {
329     $map->change_begin ('attredit');
330 elmex 1.64 $map->change_stack ($ix, $iy, $istack);
331 elmex 1.38
332     if (my $changeset = $map->change_end) {
333     splice @{ $map->{undo_stack} ||= [] },
334     $map->{undo_stack_pos}++, 1e6,
335     $changeset;
336     }
337     }
338     );
339     }
340    
341     return @refs;
342     }
343    
344 root 1.9 sub redo {
345     my ($self) = @_;
346 elmex 1.7
347 root 1.9 my $map = $self->{map}; # the Crossfire::MapWidget
348 elmex 1.7
349 elmex 1.14 $map->{undo_stack}
350     and $map->{undo_stack_pos} < @{$map->{undo_stack}}
351     or return;
352 elmex 1.1
353 root 1.9 $map->change_swap ($map->{undo_stack}[$map->{undo_stack_pos}++]);
354 root 1.3 }
355    
356 elmex 1.43 sub load_meta_info {
357     my ($mapfile) = @_;
358     if (-e "$mapfile.meta") {
359     open my $metafh, "<", "$mapfile.meta"
360     or warn "Couldn't open meta file $mapfile.meta: $!";
361     my $metadata = do { local $/; <$metafh> };
362     return Crossfire::from_json ($metadata);
363     }
364     }
365    
366     sub save_meta_info {
367     my ($mapfile, $metainfo) = @_;
368     open my $metafh, ">", "$mapfile.meta"
369     or warn "Couldn't write meta file $mapfile.meta: $!";
370     print $metafh Crossfire::to_json ($metainfo);
371     }
372    
373 root 1.3 sub open_map {
374 elmex 1.26 my ($self, $path, $key) = @_;
375    
376     $self->{mapkey} = $key;
377 root 1.3
378 elmex 1.18 if (ref $path) {
379     $self->{map}->set_map ($path);
380 elmex 1.43 delete $self->{meta_info};
381 elmex 1.60 $self->set_window_title;
382 elmex 1.18
383     } else {
384 elmex 1.51 my $ok = 0;
385 elmex 1.52 if (-e $path && -f $path) {
386     $ok = 1;
387     } else {
388 elmex 1.51 unless ($path =~ m/\.map$/) { # yuck
389     my $p = $path . '.map';
390     if ($ok = -e $p && -f $p) {
391     $path = $p;
392     }
393     }
394     }
395     unless ($ok) {
396 elmex 1.52 die "Couldn't open '$path' or find '$path.map': No such file or it is not a file.\n";
397 elmex 1.48 }
398 elmex 1.18 $self->{path} = $path;
399     $self->{map}->set_map (my $m = new_from_file Crossfire::Map $path);
400 elmex 1.43 $self->{meta_info} = load_meta_info ($path);
401 elmex 1.60 $self->set_window_title ($self->{path});
402 elmex 1.59 $::MAINWIN->add_recent($path);
403 elmex 1.18 }
404 elmex 1.61 $self->update_overlays;
405 elmex 1.44 $self->close_windows;
406 root 1.3 }
407    
408 root 1.13 sub save_map {
409     my ($self) = @_;
410    
411 elmex 1.19 if ($self->{path}) {
412     $self->{map}{map}->write_file ($self->{path});
413 elmex 1.43 if ($self->{meta_info}) {
414     save_meta_info ($self->{path}, $self->{meta_info});
415     }
416 elmex 1.22 quick_msg ($self, "saved to $self->{path}");
417 elmex 1.60 $self->set_window_title ($self->{path});
418 elmex 1.59 $::MAINWIN->add_recent($self->{path});
419 elmex 1.22 } else {
420     $self->save_map_as;
421 elmex 1.19 }
422 root 1.13 }
423    
424 elmex 1.60 sub set_window_title {
425     my ($self, $title) = @_;
426    
427     $title = 'Unsaved'
428     unless $title;
429    
430     $self->set_title(File::Basename::basename($title).' - gcrossedit');
431     }
432    
433 root 1.13 sub save_map_as {
434     my ($self) = @_;
435 elmex 1.19
436 elmex 1.22 my $fc = $::MAINWIN->new_filechooser ('gce - save map', 1, $self->{path});
437 elmex 1.19
438     if ('ok' eq $fc->run) {
439    
440     $::MAINWIN->{fc_last_folder} = $fc->get_current_folder;
441     $::MAINWIN->{fc_last_folders}->{$self->{fc_last_folder}}++;
442    
443 elmex 1.59 if($fc->get_filename) {
444     $self->{path} = $fc->get_filename;
445     $self->save_map;
446 elmex 1.43 }
447 elmex 1.19 }
448    
449     $fc->destroy;
450 root 1.13 }
451    
452 elmex 1.66 sub _get_conns_for_obj {
453     my ($obj, $x, $y, $rconns) = @_;
454    
455     if (defined $obj->{connected}) {
456     $rconns->{$x}->{$y}->{$obj->{connected}} = 1;
457    
458     } elsif (defined $obj->{msg}) {
459     my $msg = $obj->{msg};
460    
461     while ($msg =~ s/\@trigger\s+(\d+)//) {
462     $rconns->{$x}->{$y}->{$1} = 1;
463     }
464     }
465     }
466    
467 elmex 1.61 sub update_overlays {
468     my ($self, $sx, $sy, $stack) = @_;
469     my $conns = {};
470    
471     if (not $stack) {
472 elmex 1.65 my ($w, $h) = ($self->{map}{map}{width}, $self->{map}{map}{height});
473 elmex 1.61
474 elmex 1.66 for (my $x = 0; $x < $w; $x++) {
475     for (my $y = 0; $y < $h; $y++) {
476     _get_conns_for_obj ($_, $x, $y, $conns)
477 elmex 1.67 for @{$self->{map}->get_ro ($x, $y)};
478 elmex 1.61 }
479     }
480    
481     # delete prev. overlays
482     for (keys %{$self->{_conn_overlays}}) {
483     $self->{map}->overlay ($_);
484     }
485     } else {
486     # del old overlay for this place
487     my $ovl = "connection_$sx\_$sy";
488     $self->{map}->overlay ($ovl) if delete $self->{_conn_overlays}->{$ovl};
489 elmex 1.66 _get_conns_for_obj ($_, $sx, $sy, $conns)
490     for @$stack;
491 elmex 1.61 }
492    
493     # put new overlays there
494     for my $x (keys %$conns) {
495     for my $y (keys %{$conns->{$x}}) {
496     my $ovlname = "connection_$x\_$y";
497     my $conns_ovl = join (', ', keys %{$conns->{$x}->{$y}});
498     $self->{_conn_overlays}->{$ovlname} = 1;
499 elmex 1.62 my ($a, $t, $ac)
500     = Gtk2::Pango->parse_markup (
501     "<span size=\"xx-small\">$conns_ovl</span>", ''
502     );
503     my $pl = $self->{map}->create_pango_layout ('');
504     $pl->set_attributes ($a);
505     $pl->set_text ($t);
506     my ($ink_rect, $logical_rect) = $pl->get_pixel_extents;
507 elmex 1.61
508     $self->{map}->overlay (
509     $ovlname, $x * TILESIZE, $y * TILESIZE, TILESIZE, TILESIZE,
510     sub {
511     my ($mapwin, $x, $y) = @_;
512 elmex 1.62 if (!$self->{_conn_upd_ovl_gc_fg}) {
513     my $gc
514     = $self->{_conn_upd_ovl_gc_fg}
515     = Gtk2::Gdk::GC->new ($mapwin->{window});
516     my $cm = $mapwin->{window}->get_colormap;
517     $gc->set_foreground (gtk2_get_color ($mapwin, "yellow"));
518     $gc->set_background (gtk2_get_color ($mapwin, "black"));
519     }
520     $mapwin->{window}->draw_rectangle (
521     $mapwin->style->black_gc,
522     1,
523     $x, $y, $logical_rect->{width} + 2, $logical_rect->{height} + 2,
524     );
525     $mapwin->{window}->draw_rectangle (
526     $self->{_conn_upd_ovl_gc_fg},
527     0,
528     $x, $y, $logical_rect->{width} + 2, $logical_rect->{height} + 2,
529     );
530 elmex 1.61 $mapwin->{window}->draw_layout_with_colors (
531 elmex 1.62 $mapwin->style->black_gc, $x + 1, $y + 1, $pl,
532 elmex 1.61 $self->{connection_overlay_foreground},
533     $self->{connection_overlay_background},
534     )
535     }
536     );
537     }
538     }
539     }
540    
541 elmex 1.44 #################################################################
542     ###### DIALOGOUES ###############################################
543     #################################################################
544 elmex 1.17
545     sub open_resize_map {
546     my ($self) = @_;
547    
548 elmex 1.44 return if $self->{meta_info_win};
549 elmex 1.17
550 elmex 1.44 my $w = $self->{meta_info_win} = GCE::HashDialogue->new ();
551 elmex 1.17
552 elmex 1.44 $w->init (
553     dialog_default_size => [500, 200, 220, 20],
554     layout_name => 'resize_win',
555     title => 'resize map',
556     ref_hash => $self->{map}{map}{info},
557     dialog => [
558     [width => 'Width' => 'string'],
559     [height => 'Height' => 'string'],
560     ],
561 elmex 1.58 close_on_save => 1,
562     save_button_label => 'resize',
563 elmex 1.44 save_cb => sub {
564     my ($info) = @_;
565     $self->{map}{map}->resize ($info->{width}, $info->{height});
566 elmex 1.58 $self->{map}->set_map ($self->{map}{map});
567 elmex 1.61 $self->update_overlays;
568 elmex 1.44 }
569 elmex 1.27 );
570    
571 elmex 1.44 $w->signal_connect (destroy => sub { delete $self->{meta_info_win} });
572 elmex 1.27
573 elmex 1.44 $w->show_all;
574 elmex 1.27 }
575    
576 elmex 1.42 sub open_attach_edit {
577     my ($self) = @_;
578    
579     my $w = GCE::AttachEditor->new;
580     $w->set_attachment (
581     $self->{map}{map}{info}{attach},
582     sub {
583     if (@{$_[0]}) {
584     $self->{map}{map}{info}{attach} = $_[0]
585     } else {
586     delete $self->{map}{map}{info}{attach};
587     }
588     }
589     );
590     $self->{attach_editor} = $w;
591     $w->signal_connect (destroy => sub { delete $self->{attach_editor} });
592     $w->show_all;
593     }
594    
595 elmex 1.45 sub upload_map_incl {
596     my ($self) = @_;
597    
598     my $meta = dclone $self->{meta_info};
599    
600     my $w = $self->{meta_info_win} = GCE::HashDialogue->new ();
601    
602     $w->init (
603     dialog_default_size => [500, 300, 220, 20],
604     layout_name => 'map_upload_incl',
605     title => 'gce - map inclusion upload',
606     ref_hash => $meta,
607     text_entry => { key => 'changes', label => 'Changes (required for inclusion):' },
608     dialog => [
609     [gameserver => 'Game server' => 'label'],
610     [testserver => 'Test server' => 'label'],
611     [undef => x => 'sep' ],
612     [cf_login => 'Server login name' => 'string'],
613     [cf_password=> 'Password' => 'password'],
614     [path => 'Map path' => 'string'],
615     ],
616 elmex 1.50 close_on_save => 1,
617 elmex 1.45 save_cb => sub {
618     my ($meta) = @_;
619     warn "UPLOAD[".Crossfire::to_json ($meta)."]\n";
620     }
621     );
622    
623     $w->signal_connect (destroy => sub { delete $self->{meta_info_win} });
624    
625     $w->show_all;
626     }
627    
628     sub upload_map_test {
629     my ($self) = @_;
630    
631     my $meta = dclone $self->{meta_info};
632    
633     my $w = $self->{meta_info_win} = GCE::HashDialogue->new ();
634    
635     $w->init (
636     dialog_default_size => [500, 300, 220, 20],
637     layout_name => 'map_upload_test',
638     title => 'gce - map test upload',
639     ref_hash => $meta,
640     dialog => [
641     [gameserver => 'Game server' => 'string'],
642     [testserver => 'Test server' => 'string'],
643     [undef => x => 'sep' ],
644     [cf_login => 'Server login name' => 'string'],
645     [cf_password=> 'Password' => 'password'],
646     [path => 'Map path' => 'string'],
647     ],
648     save_cb => sub {
649     my ($meta) = @_;
650     warn "UPLOAD[".Crossfire::to_json ($meta)."]\n";
651     }
652     );
653    
654     $w->signal_connect (destroy => sub { delete $self->{meta_info_win} });
655    
656     $w->show_all;
657    
658    
659     }
660 elmex 1.44
661     sub open_meta_info {
662 elmex 1.42 my ($self) = @_;
663    
664 elmex 1.44 return if $self->{meta_info_win};
665    
666     my $w = $self->{meta_info_win} = GCE::HashDialogue->new ();
667    
668     $w->init (
669     dialog_default_size => [500, 300, 220, 20],
670     layout_name => 'meta_info_win',
671     title => 'meta info',
672     ref_hash => $self->{meta_info},
673     dialog => [
674     [path => 'Map path' => 'string'],
675     [cf_login => 'Login name' => 'string'],
676     [revision => 'CVS Revision' => 'label'],
677     [cvs_root => 'CVS Root' => 'label'],
678     [lib_root => 'LIB Root' => 'label'],
679     [testserver => 'Test server' => 'label'],
680     [gameserver => 'Game server' => 'label'],
681     ],
682     );
683    
684     $w->signal_connect (destroy => sub { delete $self->{meta_info_win} });
685    
686     $w->show_all;
687 elmex 1.42 }
688    
689 elmex 1.49 sub open_map_info {
690     my ($self) = @_;
691     return if $self->{map_info};
692    
693     my $w = $self->{map_info} = Gtk2::Window->new ('toplevel');
694     $w->set_title ("gcrossedit - map info");
695    
696     $w->add (my $vb = Gtk2::VBox->new);
697     $vb->add (my $sw = Gtk2::ScrolledWindow->new);
698     $sw->set_policy ('automatic', 'automatic');
699     $sw->add (my $txt = Gtk2::TextView->new);
700     $vb->pack_start (my $hb = Gtk2::HBox->new (1, 1), 0, 1, 0);
701     $hb->pack_start (my $svbtn = Gtk2::Button->new ("save"), 1, 1, 0);
702     $hb->pack_start (my $logbtn = Gtk2::Button->new ("add log"), 1, 1, 0);
703     $hb->pack_start (my $closebtn = Gtk2::Button->new ("close"), 1, 1, 0);
704    
705     my $buf = $txt->get_buffer ();
706     $buf->set_text ($self->{map}{map}{info}{msg});
707    
708     $svbtn->signal_connect (clicked => sub {
709     my $buf = $txt->get_buffer ();
710     my $txt = $buf->get_text ($buf->get_start_iter, $buf->get_end_iter, 0);
711     $self->{map}{map}{info}{msg} = $txt;
712     });
713    
714     $logbtn->signal_connect (clicked => sub {
715     my $buf = $txt->get_buffer ();
716     $buf->insert ($buf->get_start_iter, "- " . strftime ("%F %T %Z", localtime (time)) . " by " . ($main::CFG->{username} || $ENV{USER}) . ":\n");
717     $txt->set_buffer ($buf);
718     });
719    
720     $closebtn->signal_connect (clicked => sub {
721     $w->destroy;
722     });
723    
724     ::set_pos_and_size ($w, $main::CFG->{map_info}, 400, 400, 220, 20);
725     $w->signal_connect (destroy => sub {
726     delete $self->{map_info};
727     });
728     $w->show_all;
729     }
730    
731 elmex 1.17 sub open_map_prop {
732     my ($self) = @_;
733    
734 elmex 1.42 return if $self->{map_properties};
735    
736 elmex 1.44 my $w = $self->{map_properties} = GCE::HashDialogue->new ();
737 elmex 1.17
738 elmex 1.44 $w->init (
739     dialog_default_size => [500, 500, 220, 20],
740     layout_name => 'map_prop_win',
741     title => 'map properties',
742     ref_hash => $self->{map}{map}{info},
743 elmex 1.50 close_on_save => 1,
744 elmex 1.44 dialog => [
745 elmex 1.27 [qw/name Name string/],
746     [qw/region Region string/],
747     [qw/enter_x Enter-x string/],
748     [qw/enter_y Enter-y string/],
749     [qw/reset_timeout Reset-timeout string/],
750     [qw/swap_time Swap-timeout string/],
751 elmex 1.44 [undef, qw/x sep/],
752 elmex 1.27 [qw/difficulty Difficulty string/],
753     [qw/windspeed Windspeed string/],
754     [qw/pressure Pressure string/],
755     [qw/humid Humid string/],
756     [qw/temp Temp string/],
757     [qw/darkness Darkness string/],
758     [qw/sky Sky string/],
759     [qw/winddir Winddir string/],
760 elmex 1.44 [undef, qw/x sep/],
761 elmex 1.27 [qw/width Width label/], # sub { $self->{map}{map}->resize ($_[0], $self->{map}{map}{height}) }],
762     [qw/height Height label/], # sub { $self->{map}{map}->resize ($self->{map}{map}{width}, $_[0]) }],
763 elmex 1.44 [undef, qw/x sep/],
764     # [qw/msg Text text/],
765     # [qw/maplore Maplore text/],
766 elmex 1.27 [qw/outdoor Outdoor check/],
767     [qw/unique Unique check/],
768     [qw/fixed_resettime Fixed-resettime check/],
769 elmex 1.55 [per_player => 'Per player' => 'check'],
770     [per_party => 'Per party' => 'check'],
771 elmex 1.54 [no_reset => 'No reset' => 'check'],
772 elmex 1.57 [music => 'Map Music' => 'string'],
773 elmex 1.44 [undef, qw/x sep/],
774 elmex 1.27 [qw/tile_path_1 Northpath string/],
775     [qw/tile_path_2 Eastpath string/],
776     [qw/tile_path_3 Southpath string/],
777     [qw/tile_path_4 Westpath string/],
778     [qw/tile_path_5 Toppath string/],
779     [qw/tile_path_6 Bottompath string/],
780 elmex 1.44 [undef, qw/x sep/],
781     [undef, 'For shop description look in the manual',
782     'button', sub { $::MAINWIN->show_help_window }],
783 elmex 1.39 [qw/shopmin Shopmin string/],
784     [qw/shopmax Shopmax string/],
785     [qw/shoprace Shoprace string/],
786     [qw/shopgreed Shopgreed string/],
787     [qw/shopitems Shopitems string/],
788 elmex 1.44 ]
789     );
790 elmex 1.17
791 elmex 1.44 $w->signal_connect (destroy => sub { delete $self->{map_properties} });
792 elmex 1.17 $w->show_all;
793     }
794    
795 elmex 1.44 #################################################################
796     ###### MAP EDITOR INIT ##########################################
797     #################################################################
798    
799     sub INIT_INSTANCE {
800     my ($self) = @_;
801    
802 elmex 1.60 $self->set_window_title;
803 elmex 1.44 $self->add (my $vb = Gtk2::VBox->new);
804    
805     $vb->pack_start (my $menu = $self->build_menu, 0, 1, 0);
806    
807     $vb->pack_start (my $map = $self->{map} = Crossfire::MapWidget->new, 1, 1, 0);
808    
809 elmex 1.61 $map->signal_connect_after (stack_change => sub {
810     my ($map, $x, $y, $stack) = @_;
811     $self->update_overlays ($x, $y, $stack);
812 elmex 1.68 $::MAINWIN->update_map_pos ($self, $x, $y);
813 elmex 1.61 });
814     $map->signal_connect_after (swap_stack_change => sub {
815     my ($map, $x, $y, $stack) = @_;
816     $self->update_overlays ($x, $y, $stack);
817 elmex 1.68 $::MAINWIN->update_map_pos ($self, $x, $y);
818 elmex 1.61 });
819    
820     $self->{connection_overlay_foreground}
821     = Gtk2::Gdk::Color->new (257 * 255, 257 * 255, 0);
822     $self->{connection_overlay_background}
823     = Gtk2::Gdk::Color->new (0, 0, 0);
824 elmex 1.62 # my $ygc
825     # = $self->{connection_overlay_yellow_gc}
826     # = Gtk2::Gdk::GC->new ($self->{map}{window});
827     # $ygc->set_foreground (Gtk2::Gdk::Color->new (257 * 255, 257 * 255, 0));
828 elmex 1.61
829 elmex 1.44 $map->signal_connect_after (key_press_event => sub {
830     my ($map, $event) = @_;
831    
832     my $kv = $event->keyval;
833    
834     my $ret = 0;
835    
836     my ($x, $y) = $map->coord ($map->get_pointer);
837     for ([Control_L => sub { $self->{ea_alt} = $::MAINWIN->{edit_collection}{erase} }],
838     [Alt_L => sub { $self->{ea_alt} = $::MAINWIN->{edit_collection}{pick} }],
839     [c => sub { $::MAINWIN->{edit_collection}{select}->copy }],
840     [v => sub { $::MAINWIN->{edit_collection}{select}->paste ($map, $x, $y) }],
841     [n => sub { $::MAINWIN->{edit_collection}{select}->invoke }],
842     )
843     {
844     my $ed = $_;
845    
846     if ($kv == $Gtk2::Gdk::Keysyms{$ed->[0]}) {
847     my $was_in_draw = defined $self->{draw_mode};
848    
849     $self->stop_drawmode ($map)
850     if $was_in_draw && grep { $ed->[0] eq $_ } qw/Control_L Alt_L/;
851    
852     $ed->[1]->();
853     $ret = 1;
854    
855     $self->start_drawmode ($map)
856     if $was_in_draw && grep { $ed->[0] eq $_ } qw/Control_L Alt_L/;
857     }
858     }
859    
860     if ($self->ea->special_arrow) {
861     $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow));
862     } else {
863     # FIXME: Get the original cursor and insert it here
864     $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR'));
865     }
866    
867     $ret
868     });
869    
870     $map->signal_connect_after (key_release_event => sub {
871     my ($map, $event) = @_;
872    
873     my $ret = 0;
874    
875     if ($event->keyval == $Gtk2::Gdk::Keysyms{Control_L}
876     or $event->keyval == $Gtk2::Gdk::Keysyms{Alt_L})
877     {
878     my $was_in_draw = defined $self->{draw_mode};
879    
880     $self->stop_drawmode ($map)
881     if $was_in_draw;
882    
883     delete $self->{ea_alt};
884     $ret = 1;
885    
886     $self->start_drawmode ($map)
887     if $was_in_draw;
888     }
889    
890     if ($self->ea->special_arrow) {
891     $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow));
892     } else {
893     # FIXME: Get the original cursor and insert it here
894     $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR'));
895     }
896    
897     $ret
898     });
899     $map->signal_connect_after (button_press_event => sub {
900     my ($map, $event) = @_;
901    
902     if ((not $self->{draw_mode}) and $event->button == 1) {
903     my $ea = $self->ea;
904    
905     $self->start_drawmode ($map);
906    
907     $ea->want_cursor
908     or $map->disable_tooltip;
909    
910     return 1;
911     } elsif ($event->button == 3) {
912     $self->do_context_menu ($map, $event);
913     return 1;
914     }
915    
916     0
917     });
918    
919     $map->signal_connect_after (motion_notify_event => sub {
920     my ($map, $event) = @_;
921    
922     $self->{draw_mode}
923     or return;
924    
925     my $ea = $self->ea;
926    
927     my ($X, $Y) = @{$self->{draw_mode}}[0,1];
928     my ($x, $y) = $map->coord ($map->get_pointer);
929    
930     while ($x != $X || $y != $Y) {
931    
932     $X++ if $X < $x;
933     $X-- if $X > $x;
934     $Y++ if $Y < $y;
935     $Y-- if $Y > $y;
936    
937     unless ($ea->only_on_click) {
938     $ea->edit ($map, $X, $Y, $self)
939     if $X >= 0 and $Y >= 0 and $X < $map->{map}{width} and $Y < $map->{map}{height};
940     }
941     }
942    
943     @{$self->{draw_mode}}[0,1] = ($X, $Y);
944    
945     1
946     });
947    
948     $map->signal_connect_after (button_release_event => sub {
949     my ($map, $event) = @_;
950    
951     if ($self->{draw_mode} and $event->button == 1) {
952     my $ea = $self->ea;
953    
954     $self->stop_drawmode ($map);
955    
956     $ea->want_cursor
957     or $map->enable_tooltip;
958    
959     return 1;
960     }
961    
962     0
963     });
964    
965     ::set_pos_and_size ($self, $main::CFG->{map_window}, 500, 500, 200, 0);
966     }
967    
968 elmex 1.1 =head1 AUTHOR
969    
970     Marc Lehmann <schmorp@schmorp.de>
971     http://home.schmorp.de/
972    
973     Robin Redeker <elmex@ta-sa.org>
974     http://www.ta-sa.org/
975    
976     =cut
977 root 1.46
978     1
979 elmex 1.1