--- deliantra/gde/GCE/MapEditor.pm 2006/03/17 17:59:43 1.27 +++ deliantra/gde/GCE/MapEditor.pm 2007/08/10 12:20:51 1.57 @@ -16,12 +16,87 @@ use GCE::AttrEdit; use GCE::Util; +use GCE::HashDialog; + +use POSIX qw/strftime/; use Glib::Object::Subclass Gtk2::Window; +use Storable qw/dclone/; + use strict; +################################################################# +###### WINDOW MANAGEMENT ######################################## +################################################################# + +sub save_layout { + my ($self) = @_; + + $self->{attach_editor}->save_layout if $self->{attach_editor}; + $self->{map_properties}->save_layout if $self->{map_properties}; + $self->{meta_info_win}->save_layout if $self->{meta_info_win}; + + $main::CFG->{map_info} = main::get_pos_and_size ($self->{map_info}) + if $self->{map_info}; +} + +sub close_windows { + my ($self) = @_; + + $self->{attach_editor}->destroy if $self->{attach_editor}; + $self->{map_properties}->destroy if $self->{map_properties}; + $self->{meta_info_win}->destroy if $self->{meta_info_win}; +} + +################################################################# +###### MENU MANAGEMENT ########################################## +################################################################# + +sub do_context_menu { + my ($self, $map, $event) = @_; + + my ($x, $y) = $map->coord ($event->x, $event->y); + + my $menu = Gtk2::Menu->new; + foreach my $cm ( + [ + Follow => sub { + $::MAINWIN->{edit_collection}{followexit}->edit ($map, $x, $y, $self) + }, + ] + ) { + my $item = Gtk2::MenuItem->new ($cm->[0]); + $menu->append ($item); + $item->show; + $item->signal_connect (activate => $cm->[1]); + } + + $menu->append (my $sep = new Gtk2::SeparatorMenuItem); + $sep->show; + + for my $sr (reverse $self->get_stack_refs ($map, $x, $y)) { + my $item = Gtk2::MenuItem->new ($sr->longname); + $menu->append ($item); + $item->set_submenu (my $smenu = new Gtk2::Menu); + + for my $act ( + [ 'Add inventory' => sub { $_[0]->add_inv ($::MAINWIN->get_pick) } ], + [ 'Find in picker' => sub { $::MAINWIN->open_pick_window ({ selection => $sr->picker_folder }) } ], + ) { + my $sitem = Gtk2::MenuItem->new ($act->[0]); + $smenu->append ($sitem); + $sitem->signal_connect (activate => sub { $act->[1]->($sr) }); + $sitem->show; + } + + $item->show; + } + + $menu->popup (undef, undef, undef, undef, $event->button, $event->time); +} + sub build_menu { my ($self) = @_; @@ -36,8 +111,37 @@ "Save As" => { callback => sub { $self->save_map_as }, }, + "Map _Info" => { + callback => sub { $self->open_map_info }, + accelerator => "I", + }, + "Map _Properties" => { + callback => sub { $self->open_map_prop }, + accelerator => "P" + }, + "Map _Attachments" => { + callback => sub { $self->open_attach_edit }, + accelerator => "A" + }, + "Map Meta _Info" => { + callback => sub { $self->open_meta_info }, + }, + Upload => { + item_type => '', + children => [ + "Upload for testing" => { + callback => sub { $self->upload_map_test }, + }, + "Upload for inclusion" => { + callback => sub { $self->upload_map_incl }, + }, + ] + }, + "_Map Resize" => { + callback => sub { $self->open_resize_map }, + }, "Close" => { - callback => sub { $self->delete; 1 }, + callback => sub { $self->destroy }, }, ] }, @@ -52,14 +156,6 @@ 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 => { @@ -67,23 +163,31 @@ children => [ "_Up" => { callback => sub { $self->follow ('u') }, - accelerator => "U" + accelerator => "Up" }, "_Down" => { callback => sub { $self->follow ('d') }, - accelerator => "D" + accelerator => "Down" }, "_Right" => { callback => sub { $self->follow ('r') }, - accelerator => "R" + accelerator => "Right" }, "_Left" => { callback => sub { $self->follow ('l') }, - accelerator => "L" + accelerator => "Left" + }, + ] + }, + _Help => { + item_type => '', + children => [ + _Manual => { + callback => sub { $::MAINWIN->show_help_window }, + accelerator => "H" }, ] }, - ]; my $men = @@ -98,7 +202,7 @@ [e => 'erase'], [s => 'select'], [l => 'eval'], - [x => 'connectexit'], + [t => 'connect'], [f => 'followexit'] ) { @@ -107,147 +211,88 @@ sub { $::MAINWIN->set_edit_tool ($tool) }); } + $men->{accel_group}->connect ($Gtk2::Gdk::Keysyms{'r'}, ['control-mask'], 'visible', + sub { $self->redo }); + $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; 1 }); - - $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')); - } - delete $self->{ea_alt}; - }); - - $map->signal_connect (key_press_event => sub { - my ($map, $event) = @_; - - my $kv = $event->keyval; - - 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]->(); - } - } - - 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); +################################################################# +###### EDIT TOOL STUFF ########################################## +################################################################# - if ((not $self->{draw_mode}) and $event->button != 2) { +sub set_edit_tool { + my ($self, $tool) = @_; - my $ea = $self->ea; + $self->{etool} = $tool; - $ea->begin ($map, $x, $y, $self) - if $x >= 0 and $y >= 0 and $x < $map->{map}{width} and $y < $map->{map}{height}; + if ($self->ea->special_arrow) { + $self->{map}{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow)); + } else { + # FIXME: Get the original cursor and insert it here + $self->{map}{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR')); + } +} - $self->{draw_mode} = [$x, $y]; +sub ea { + my ($self) = @_; + $self->{ea_alt} || $self->{etool}; +} - $ea->want_cursor - or $map->disable_tooltip; +sub start_drawmode { + my ($self, $map) = @_; - return 1; - } - 0 - }); + $self->{draw_mode} and return; - $map->signal_connect_after (motion_notify_event => sub { - my ($map, $event) = @_; + # XXX: is this okay? my ($x, $y) = $map->coord ($event->x, $event->y); + my ($x, $y) = $map->coord ($map->get_pointer); - $self->{draw_mode} - or return; - - my $ea = $self->ea; + my $ea = $self->ea; - my ($X, $Y) = @{$self->{draw_mode}}[0,1]; - my ($x, $y) = $map->coord ($map->get_pointer); + $ea->begin ($map, $x, $y, $self); - while ($x != $X || $y != $Y) { + $ea->edit ($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]; +} - $ea->edit ($map, $X, $Y, $self) - if $X >= 0 and $Y >= 0 and $X < $map->{map}{width} and $Y < $map->{map}{height}; - } +sub stop_drawmode { + my ($self, $map) = @_; - @{$self->{draw_mode}}[0,1] = ($X, $Y); + $self->{draw_mode} or return; - 1 - }); + my ($x, $y) = $map->coord ($map->get_pointer); - $map->signal_connect (button_release_event => sub { - my ($map, $event) = @_; + my $ea = $self->ea; + $ea->end ($map, $x, $y, $self); - if ($self->{draw_mode}) { - my ($x, $y) = $map->coord ($map->get_pointer); + delete $self->{draw_mode}; +} - my $ea = $self->ea; - $ea->end ($map, $x, $y, $self); +################################################################# +###### UTILITY FUNCTIONS ######################################## +################################################################# - delete $self->{draw_mode}; +sub follow { + my ($self, $dir) = @_; - $ea->want_cursor - or $map->enable_tooltip; + my %dir_to_path = ( + u => 'tile_path_1', + d => 'tile_path_3', + r => 'tile_path_2', + l => 'tile_path_4', + ); - return 1; - } + defined $dir_to_path{$dir} + or return; + my $map = $self->{map}{map}{info}{$dir_to_path{$dir}} + or return; - 0 - }); + $map = map2abs ($map, $self); + $::MAINWIN->open_map_editor ($map); } # FIXME: Fix the automatic update of the attribute editor! and also the stack view! @@ -262,6 +307,45 @@ $map->change_swap ($map->{undo_stack}[--$map->{undo_stack_pos}]); } +sub get_stack_refs { + my ($self, $map, $x, $y) = @_; + + my $cstack = $map->get ($x, $y); + + return [] unless @$cstack; + + my @refs; + + for my $arch (@$cstack) { + my ($ox, $oy) = ($x, $y); + if ($arch->{_virtual}) { + $ox = $arch->{virtual_x}; + $oy = $arch->{virtual_y}; + $arch = $arch->{_virtual}; + $cstack = $map->get ($ox, $oy); + # XXX: This heavily blows up if $arch isn't on $cstack now.. and it actually really does :( + } + + push @refs, + GCE::ArchRef->new ( + arch => $arch, + source => 'map', + cb => sub { + $map->change_begin ('attredit'); + $map->change_stack ($ox, $oy, $cstack); + + if (my $changeset = $map->change_end) { + splice @{ $map->{undo_stack} ||= [] }, + $map->{undo_stack_pos}++, 1e6, + $changeset; + } + } + ); + } + + return @refs; +} + sub redo { my ($self) = @_; @@ -274,17 +358,21 @@ $map->change_swap ($map->{undo_stack}[$map->{undo_stack_pos}++]); } -sub delete { - my ($self) = @_; - - # check and modla dialog if "dirty" - - 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}}; +sub load_meta_info { + my ($mapfile) = @_; + if (-e "$mapfile.meta") { + open my $metafh, "<", "$mapfile.meta" + or warn "Couldn't open meta file $mapfile.meta: $!"; + my $metadata = do { local $/; <$metafh> }; + return Crossfire::from_json ($metadata); } +} - $self->destroy; +sub save_meta_info { + my ($mapfile, $metainfo) = @_; + open my $metafh, ">", "$mapfile.meta" + or warn "Couldn't write meta file $mapfile.meta: $!"; + print $metafh Crossfire::to_json ($metainfo); } sub open_map { @@ -294,14 +382,30 @@ if (ref $path) { $self->{map}->set_map ($path); + delete $self->{meta_info}; + $self->set_title (''); } else { + my $ok = 0; + if (-e $path && -f $path) { + $ok = 1; + } else { + unless ($path =~ m/\.map$/) { # yuck + my $p = $path . '.map'; + if ($ok = -e $p && -f $p) { + $path = $p; + } + } + } + unless ($ok) { + die "Couldn't open '$path' or find '$path.map': No such file or it is not a file.\n"; + } $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"; + $self->{meta_info} = load_meta_info ($path); + $self->set_title ("gce - map editor - $self->{path}"); } + $self->close_windows; } sub save_map { @@ -309,7 +413,11 @@ if ($self->{path}) { $self->{map}{map}->write_file ($self->{path}); + if ($self->{meta_info}) { + save_meta_info ($self->{path}, $self->{meta_info}); + } quick_msg ($self, "saved to $self->{path}"); + $self->set_title ("gce - map editor - $self->{path}"); } else { $self->save_map_as; } @@ -326,123 +434,225 @@ $::MAINWIN->{fc_last_folders}->{$self->{fc_last_folder}}++; $self->{map}{map}->write_file ($self->{path} = $fc->get_filename); + if ($self->{meta_info}) { + save_meta_info ($self->{path}, $self->{meta_info}); + } quick_msg ($self, "saved to $self->{path}"); + $self->set_title ("gce - map editor - $self->{path}"); } $fc->destroy; } -sub _add_prop_entry { - my ($self, $table, $idx, $key, $desc, $type, $changecb) = @_; +################################################################# +###### DIALOGOUES ############################################### +################################################################# + +sub open_resize_map { + my ($self) = @_; + + return if $self->{meta_info_win}; - my $edwid; + my $w = $self->{meta_info_win} = GCE::HashDialogue->new (); - 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); + $w->init ( + dialog_default_size => [500, 200, 220, 20], + layout_name => 'resize_win', + title => 'resize map', + ref_hash => $self->{map}{map}{info}, + dialog => [ + [width => 'Width' => 'string'], + [height => 'Height' => 'string'], + ], + close_on_save => 1, + save_cb => sub { + my ($info) = @_; + $self->{map}{map}->resize ($info->{width}, $info->{height}); + $self->{map}->invalidate_all; + } + ); - } 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); - }); + $w->signal_connect (destroy => sub { delete $self->{meta_info_win} }); - } elsif ($type eq 'sep') { - $table->attach_defaults (my $lbl1 = Gtk2::HSeparator->new, 0, 2, $idx, $idx + 1); - } else { - $edwid = Gtk2::Label->new ("FOO"); - } + $w->show_all; } -sub open_resize_map { +sub open_attach_edit { 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 $w = GCE::AttachEditor->new; + $w->set_attachment ( + $self->{map}{map}{info}{attach}, + sub { + if (@{$_[0]}) { + $self->{map}{map}{info}{attach} = $_[0] + } else { + delete $self->{map}{map}{info}{attach}; + } + } + ); + $self->{attach_editor} = $w; + $w->signal_connect (destroy => sub { delete $self->{attach_editor} }); + $w->show_all; +} - 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++, @$_); - } +sub upload_map_incl { + my ($self) = @_; + + my $meta = dclone $self->{meta_info}; + + my $w = $self->{meta_info_win} = GCE::HashDialogue->new (); + + $w->init ( + dialog_default_size => [500, 300, 220, 20], + layout_name => 'map_upload_incl', + title => 'gce - map inclusion upload', + ref_hash => $meta, + text_entry => { key => 'changes', label => 'Changes (required for inclusion):' }, + dialog => [ + [gameserver => 'Game server' => 'label'], + [testserver => 'Test server' => 'label'], + [undef => x => 'sep' ], + [cf_login => 'Server login name' => 'string'], + [cf_password=> 'Password' => 'password'], + [path => 'Map path' => 'string'], + ], + close_on_save => 1, + save_cb => sub { + my ($meta) = @_; + warn "UPLOAD[".Crossfire::to_json ($meta)."]\n"; + } + ); + + $w->signal_connect (destroy => sub { delete $self->{meta_info_win} }); $w->show_all; } -sub follow { - my ($self, $dir) = @_; +sub upload_map_test { + my ($self) = @_; - my %dir_to_path = ( - u => 'tile_path_1', - d => 'tile_path_3', - r => 'tile_path_2', - l => 'tile_path_4', + my $meta = dclone $self->{meta_info}; + + my $w = $self->{meta_info_win} = GCE::HashDialogue->new (); + + $w->init ( + dialog_default_size => [500, 300, 220, 20], + layout_name => 'map_upload_test', + title => 'gce - map test upload', + ref_hash => $meta, + dialog => [ + [gameserver => 'Game server' => 'string'], + [testserver => 'Test server' => 'string'], + [undef => x => 'sep' ], + [cf_login => 'Server login name' => 'string'], + [cf_password=> 'Password' => 'password'], + [path => 'Map path' => 'string'], + ], + save_cb => sub { + my ($meta) = @_; + warn "UPLOAD[".Crossfire::to_json ($meta)."]\n"; + } ); - defined $dir_to_path{$dir} - or return; - my $map = $self->{map}{map}{info}{$dir_to_path{$dir}} - or return; + $w->signal_connect (destroy => sub { delete $self->{meta_info_win} }); + + $w->show_all; + - $map = map2abs ($map, $self); - $::MAINWIN->open_map_editor ($map); } -sub open_map_prop { +sub open_meta_info { my ($self) = @_; + return if $self->{meta_info_win}; + + my $w = $self->{meta_info_win} = GCE::HashDialogue->new (); + + $w->init ( + dialog_default_size => [500, 300, 220, 20], + layout_name => 'meta_info_win', + title => 'meta info', + ref_hash => $self->{meta_info}, + dialog => [ + [path => 'Map path' => 'string'], + [cf_login => 'Login name' => 'string'], + [revision => 'CVS Revision' => 'label'], + [cvs_root => 'CVS Root' => 'label'], + [lib_root => 'LIB Root' => 'label'], + [testserver => 'Test server' => 'label'], + [gameserver => 'Game server' => 'label'], + ], + ); + + $w->signal_connect (destroy => sub { delete $self->{meta_info_win} }); + + $w->show_all; +} + +sub open_map_info { + my ($self) = @_; + return if $self->{map_info}; - 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); + my $w = $self->{map_info} = Gtk2::Window->new ('toplevel'); + $w->set_title ("gcrossedit - map info"); + + $w->add (my $vb = Gtk2::VBox->new); + $vb->add (my $sw = Gtk2::ScrolledWindow->new); $sw->set_policy ('automatic', 'automatic'); - $v->pack_start (my $t = Gtk2::Table->new (2, 10), 0, 0, 0); + $sw->add (my $txt = Gtk2::TextView->new); + $vb->pack_start (my $hb = Gtk2::HBox->new (1, 1), 0, 1, 0); + $hb->pack_start (my $svbtn = Gtk2::Button->new ("save"), 1, 1, 0); + $hb->pack_start (my $logbtn = Gtk2::Button->new ("add log"), 1, 1, 0); + $hb->pack_start (my $closebtn = Gtk2::Button->new ("close"), 1, 1, 0); + + my $buf = $txt->get_buffer (); + $buf->set_text ($self->{map}{map}{info}{msg}); + + $svbtn->signal_connect (clicked => sub { + my $buf = $txt->get_buffer (); + my $txt = $buf->get_text ($buf->get_start_iter, $buf->get_end_iter, 0); + $self->{map}{map}{info}{msg} = $txt; + }); - my $i = 0; - for ( + $logbtn->signal_connect (clicked => sub { + my $buf = $txt->get_buffer (); + $buf->insert ($buf->get_start_iter, "- " . strftime ("%F %T %Z", localtime (time)) . " by " . ($main::CFG->{username} || $ENV{USER}) . ":\n"); + $txt->set_buffer ($buf); + }); + + $closebtn->signal_connect (clicked => sub { + $w->destroy; + }); + + ::set_pos_and_size ($w, $main::CFG->{map_info}, 400, 400, 220, 20); + $w->signal_connect (destroy => sub { + delete $self->{map_info}; + }); + $w->show_all; +} + +sub open_map_prop { + my ($self) = @_; + + return if $self->{map_properties}; + + my $w = $self->{map_properties} = GCE::HashDialogue->new (); + + $w->init ( + dialog_default_size => [500, 500, 220, 20], + layout_name => 'map_prop_win', + title => 'map properties', + ref_hash => $self->{map}{map}{info}, + close_on_save => 1, + dialog => [ [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/], + [undef, qw/x sep/], [qw/difficulty Difficulty string/], [qw/windspeed Windspeed string/], [qw/pressure Pressure string/], @@ -451,30 +661,194 @@ [qw/darkness Darkness string/], [qw/sky Sky string/], [qw/winddir Winddir string/], - [qw/x x sep/], + [undef, qw/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/], + [undef, qw/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/], + [per_player => 'Per player' => 'check'], + [per_party => 'Per party' => 'check'], + [no_reset => 'No reset' => 'check'], + [music => 'Map Music' => 'string'], + [undef, qw/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++, @$_); - } + [undef, qw/x sep/], + [undef, 'For shop description look in the manual', + 'button', sub { $::MAINWIN->show_help_window }], + [qw/shopmin Shopmin string/], + [qw/shopmax Shopmax string/], + [qw/shoprace Shoprace string/], + [qw/shopgreed Shopgreed string/], + [qw/shopitems Shopitems string/], + ] + ); + $w->signal_connect (destroy => sub { delete $self->{map_properties} }); $w->show_all; } +################################################################# +###### MAP EDITOR INIT ########################################## +################################################################# + +sub INIT_INSTANCE { + my ($self) = @_; + + $self->set_title ('gce - map editor'); + $self->add (my $vb = Gtk2::VBox->new); + + $vb->pack_start (my $menu = $self->build_menu, 0, 1, 0); + + $vb->pack_start (my $map = $self->{map} = Crossfire::MapWidget->new, 1, 1, 0); + + $map->signal_connect_after (key_press_event => sub { + my ($map, $event) = @_; + + my $kv = $event->keyval; + + my $ret = 0; + + 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 ($map, $x, $y) }], + [n => sub { $::MAINWIN->{edit_collection}{select}->invoke }], + ) + { + my $ed = $_; + + if ($kv == $Gtk2::Gdk::Keysyms{$ed->[0]}) { + my $was_in_draw = defined $self->{draw_mode}; + + $self->stop_drawmode ($map) + if $was_in_draw && grep { $ed->[0] eq $_ } qw/Control_L Alt_L/; + + $ed->[1]->(); + $ret = 1; + + $self->start_drawmode ($map) + if $was_in_draw && grep { $ed->[0] eq $_ } qw/Control_L Alt_L/; + } + } + + if ($self->ea->special_arrow) { + $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow)); + } else { + # FIXME: Get the original cursor and insert it here + $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR')); + } + + $ret + }); + + $map->signal_connect_after (key_release_event => sub { + my ($map, $event) = @_; + + my $ret = 0; + + if ($event->keyval == $Gtk2::Gdk::Keysyms{Control_L} + or $event->keyval == $Gtk2::Gdk::Keysyms{Alt_L}) + { + my $was_in_draw = defined $self->{draw_mode}; + + $self->stop_drawmode ($map) + if $was_in_draw; + + delete $self->{ea_alt}; + $ret = 1; + + $self->start_drawmode ($map) + if $was_in_draw; + } + + if ($self->ea->special_arrow) { + $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow)); + } else { + # FIXME: Get the original cursor and insert it here + $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR')); + } + + $ret + }); + $map->signal_connect_after (button_press_event => sub { + my ($map, $event) = @_; + + if ((not $self->{draw_mode}) and $event->button == 1) { + my $ea = $self->ea; + + $self->start_drawmode ($map); + + $ea->want_cursor + or $map->disable_tooltip; + + return 1; + } elsif ($event->button == 3) { + $self->do_context_menu ($map, $event); + 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; + + unless ($ea->only_on_click) { + $ea->edit ($map, $X, $Y, $self) + 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_after (button_release_event => sub { + my ($map, $event) = @_; + + if ($self->{draw_mode} and $event->button == 1) { + my $ea = $self->ea; + + $self->stop_drawmode ($map); + + $ea->want_cursor + or $map->enable_tooltip; + + return 1; + } + + 0 + }); + + ::set_pos_and_size ($self, $main::CFG->{map_window}, 500, 500, 200, 0); +} + =head1 AUTHOR Marc Lehmann @@ -484,5 +858,6 @@ http://www.ta-sa.org/ =cut -1; + +1