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

Comparing deliantra/gde/GCE/MapEditor.pm (file contents):
Revision 1.29 by elmex, Mon Mar 20 04:11:41 2006 UTC vs.
Revision 1.46 by root, Thu Dec 7 20:30:25 2006 UTC

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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines