ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/gde/GCE/MapEditor.pm
Revision: 1.65
Committed: Fri Aug 31 08:28:09 2007 UTC (16 years, 9 months ago) by elmex
Branch: MAIN
Changes since 1.64: +2 -2 lines
Log Message:
fixed a major slowdown

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.44 "Map Meta _Info" => {
127     callback => sub { $self->open_meta_info },
128     },
129 elmex 1.45 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     }
310 elmex 1.1
311 elmex 1.38 sub get_stack_refs {
312     my ($self, $map, $x, $y) = @_;
313    
314     my $cstack = $map->get ($x, $y);
315    
316     return [] unless @$cstack;
317    
318     my @refs;
319    
320     for my $arch (@$cstack) {
321 elmex 1.64 my ($ix, $iy, $iarch, $istack) = devirtualize ($map, $x, $y, $arch, $cstack);
322 elmex 1.38 push @refs,
323     GCE::ArchRef->new (
324 elmex 1.64 arch => $iarch,
325 elmex 1.56 source => 'map',
326 elmex 1.38 cb => sub {
327     $map->change_begin ('attredit');
328 elmex 1.64 $map->change_stack ($ix, $iy, $istack);
329 elmex 1.38
330     if (my $changeset = $map->change_end) {
331     splice @{ $map->{undo_stack} ||= [] },
332     $map->{undo_stack_pos}++, 1e6,
333     $changeset;
334     }
335     }
336     );
337     }
338    
339     return @refs;
340     }
341    
342 root 1.9 sub redo {
343     my ($self) = @_;
344 elmex 1.7
345 root 1.9 my $map = $self->{map}; # the Crossfire::MapWidget
346 elmex 1.7
347 elmex 1.14 $map->{undo_stack}
348     and $map->{undo_stack_pos} < @{$map->{undo_stack}}
349     or return;
350 elmex 1.1
351 root 1.9 $map->change_swap ($map->{undo_stack}[$map->{undo_stack_pos}++]);
352 root 1.3 }
353    
354 elmex 1.43 sub load_meta_info {
355     my ($mapfile) = @_;
356     if (-e "$mapfile.meta") {
357     open my $metafh, "<", "$mapfile.meta"
358     or warn "Couldn't open meta file $mapfile.meta: $!";
359     my $metadata = do { local $/; <$metafh> };
360     return Crossfire::from_json ($metadata);
361     }
362     }
363    
364     sub save_meta_info {
365     my ($mapfile, $metainfo) = @_;
366     open my $metafh, ">", "$mapfile.meta"
367     or warn "Couldn't write meta file $mapfile.meta: $!";
368     print $metafh Crossfire::to_json ($metainfo);
369     }
370    
371 root 1.3 sub open_map {
372 elmex 1.26 my ($self, $path, $key) = @_;
373    
374     $self->{mapkey} = $key;
375 root 1.3
376 elmex 1.18 if (ref $path) {
377     $self->{map}->set_map ($path);
378 elmex 1.43 delete $self->{meta_info};
379 elmex 1.60 $self->set_window_title;
380 elmex 1.18
381     } else {
382 elmex 1.51 my $ok = 0;
383 elmex 1.52 if (-e $path && -f $path) {
384     $ok = 1;
385     } else {
386 elmex 1.51 unless ($path =~ m/\.map$/) { # yuck
387     my $p = $path . '.map';
388     if ($ok = -e $p && -f $p) {
389     $path = $p;
390     }
391     }
392     }
393     unless ($ok) {
394 elmex 1.52 die "Couldn't open '$path' or find '$path.map': No such file or it is not a file.\n";
395 elmex 1.48 }
396 elmex 1.18 $self->{path} = $path;
397     $self->{map}->set_map (my $m = new_from_file Crossfire::Map $path);
398 elmex 1.43 $self->{meta_info} = load_meta_info ($path);
399 elmex 1.60 $self->set_window_title ($self->{path});
400 elmex 1.59 $::MAINWIN->add_recent($path);
401 elmex 1.18 }
402 elmex 1.61 $self->update_overlays;
403 elmex 1.44 $self->close_windows;
404 root 1.3 }
405    
406 root 1.13 sub save_map {
407     my ($self) = @_;
408    
409 elmex 1.19 if ($self->{path}) {
410     $self->{map}{map}->write_file ($self->{path});
411 elmex 1.43 if ($self->{meta_info}) {
412     save_meta_info ($self->{path}, $self->{meta_info});
413     }
414 elmex 1.22 quick_msg ($self, "saved to $self->{path}");
415 elmex 1.60 $self->set_window_title ($self->{path});
416 elmex 1.59 $::MAINWIN->add_recent($self->{path});
417 elmex 1.22 } else {
418     $self->save_map_as;
419 elmex 1.19 }
420 root 1.13 }
421    
422 elmex 1.60 sub set_window_title {
423     my ($self, $title) = @_;
424    
425     $title = 'Unsaved'
426     unless $title;
427    
428     $self->set_title(File::Basename::basename($title).' - gcrossedit');
429     }
430    
431 root 1.13 sub save_map_as {
432     my ($self) = @_;
433 elmex 1.19
434 elmex 1.22 my $fc = $::MAINWIN->new_filechooser ('gce - save map', 1, $self->{path});
435 elmex 1.19
436     if ('ok' eq $fc->run) {
437    
438     $::MAINWIN->{fc_last_folder} = $fc->get_current_folder;
439     $::MAINWIN->{fc_last_folders}->{$self->{fc_last_folder}}++;
440    
441 elmex 1.59 if($fc->get_filename) {
442     $self->{path} = $fc->get_filename;
443     $self->save_map;
444 elmex 1.43 }
445 elmex 1.19 }
446    
447     $fc->destroy;
448 root 1.13 }
449    
450 elmex 1.61 sub update_overlays {
451     my ($self, $sx, $sy, $stack) = @_;
452     my $conns = {};
453    
454     if (not $stack) {
455 elmex 1.65 my ($w, $h) = ($self->{map}{map}{width}, $self->{map}{map}{height});
456 elmex 1.61
457     for (my $x = 0; $x <= $w; $x++) {
458     for (my $y = 0; $y <= $h; $y++) {
459 elmex 1.65 my $stk = $self->{map}->get ($x, $y);
460 elmex 1.61 for (@$stk) {
461     if (defined $_->{connected}) {
462     $conns->{$x}->{$y}->{$_->{connected}} = 1;
463     }
464     }
465     }
466     }
467    
468     # delete prev. overlays
469     for (keys %{$self->{_conn_overlays}}) {
470     $self->{map}->overlay ($_);
471     }
472     } else {
473     # del old overlay for this place
474     my $ovl = "connection_$sx\_$sy";
475     $self->{map}->overlay ($ovl) if delete $self->{_conn_overlays}->{$ovl};
476     for (@$stack) {
477     if (defined $_->{connected}) {
478     $conns->{$sx}->{$sy}->{$_->{connected}} = 1;
479     }
480     }
481     }
482    
483     # put new overlays there
484     for my $x (keys %$conns) {
485     for my $y (keys %{$conns->{$x}}) {
486     my $ovlname = "connection_$x\_$y";
487     my $conns_ovl = join (', ', keys %{$conns->{$x}->{$y}});
488     $self->{_conn_overlays}->{$ovlname} = 1;
489 elmex 1.62 my ($a, $t, $ac)
490     = Gtk2::Pango->parse_markup (
491     "<span size=\"xx-small\">$conns_ovl</span>", ''
492     );
493     my $pl = $self->{map}->create_pango_layout ('');
494     $pl->set_attributes ($a);
495     $pl->set_text ($t);
496     my ($ink_rect, $logical_rect) = $pl->get_pixel_extents;
497 elmex 1.61
498     $self->{map}->overlay (
499     $ovlname, $x * TILESIZE, $y * TILESIZE, TILESIZE, TILESIZE,
500     sub {
501     my ($mapwin, $x, $y) = @_;
502 elmex 1.62 if (!$self->{_conn_upd_ovl_gc_fg}) {
503     my $gc
504     = $self->{_conn_upd_ovl_gc_fg}
505     = Gtk2::Gdk::GC->new ($mapwin->{window});
506     my $cm = $mapwin->{window}->get_colormap;
507     $gc->set_foreground (gtk2_get_color ($mapwin, "yellow"));
508     $gc->set_background (gtk2_get_color ($mapwin, "black"));
509     }
510     $mapwin->{window}->draw_rectangle (
511     $mapwin->style->black_gc,
512     1,
513     $x, $y, $logical_rect->{width} + 2, $logical_rect->{height} + 2,
514     );
515     $mapwin->{window}->draw_rectangle (
516     $self->{_conn_upd_ovl_gc_fg},
517     0,
518     $x, $y, $logical_rect->{width} + 2, $logical_rect->{height} + 2,
519     );
520 elmex 1.61 $mapwin->{window}->draw_layout_with_colors (
521 elmex 1.62 $mapwin->style->black_gc, $x + 1, $y + 1, $pl,
522 elmex 1.61 $self->{connection_overlay_foreground},
523     $self->{connection_overlay_background},
524     )
525     }
526     );
527     }
528     }
529     }
530    
531 elmex 1.44 #################################################################
532     ###### DIALOGOUES ###############################################
533     #################################################################
534 elmex 1.17
535     sub open_resize_map {
536     my ($self) = @_;
537    
538 elmex 1.44 return if $self->{meta_info_win};
539 elmex 1.17
540 elmex 1.44 my $w = $self->{meta_info_win} = GCE::HashDialogue->new ();
541 elmex 1.17
542 elmex 1.44 $w->init (
543     dialog_default_size => [500, 200, 220, 20],
544     layout_name => 'resize_win',
545     title => 'resize map',
546     ref_hash => $self->{map}{map}{info},
547     dialog => [
548     [width => 'Width' => 'string'],
549     [height => 'Height' => 'string'],
550     ],
551 elmex 1.58 close_on_save => 1,
552     save_button_label => 'resize',
553 elmex 1.44 save_cb => sub {
554     my ($info) = @_;
555     $self->{map}{map}->resize ($info->{width}, $info->{height});
556 elmex 1.58 $self->{map}->set_map ($self->{map}{map});
557 elmex 1.61 $self->update_overlays;
558 elmex 1.44 }
559 elmex 1.27 );
560    
561 elmex 1.44 $w->signal_connect (destroy => sub { delete $self->{meta_info_win} });
562 elmex 1.27
563 elmex 1.44 $w->show_all;
564 elmex 1.27 }
565    
566 elmex 1.42 sub open_attach_edit {
567     my ($self) = @_;
568    
569     my $w = GCE::AttachEditor->new;
570     $w->set_attachment (
571     $self->{map}{map}{info}{attach},
572     sub {
573     if (@{$_[0]}) {
574     $self->{map}{map}{info}{attach} = $_[0]
575     } else {
576     delete $self->{map}{map}{info}{attach};
577     }
578     }
579     );
580     $self->{attach_editor} = $w;
581     $w->signal_connect (destroy => sub { delete $self->{attach_editor} });
582     $w->show_all;
583     }
584    
585 elmex 1.45 sub upload_map_incl {
586     my ($self) = @_;
587    
588     my $meta = dclone $self->{meta_info};
589    
590     my $w = $self->{meta_info_win} = GCE::HashDialogue->new ();
591    
592     $w->init (
593     dialog_default_size => [500, 300, 220, 20],
594     layout_name => 'map_upload_incl',
595     title => 'gce - map inclusion upload',
596     ref_hash => $meta,
597     text_entry => { key => 'changes', label => 'Changes (required for inclusion):' },
598     dialog => [
599     [gameserver => 'Game server' => 'label'],
600     [testserver => 'Test server' => 'label'],
601     [undef => x => 'sep' ],
602     [cf_login => 'Server login name' => 'string'],
603     [cf_password=> 'Password' => 'password'],
604     [path => 'Map path' => 'string'],
605     ],
606 elmex 1.50 close_on_save => 1,
607 elmex 1.45 save_cb => sub {
608     my ($meta) = @_;
609     warn "UPLOAD[".Crossfire::to_json ($meta)."]\n";
610     }
611     );
612    
613     $w->signal_connect (destroy => sub { delete $self->{meta_info_win} });
614    
615     $w->show_all;
616     }
617    
618     sub upload_map_test {
619     my ($self) = @_;
620    
621     my $meta = dclone $self->{meta_info};
622    
623     my $w = $self->{meta_info_win} = GCE::HashDialogue->new ();
624    
625     $w->init (
626     dialog_default_size => [500, 300, 220, 20],
627     layout_name => 'map_upload_test',
628     title => 'gce - map test upload',
629     ref_hash => $meta,
630     dialog => [
631     [gameserver => 'Game server' => 'string'],
632     [testserver => 'Test server' => 'string'],
633     [undef => x => 'sep' ],
634     [cf_login => 'Server login name' => 'string'],
635     [cf_password=> 'Password' => 'password'],
636     [path => 'Map path' => 'string'],
637     ],
638     save_cb => sub {
639     my ($meta) = @_;
640     warn "UPLOAD[".Crossfire::to_json ($meta)."]\n";
641     }
642     );
643    
644     $w->signal_connect (destroy => sub { delete $self->{meta_info_win} });
645    
646     $w->show_all;
647    
648    
649     }
650 elmex 1.44
651     sub open_meta_info {
652 elmex 1.42 my ($self) = @_;
653    
654 elmex 1.44 return if $self->{meta_info_win};
655    
656     my $w = $self->{meta_info_win} = GCE::HashDialogue->new ();
657    
658     $w->init (
659     dialog_default_size => [500, 300, 220, 20],
660     layout_name => 'meta_info_win',
661     title => 'meta info',
662     ref_hash => $self->{meta_info},
663     dialog => [
664     [path => 'Map path' => 'string'],
665     [cf_login => 'Login name' => 'string'],
666     [revision => 'CVS Revision' => 'label'],
667     [cvs_root => 'CVS Root' => 'label'],
668     [lib_root => 'LIB Root' => 'label'],
669     [testserver => 'Test server' => 'label'],
670     [gameserver => 'Game server' => 'label'],
671     ],
672     );
673    
674     $w->signal_connect (destroy => sub { delete $self->{meta_info_win} });
675    
676     $w->show_all;
677 elmex 1.42 }
678    
679 elmex 1.49 sub open_map_info {
680     my ($self) = @_;
681     return if $self->{map_info};
682    
683     my $w = $self->{map_info} = Gtk2::Window->new ('toplevel');
684     $w->set_title ("gcrossedit - map info");
685    
686     $w->add (my $vb = Gtk2::VBox->new);
687     $vb->add (my $sw = Gtk2::ScrolledWindow->new);
688     $sw->set_policy ('automatic', 'automatic');
689     $sw->add (my $txt = Gtk2::TextView->new);
690     $vb->pack_start (my $hb = Gtk2::HBox->new (1, 1), 0, 1, 0);
691     $hb->pack_start (my $svbtn = Gtk2::Button->new ("save"), 1, 1, 0);
692     $hb->pack_start (my $logbtn = Gtk2::Button->new ("add log"), 1, 1, 0);
693     $hb->pack_start (my $closebtn = Gtk2::Button->new ("close"), 1, 1, 0);
694    
695     my $buf = $txt->get_buffer ();
696     $buf->set_text ($self->{map}{map}{info}{msg});
697    
698     $svbtn->signal_connect (clicked => sub {
699     my $buf = $txt->get_buffer ();
700     my $txt = $buf->get_text ($buf->get_start_iter, $buf->get_end_iter, 0);
701     $self->{map}{map}{info}{msg} = $txt;
702     });
703    
704     $logbtn->signal_connect (clicked => sub {
705     my $buf = $txt->get_buffer ();
706     $buf->insert ($buf->get_start_iter, "- " . strftime ("%F %T %Z", localtime (time)) . " by " . ($main::CFG->{username} || $ENV{USER}) . ":\n");
707     $txt->set_buffer ($buf);
708     });
709    
710     $closebtn->signal_connect (clicked => sub {
711     $w->destroy;
712     });
713    
714     ::set_pos_and_size ($w, $main::CFG->{map_info}, 400, 400, 220, 20);
715     $w->signal_connect (destroy => sub {
716     delete $self->{map_info};
717     });
718     $w->show_all;
719     }
720    
721 elmex 1.17 sub open_map_prop {
722     my ($self) = @_;
723    
724 elmex 1.42 return if $self->{map_properties};
725    
726 elmex 1.44 my $w = $self->{map_properties} = GCE::HashDialogue->new ();
727 elmex 1.17
728 elmex 1.44 $w->init (
729     dialog_default_size => [500, 500, 220, 20],
730     layout_name => 'map_prop_win',
731     title => 'map properties',
732     ref_hash => $self->{map}{map}{info},
733 elmex 1.50 close_on_save => 1,
734 elmex 1.44 dialog => [
735 elmex 1.27 [qw/name Name string/],
736     [qw/region Region string/],
737     [qw/enter_x Enter-x string/],
738     [qw/enter_y Enter-y string/],
739     [qw/reset_timeout Reset-timeout string/],
740     [qw/swap_time Swap-timeout string/],
741 elmex 1.44 [undef, qw/x sep/],
742 elmex 1.27 [qw/difficulty Difficulty string/],
743     [qw/windspeed Windspeed string/],
744     [qw/pressure Pressure string/],
745     [qw/humid Humid string/],
746     [qw/temp Temp string/],
747     [qw/darkness Darkness string/],
748     [qw/sky Sky string/],
749     [qw/winddir Winddir string/],
750 elmex 1.44 [undef, qw/x sep/],
751 elmex 1.27 [qw/width Width label/], # sub { $self->{map}{map}->resize ($_[0], $self->{map}{map}{height}) }],
752     [qw/height Height label/], # sub { $self->{map}{map}->resize ($self->{map}{map}{width}, $_[0]) }],
753 elmex 1.44 [undef, qw/x sep/],
754     # [qw/msg Text text/],
755     # [qw/maplore Maplore text/],
756 elmex 1.27 [qw/outdoor Outdoor check/],
757     [qw/unique Unique check/],
758     [qw/fixed_resettime Fixed-resettime check/],
759 elmex 1.55 [per_player => 'Per player' => 'check'],
760     [per_party => 'Per party' => 'check'],
761 elmex 1.54 [no_reset => 'No reset' => 'check'],
762 elmex 1.57 [music => 'Map Music' => 'string'],
763 elmex 1.44 [undef, qw/x sep/],
764 elmex 1.27 [qw/tile_path_1 Northpath string/],
765     [qw/tile_path_2 Eastpath string/],
766     [qw/tile_path_3 Southpath string/],
767     [qw/tile_path_4 Westpath string/],
768     [qw/tile_path_5 Toppath string/],
769     [qw/tile_path_6 Bottompath string/],
770 elmex 1.44 [undef, qw/x sep/],
771     [undef, 'For shop description look in the manual',
772     'button', sub { $::MAINWIN->show_help_window }],
773 elmex 1.39 [qw/shopmin Shopmin string/],
774     [qw/shopmax Shopmax string/],
775     [qw/shoprace Shoprace string/],
776     [qw/shopgreed Shopgreed string/],
777     [qw/shopitems Shopitems string/],
778 elmex 1.44 ]
779     );
780 elmex 1.17
781 elmex 1.44 $w->signal_connect (destroy => sub { delete $self->{map_properties} });
782 elmex 1.17 $w->show_all;
783     }
784    
785 elmex 1.44 #################################################################
786     ###### MAP EDITOR INIT ##########################################
787     #################################################################
788    
789     sub INIT_INSTANCE {
790     my ($self) = @_;
791    
792 elmex 1.60 $self->set_window_title;
793 elmex 1.44 $self->add (my $vb = Gtk2::VBox->new);
794    
795     $vb->pack_start (my $menu = $self->build_menu, 0, 1, 0);
796    
797     $vb->pack_start (my $map = $self->{map} = Crossfire::MapWidget->new, 1, 1, 0);
798    
799 elmex 1.61 $map->signal_connect_after (stack_change => sub {
800     my ($map, $x, $y, $stack) = @_;
801     $self->update_overlays ($x, $y, $stack);
802     });
803     $map->signal_connect_after (swap_stack_change => sub {
804     my ($map, $x, $y, $stack) = @_;
805     $self->update_overlays ($x, $y, $stack);
806     });
807    
808     $self->{connection_overlay_foreground}
809     = Gtk2::Gdk::Color->new (257 * 255, 257 * 255, 0);
810     $self->{connection_overlay_background}
811     = Gtk2::Gdk::Color->new (0, 0, 0);
812 elmex 1.62 # my $ygc
813     # = $self->{connection_overlay_yellow_gc}
814     # = Gtk2::Gdk::GC->new ($self->{map}{window});
815     # $ygc->set_foreground (Gtk2::Gdk::Color->new (257 * 255, 257 * 255, 0));
816 elmex 1.61
817 elmex 1.44 $map->signal_connect_after (key_press_event => sub {
818     my ($map, $event) = @_;
819    
820     my $kv = $event->keyval;
821    
822     my $ret = 0;
823    
824     my ($x, $y) = $map->coord ($map->get_pointer);
825     for ([Control_L => sub { $self->{ea_alt} = $::MAINWIN->{edit_collection}{erase} }],
826     [Alt_L => sub { $self->{ea_alt} = $::MAINWIN->{edit_collection}{pick} }],
827     [c => sub { $::MAINWIN->{edit_collection}{select}->copy }],
828     [v => sub { $::MAINWIN->{edit_collection}{select}->paste ($map, $x, $y) }],
829     [n => sub { $::MAINWIN->{edit_collection}{select}->invoke }],
830     )
831     {
832     my $ed = $_;
833    
834     if ($kv == $Gtk2::Gdk::Keysyms{$ed->[0]}) {
835     my $was_in_draw = defined $self->{draw_mode};
836    
837     $self->stop_drawmode ($map)
838     if $was_in_draw && grep { $ed->[0] eq $_ } qw/Control_L Alt_L/;
839    
840     $ed->[1]->();
841     $ret = 1;
842    
843     $self->start_drawmode ($map)
844     if $was_in_draw && grep { $ed->[0] eq $_ } qw/Control_L Alt_L/;
845     }
846     }
847    
848     if ($self->ea->special_arrow) {
849     $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow));
850     } else {
851     # FIXME: Get the original cursor and insert it here
852     $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR'));
853     }
854    
855     $ret
856     });
857    
858     $map->signal_connect_after (key_release_event => sub {
859     my ($map, $event) = @_;
860    
861     my $ret = 0;
862    
863     if ($event->keyval == $Gtk2::Gdk::Keysyms{Control_L}
864     or $event->keyval == $Gtk2::Gdk::Keysyms{Alt_L})
865     {
866     my $was_in_draw = defined $self->{draw_mode};
867    
868     $self->stop_drawmode ($map)
869     if $was_in_draw;
870    
871     delete $self->{ea_alt};
872     $ret = 1;
873    
874     $self->start_drawmode ($map)
875     if $was_in_draw;
876     }
877    
878     if ($self->ea->special_arrow) {
879     $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow));
880     } else {
881     # FIXME: Get the original cursor and insert it here
882     $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR'));
883     }
884    
885     $ret
886     });
887     $map->signal_connect_after (button_press_event => sub {
888     my ($map, $event) = @_;
889    
890     if ((not $self->{draw_mode}) and $event->button == 1) {
891     my $ea = $self->ea;
892    
893     $self->start_drawmode ($map);
894    
895     $ea->want_cursor
896     or $map->disable_tooltip;
897    
898     return 1;
899     } elsif ($event->button == 3) {
900     $self->do_context_menu ($map, $event);
901     return 1;
902     }
903    
904     0
905     });
906    
907     $map->signal_connect_after (motion_notify_event => sub {
908     my ($map, $event) = @_;
909    
910     $self->{draw_mode}
911     or return;
912    
913     my $ea = $self->ea;
914    
915     my ($X, $Y) = @{$self->{draw_mode}}[0,1];
916     my ($x, $y) = $map->coord ($map->get_pointer);
917    
918     while ($x != $X || $y != $Y) {
919    
920     $X++ if $X < $x;
921     $X-- if $X > $x;
922     $Y++ if $Y < $y;
923     $Y-- if $Y > $y;
924    
925     unless ($ea->only_on_click) {
926     $ea->edit ($map, $X, $Y, $self)
927     if $X >= 0 and $Y >= 0 and $X < $map->{map}{width} and $Y < $map->{map}{height};
928     }
929     }
930    
931     @{$self->{draw_mode}}[0,1] = ($X, $Y);
932    
933     1
934     });
935    
936     $map->signal_connect_after (button_release_event => sub {
937     my ($map, $event) = @_;
938    
939     if ($self->{draw_mode} and $event->button == 1) {
940     my $ea = $self->ea;
941    
942     $self->stop_drawmode ($map);
943    
944     $ea->want_cursor
945     or $map->enable_tooltip;
946    
947     return 1;
948     }
949    
950     0
951     });
952    
953     ::set_pos_and_size ($self, $main::CFG->{map_window}, 500, 500, 200, 0);
954     }
955    
956 elmex 1.1 =head1 AUTHOR
957    
958     Marc Lehmann <schmorp@schmorp.de>
959     http://home.schmorp.de/
960    
961     Robin Redeker <elmex@ta-sa.org>
962     http://www.ta-sa.org/
963    
964     =cut
965 root 1.46
966     1
967 elmex 1.1