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.12 by root, Tue Feb 21 18:07:39 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;
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}
23 45
24sub build_menu { 46sub build_menu {
25 my ($self) = @_; 47 my ($self) = @_;
26 48
27 my $menu_tree = [ 49 my $menu_tree = [
33 accelerator => '<ctrl>S' 55 accelerator => '<ctrl>S'
34 }, 56 },
35 "Save As" => { 57 "Save As" => {
36 callback => sub { $self->save_map_as }, 58 callback => sub { $self->save_map_as },
37 }, 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 },
38 ] 71 ]
39 }, 72 },
40 _Edit => { 73 _Edit => {
41 item_type => '<Branch>', 74 item_type => '<Branch>',
42 children => [ 75 children => [
43 "_Undo" => { 76 "_Undo" => {
44 callback => sub { $self->undo }, 77 callback => sub { $self->undo },
45 accelerator => "<ctrl>Z" 78 accelerator => "<ctrl>Z"
46 }, 79 },
47 "_Redo" => { 80 "_Redo" => {
48 callback => sub { $self->redo }, 81 callback => sub { $self->redo },
49 accelerator => "<ctrl>Y" 82 accelerator => "<ctrl>Y"
50 }, 83 },
51
52 ] 84 ]
53 }, 85 },
54 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 },
55 ]; 116 ];
56 117
57 my $men = 118 my $men =
58 Gtk2::SimpleMenu->new ( 119 Gtk2::SimpleMenu->new (
59 menu_tree => $menu_tree, 120 menu_tree => $menu_tree,
60 default_callback => \&default_cb, 121 default_callback => \&default_cb,
61 ); 122 );
62 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
63 $self->add_accel_group ($men->{accel_group}); 139 $self->add_accel_group ($men->{accel_group});
64 140
65 return $men->{widget}; 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};
66} 160}
67 161
68sub INIT_INSTANCE { 162sub INIT_INSTANCE {
69 my ($self) = @_; 163 my ($self) = @_;
70 164
73 167
74 $vb->pack_start (my $menu = $self->build_menu, 0, 1, 0); 168 $vb->pack_start (my $menu = $self->build_menu, 0, 1, 0);
75 169
76 $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);
77 171
78 $map->signal_connect (button_press_event => sub { 172 $map->signal_connect_after (key_press_event => sub {
79 my ($map, $event) = @_; 173 my ($map, $event) = @_;
80 174
81 my ($x, $y) = $map->coord ($event->x, $event->y); 175 my $kv = $event->keyval;
82 my $as = $map->get ($x, $y);
83 176
84 my $btn = $event->button; 177 my $ret = 0;
85 178
86 if ((not $self->{draw_mode}) 179 my ($x, $y) = $map->coord ($map->get_pointer);
87 and $btn != 2 180 for ([Control_L => sub { $self->{ea_alt} = $::MAINWIN->{edit_collection}{erase} }],
88 and my $ea = $::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 = $_;
89 188
90 $ea->begin ($map, $x, $y, $btn); 189 if ($kv == $Gtk2::Gdk::Keysyms{$ed->[0]}) {
190 my $was_in_draw = defined $self->{draw_mode};
91 191
92 $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/;
93 194
195 $ed->[1]->();
196 $ret = 1;
197
198 $self->start_drawmode ($map)
199 if $was_in_draw && grep { $ed->[0] eq $_ } qw/Control_L Alt_L/;
200 }
201 }
202
203 if ($self->ea->special_arrow) {
204 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow));
205 } else {
206 # FIXME: Get the original cursor and insert it here
207 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR'));
208 }
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
94 $ea->want_cursor 250 $ea->want_cursor
95 or $map->disable_tooltip; 251 or $map->disable_tooltip;
96 252
97 if ($ea->special_arrow) {
98
99 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($ea->special_arrow));
100 #'GDK_QUESTION_ARROW'));
101 }
102
103 return 1; 253 return 1;
254 } elsif ($event->button == 3) {
255 $self->do_context_menu ($map, $event);
256 return 1;
104 } 257 }
105 258
106 0 259 0
107 }); 260 });
108 261
109 $map->signal_connect_after (motion_notify_event => sub { 262 $map->signal_connect_after (motion_notify_event => sub {
110 my ($map, $event) = @_; 263 my ($map, $event) = @_;
111 264
112 $self->{draw_mode} 265 $self->{draw_mode}
113 or return; 266 or return;
114 267
268 my $ea = $self->ea;
269
115 my ($X, $Y) = @{$self->{draw_mode}}[1,2]; 270 my ($X, $Y) = @{$self->{draw_mode}}[0,1];
116 my ($x, $y) = $map->coord ($map->get_pointer); 271 my ($x, $y) = $map->coord ($map->get_pointer);
117 272
118 while ($x != $X || $y != $Y) { 273 while ($x != $X || $y != $Y) {
119 274
120 $X++ if $X < $x; 275 $X++ if $X < $x;
121 $X-- if $X > $x; 276 $X-- if $X > $x;
122 $Y++ if $Y < $y; 277 $Y++ if $Y < $y;
123 $Y-- if $Y > $y; 278 $Y-- if $Y > $y;
124 279
125 $self->{draw_mode}[0]->edit ($map, $X, $Y, $self->{draw_mode}[3]); 280 $ea->edit ($map, $X, $Y, $self)
281 if $X >= 0 and $Y >= 0 and $X < $map->{map}{width} and $Y < $map->{map}{height};
126 } 282 }
127 283
128 @{$self->{draw_mode}}[1,2] = ($X, $Y); 284 @{$self->{draw_mode}}[0,1] = ($X, $Y);
129 285
130 1 286 1
131 }); 287 });
132 288
133 $map->signal_connect (button_release_event => sub { 289 $map->signal_connect_after (button_release_event => sub {
134 my ($map, $event) = @_; 290 my ($map, $event) = @_;
135 291
136 if ($self->{draw_mode}
137 and my $ea = $self->{draw_mode}[0]
138 and $self->{draw_mode}[3] == $event->button) { 292 if ($self->{draw_mode} and $event->button == 1) {
293 my $ea = $self->ea;
139 294
140 my ($x, $y) = $map->coord ($map->get_pointer); 295 $self->stop_drawmode ($map);
141
142 $ea->end ($map, $x, $y, $event->button);
143
144 if ($ea->special_arrow) {
145
146 # XXX: Get the original cursor and insert it here
147 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR'));
148 }
149
150 delete $self->{draw_mode};
151 296
152 $ea->want_cursor 297 $ea->want_cursor
153 or $map->enable_tooltip; 298 or $map->enable_tooltip;
154 299
155 return 1; 300 return 1;
157 302
158 0 303 0
159 }); 304 });
160} 305}
161 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!
162sub undo { 339sub undo {
163 my ($self) = @_; 340 my ($self) = @_;
164 341
165 my $map = $self->{map}; # the Crossfire::MapWidget 342 my $map = $self->{map}; # the Crossfire::MapWidget
166 343
173sub redo { 350sub redo {
174 my ($self) = @_; 351 my ($self) = @_;
175 352
176 my $map = $self->{map}; # the Crossfire::MapWidget 353 my $map = $self->{map}; # the Crossfire::MapWidget
177 354
355 $map->{undo_stack}
178 $map->{undo_stack_pos} < @{$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);
420 }
421 });
422 $table->attach_defaults ($edwid, 1, 2, $idx, $idx + 1);
423
424 } elsif ($type eq 'button') {
425 $table->attach_defaults (my $b = Gtk2::Button->new_with_label ($desc), 0, 2, $idx, $idx + 1);
426 $b->signal_connect (clicked => ($changecb || sub {}));
427
428 } elsif ($type eq 'label') {
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);
432
433 } elsif ($type eq 'check') {
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 });
442
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}
449
450sub open_resize_map {
451 my ($self) = @_;
452
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;
469 }
470 ],
471 )
472 {
473 $self->_add_prop_entry ($t, $i++, @$_);
474 }
475
476 $w->show_all;
477}
478
479sub follow {
480 my ($self, $dir) = @_;
481
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}
179 or return; 490 or return;
180 491 my $map = $self->{map}{map}{info}{$dir_to_path{$dir}}
181 $map->change_swap ($map->{undo_stack}[$map->{undo_stack_pos}++]);
182}
183
184sub delete_arch {
185 my ($self, $x, $y) = @_;
186
187 defined $self->{map}
188 or return 0; 492 or return;
189 493
190 my $as = $self->{map}->get ($x, $y); 494 $map = map2abs ($map, $self);
191 pop @$as; 495 $::MAINWIN->open_map_editor ($map);
192 $self->{map}->set ($x, $y, $as);
193} 496}
194 497
195sub place_pick {
196 my ($self, $x, $y) = @_;
197
198 my $pick = $::MAINWIN->get_pick;
199 my $as = $self->{map}->get ($x, $y);
200
201 my $arch = { _name => $pick->{_name} };
202
203 push @$as, $arch
204 unless @$as && $as->[-1]->{_name} eq $arch->{_name};
205
206 $self->{map}->set ($x, $y, $as);
207}
208
209sub open_map { 498sub open_map_prop {
210 my ($self, $path) = @_; 499 my ($self) = @_;
211 500
212 $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;
213} 548}
214 549
215=head1 AUTHOR 550=head1 AUTHOR
216 551
217 Marc Lehmann <schmorp@schmorp.de> 552 Marc Lehmann <schmorp@schmorp.de>

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines