--- deliantra/gde/GCE/MapEditor.pm 2006/02/20 13:30:28 1.6 +++ deliantra/gde/GCE/MapEditor.pm 2006/03/17 17:59:43 1.27 @@ -15,123 +15,464 @@ use Crossfire::MapWidget; use GCE::AttrEdit; +use GCE::Util; use Glib::Object::Subclass Gtk2::Window; use strict; +sub build_menu { + my ($self) = @_; + + my $menu_tree = [ + _File => { + item_type => '', + children => [ + "_Save" => { + callback => sub { $self->save_map }, + accelerator => 'S' + }, + "Save As" => { + callback => sub { $self->save_map_as }, + }, + "Close" => { + callback => sub { $self->delete; 1 }, + }, + ] + }, + _Edit => { + item_type => '', + children => [ + "_Undo" => { + callback => sub { $self->undo }, + accelerator => "Z" + }, + "_Redo" => { + callback => sub { $self->redo }, + accelerator => "Y" + }, + "_Map Properties" => { + callback => sub { $self->open_map_prop }, + accelerator => "P" + }, + "_Map Resize" => { + callback => sub { $self->open_resize_map }, + accelerator => "R" + }, + ] + }, + _Go => { + item_type => '', + children => [ + "_Up" => { + callback => sub { $self->follow ('u') }, + accelerator => "U" + }, + "_Down" => { + callback => sub { $self->follow ('d') }, + accelerator => "D" + }, + "_Right" => { + callback => sub { $self->follow ('r') }, + accelerator => "R" + }, + "_Left" => { + callback => sub { $self->follow ('l') }, + accelerator => "L" + }, + ] + }, + + ]; + + my $men = + Gtk2::SimpleMenu->new ( + menu_tree => $menu_tree, + default_callback => \&default_cb, + ); + + for ( + [i => 'pick'], + [p => 'place'], + [e => 'erase'], + [s => 'select'], + [l => 'eval'], + [x => 'connectexit'], + [f => 'followexit'] + ) + { + my $tool = $_->[1]; + $men->{accel_group}->connect ($Gtk2::Gdk::Keysyms{$_->[0]}, [], 'visible', + sub { $::MAINWIN->set_edit_tool ($tool) }); + } + + $self->add_accel_group ($men->{accel_group}); + + return $men->{widget}; +} + +sub ea { + my ($self) = @_; + $self->{ea_alt} || $::MAINWIN->{sel_editaction}; +} + sub INIT_INSTANCE { my ($self) = @_; $self->set_title ('gce - map editor'); $self->add (my $vb = Gtk2::VBox->new); - $vb->pack_start (my $map = $self->{map} = Crossfire::MapWidget->new, 1, 1, 0); - $map->signal_connect (button_press_event => sub { - my ($map, $event) = @_; - my ($x, $y) = $map->coord ($event->x, $event->y); - my $as = $map->get ($x, $y); + $self->signal_connect (delete_event => sub { $self->delete; 1 }); - my $btn = $event->button; + $vb->pack_start (my $menu = $self->build_menu, 0, 1, 0); - if ($btn != 2 and my $ea = $GCE::MainWindow::MAINWIN->{sel_editaction}) { + $vb->pack_start (my $map = $self->{map} = Crossfire::MapWidget->new, 1, 1, 0); - $ea->begin ($map, $x, $y, $btn); + $self->signal_connect (focus_in_event => sub { + my $ea = $::MAINWIN->{sel_editaction}; + if ($ea->special_arrow) { + $self->{map}->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($ea->special_arrow)); + } else { + $self->{map}->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR')); + } + delete $self->{ea_alt}; + }); - $self->{draw_mode} = [$ea, $x, $y, $btn]; + $map->signal_connect (key_press_event => sub { + my ($map, $event) = @_; - $ea->want_cursor - or $map->disable_tooltip; + my $kv = $event->keyval; - if ($ea->special_arrow) { + my ($x, $y) = $map->coord ($map->get_pointer); + for ([Control_L => sub { $self->{ea_alt} = $::MAINWIN->{edit_collection}{erase} }], + [Alt_L => sub { $self->{ea_alt} = $::MAINWIN->{edit_collection}{pick} }], + [c => sub { $::MAINWIN->{edit_collection}{select}->copy }], + [v => sub { $::MAINWIN->{edit_collection}{select}->paste ($x, $y) }], + [n => sub { $::MAINWIN->{edit_collection}{select}->invoke }], + ) + { + if ($kv == $Gtk2::Gdk::Keysyms{$_->[0]}) { + $_->[1]->(); + } + } - $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($ea->special_arrow)); - #'GDK_QUESTION_ARROW')); - } + if ($self->ea->special_arrow) { + $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow)); + } + 1; + }); + $map->signal_connect (key_release_event => sub { + my ($map, $event) = @_; - return 1; - } + if ($event->keyval == $Gtk2::Gdk::Keysyms{Control_L} + or $event->keyval == $Gtk2::Gdk::Keysyms{Alt_L}) + { + delete $self->{ea_alt}; + } - 0 - }); + if ($self->ea->special_arrow) { + $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow)); + } else { + # XXX: Get the original cursor and insert it here + $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR')); + } + 1; + }); + $map->signal_connect (button_press_event => sub { + my ($map, $event) = @_; - $map->signal_connect_after (motion_notify_event => sub { - my ($map, $event) = @_; + my ($x, $y) = $map->coord ($event->x, $event->y); + my $as = $map->get ($x, $y); - $self->{draw_mode} - or return; + if ((not $self->{draw_mode}) and $event->button != 2) { - my ($X, $Y) = @{$self->{draw_mode}}[1,2]; - my ($x, $y) = $map->coord ($map->get_pointer); + my $ea = $self->ea; - while ($x != $X || $y != $Y) { + $ea->begin ($map, $x, $y, $self) + if $x >= 0 and $y >= 0 and $x < $map->{map}{width} and $y < $map->{map}{height}; - $X++ if $X < $x; - $X-- if $X > $x; - $Y++ if $Y < $y; - $Y-- if $Y > $y; + $self->{draw_mode} = [$x, $y]; - $self->{draw_mode}[0]->edit ($map, $X, $Y, $self->{draw_mode}[3]); - } + $ea->want_cursor + or $map->disable_tooltip; - @{$self->{draw_mode}}[1,2] = ($X, $Y); + return 1; + } + 0 + }); - 1 - }); + $map->signal_connect_after (motion_notify_event => sub { + my ($map, $event) = @_; - $map->signal_connect (button_release_event => sub { - my ($map, $event) = @_; + $self->{draw_mode} + or return; - if ($self->{draw_mode} and my $ea = $self->{draw_mode}[0]) { - $ea->end; + my $ea = $self->ea; - $ea->want_cursor - or $map->enable_tooltip; + my ($X, $Y) = @{$self->{draw_mode}}[0,1]; + my ($x, $y) = $map->coord ($map->get_pointer); + while ($x != $X || $y != $Y) { - if ($ea->special_arrow) { + $X++ if $X < $x; + $X-- if $X > $x; + $Y++ if $Y < $y; + $Y-- if $Y > $y; - # XXX: Get the original cursor and insert it here - $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR')); - } + $ea->edit ($map, $X, $Y, $self) + if $X >= 0 and $Y >= 0 and $X < $map->{map}{width} and $Y < $map->{map}{height}; + } - delete $self->{draw_mode}; - } + @{$self->{draw_mode}}[0,1] = ($X, $Y); - 0 - }); + 1 + }); + + $map->signal_connect (button_release_event => sub { + my ($map, $event) = @_; + + if ($self->{draw_mode}) { + my ($x, $y) = $map->coord ($map->get_pointer); + + my $ea = $self->ea; + $ea->end ($map, $x, $y, $self); + + delete $self->{draw_mode}; + + $ea->want_cursor + or $map->enable_tooltip; + + return 1; + } + + 0 + }); } -sub delete_arch { - my ($self, $x, $y) = @_; +# FIXME: Fix the automatic update of the attribute editor! and also the stack view! +sub undo { + my ($self) = @_; + + my $map = $self->{map}; # the Crossfire::MapWidget - defined $self->{map} - or return 0; + $map->{undo_stack_pos} + or return; - my $as = $self->{map}->get ($x, $y); - pop @$as; - $self->{map}->set ($x, $y, $as); + $map->change_swap ($map->{undo_stack}[--$map->{undo_stack_pos}]); } -sub place_pick { - my ($self, $x, $y) = @_; +sub redo { + my ($self) = @_; + + my $map = $self->{map}; # the Crossfire::MapWidget - my $pick = $GCE::MainWindow::MAINWIN->get_pick; - my $as = $self->{map}->get ($x, $y); + $map->{undo_stack} + and $map->{undo_stack_pos} < @{$map->{undo_stack}} + or return; - my $arch = { _name => $pick->{_name} }; + $map->change_swap ($map->{undo_stack}[$map->{undo_stack_pos}++]); +} + +sub delete { + my ($self) = @_; + + # check and modla dialog if "dirty" - push @$as, $arch - unless @$as && $as->[-1]->{_name} eq $arch->{_name}; + if ($self->{mapkey}) { + # XXX: This should be in a delete event handler in the MainWindow.pm... but it doesnt work + delete $::MAINWIN->{loaded_maps}->{$self->{mapkey}}; + } - $self->{map}->set ($x, $y, $as); + $self->destroy; } sub open_map { - my ($self, $path) = @_; + my ($self, $path, $key) = @_; + + $self->{mapkey} = $key; + + if (ref $path) { + $self->{map}->set_map ($path); + + } else { + $self->{path} = $path; +# print "OPENMAP $path\n"; + $self->{map}->set_map (my $m = new_from_file Crossfire::Map $path); + require Data::Dumper; +# print "FOO:" .Data::Dumper::Dumper ($m) . "\n"; + } +} + +sub save_map { + my ($self) = @_; + + if ($self->{path}) { + $self->{map}{map}->write_file ($self->{path}); + quick_msg ($self, "saved to $self->{path}"); + } else { + $self->save_map_as; + } +} + +sub save_map_as { + my ($self) = @_; + + my $fc = $::MAINWIN->new_filechooser ('gce - save map', 1, $self->{path}); + + if ('ok' eq $fc->run) { + + $::MAINWIN->{fc_last_folder} = $fc->get_current_folder; + $::MAINWIN->{fc_last_folders}->{$self->{fc_last_folder}}++; + + $self->{map}{map}->write_file ($self->{path} = $fc->get_filename); + quick_msg ($self, "saved to $self->{path}"); + } + + $fc->destroy; +} + +sub _add_prop_entry { + my ($self, $table, $idx, $key, $desc, $type, $changecb) = @_; + + my $edwid; + + if ($type eq 'string') { + $table->attach_defaults (my $lbl = Gtk2::Label->new ($desc), 0, 1, $idx, $idx + 1); + $edwid = Gtk2::Entry->new; + $edwid->set_text ($self->{map}{map}{info}{$key}); + $edwid->signal_connect (changed => sub { + $self->{map}{map}{info}{$key} = $_[0]->get_text; + if ($changecb) { + $changecb->($_[0]->get_text); + } + }); + $table->attach_defaults ($edwid, 1, 2, $idx, $idx + 1); + + } elsif ($type eq 'button') { + $table->attach_defaults (my $b = Gtk2::Button->new_with_label ($desc), 0, 2, $idx, $idx + 1); + $b->signal_connect (clicked => ($changecb || sub {})); + + } elsif ($type eq 'label') { + $table->attach_defaults (my $lbl = Gtk2::Label->new ($desc), 0, 1, $idx, $idx + 1); + $edwid = Gtk2::Label->new ($self->{map}{map}{info}{$key}); + $table->attach_defaults ($edwid, 1, 2, $idx, $idx + 1); + + } elsif ($type eq 'check') { + $table->attach_defaults (my $lbl1 = Gtk2::Label->new ($desc), 0, 1, $idx, $idx + 1); + $table->attach_defaults (my $lbl = Gtk2::CheckButton->new, 1, 2, $idx, $idx + 1); + $lbl->set_active ($self->{map}{map}{info}{$key}); + $lbl->signal_connect (toggled => sub { + my ($lbl) = @_; + $self->{map}{map}{info}{$key} = $lbl->get_active * 1; + ($changecb || sub {})->($lbl->get_active); + }); + + } elsif ($type eq 'sep') { + $table->attach_defaults (my $lbl1 = Gtk2::HSeparator->new, 0, 2, $idx, $idx + 1); + } else { + $edwid = Gtk2::Label->new ("FOO"); + } +} + +sub open_resize_map { + my ($self) = @_; + + my $w = Gtk2::Window->new ('toplevel'); + $w->set_default_size (250, 150); + $w->add (my $sw = Gtk2::ScrolledWindow->new); + $sw->add_with_viewport (my $v = Gtk2::VBox->new); + $sw->set_policy ('automatic', 'automatic'); + $v->pack_start (my $t = Gtk2::Table->new (2, 10), 0, 0, 0); + + my $i = 0; + for ( + [qw/width Width string/], + [qw/height Height string/], + [qw/save Save button/, + sub { + $self->{map}{map}->resize ($self->{map}{map}{info}{width}, $self->{map}{map}{info}{height}); + $self->{map}->invalidate_all; + $w->destroy; + } + ], + ) + { + $self->_add_prop_entry ($t, $i++, @$_); + } + + $w->show_all; +} + +sub follow { + my ($self, $dir) = @_; + + my %dir_to_path = ( + u => 'tile_path_1', + d => 'tile_path_3', + r => 'tile_path_2', + l => 'tile_path_4', + ); + + defined $dir_to_path{$dir} + or return; + my $map = $self->{map}{map}{info}{$dir_to_path{$dir}} + or return; + + $map = map2abs ($map, $self); + $::MAINWIN->open_map_editor ($map); +} + +sub open_map_prop { + my ($self) = @_; + + + my $w = Gtk2::Window->new ('toplevel'); + $w->set_default_size (500, 500); + $w->add (my $sw = Gtk2::ScrolledWindow->new); + $sw->add_with_viewport (my $v = Gtk2::VBox->new); + $sw->set_policy ('automatic', 'automatic'); + $v->pack_start (my $t = Gtk2::Table->new (2, 10), 0, 0, 0); + + my $i = 0; + for ( + [qw/name Name string/], + [qw/region Region string/], + [qw/enter_x Enter-x string/], + [qw/enter_y Enter-y string/], + [qw/reset_timeout Reset-timeout string/], + [qw/swap_time Swap-timeout string/], + [qw/x x sep/], + [qw/difficulty Difficulty string/], + [qw/windspeed Windspeed string/], + [qw/pressure Pressure string/], + [qw/humid Humid string/], + [qw/temp Temp string/], + [qw/darkness Darkness string/], + [qw/sky Sky string/], + [qw/winddir Winddir string/], + [qw/x x sep/], + [qw/width Width label/], # sub { $self->{map}{map}->resize ($_[0], $self->{map}{map}{height}) }], + [qw/height Height label/], # sub { $self->{map}{map}->resize ($self->{map}{map}{width}, $_[0]) }], + [qw/x x sep/], + [qw/msg Text text/], + [qw/maplore Maplore text/], + [qw/outdoor Outdoor check/], + [qw/unique Unique check/], + [qw/fixed_resettime Fixed-resettime check/], + [qw/x x sep/], + [qw/tile_path_1 Northpath string/], + [qw/tile_path_2 Eastpath string/], + [qw/tile_path_3 Southpath string/], + [qw/tile_path_4 Westpath string/], + [qw/tile_path_5 Toppath string/], + [qw/tile_path_6 Bottompath string/], + ) + { + $self->_add_prop_entry ($t, $i++, @$_); + } - $self->{map}->set_map (new_from_file Crossfire::Map $path); + $w->show_all; } =head1 AUTHOR