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

File Contents

# User Rev Content
1 elmex 1.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 root 1.3 use Crossfire::Map;
15 elmex 1.1 use Crossfire::MapWidget;
16    
17     use GCE::AttrEdit;
18 elmex 1.22 use GCE::Util;
19 elmex 1.1
20 elmex 1.4 use Glib::Object::Subclass
21 elmex 1.6 Gtk2::Window;
22 elmex 1.1
23     use strict;
24    
25 root 1.9 sub build_menu {
26     my ($self) = @_;
27    
28     my $menu_tree = [
29     _File => {
30     item_type => '<Branch>',
31     children => [
32     "_Save" => {
33 root 1.10 callback => sub { $self->save_map },
34 root 1.9 accelerator => '<ctrl>S'
35     },
36 root 1.10 "Save As" => {
37     callback => sub { $self->save_map_as },
38     },
39 root 1.13 "Close" => {
40     callback => sub { $self->delete; 1 },
41     },
42 root 1.10 ]
43 root 1.9 },
44     _Edit => {
45     item_type => '<Branch>',
46     children => [
47     "_Undo" => {
48     callback => sub { $self->undo },
49 elmex 1.17 accelerator => "<ctrl>Z"
50 root 1.9 },
51     "_Redo" => {
52     callback => sub { $self->redo },
53 elmex 1.17 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 root 1.9 },
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 elmex 1.23 for (
75     [i => 'pick'],
76     [p => 'place'],
77 elmex 1.24 [e => 'erase'],
78     [s => 'select'],
79     [l => 'eval'],
80     [x => 'connectexit'],
81     [f => 'followexit']
82 elmex 1.23 )
83     {
84 elmex 1.24 my $tool = $_->[1];
85 elmex 1.23 $men->{accel_group}->connect ($Gtk2::Gdk::Keysyms{$_->[0]}, [], 'visible',
86 elmex 1.24 sub { $::MAINWIN->set_edit_tool ($tool) });
87 elmex 1.23 }
88    
89 root 1.9 $self->add_accel_group ($men->{accel_group});
90    
91     return $men->{widget};
92     }
93    
94 elmex 1.19 sub ea {
95     my ($self) = @_;
96     $self->{ea_alt} || $::MAINWIN->{sel_editaction};
97     }
98    
99 elmex 1.1 sub INIT_INSTANCE {
100     my ($self) = @_;
101    
102 elmex 1.6 $self->set_title ('gce - map editor');
103     $self->add (my $vb = Gtk2::VBox->new);
104 elmex 1.1
105 elmex 1.25 $self->signal_connect (delete_event => sub { $self->delete; 1 });
106 root 1.13
107 root 1.9 $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 elmex 1.19 $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 elmex 1.22 delete $self->{ea_alt};
119 elmex 1.19 });
120    
121     $map->signal_connect (key_press_event => sub {
122     my ($map, $event) = @_;
123 root 1.9
124 elmex 1.19 my $kv = $event->keyval;
125 root 1.9
126 elmex 1.23 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 elmex 1.24 [v => sub { $::MAINWIN->{edit_collection}{select}->paste ($x, $y) }],
131     [n => sub { $::MAINWIN->{edit_collection}{select}->invoke }],
132 elmex 1.23 )
133     {
134     if ($kv == $Gtk2::Gdk::Keysyms{$_->[0]}) {
135     $_->[1]->();
136     }
137 elmex 1.19 }
138 root 1.9
139 elmex 1.19 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 elmex 1.1
164 elmex 1.19 my ($x, $y) = $map->coord ($event->x, $event->y);
165     my $as = $map->get ($x, $y);
166 elmex 1.1
167 elmex 1.19 if ((not $self->{draw_mode}) and $event->button != 2) {
168 elmex 1.4
169 elmex 1.19 my $ea = $self->ea;
170 elmex 1.4
171 elmex 1.22 $ea->begin ($map, $x, $y, $self)
172 elmex 1.19 if $x >= 0 and $y >= 0 and $x < $map->{map}{width} and $y < $map->{map}{height};
173    
174     $self->{draw_mode} = [$x, $y];
175 root 1.9
176 elmex 1.19 $ea->want_cursor
177     or $map->disable_tooltip;
178 root 1.9
179 elmex 1.19 return 1;
180     }
181     0
182 root 1.9 });
183    
184     $map->signal_connect_after (motion_notify_event => sub {
185     my ($map, $event) = @_;
186    
187     $self->{draw_mode}
188     or return;
189    
190 elmex 1.19 my $ea = $self->ea;
191    
192     my ($X, $Y) = @{$self->{draw_mode}}[0,1];
193 root 1.9 my ($x, $y) = $map->coord ($map->get_pointer);
194 elmex 1.4
195 root 1.9 while ($x != $X || $y != $Y) {
196 elmex 1.4
197 root 1.9 $X++ if $X < $x;
198     $X-- if $X > $x;
199     $Y++ if $Y < $y;
200     $Y-- if $Y > $y;
201 elmex 1.4
202 elmex 1.22 $ea->edit ($map, $X, $Y, $self)
203 elmex 1.15 if $X >= 0 and $Y >= 0 and $X < $map->{map}{width} and $Y < $map->{map}{height};
204 root 1.9 }
205 elmex 1.4
206 elmex 1.19 @{$self->{draw_mode}}[0,1] = ($X, $Y);
207 elmex 1.1
208 root 1.9 1
209     });
210 elmex 1.1
211 root 1.9 $map->signal_connect (button_release_event => sub {
212     my ($map, $event) = @_;
213 elmex 1.1
214 elmex 1.19 if ($self->{draw_mode}) {
215 root 1.9 my ($x, $y) = $map->coord ($map->get_pointer);
216 elmex 1.4
217 elmex 1.19 my $ea = $self->ea;
218 elmex 1.22 $ea->end ($map, $x, $y, $self);
219 elmex 1.1
220 root 1.9 delete $self->{draw_mode};
221 elmex 1.4
222 root 1.9 $ea->want_cursor
223     or $map->enable_tooltip;
224 elmex 1.1
225 root 1.9 return 1;
226     }
227 elmex 1.1
228 root 1.9 0
229     });
230     }
231 elmex 1.8
232 elmex 1.16 # FIXME: Fix the automatic update of the attribute editor! and also the stack view!
233 root 1.9 sub undo {
234     my ($self) = @_;
235 elmex 1.8
236 root 1.9 my $map = $self->{map}; # the Crossfire::MapWidget
237 elmex 1.1
238 root 1.9 $map->{undo_stack_pos}
239     or return;
240 elmex 1.1
241 root 1.9 $map->change_swap ($map->{undo_stack}[--$map->{undo_stack_pos}]);
242     }
243 elmex 1.1
244 root 1.9 sub redo {
245     my ($self) = @_;
246 elmex 1.7
247 root 1.9 my $map = $self->{map}; # the Crossfire::MapWidget
248 elmex 1.7
249 elmex 1.14 $map->{undo_stack}
250     and $map->{undo_stack_pos} < @{$map->{undo_stack}}
251     or return;
252 elmex 1.1
253 root 1.9 $map->change_swap ($map->{undo_stack}[$map->{undo_stack_pos}++]);
254 root 1.3 }
255    
256 root 1.13 sub delete {
257     my ($self) = @_;
258    
259     # check and modla dialog if "dirty"
260    
261 elmex 1.26 if ($self->{mapkey}) {
262 elmex 1.25 # XXX: This should be in a delete event handler in the MainWindow.pm... but it doesnt work
263 elmex 1.26 delete $::MAINWIN->{loaded_maps}->{$self->{mapkey}};
264 elmex 1.25 }
265    
266 root 1.13 $self->destroy;
267     }
268    
269 root 1.3 sub open_map {
270 elmex 1.26 my ($self, $path, $key) = @_;
271    
272     $self->{mapkey} = $key;
273 root 1.3
274 elmex 1.18 if (ref $path) {
275     $self->{map}->set_map ($path);
276    
277     } else {
278     $self->{path} = $path;
279 elmex 1.17 # print "OPENMAP $path\n";
280 elmex 1.18 $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 root 1.3 }
285    
286 root 1.13 sub save_map {
287     my ($self) = @_;
288    
289 elmex 1.19 if ($self->{path}) {
290     $self->{map}{map}->write_file ($self->{path});
291 elmex 1.22 quick_msg ($self, "saved to $self->{path}");
292     } else {
293     $self->save_map_as;
294 elmex 1.19 }
295 root 1.13 }
296    
297     sub save_map_as {
298     my ($self) = @_;
299 elmex 1.19
300 elmex 1.22 my $fc = $::MAINWIN->new_filechooser ('gce - save map', 1, $self->{path});
301 elmex 1.19
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 elmex 1.22 quick_msg ($self, "saved to $self->{path}");
309 elmex 1.19 }
310    
311     $fc->destroy;
312 root 1.13 }
313    
314 elmex 1.17 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 elmex 1.19 $self->{map}->invalidate_all;
363     $w->destroy;
364 elmex 1.17 }
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 elmex 1.1 =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