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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines