ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/gde/GCE/MapEditor.pm
Revision: 1.33
Committed: Sun Apr 2 10:58:52 2006 UTC (18 years, 2 months ago) by elmex
Branch: MAIN
Changes since 1.32: +4 -4 lines
Log Message:
changed map navigation to cursor keys and made active tab stay the same on object switch in attreditor

File Contents

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