ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/gde/GCE/MapEditor.pm
Revision: 1.24
Committed: Thu Mar 16 22:43:22 2006 UTC (18 years, 3 months ago) by elmex
Branch: MAIN
Changes since 1.23: +9 -3 lines
Log Message:
put some keybindings in

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 });
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 $self->destroy;
262 }
263
264 sub open_map {
265 my ($self, $path) = @_;
266
267 if (ref $path) {
268 $self->{map}->set_map ($path);
269
270 } else {
271 $self->{path} = $path;
272 # print "OPENMAP $path\n";
273 $self->{map}->set_map (my $m = new_from_file Crossfire::Map $path);
274 require Data::Dumper;
275 # print "FOO:" .Data::Dumper::Dumper ($m) . "\n";
276 }
277 }
278
279 sub save_map {
280 my ($self) = @_;
281
282 if ($self->{path}) {
283 $self->{map}{map}->write_file ($self->{path});
284 quick_msg ($self, "saved to $self->{path}");
285 } else {
286 $self->save_map_as;
287 }
288 }
289
290 sub save_map_as {
291 my ($self) = @_;
292
293 my $fc = $::MAINWIN->new_filechooser ('gce - save map', 1, $self->{path});
294
295 if ('ok' eq $fc->run) {
296
297 $::MAINWIN->{fc_last_folder} = $fc->get_current_folder;
298 $::MAINWIN->{fc_last_folders}->{$self->{fc_last_folder}}++;
299
300 $self->{map}{map}->write_file ($self->{path} = $fc->get_filename);
301 quick_msg ($self, "saved to $self->{path}");
302 }
303
304 $fc->destroy;
305 }
306
307 sub _add_prop_entry {
308 my ($self, $table, $idx, $key, $desc, $type, $changecb) = @_;
309
310 my $edwid;
311
312 if ($type eq 'string') {
313 $table->attach_defaults (my $lbl = Gtk2::Label->new ($desc), 0, 1, $idx, $idx + 1);
314 $edwid = Gtk2::Entry->new;
315 $edwid->set_text ($self->{map}{map}{info}{$key});
316 $edwid->signal_connect (changed => sub {
317 $self->{map}{map}{info}{$key} = $_[0]->get_text;
318 if ($changecb) {
319 $changecb->($_[0]->get_text);
320 }
321 });
322 $table->attach_defaults ($edwid, 1, 2, $idx, $idx + 1);
323
324 } elsif ($type eq 'button') {
325 $table->attach_defaults (my $b = Gtk2::Button->new_with_label ($desc), 0, 2, $idx, $idx + 1);
326 $b->signal_connect (clicked => ($changecb || sub {}));
327
328 } elsif ($type eq 'label') {
329 $table->attach_defaults (my $lbl = Gtk2::Label->new ($desc), 0, 1, $idx, $idx + 1);
330 $edwid = Gtk2::Label->new ($self->{map}{map}{info}{$key});
331 $table->attach_defaults ($edwid, 1, 2, $idx, $idx + 1);
332
333 } else {
334 $edwid = Gtk2::Label->new ("FOO");
335 }
336 }
337
338 sub open_resize_map {
339 my ($self) = @_;
340
341 my $w = Gtk2::Window->new ('toplevel');
342 $w->set_default_size (250, 150);
343 $w->add (my $sw = Gtk2::ScrolledWindow->new);
344 $sw->add_with_viewport (my $v = Gtk2::VBox->new);
345 $sw->set_policy ('automatic', 'automatic');
346 $v->pack_start (my $t = Gtk2::Table->new (2, 10), 0, 0, 0);
347
348 my $i = 0;
349 for (
350 [qw/width Width string/],
351 [qw/height Height string/],
352 [qw/save Save button/,
353 sub {
354 $self->{map}{map}->resize ($self->{map}{map}{info}{width}, $self->{map}{map}{info}{height});
355 $self->{map}->invalidate_all;
356 $w->destroy;
357 }
358 ],
359 )
360 {
361 $self->_add_prop_entry ($t, $i++, @$_);
362 }
363
364 $w->show_all;
365 }
366
367 sub open_map_prop {
368 my ($self) = @_;
369
370
371 my $w = Gtk2::Window->new ('toplevel');
372 $w->set_default_size (500, 500);
373 $w->add (my $sw = Gtk2::ScrolledWindow->new);
374 $sw->add_with_viewport (my $v = Gtk2::VBox->new);
375 $sw->set_policy ('automatic', 'automatic');
376 $v->pack_start (my $t = Gtk2::Table->new (2, 10), 0, 0, 0);
377
378 my $i = 0;
379 for (
380 [qw/name Name string/],
381 [qw/region Region string/],
382 [qw/difficulty Difficulty string/],
383 [qw/width Width label/], # sub { $self->{map}{map}->resize ($_[0], $self->{map}{map}{height}) }],
384 [qw/height Height label/],# sub { $self->{map}{map}->resize ($self->{map}{map}{width}, $_[0]) }],
385 [qw/msg Text text/],
386 [qw/tile_path_1 Northpath string/],
387 [qw/tile_path_2 Eastpath string/],
388 [qw/tile_path_3 Southpath string/],
389 [qw/tile_path_4 Westpath string/],
390 [qw/tile_path_5 Toppath string/],
391 [qw/tile_path_6 Bottompath string/],
392 )
393 {
394 $self->_add_prop_entry ($t, $i++, @$_);
395 }
396
397 $w->show_all;
398 }
399
400 # 'info' => {
401 # 'windspeed' => '10',
402 # 'outdoor' => '1',
403 # 'width' => '20',
404 # 'pressure' => '8',
405 # 'test' => '',
406 # 'tile_path_2' => 'east',
407 # 'tile_path_3' => 'south',
408 # 'enter_y' => '2',
409 # 'tile_path_6' => 'bottom',
410 # 'enter_x' => '1',
411 # 'tile_path_5' => 'top',
412 # 'darkness' => '4',
413 # 'maplore' => '',
414 # 'sky' => '12',
415 # 'winddir' => '11',
416 # 'unique' => '1',
417 # 'msg' => 'Creator: CF Java FUCK Map Editor
418 #Date: 3/12/2006
419 #',
420 # 'difficulty' => '3',
421 # 'humid' => '9',
422 # 'endmaplore' => '',
423 # 'fixed_resettime' => '1',
424 # 'name' => 'test',
425 # 'region' => 'REGION',
426 # 'height' => '40',
427 # 'reset_timeout' => '6',
428 # '_name' => 'map',
429 # 'swap_time' => '5',
430 # 'temp' => '7',
431 # 'tile_path_4' => 'west',
432 # 'tile_path_1' => 'north'
433 # },
434
435
436 =head1 AUTHOR
437
438 Marc Lehmann <schmorp@schmorp.de>
439 http://home.schmorp.de/
440
441 Robin Redeker <elmex@ta-sa.org>
442 http://www.ta-sa.org/
443
444 =cut
445 1;
446