ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/gde/GCE/MapEditor.pm
Revision: 1.26
Committed: Fri Mar 17 00:20:31 2006 UTC (18 years, 2 months ago) by elmex
Branch: MAIN
Changes since 1.25: +5 -3 lines
Log Message:
improved follow exit a little bit

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 "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
66 ];
67
68 my $men =
69 Gtk2::SimpleMenu->new (
70 menu_tree => $menu_tree,
71 default_callback => \&default_cb,
72 );
73
74 for (
75 [i => 'pick'],
76 [p => 'place'],
77 [e => 'erase'],
78 [s => 'select'],
79 [l => 'eval'],
80 [x => 'connectexit'],
81 [f => 'followexit']
82 )
83 {
84 my $tool = $_->[1];
85 $men->{accel_group}->connect ($Gtk2::Gdk::Keysyms{$_->[0]}, [], 'visible',
86 sub { $::MAINWIN->set_edit_tool ($tool) });
87 }
88
89 $self->add_accel_group ($men->{accel_group});
90
91 return $men->{widget};
92 }
93
94 sub ea {
95 my ($self) = @_;
96 $self->{ea_alt} || $::MAINWIN->{sel_editaction};
97 }
98
99 sub INIT_INSTANCE {
100 my ($self) = @_;
101
102 $self->set_title ('gce - map editor');
103 $self->add (my $vb = Gtk2::VBox->new);
104
105 $self->signal_connect (delete_event => sub { $self->delete; 1 });
106
107 $vb->pack_start (my $menu = $self->build_menu, 0, 1, 0);
108
109 $vb->pack_start (my $map = $self->{map} = Crossfire::MapWidget->new, 1, 1, 0);
110
111 $self->signal_connect (focus_in_event => sub {
112 my $ea = $::MAINWIN->{sel_editaction};
113 if ($ea->special_arrow) {
114 $self->{map}->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($ea->special_arrow));
115 } else {
116 $self->{map}->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR'));
117 }
118 delete $self->{ea_alt};
119 });
120
121 $map->signal_connect (key_press_event => sub {
122 my ($map, $event) = @_;
123
124 my $kv = $event->keyval;
125
126 my ($x, $y) = $map->coord ($map->get_pointer);
127 for ([Control_L => sub { $self->{ea_alt} = $::MAINWIN->{edit_collection}{erase} }],
128 [Alt_L => sub { $self->{ea_alt} = $::MAINWIN->{edit_collection}{pick} }],
129 [c => sub { $::MAINWIN->{edit_collection}{select}->copy }],
130 [v => sub { $::MAINWIN->{edit_collection}{select}->paste ($x, $y) }],
131 [n => sub { $::MAINWIN->{edit_collection}{select}->invoke }],
132 )
133 {
134 if ($kv == $Gtk2::Gdk::Keysyms{$_->[0]}) {
135 $_->[1]->();
136 }
137 }
138
139 if ($self->ea->special_arrow) {
140 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow));
141 }
142 1;
143 });
144 $map->signal_connect (key_release_event => sub {
145 my ($map, $event) = @_;
146
147 if ($event->keyval == $Gtk2::Gdk::Keysyms{Control_L}
148 or $event->keyval == $Gtk2::Gdk::Keysyms{Alt_L})
149 {
150 delete $self->{ea_alt};
151 }
152
153 if ($self->ea->special_arrow) {
154 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow));
155 } else {
156 # XXX: Get the original cursor and insert it here
157 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR'));
158 }
159 1;
160 });
161 $map->signal_connect (button_press_event => sub {
162 my ($map, $event) = @_;
163
164 my ($x, $y) = $map->coord ($event->x, $event->y);
165 my $as = $map->get ($x, $y);
166
167 if ((not $self->{draw_mode}) and $event->button != 2) {
168
169 my $ea = $self->ea;
170
171 $ea->begin ($map, $x, $y, $self)
172 if $x >= 0 and $y >= 0 and $x < $map->{map}{width} and $y < $map->{map}{height};
173
174 $self->{draw_mode} = [$x, $y];
175
176 $ea->want_cursor
177 or $map->disable_tooltip;
178
179 return 1;
180 }
181 0
182 });
183
184 $map->signal_connect_after (motion_notify_event => sub {
185 my ($map, $event) = @_;
186
187 $self->{draw_mode}
188 or return;
189
190 my $ea = $self->ea;
191
192 my ($X, $Y) = @{$self->{draw_mode}}[0,1];
193 my ($x, $y) = $map->coord ($map->get_pointer);
194
195 while ($x != $X || $y != $Y) {
196
197 $X++ if $X < $x;
198 $X-- if $X > $x;
199 $Y++ if $Y < $y;
200 $Y-- if $Y > $y;
201
202 $ea->edit ($map, $X, $Y, $self)
203 if $X >= 0 and $Y >= 0 and $X < $map->{map}{width} and $Y < $map->{map}{height};
204 }
205
206 @{$self->{draw_mode}}[0,1] = ($X, $Y);
207
208 1
209 });
210
211 $map->signal_connect (button_release_event => sub {
212 my ($map, $event) = @_;
213
214 if ($self->{draw_mode}) {
215 my ($x, $y) = $map->coord ($map->get_pointer);
216
217 my $ea = $self->ea;
218 $ea->end ($map, $x, $y, $self);
219
220 delete $self->{draw_mode};
221
222 $ea->want_cursor
223 or $map->enable_tooltip;
224
225 return 1;
226 }
227
228 0
229 });
230 }
231
232 # FIXME: Fix the automatic update of the attribute editor! and also the stack view!
233 sub undo {
234 my ($self) = @_;
235
236 my $map = $self->{map}; # the Crossfire::MapWidget
237
238 $map->{undo_stack_pos}
239 or return;
240
241 $map->change_swap ($map->{undo_stack}[--$map->{undo_stack_pos}]);
242 }
243
244 sub redo {
245 my ($self) = @_;
246
247 my $map = $self->{map}; # the Crossfire::MapWidget
248
249 $map->{undo_stack}
250 and $map->{undo_stack_pos} < @{$map->{undo_stack}}
251 or return;
252
253 $map->change_swap ($map->{undo_stack}[$map->{undo_stack_pos}++]);
254 }
255
256 sub delete {
257 my ($self) = @_;
258
259 # check and modla dialog if "dirty"
260
261 if ($self->{mapkey}) {
262 # XXX: This should be in a delete event handler in the MainWindow.pm... but it doesnt work
263 delete $::MAINWIN->{loaded_maps}->{$self->{mapkey}};
264 }
265
266 $self->destroy;
267 }
268
269 sub open_map {
270 my ($self, $path, $key) = @_;
271
272 $self->{mapkey} = $key;
273
274 if (ref $path) {
275 $self->{map}->set_map ($path);
276
277 } else {
278 $self->{path} = $path;
279 # print "OPENMAP $path\n";
280 $self->{map}->set_map (my $m = new_from_file Crossfire::Map $path);
281 require Data::Dumper;
282 # print "FOO:" .Data::Dumper::Dumper ($m) . "\n";
283 }
284 }
285
286 sub save_map {
287 my ($self) = @_;
288
289 if ($self->{path}) {
290 $self->{map}{map}->write_file ($self->{path});
291 quick_msg ($self, "saved to $self->{path}");
292 } else {
293 $self->save_map_as;
294 }
295 }
296
297 sub save_map_as {
298 my ($self) = @_;
299
300 my $fc = $::MAINWIN->new_filechooser ('gce - save map', 1, $self->{path});
301
302 if ('ok' eq $fc->run) {
303
304 $::MAINWIN->{fc_last_folder} = $fc->get_current_folder;
305 $::MAINWIN->{fc_last_folders}->{$self->{fc_last_folder}}++;
306
307 $self->{map}{map}->write_file ($self->{path} = $fc->get_filename);
308 quick_msg ($self, "saved to $self->{path}");
309 }
310
311 $fc->destroy;
312 }
313
314 sub _add_prop_entry {
315 my ($self, $table, $idx, $key, $desc, $type, $changecb) = @_;
316
317 my $edwid;
318
319 if ($type eq 'string') {
320 $table->attach_defaults (my $lbl = Gtk2::Label->new ($desc), 0, 1, $idx, $idx + 1);
321 $edwid = Gtk2::Entry->new;
322 $edwid->set_text ($self->{map}{map}{info}{$key});
323 $edwid->signal_connect (changed => sub {
324 $self->{map}{map}{info}{$key} = $_[0]->get_text;
325 if ($changecb) {
326 $changecb->($_[0]->get_text);
327 }
328 });
329 $table->attach_defaults ($edwid, 1, 2, $idx, $idx + 1);
330
331 } elsif ($type eq 'button') {
332 $table->attach_defaults (my $b = Gtk2::Button->new_with_label ($desc), 0, 2, $idx, $idx + 1);
333 $b->signal_connect (clicked => ($changecb || sub {}));
334
335 } elsif ($type eq 'label') {
336 $table->attach_defaults (my $lbl = Gtk2::Label->new ($desc), 0, 1, $idx, $idx + 1);
337 $edwid = Gtk2::Label->new ($self->{map}{map}{info}{$key});
338 $table->attach_defaults ($edwid, 1, 2, $idx, $idx + 1);
339
340 } else {
341 $edwid = Gtk2::Label->new ("FOO");
342 }
343 }
344
345 sub open_resize_map {
346 my ($self) = @_;
347
348 my $w = Gtk2::Window->new ('toplevel');
349 $w->set_default_size (250, 150);
350 $w->add (my $sw = Gtk2::ScrolledWindow->new);
351 $sw->add_with_viewport (my $v = Gtk2::VBox->new);
352 $sw->set_policy ('automatic', 'automatic');
353 $v->pack_start (my $t = Gtk2::Table->new (2, 10), 0, 0, 0);
354
355 my $i = 0;
356 for (
357 [qw/width Width string/],
358 [qw/height Height string/],
359 [qw/save Save button/,
360 sub {
361 $self->{map}{map}->resize ($self->{map}{map}{info}{width}, $self->{map}{map}{info}{height});
362 $self->{map}->invalidate_all;
363 $w->destroy;
364 }
365 ],
366 )
367 {
368 $self->_add_prop_entry ($t, $i++, @$_);
369 }
370
371 $w->show_all;
372 }
373
374 sub open_map_prop {
375 my ($self) = @_;
376
377
378 my $w = Gtk2::Window->new ('toplevel');
379 $w->set_default_size (500, 500);
380 $w->add (my $sw = Gtk2::ScrolledWindow->new);
381 $sw->add_with_viewport (my $v = Gtk2::VBox->new);
382 $sw->set_policy ('automatic', 'automatic');
383 $v->pack_start (my $t = Gtk2::Table->new (2, 10), 0, 0, 0);
384
385 my $i = 0;
386 for (
387 [qw/name Name string/],
388 [qw/region Region string/],
389 [qw/difficulty Difficulty string/],
390 [qw/width Width label/], # sub { $self->{map}{map}->resize ($_[0], $self->{map}{map}{height}) }],
391 [qw/height Height label/],# sub { $self->{map}{map}->resize ($self->{map}{map}{width}, $_[0]) }],
392 [qw/msg Text text/],
393 [qw/tile_path_1 Northpath string/],
394 [qw/tile_path_2 Eastpath string/],
395 [qw/tile_path_3 Southpath string/],
396 [qw/tile_path_4 Westpath string/],
397 [qw/tile_path_5 Toppath string/],
398 [qw/tile_path_6 Bottompath string/],
399 )
400 {
401 $self->_add_prop_entry ($t, $i++, @$_);
402 }
403
404 $w->show_all;
405 }
406
407 # 'info' => {
408 # 'windspeed' => '10',
409 # 'outdoor' => '1',
410 # 'width' => '20',
411 # 'pressure' => '8',
412 # 'test' => '',
413 # 'tile_path_2' => 'east',
414 # 'tile_path_3' => 'south',
415 # 'enter_y' => '2',
416 # 'tile_path_6' => 'bottom',
417 # 'enter_x' => '1',
418 # 'tile_path_5' => 'top',
419 # 'darkness' => '4',
420 # 'maplore' => '',
421 # 'sky' => '12',
422 # 'winddir' => '11',
423 # 'unique' => '1',
424 # 'msg' => 'Creator: CF Java FUCK Map Editor
425 #Date: 3/12/2006
426 #',
427 # 'difficulty' => '3',
428 # 'humid' => '9',
429 # 'endmaplore' => '',
430 # 'fixed_resettime' => '1',
431 # 'name' => 'test',
432 # 'region' => 'REGION',
433 # 'height' => '40',
434 # 'reset_timeout' => '6',
435 # '_name' => 'map',
436 # 'swap_time' => '5',
437 # 'temp' => '7',
438 # 'tile_path_4' => 'west',
439 # 'tile_path_1' => 'north'
440 # },
441
442
443 =head1 AUTHOR
444
445 Marc Lehmann <schmorp@schmorp.de>
446 http://home.schmorp.de/
447
448 Robin Redeker <elmex@ta-sa.org>
449 http://www.ta-sa.org/
450
451 =cut
452 1;
453