ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/cf.schmorp.de/gde/GCE/MapEditor.pm
Revision: 1.39
Committed: Thu Jun 29 09:45:17 2006 UTC (20 years, 3 months ago) by elmex
Branch: MAIN
Changes since 1.38: +18 -3 lines
Log Message:
Implemented some todo items:
- ctrl+r fuer redo
- pick is click
- map name anzeigen (z.b. title)
And added shopheaders to the map properties along with documentation
in the gce manual for the headers.

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 elmex 1.35 sub 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 elmex 1.36 $::MAINWIN->{edit_collection}{followexit}->edit ($map, $x, $y, $self)
35 elmex 1.38 },
36 elmex 1.35 ]
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 elmex 1.38
44     my $inv_item = Gtk2::MenuItem->new ("Add inventory");
45     $inv_item->set_submenu (my $smenu = new Gtk2::Menu);
46     $menu->append ($inv_item);
47    
48     for my $sr (reverse $self->get_stack_refs ($map, $x, $y)) {
49     my $item = Gtk2::MenuItem->new ($sr->longname);
50     $smenu->append ($item);
51     $item->show;
52     $item->signal_connect (activate => sub {
53     $sr->add_inv ($::MAINWIN->get_pick);
54     });
55     }
56    
57     $inv_item->show;
58 elmex 1.35 $menu->popup (undef, undef, undef, undef, $event->button, $event->time);
59     }
60    
61 root 1.9 sub build_menu {
62     my ($self) = @_;
63    
64     my $menu_tree = [
65     _File => {
66     item_type => '<Branch>',
67     children => [
68     "_Save" => {
69 root 1.10 callback => sub { $self->save_map },
70 root 1.9 accelerator => '<ctrl>S'
71     },
72 root 1.10 "Save As" => {
73     callback => sub { $self->save_map_as },
74     },
75 elmex 1.32 "_Map Properties" => {
76     callback => sub { $self->open_map_prop },
77     accelerator => "<ctrl>P"
78     },
79     "_Map Resize" => {
80     callback => sub { $self->open_resize_map },
81     },
82 root 1.13 "Close" => {
83 elmex 1.31 callback => sub { $self->destroy },
84 root 1.13 },
85 root 1.10 ]
86 root 1.9 },
87     _Edit => {
88     item_type => '<Branch>',
89     children => [
90     "_Undo" => {
91     callback => sub { $self->undo },
92 elmex 1.17 accelerator => "<ctrl>Z"
93 root 1.9 },
94     "_Redo" => {
95     callback => sub { $self->redo },
96 elmex 1.17 accelerator => "<ctrl>Y"
97     },
98 root 1.9 ]
99     },
100 elmex 1.27 _Go => {
101     item_type => '<Branch>',
102     children => [
103     "_Up" => {
104     callback => sub { $self->follow ('u') },
105 elmex 1.33 accelerator => "<ctrl>Up"
106 elmex 1.27 },
107     "_Down" => {
108     callback => sub { $self->follow ('d') },
109 elmex 1.33 accelerator => "<ctrl>Down"
110 elmex 1.27 },
111     "_Right" => {
112     callback => sub { $self->follow ('r') },
113 elmex 1.33 accelerator => "<ctrl>Right"
114 elmex 1.27 },
115     "_Left" => {
116     callback => sub { $self->follow ('l') },
117 elmex 1.33 accelerator => "<ctrl>Left"
118 elmex 1.27 },
119     ]
120     },
121 elmex 1.29 _Help => {
122     item_type => '<Branch>',
123     children => [
124 elmex 1.30 _Manual => {
125 elmex 1.29 callback => sub { $::MAINWIN->show_help_window },
126     accelerator => "<ctrl>H"
127     },
128     ]
129     },
130 root 1.9 ];
131    
132     my $men =
133     Gtk2::SimpleMenu->new (
134     menu_tree => $menu_tree,
135     default_callback => \&default_cb,
136     );
137    
138 elmex 1.23 for (
139     [i => 'pick'],
140     [p => 'place'],
141 elmex 1.24 [e => 'erase'],
142     [s => 'select'],
143     [l => 'eval'],
144 elmex 1.37 [t => 'connect'],
145 elmex 1.24 [f => 'followexit']
146 elmex 1.23 )
147     {
148 elmex 1.24 my $tool = $_->[1];
149 elmex 1.23 $men->{accel_group}->connect ($Gtk2::Gdk::Keysyms{$_->[0]}, [], 'visible',
150 elmex 1.24 sub { $::MAINWIN->set_edit_tool ($tool) });
151 elmex 1.23 }
152    
153 elmex 1.39 $men->{accel_group}->connect ($Gtk2::Gdk::Keysyms{'r'}, ['control-mask'], 'visible',
154     sub { $self->redo });
155    
156 root 1.9 $self->add_accel_group ($men->{accel_group});
157    
158     return $men->{widget};
159     }
160    
161 elmex 1.31 sub set_edit_tool {
162     my ($self, $tool) = @_;
163    
164     $self->{etool} = $tool;
165    
166     if ($self->ea->special_arrow) {
167     $self->{map}{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow));
168 elmex 1.34 } else {
169     # FIXME: Get the original cursor and insert it here
170     $self->{map}{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR'));
171 elmex 1.31 }
172     }
173    
174 elmex 1.19 sub ea {
175     my ($self) = @_;
176 elmex 1.31 $self->{ea_alt} || $self->{etool};
177 elmex 1.19 }
178    
179 elmex 1.1 sub INIT_INSTANCE {
180     my ($self) = @_;
181    
182 elmex 1.6 $self->set_title ('gce - map editor');
183     $self->add (my $vb = Gtk2::VBox->new);
184 elmex 1.1
185 root 1.9 $vb->pack_start (my $menu = $self->build_menu, 0, 1, 0);
186    
187     $vb->pack_start (my $map = $self->{map} = Crossfire::MapWidget->new, 1, 1, 0);
188    
189 elmex 1.31 $map->signal_connect_after (key_press_event => sub {
190 elmex 1.19 my ($map, $event) = @_;
191 root 1.9
192 elmex 1.19 my $kv = $event->keyval;
193 root 1.9
194 elmex 1.31 my $ret = 0;
195    
196 elmex 1.23 my ($x, $y) = $map->coord ($map->get_pointer);
197     for ([Control_L => sub { $self->{ea_alt} = $::MAINWIN->{edit_collection}{erase} }],
198     [Alt_L => sub { $self->{ea_alt} = $::MAINWIN->{edit_collection}{pick} }],
199     [c => sub { $::MAINWIN->{edit_collection}{select}->copy }],
200 elmex 1.28 [v => sub { $::MAINWIN->{edit_collection}{select}->paste ($map, $x, $y) }],
201 elmex 1.24 [n => sub { $::MAINWIN->{edit_collection}{select}->invoke }],
202 elmex 1.23 )
203     {
204 elmex 1.34 my $ed = $_;
205    
206     if ($kv == $Gtk2::Gdk::Keysyms{$ed->[0]}) {
207     my $was_in_draw = defined $self->{draw_mode};
208    
209     $self->stop_drawmode ($map)
210     if $was_in_draw && grep { $ed->[0] eq $_ } qw/Control_L Alt_L/;
211    
212     $ed->[1]->();
213 elmex 1.31 $ret = 1;
214 elmex 1.34
215     $self->start_drawmode ($map)
216     if $was_in_draw && grep { $ed->[0] eq $_ } qw/Control_L Alt_L/;
217 elmex 1.23 }
218 elmex 1.19 }
219 root 1.9
220 elmex 1.19 if ($self->ea->special_arrow) {
221     $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow));
222 elmex 1.34 } else {
223     # FIXME: Get the original cursor and insert it here
224     $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR'));
225 elmex 1.19 }
226 elmex 1.31
227     $ret
228 elmex 1.19 });
229 elmex 1.31
230     $map->signal_connect_after (key_release_event => sub {
231 elmex 1.19 my ($map, $event) = @_;
232    
233 elmex 1.31 my $ret = 0;
234    
235 elmex 1.19 if ($event->keyval == $Gtk2::Gdk::Keysyms{Control_L}
236     or $event->keyval == $Gtk2::Gdk::Keysyms{Alt_L})
237     {
238 elmex 1.34 my $was_in_draw = defined $self->{draw_mode};
239    
240     $self->stop_drawmode ($map)
241     if $was_in_draw;
242    
243 elmex 1.19 delete $self->{ea_alt};
244 elmex 1.31 $ret = 1;
245 elmex 1.34
246     $self->start_drawmode ($map)
247     if $was_in_draw;
248 elmex 1.19 }
249    
250     if ($self->ea->special_arrow) {
251     $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ($self->ea->special_arrow));
252     } else {
253 elmex 1.31 # FIXME: Get the original cursor and insert it here
254 elmex 1.19 $map->{window}->set_cursor (Gtk2::Gdk::Cursor->new ('GDK_LEFT_PTR'));
255     }
256 elmex 1.31
257     $ret
258 elmex 1.19 });
259 elmex 1.31 $map->signal_connect_after (button_press_event => sub {
260 elmex 1.19 my ($map, $event) = @_;
261 elmex 1.1
262 elmex 1.31 if ((not $self->{draw_mode}) and $event->button == 1) {
263 elmex 1.19 my $ea = $self->ea;
264 elmex 1.4
265 elmex 1.34 $self->start_drawmode ($map);
266 root 1.9
267 elmex 1.19 $ea->want_cursor
268     or $map->disable_tooltip;
269 root 1.9
270 elmex 1.19 return 1;
271 elmex 1.35 } elsif ($event->button == 3) {
272     $self->do_context_menu ($map, $event);
273     return 1;
274 elmex 1.19 }
275 elmex 1.31
276 elmex 1.19 0
277 root 1.9 });
278    
279     $map->signal_connect_after (motion_notify_event => sub {
280     my ($map, $event) = @_;
281    
282     $self->{draw_mode}
283     or return;
284    
285 elmex 1.19 my $ea = $self->ea;
286    
287     my ($X, $Y) = @{$self->{draw_mode}}[0,1];
288 root 1.9 my ($x, $y) = $map->coord ($map->get_pointer);
289 elmex 1.4
290 root 1.9 while ($x != $X || $y != $Y) {
291 elmex 1.4
292 root 1.9 $X++ if $X < $x;
293     $X-- if $X > $x;
294     $Y++ if $Y < $y;
295     $Y-- if $Y > $y;
296 elmex 1.4
297 elmex 1.39 unless ($ea->only_on_click) {
298     $ea->edit ($map, $X, $Y, $self)
299     if $X >= 0 and $Y >= 0 and $X < $map->{map}{width} and $Y < $map->{map}{height};
300     }
301 root 1.9 }
302 elmex 1.4
303 elmex 1.19 @{$self->{draw_mode}}[0,1] = ($X, $Y);
304 elmex 1.1
305 root 1.9 1
306     });
307 elmex 1.1
308 elmex 1.31 $map->signal_connect_after (button_release_event => sub {
309 root 1.9 my ($map, $event) = @_;
310 elmex 1.1
311 elmex 1.31 if ($self->{draw_mode} and $event->button == 1) {
312 elmex 1.19 my $ea = $self->ea;
313 elmex 1.1
314 elmex 1.34 $self->stop_drawmode ($map);
315 elmex 1.4
316 root 1.9 $ea->want_cursor
317     or $map->enable_tooltip;
318 elmex 1.1
319 root 1.9 return 1;
320     }
321 elmex 1.1
322 root 1.9 0
323     });
324     }
325 elmex 1.8
326 elmex 1.34 sub start_drawmode {
327     my ($self, $map) = @_;
328    
329     $self->{draw_mode} and return;
330    
331     # XXX: is this okay? my ($x, $y) = $map->coord ($event->x, $event->y);
332     my ($x, $y) = $map->coord ($map->get_pointer);
333    
334     my $ea = $self->ea;
335    
336 elmex 1.36 $ea->begin ($map, $x, $y, $self);
337    
338     $ea->edit ($map, $x, $y, $self)
339 elmex 1.34 if $x >= 0 and $y >= 0 and $x < $map->{map}{width} and $y < $map->{map}{height};
340    
341     $self->{draw_mode} = [$x, $y];
342     }
343    
344     sub stop_drawmode {
345     my ($self, $map) = @_;
346    
347     $self->{draw_mode} or return;
348    
349     my ($x, $y) = $map->coord ($map->get_pointer);
350    
351     my $ea = $self->ea;
352     $ea->end ($map, $x, $y, $self);
353    
354     delete $self->{draw_mode};
355     }
356    
357 elmex 1.16 # FIXME: Fix the automatic update of the attribute editor! and also the stack view!
358 root 1.9 sub undo {
359     my ($self) = @_;
360 elmex 1.8
361 root 1.9 my $map = $self->{map}; # the Crossfire::MapWidget
362 elmex 1.1
363 root 1.9 $map->{undo_stack_pos}
364     or return;
365 elmex 1.1
366 root 1.9 $map->change_swap ($map->{undo_stack}[--$map->{undo_stack_pos}]);
367     }
368 elmex 1.1
369 elmex 1.38 sub get_stack_refs {
370     my ($self, $map, $x, $y) = @_;
371    
372     my $cstack = $map->get ($x, $y);
373    
374     return [] unless @$cstack;
375    
376     my @refs;
377    
378     for my $arch (@$cstack) {
379     my ($ox, $oy) = ($x, $y);
380     if ($arch->{_virtual}) {
381     $ox = $arch->{virtual_x};
382     $oy = $arch->{virtual_y};
383     $arch = $arch->{_virtual};
384     $cstack = $map->get ($ox, $oy);
385     # XXX: This heavily blows up if $arch isn't on $cstack now.. and it actually really does :(
386     }
387    
388     push @refs,
389     GCE::ArchRef->new (
390     arch => $arch,
391     cb => sub {
392     $map->change_begin ('attredit');
393     $map->change_stack ($ox, $oy, $cstack);
394    
395     if (my $changeset = $map->change_end) {
396     splice @{ $map->{undo_stack} ||= [] },
397     $map->{undo_stack_pos}++, 1e6,
398     $changeset;
399     }
400     }
401     );
402     }
403    
404     return @refs;
405     }
406    
407 root 1.9 sub redo {
408     my ($self) = @_;
409 elmex 1.7
410 root 1.9 my $map = $self->{map}; # the Crossfire::MapWidget
411 elmex 1.7
412 elmex 1.14 $map->{undo_stack}
413     and $map->{undo_stack_pos} < @{$map->{undo_stack}}
414     or return;
415 elmex 1.1
416 root 1.9 $map->change_swap ($map->{undo_stack}[$map->{undo_stack_pos}++]);
417 root 1.3 }
418    
419     sub open_map {
420 elmex 1.26 my ($self, $path, $key) = @_;
421    
422     $self->{mapkey} = $key;
423 root 1.3
424 elmex 1.18 if (ref $path) {
425     $self->{map}->set_map ($path);
426 elmex 1.39 $self->set_title ('<ram>');
427 elmex 1.18
428     } else {
429     $self->{path} = $path;
430 elmex 1.17 # print "OPENMAP $path\n";
431 elmex 1.18 $self->{map}->set_map (my $m = new_from_file Crossfire::Map $path);
432 elmex 1.39 $self->set_title ("gce - map editor - $self->{path}");
433 elmex 1.18 require Data::Dumper;
434     # print "FOO:" .Data::Dumper::Dumper ($m) . "\n";
435     }
436 root 1.3 }
437    
438 root 1.13 sub save_map {
439     my ($self) = @_;
440    
441 elmex 1.19 if ($self->{path}) {
442     $self->{map}{map}->write_file ($self->{path});
443 elmex 1.22 quick_msg ($self, "saved to $self->{path}");
444 elmex 1.39 $self->set_title ("gce - map editor - $self->{path}");
445 elmex 1.22 } else {
446     $self->save_map_as;
447 elmex 1.19 }
448 root 1.13 }
449    
450     sub save_map_as {
451     my ($self) = @_;
452 elmex 1.19
453 elmex 1.22 my $fc = $::MAINWIN->new_filechooser ('gce - save map', 1, $self->{path});
454 elmex 1.19
455     if ('ok' eq $fc->run) {
456    
457     $::MAINWIN->{fc_last_folder} = $fc->get_current_folder;
458     $::MAINWIN->{fc_last_folders}->{$self->{fc_last_folder}}++;
459    
460     $self->{map}{map}->write_file ($self->{path} = $fc->get_filename);
461 elmex 1.22 quick_msg ($self, "saved to $self->{path}");
462 elmex 1.39 $self->set_title ("gce - map editor - $self->{path}");
463 elmex 1.19 }
464    
465     $fc->destroy;
466 root 1.13 }
467    
468 elmex 1.17 sub _add_prop_entry {
469     my ($self, $table, $idx, $key, $desc, $type, $changecb) = @_;
470    
471     my $edwid;
472    
473     if ($type eq 'string') {
474     $table->attach_defaults (my $lbl = Gtk2::Label->new ($desc), 0, 1, $idx, $idx + 1);
475     $edwid = Gtk2::Entry->new;
476     $edwid->set_text ($self->{map}{map}{info}{$key});
477     $edwid->signal_connect (changed => sub {
478     $self->{map}{map}{info}{$key} = $_[0]->get_text;
479     if ($changecb) {
480     $changecb->($_[0]->get_text);
481     }
482     });
483     $table->attach_defaults ($edwid, 1, 2, $idx, $idx + 1);
484    
485     } elsif ($type eq 'button') {
486     $table->attach_defaults (my $b = Gtk2::Button->new_with_label ($desc), 0, 2, $idx, $idx + 1);
487     $b->signal_connect (clicked => ($changecb || sub {}));
488    
489     } elsif ($type eq 'label') {
490     $table->attach_defaults (my $lbl = Gtk2::Label->new ($desc), 0, 1, $idx, $idx + 1);
491     $edwid = Gtk2::Label->new ($self->{map}{map}{info}{$key});
492     $table->attach_defaults ($edwid, 1, 2, $idx, $idx + 1);
493    
494 elmex 1.27 } elsif ($type eq 'check') {
495     $table->attach_defaults (my $lbl1 = Gtk2::Label->new ($desc), 0, 1, $idx, $idx + 1);
496     $table->attach_defaults (my $lbl = Gtk2::CheckButton->new, 1, 2, $idx, $idx + 1);
497     $lbl->set_active ($self->{map}{map}{info}{$key});
498     $lbl->signal_connect (toggled => sub {
499     my ($lbl) = @_;
500     $self->{map}{map}{info}{$key} = $lbl->get_active * 1;
501     ($changecb || sub {})->($lbl->get_active);
502     });
503    
504     } elsif ($type eq 'sep') {
505     $table->attach_defaults (my $lbl1 = Gtk2::HSeparator->new, 0, 2, $idx, $idx + 1);
506 elmex 1.17 } else {
507     $edwid = Gtk2::Label->new ("FOO");
508     }
509     }
510    
511     sub open_resize_map {
512     my ($self) = @_;
513    
514     my $w = Gtk2::Window->new ('toplevel');
515     $w->set_default_size (250, 150);
516     $w->add (my $sw = Gtk2::ScrolledWindow->new);
517     $sw->add_with_viewport (my $v = Gtk2::VBox->new);
518     $sw->set_policy ('automatic', 'automatic');
519     $v->pack_start (my $t = Gtk2::Table->new (2, 10), 0, 0, 0);
520    
521     my $i = 0;
522     for (
523     [qw/width Width string/],
524     [qw/height Height string/],
525     [qw/save Save button/,
526     sub {
527     $self->{map}{map}->resize ($self->{map}{map}{info}{width}, $self->{map}{map}{info}{height});
528 elmex 1.19 $self->{map}->invalidate_all;
529     $w->destroy;
530 elmex 1.17 }
531     ],
532     )
533     {
534     $self->_add_prop_entry ($t, $i++, @$_);
535     }
536    
537     $w->show_all;
538     }
539    
540 elmex 1.27 sub follow {
541     my ($self, $dir) = @_;
542    
543     my %dir_to_path = (
544     u => 'tile_path_1',
545     d => 'tile_path_3',
546     r => 'tile_path_2',
547     l => 'tile_path_4',
548     );
549    
550     defined $dir_to_path{$dir}
551     or return;
552     my $map = $self->{map}{map}{info}{$dir_to_path{$dir}}
553     or return;
554    
555     $map = map2abs ($map, $self);
556     $::MAINWIN->open_map_editor ($map);
557     }
558    
559 elmex 1.17 sub open_map_prop {
560     my ($self) = @_;
561    
562    
563     my $w = Gtk2::Window->new ('toplevel');
564     $w->set_default_size (500, 500);
565     $w->add (my $sw = Gtk2::ScrolledWindow->new);
566     $sw->add_with_viewport (my $v = Gtk2::VBox->new);
567     $sw->set_policy ('automatic', 'automatic');
568     $v->pack_start (my $t = Gtk2::Table->new (2, 10), 0, 0, 0);
569    
570     my $i = 0;
571     for (
572 elmex 1.27 [qw/name Name string/],
573     [qw/region Region string/],
574     [qw/enter_x Enter-x string/],
575     [qw/enter_y Enter-y string/],
576     [qw/reset_timeout Reset-timeout string/],
577     [qw/swap_time Swap-timeout string/],
578     [qw/x x sep/],
579     [qw/difficulty Difficulty string/],
580     [qw/windspeed Windspeed string/],
581     [qw/pressure Pressure string/],
582     [qw/humid Humid string/],
583     [qw/temp Temp string/],
584     [qw/darkness Darkness string/],
585     [qw/sky Sky string/],
586     [qw/winddir Winddir string/],
587     [qw/x x sep/],
588     [qw/width Width label/], # sub { $self->{map}{map}->resize ($_[0], $self->{map}{map}{height}) }],
589     [qw/height Height label/], # sub { $self->{map}{map}->resize ($self->{map}{map}{width}, $_[0]) }],
590     [qw/x x sep/],
591     [qw/msg Text text/],
592     [qw/maplore Maplore text/],
593     [qw/outdoor Outdoor check/],
594     [qw/unique Unique check/],
595     [qw/fixed_resettime Fixed-resettime check/],
596     [qw/x x sep/],
597     [qw/tile_path_1 Northpath string/],
598     [qw/tile_path_2 Eastpath string/],
599     [qw/tile_path_3 Southpath string/],
600     [qw/tile_path_4 Westpath string/],
601     [qw/tile_path_5 Toppath string/],
602     [qw/tile_path_6 Bottompath string/],
603 elmex 1.39 [qw/x x sep/],
604     ['x', 'For shop description look in the manual', 'button', sub { $::MAINWIN->show_help_window }],
605     [qw/shopmin Shopmin string/],
606     [qw/shopmax Shopmax string/],
607     [qw/shoprace Shoprace string/],
608     [qw/shopgreed Shopgreed string/],
609     [qw/shopitems Shopitems string/],
610 elmex 1.17 )
611     {
612     $self->_add_prop_entry ($t, $i++, @$_);
613     }
614    
615     $w->show_all;
616     }
617    
618 elmex 1.1 =head1 AUTHOR
619    
620     Marc Lehmann <schmorp@schmorp.de>
621     http://home.schmorp.de/
622    
623     Robin Redeker <elmex@ta-sa.org>
624     http://www.ta-sa.org/
625    
626     =cut
627     1;
628