package GCE::MapEditor; =head1 NAME GCE::MapEditor - the map editing widget =cut use Gtk2; use Gtk2::Gdk::Keysyms; use Gtk2::SimpleMenu; use Crossfire; use Crossfire::Map; use Crossfire::MapWidget; use GCE::AttrEdit; 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" }, ] }, ]; my $men = Gtk2::SimpleMenu->new ( menu_tree => $menu_tree, default_callback => \&default_cb, ); $men->{accel_group}->connect ($Gtk2::Gdk::Keysyms{'i'}, [], 'visible', sub { $::MAINWIN->set_edit_tool ('pick') }); $men->{accel_group}->connect ($Gtk2::Gdk::Keysyms{'p'}, [], 'visible', sub { $::MAINWIN->set_edit_tool ('place') }); $men->{accel_group}->connect ($Gtk2::Gdk::Keysyms{'e'}, [], 'visible', sub { $::MAINWIN->set_edit_tool ('erase') }); $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); $self->signal_connect (delete_event => sub { $self->delete }); $vb->pack_start (my $menu = $self->build_menu, 0, 1, 0); $vb->pack_start (my $map = $self->{map} = Crossfire::MapWidget->new, 1, 1, 0); $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')); } }); $map->signal_connect (key_press_event => sub { my ($map, $event) = @_; my $kv = $event->keyval; if ($kv == $Gtk2::Gdk::Keysyms{Control_L}) { $self->{ea_alt} = $::MAINWIN->{edit_collection}{erase}; } elsif ($kv == $Gtk2::Gdk::Keysyms{Alt_L}) { $self->{ea_alt} = $::MAINWIN->{edit_collection}{pick}; } 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) = @_; if ($event->keyval == $Gtk2::Gdk::Keysyms{Control_L} or $event->keyval == $Gtk2::Gdk::Keysyms{Alt_L}) { delete $self->{ea_alt}; } 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) = @_; my ($x, $y) = $map->coord ($event->x, $event->y); my $as = $map->get ($x, $y); if ((not $self->{draw_mode}) and $event->button != 2) { my $ea = $self->ea; $ea->begin ($map, $x, $y) if $x >= 0 and $y >= 0 and $x < $map->{map}{width} and $y < $map->{map}{height}; $self->{draw_mode} = [$x, $y]; $ea->want_cursor or $map->disable_tooltip; return 1; } 0 }); $map->signal_connect_after (motion_notify_event => sub { my ($map, $event) = @_; $self->{draw_mode} or return; my $ea = $self->ea; my ($X, $Y) = @{$self->{draw_mode}}[0,1]; my ($x, $y) = $map->coord ($map->get_pointer); while ($x != $X || $y != $Y) { $X++ if $X < $x; $X-- if $X > $x; $Y++ if $Y < $y; $Y-- if $Y > $y; $ea->edit ($map, $X, $Y) if $X >= 0 and $Y >= 0 and $X < $map->{map}{width} and $Y < $map->{map}{height}; } @{$self->{draw_mode}}[0,1] = ($X, $Y); 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); delete $self->{draw_mode}; $ea->want_cursor or $map->enable_tooltip; return 1; } 0 }); } # 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 $map->{undo_stack_pos} or return; $map->change_swap ($map->{undo_stack}[--$map->{undo_stack_pos}]); } sub redo { my ($self) = @_; my $map = $self->{map}; # the Crossfire::MapWidget $map->{undo_stack} and $map->{undo_stack_pos} < @{$map->{undo_stack}} or return; $map->change_swap ($map->{undo_stack}[$map->{undo_stack_pos}++]); } sub delete_arch { my ($self, $x, $y) = @_; defined $self->{map} or return 0; my $as = $self->{map}->get ($x, $y); pop @$as; $self->{map}->set ($x, $y, $as); } sub place_pick { my ($self, $x, $y) = @_; my $pick = $::MAINWIN->get_pick; my $as = $self->{map}->get ($x, $y); my $arch = { _name => $pick->{_name} }; push @$as, $arch unless @$as && $as->[-1]->{_name} eq $arch->{_name}; $self->{map}->set ($x, $y, $as); } sub delete { my ($self) = @_; # check and modla dialog if "dirty" $self->destroy; } sub open_map { my ($self, $path) = @_; 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}); } } sub save_map_as { my ($self) = @_; my $fc = $::MAINWIN->new_filechooser ('gce - save map', 1); 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); } $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); } 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 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/difficulty Difficulty string/], [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/msg Text text/], [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++, @$_); } $w->show_all; } # 'info' => { # 'windspeed' => '10', # 'outdoor' => '1', # 'width' => '20', # 'pressure' => '8', # 'test' => '', # 'tile_path_2' => 'east', # 'tile_path_3' => 'south', # 'enter_y' => '2', # 'tile_path_6' => 'bottom', # 'enter_x' => '1', # 'tile_path_5' => 'top', # 'darkness' => '4', # 'maplore' => '', # 'sky' => '12', # 'winddir' => '11', # 'unique' => '1', # 'msg' => 'Creator: CF Java FUCK Map Editor #Date: 3/12/2006 #', # 'difficulty' => '3', # 'humid' => '9', # 'endmaplore' => '', # 'fixed_resettime' => '1', # 'name' => 'test', # 'region' => 'REGION', # 'height' => '40', # 'reset_timeout' => '6', # '_name' => 'map', # 'swap_time' => '5', # 'temp' => '7', # 'tile_path_4' => 'west', # 'tile_path_1' => 'north' # }, =head1 AUTHOR Marc Lehmann http://home.schmorp.de/ Robin Redeker http://www.ta-sa.org/ =cut 1;