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.7 by elmex, Mon Feb 20 18:21:04 2006 UTC vs.
Revision 1.27 by elmex, Fri Mar 17 17:59:43 2006 UTC

13use Crossfire; 13use Crossfire;
14use Crossfire::Map; 14use Crossfire::Map;
15use Crossfire::MapWidget; 15use Crossfire::MapWidget;
16 16
17use GCE::AttrEdit; 17use GCE::AttrEdit;
18use GCE::Util;
18 19
19use Glib::Object::Subclass 20use Glib::Object::Subclass
20 Gtk2::Window; 21 Gtk2::Window;
21 22
22use strict; 23use strict;
23 24
25sub build_menu {
26 my ($self) = @_;
27
28 my $menu_tree = [
29 _File => {
30 item_type => '<Branch>',
31 children => [
32 "_Save" => {
33 callback => sub { $self->save_map },
34 accelerator => '<ctrl>S'
35 },
36 "Save As" => {
37 callback => sub { $self->save_map_as },
38 },
39 "Close" => {
40 callback => sub { $self->delete; 1 },
41 },
42 ]
43 },
44 _Edit => {
45 item_type => '<Branch>',
46 children => [
47 "_Undo" => {
48 callback => sub { $self->undo },
49 accelerator => "<ctrl>Z"
50 },
51 "_Redo" => {
52 callback => sub { $self->redo },
53 accelerator => "<ctrl>Y"
54 },
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 ]
64 },
65 _Go => {
66 item_type => '<Branch>',
67 children => [
68 "_Up" => {
69 callback => sub { $self->follow ('u') },
70 accelerator => "<ctrl>U"
71 },
72 "_Down" => {
73 callback => sub { $self->follow ('d') },
74 accelerator => "<ctrl>D"
75 },
76 "_Right" => {
77 callback => sub { $self->follow ('r') },
78 accelerator => "<ctrl>R"
79 },
80 "_Left" => {
81 callback => sub { $self->follow ('l') },
82 accelerator => "<ctrl>L"
83 },
84 ]
85 },
86
87 ];
88
89 my $men =
90 Gtk2::SimpleMenu->new (
91 menu_tree => $menu_tree,
92 default_callback => \&default_cb,
93 );
94
95 for (
96 [i => 'pick'],
97 [p => 'place'],
98 [e => 'erase'],
99 [s => 'select'],
100 [l => 'eval'],
101 [x => 'connectexit'],
102 [f => 'followexit']
103 )
104 {
105 my $tool = $_->[1];
106 $men->{accel_group}->connect ($Gtk2::Gdk::Keysyms{$_->[0]}, [], 'visible',
107 sub { $::MAINWIN->set_edit_tool ($tool) });
108 }
109
110 $self->add_accel_group ($men->{accel_group});
111
112 return $men->{widget};
113}
114
115sub ea {
116 my ($self) = @_;
117 $self->{ea_alt} || $::MAINWIN->{sel_editaction};
118}
119
24sub INIT_INSTANCE { 120sub INIT_INSTANCE {
25 my ($self) = @_; 121 my ($self) = @_;
26 122
27 $self->set_title ('gce - map editor'); 123 $self->set_title ('gce - map editor');
28 $self->add (my $vb = Gtk2::VBox->new); 124 $self->add (my $vb = Gtk2::VBox->new);
125
126 $self->signal_connect (delete_event => sub { $self->delete; 1 });
127
128 $vb->pack_start (my $menu = $self->build_menu, 0, 1, 0);
129
29 $vb->pack_start (my $map = $self->{map} = Crossfire::MapWidget->new, 1, 1, 0); 130 $vb->pack_start (my $map = $self->{map} = Crossfire::MapWidget->new, 1, 1, 0);
131
132 $self->signal_connect (focus_in_event => sub {
133 my $ea = $::MAINWIN->{sel_editaction};
134 if ($ea->special_arrow) {
135 $self->{map}->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($ea->special_arrow));
136 } else {
137 $self->{map}->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR'));
138 }
139 delete $self->{ea_alt};
140 });
141
142 $map->signal_connect (key_press_event => sub {
143 my ($map, $event) = @_;
144
145 my $kv = $event->keyval;
146
147 my ($x, $y) = $map->coord ($map->get_pointer);
148 for ([Control_L => sub { $self->{ea_alt} = $::MAINWIN->{edit_collection}{erase} }],
149 [Alt_L => sub { $self->{ea_alt} = $::MAINWIN->{edit_collection}{pick} }],
150 [c => sub { $::MAINWIN->{edit_collection}{select}->copy }],
151 [v => sub { $::MAINWIN->{edit_collection}{select}->paste ($x, $y) }],
152 [n => sub { $::MAINWIN->{edit_collection}{select}->invoke }],
153 )
154 {
155 if ($kv == $Gtk2::Gdk::Keysyms{$_->[0]}) {
156 $_->[1]->();
157 }
158 }
159
160 if ($self->ea->special_arrow) {
161 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow));
162 }
163 1;
164 });
165 $map->signal_connect (key_release_event => sub {
166 my ($map, $event) = @_;
167
168 if ($event->keyval == $Gtk2::Gdk::Keysyms{Control_L}
169 or $event->keyval == $Gtk2::Gdk::Keysyms{Alt_L})
170 {
171 delete $self->{ea_alt};
172 }
173
174 if ($self->ea->special_arrow) {
175 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow));
176 } else {
177 # XXX: Get the original cursor and insert it here
178 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR'));
179 }
180 1;
181 });
30 $map->signal_connect (button_press_event => sub { 182 $map->signal_connect (button_press_event => sub {
31 my ($map, $event) = @_; 183 my ($map, $event) = @_;
32 184
33 my ($x, $y) = $map->coord ($event->x, $event->y); 185 my ($x, $y) = $map->coord ($event->x, $event->y);
34 my $as = $map->get ($x, $y); 186 my $as = $map->get ($x, $y);
35 187
36 my $btn = $event->button; 188 if ((not $self->{draw_mode}) and $event->button != 2) {
37 189
38 if ((not $self->{draw_mode}) 190 my $ea = $self->ea;
39 and $btn != 2
40 and my $ea = $GCE::MainWindow::MAINWIN->{sel_editaction}) {
41 191
42 $ea->begin ($map, $x, $y, $btn); 192 $ea->begin ($map, $x, $y, $self)
193 if $x >= 0 and $y >= 0 and $x < $map->{map}{width} and $y < $map->{map}{height};
43 194
44 $self->{draw_mode} = [$ea, $x, $y, $btn]; 195 $self->{draw_mode} = [$x, $y];
45 196
46 $ea->want_cursor 197 $ea->want_cursor
47 or $map->disable_tooltip; 198 or $map->disable_tooltip;
48 199
49 if ($ea->special_arrow) {
50
51 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($ea->special_arrow));
52 #'GDK_QUESTION_ARROW'));
53 }
54
55 return 1; 200 return 1;
201 }
202 0
203 });
204
205 $map->signal_connect_after (motion_notify_event => sub {
206 my ($map, $event) = @_;
207
208 $self->{draw_mode}
209 or return;
210
211 my $ea = $self->ea;
212
213 my ($X, $Y) = @{$self->{draw_mode}}[0,1];
214 my ($x, $y) = $map->coord ($map->get_pointer);
215
216 while ($x != $X || $y != $Y) {
217
218 $X++ if $X < $x;
219 $X-- if $X > $x;
220 $Y++ if $Y < $y;
221 $Y-- if $Y > $y;
222
223 $ea->edit ($map, $X, $Y, $self)
224 if $X >= 0 and $Y >= 0 and $X < $map->{map}{width} and $Y < $map->{map}{height};
225 }
226
227 @{$self->{draw_mode}}[0,1] = ($X, $Y);
228
229 1
230 });
231
232 $map->signal_connect (button_release_event => sub {
233 my ($map, $event) = @_;
234
235 if ($self->{draw_mode}) {
236 my ($x, $y) = $map->coord ($map->get_pointer);
237
238 my $ea = $self->ea;
239 $ea->end ($map, $x, $y, $self);
240
241 delete $self->{draw_mode};
242
243 $ea->want_cursor
244 or $map->enable_tooltip;
245
246 return 1;
247 }
248
249 0
250 });
251}
252
253# FIXME: Fix the automatic update of the attribute editor! and also the stack view!
254sub undo {
255 my ($self) = @_;
256
257 my $map = $self->{map}; # the Crossfire::MapWidget
258
259 $map->{undo_stack_pos}
260 or return;
261
262 $map->change_swap ($map->{undo_stack}[--$map->{undo_stack_pos}]);
263}
264
265sub redo {
266 my ($self) = @_;
267
268 my $map = $self->{map}; # the Crossfire::MapWidget
269
270 $map->{undo_stack}
271 and $map->{undo_stack_pos} < @{$map->{undo_stack}}
272 or return;
273
274 $map->change_swap ($map->{undo_stack}[$map->{undo_stack_pos}++]);
275}
276
277sub delete {
278 my ($self) = @_;
279
280 # check and modla dialog if "dirty"
281
282 if ($self->{mapkey}) {
283 # XXX: This should be in a delete event handler in the MainWindow.pm... but it doesnt work
284 delete $::MAINWIN->{loaded_maps}->{$self->{mapkey}};
285 }
286
287 $self->destroy;
288}
289
290sub open_map {
291 my ($self, $path, $key) = @_;
292
293 $self->{mapkey} = $key;
294
295 if (ref $path) {
296 $self->{map}->set_map ($path);
297
298 } else {
299 $self->{path} = $path;
300# print "OPENMAP $path\n";
301 $self->{map}->set_map (my $m = new_from_file Crossfire::Map $path);
302 require Data::Dumper;
303# print "FOO:" .Data::Dumper::Dumper ($m) . "\n";
304 }
305}
306
307sub save_map {
308 my ($self) = @_;
309
310 if ($self->{path}) {
311 $self->{map}{map}->write_file ($self->{path});
312 quick_msg ($self, "saved to $self->{path}");
313 } else {
314 $self->save_map_as;
315 }
316}
317
318sub save_map_as {
319 my ($self) = @_;
320
321 my $fc = $::MAINWIN->new_filechooser ('gce - save map', 1, $self->{path});
322
323 if ('ok' eq $fc->run) {
324
325 $::MAINWIN->{fc_last_folder} = $fc->get_current_folder;
326 $::MAINWIN->{fc_last_folders}->{$self->{fc_last_folder}}++;
327
328 $self->{map}{map}->write_file ($self->{path} = $fc->get_filename);
329 quick_msg ($self, "saved to $self->{path}");
330 }
331
332 $fc->destroy;
333}
334
335sub _add_prop_entry {
336 my ($self, $table, $idx, $key, $desc, $type, $changecb) = @_;
337
338 my $edwid;
339
340 if ($type eq 'string') {
341 $table->attach_defaults (my $lbl = Gtk2::Label->new ($desc), 0, 1, $idx, $idx + 1);
342 $edwid = Gtk2::Entry->new;
343 $edwid->set_text ($self->{map}{map}{info}{$key});
344 $edwid->signal_connect (changed => sub {
345 $self->{map}{map}{info}{$key} = $_[0]->get_text;
346 if ($changecb) {
347 $changecb->($_[0]->get_text);
56 } 348 }
57
58 0
59 }); 349 });
350 $table->attach_defaults ($edwid, 1, 2, $idx, $idx + 1);
60 351
61 $map->signal_connect_after (motion_notify_event => sub { 352 } elsif ($type eq 'button') {
62 my ($map, $event) = @_; 353 $table->attach_defaults (my $b = Gtk2::Button->new_with_label ($desc), 0, 2, $idx, $idx + 1);
354 $b->signal_connect (clicked => ($changecb || sub {}));
63 355
64 $self->{draw_mode} 356 } elsif ($type eq 'label') {
65 or return; 357 $table->attach_defaults (my $lbl = Gtk2::Label->new ($desc), 0, 1, $idx, $idx + 1);
358 $edwid = Gtk2::Label->new ($self->{map}{map}{info}{$key});
359 $table->attach_defaults ($edwid, 1, 2, $idx, $idx + 1);
66 360
67 my ($X, $Y) = @{$self->{draw_mode}}[1,2]; 361 } elsif ($type eq 'check') {
68 my ($x, $y) = $map->coord ($map->get_pointer); 362 $table->attach_defaults (my $lbl1 = Gtk2::Label->new ($desc), 0, 1, $idx, $idx + 1);
363 $table->attach_defaults (my $lbl = Gtk2::CheckButton->new, 1, 2, $idx, $idx + 1);
364 $lbl->set_active ($self->{map}{map}{info}{$key});
365 $lbl->signal_connect (toggled => sub {
366 my ($lbl) = @_;
367 $self->{map}{map}{info}{$key} = $lbl->get_active * 1;
368 ($changecb || sub {})->($lbl->get_active);
369 });
69 370
70 while ($x != $X || $y != $Y) { 371 } elsif ($type eq 'sep') {
372 $table->attach_defaults (my $lbl1 = Gtk2::HSeparator->new, 0, 2, $idx, $idx + 1);
373 } else {
374 $edwid = Gtk2::Label->new ("FOO");
375 }
376}
71 377
72 $X++ if $X < $x; 378sub open_resize_map {
73 $X-- if $X > $x; 379 my ($self) = @_;
74 $Y++ if $Y < $y;
75 $Y-- if $Y > $y;
76 380
77 $self->{draw_mode}[0]->edit ($map, $X, $Y, $self->{draw_mode}[3]); 381 my $w = Gtk2::Window->new ('toplevel');
382 $w->set_default_size (250, 150);
383 $w->add (my $sw = Gtk2::ScrolledWindow->new);
384 $sw->add_with_viewport (my $v = Gtk2::VBox->new);
385 $sw->set_policy ('automatic', 'automatic');
386 $v->pack_start (my $t = Gtk2::Table->new (2, 10), 0, 0, 0);
387
388 my $i = 0;
389 for (
390 [qw/width Width string/],
391 [qw/height Height string/],
392 [qw/save Save button/,
393 sub {
394 $self->{map}{map}->resize ($self->{map}{map}{info}{width}, $self->{map}{map}{info}{height});
395 $self->{map}->invalidate_all;
396 $w->destroy;
78 } 397 }
79
80 @{$self->{draw_mode}}[1,2] = ($X, $Y);
81
82 1 398 ],
83 }); 399 )
400 {
401 $self->_add_prop_entry ($t, $i++, @$_);
402 }
84 403
85 $map->signal_connect (button_release_event => sub { 404 $w->show_all;
86 my ($map, $event) = @_;
87
88 if ($self->{draw_mode}
89 and my $ea = $self->{draw_mode}[0]
90 and $self->{draw_mode}[3] == $event->button) {
91 $ea->end;
92
93 if ($ea->special_arrow) {
94
95 # XXX: Get the original cursor and insert it here
96 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR'));
97 }
98
99 delete $self->{draw_mode};
100
101 $ea->want_cursor
102 or $map->enable_tooltip;
103
104 return 1;
105 }
106
107 0
108 });
109} 405}
110 406
111sub delete_arch { 407sub follow {
112 my ($self, $x, $y) = @_; 408 my ($self, $dir) = @_;
113 409
114 defined $self->{map} 410 my %dir_to_path = (
411 u => 'tile_path_1',
412 d => 'tile_path_3',
413 r => 'tile_path_2',
414 l => 'tile_path_4',
415 );
416
417 defined $dir_to_path{$dir}
115 or return 0; 418 or return;
419 my $map = $self->{map}{map}{info}{$dir_to_path{$dir}}
420 or return;
116 421
117 my $as = $self->{map}->get ($x, $y); 422 $map = map2abs ($map, $self);
118 pop @$as; 423 $::MAINWIN->open_map_editor ($map);
119 $self->{map}->set ($x, $y, $as);
120} 424}
121 425
122sub place_pick {
123 my ($self, $x, $y) = @_;
124
125 my $pick = $GCE::MainWindow::MAINWIN->get_pick;
126 my $as = $self->{map}->get ($x, $y);
127
128 my $arch = { _name => $pick->{_name} };
129
130 push @$as, $arch
131 unless @$as && $as->[-1]->{_name} eq $arch->{_name};
132
133 $self->{map}->set ($x, $y, $as);
134}
135
136sub open_map { 426sub open_map_prop {
137 my ($self, $path) = @_; 427 my ($self) = @_;
138 428
139 $self->{map}->set_map (new_from_file Crossfire::Map $path); 429
430 my $w = Gtk2::Window->new ('toplevel');
431 $w->set_default_size (500, 500);
432 $w->add (my $sw = Gtk2::ScrolledWindow->new);
433 $sw->add_with_viewport (my $v = Gtk2::VBox->new);
434 $sw->set_policy ('automatic', 'automatic');
435 $v->pack_start (my $t = Gtk2::Table->new (2, 10), 0, 0, 0);
436
437 my $i = 0;
438 for (
439 [qw/name Name string/],
440 [qw/region Region string/],
441 [qw/enter_x Enter-x string/],
442 [qw/enter_y Enter-y string/],
443 [qw/reset_timeout Reset-timeout string/],
444 [qw/swap_time Swap-timeout string/],
445 [qw/x x sep/],
446 [qw/difficulty Difficulty string/],
447 [qw/windspeed Windspeed string/],
448 [qw/pressure Pressure string/],
449 [qw/humid Humid string/],
450 [qw/temp Temp string/],
451 [qw/darkness Darkness string/],
452 [qw/sky Sky string/],
453 [qw/winddir Winddir string/],
454 [qw/x x sep/],
455 [qw/width Width label/], # sub { $self->{map}{map}->resize ($_[0], $self->{map}{map}{height}) }],
456 [qw/height Height label/], # sub { $self->{map}{map}->resize ($self->{map}{map}{width}, $_[0]) }],
457 [qw/x x sep/],
458 [qw/msg Text text/],
459 [qw/maplore Maplore text/],
460 [qw/outdoor Outdoor check/],
461 [qw/unique Unique check/],
462 [qw/fixed_resettime Fixed-resettime check/],
463 [qw/x x sep/],
464 [qw/tile_path_1 Northpath string/],
465 [qw/tile_path_2 Eastpath string/],
466 [qw/tile_path_3 Southpath string/],
467 [qw/tile_path_4 Westpath string/],
468 [qw/tile_path_5 Toppath string/],
469 [qw/tile_path_6 Bottompath string/],
470 )
471 {
472 $self->_add_prop_entry ($t, $i++, @$_);
473 }
474
475 $w->show_all;
140} 476}
141 477
142=head1 AUTHOR 478=head1 AUTHOR
143 479
144 Marc Lehmann <schmorp@schmorp.de> 480 Marc Lehmann <schmorp@schmorp.de>

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines