ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/gde/GCE/StackRef.pm
Revision: 1.1.2.1
Committed: Mon Dec 21 15:51:30 2009 UTC (14 years, 5 months ago) by elmex
Branch: cursor
Changes since 1.1: +463 -0 lines
Log Message:
rewrote main parts in internal reference handling.

File Contents

# User Rev Content
1 elmex 1.1.2.1 package GCE::StackRef;
2     use strict;
3     =head1 NAME
4    
5     GCE::StackRef - a intelligent reference to a mapspace or inventory (arch stack)
6    
7     =head1 SYNOPSIS
8    
9     GCE::StackRef->new (...);
10    
11     =over 4
12    
13     =cut
14    
15     use Scalar::Util qw/weaken/;
16     use Storable qw/dclone/;
17     use Deliantra;
18     use Carp;
19     use GCE::Util;
20     use base qw/Object::Event/;
21    
22     sub new {
23     my $class = shift;
24     my $self = { cursors => [], @_ };
25    
26     $self->{src} ||= [];
27    
28     if (@{$self->{src}} == 3) {
29     return GCE::StackRef::Map->_new ($self);
30     } elsif (@{$self->{src}} == 2) {
31     return GCE::StackRef::Arch->_new ($self);
32     } else {
33     return GCE::StackRef::Dummy->_new ($self);
34     }
35    
36     bless $self, $class;
37    
38     return $self;
39     }
40    
41     sub _new {
42     my $class = shift;
43     my $self = shift;
44     bless $self, $class;
45    
46     $self->init_object_events;
47    
48     $self->init;
49     $self
50     }
51    
52     sub cursor {
53     my ($self, $z) = @_;
54     my $c =
55     ($self->{cursors}->[$z]
56     ||= GCE::StackRef->new (src => [$self, $z]));
57     weaken $self->{cursors}->[$z];
58     $c
59     }
60    
61     sub cursor_change {
62     my ($self, $z, $msg, $inval) = @_;
63     $self->{cursors}->[$z]->changed ($msg, $inval)
64     if defined $self->{cursors}->[$z];
65    
66     $self->{cursors}->[$z] = undef if $inval;
67     }
68    
69     sub changed : event_cb {
70     my ($self, $msg, $invalidated) = @_;
71    
72     warn "$self (@{$self->{src}}) owner $self->{owner}"
73     . " changed: $msg, invalid? ($invalidated)\n";
74     if ($invalidated) {
75     $_->changed ($msg, $invalidated)
76     for grep { defined $_ } @{$self->{cursors}};
77    
78     delete $self->{src};
79     delete $self->{ref};
80     delete $self->{map};
81     }
82     }
83    
84     sub overlay {
85     my ($self) = @_;
86     }
87    
88     package GCE::StackRef::Dummy;
89     use Storable qw/dclone/;
90    
91     use base qw/GCE::StackRef/;
92    
93     sub init {
94     my ($self) = @_;
95     }
96    
97     sub push {
98     my ($self, $arch) = @_;
99     $self->commit ('push_dummy');
100     }
101    
102     sub swap {
103     my ($self, $swapidx, $ownidx) = @_;
104     $self->commit ('swap_dummy');
105     }
106    
107     sub replace {
108     my ($self, $idx, $new) = @_;
109     $self->cursor_change ($idx, 'replace_dummy');
110     $self->commit ('replace_dummy');
111     }
112    
113     sub remove {
114     my ($self, $idx) = @_;
115     $self->cursor_change ($idx, 'remove_dummy');
116     $self->commit ('remove_dummy');
117     }
118    
119     sub commit {
120     my ($self, $change) = @_;
121     $self->changed ($self->{owner} . '#' . $change);
122     }
123    
124     sub size {
125     0
126     }
127    
128     sub get {
129     my $self = shift;
130     my $z = shift;
131    
132     return () unless defined $z;
133    
134     $self->{template}
135     ? dclone ($self->{template})
136     : { _name => 'empty_archetype' }
137     }
138    
139     package GCE::StackRef::Map;
140     use GCE::Util;
141     use Deliantra;
142     use Deliantra::MapWidget;
143    
144     use base qw/GCE::StackRef/;
145    
146     sub init {
147     my ($self) = @_;
148     $self->{map} = $self->{src}->[0];
149     $self->{x} = $self->{src}->[1];
150     $self->{y} = $self->{src}->[2];
151     $self->{ms} = $self->{map}->{map}->get ($self->{x}, $self->{y});
152     }
153    
154     sub overlay {
155     my ($self) = @_;
156     # XXX: Fixme! there is still a bug in removing overlays!!
157     $self->update_cursor_overlay ('stack_view');
158     }
159    
160     sub push {
161     my ($self, $arch) = @_;
162    
163     push @{$self->{ms}}, dclone ($arch->getarch);
164    
165     $self->commit ('push_ms');
166     }
167    
168     sub swap {
169     my ($self, $swapidx, $ownidx) = @_;
170    
171     my $ms = $self->{ms};
172    
173     ($ms->[$swapidx], $ms->[$ownidx])
174     = ($ms->[$ownidx], $ms->[$swapidx]);
175    
176     $self->cursor_change ($swapidx, 'swap_ms');
177     $self->cursor_change ($ownidx, 'swap_ms');
178    
179     $self->commit ('swap_ms');
180     }
181    
182     sub replace {
183     my ($self, $idx, $new) = @_;
184    
185     splice @{$self->{'ms'}}, $idx, 1, $new;
186     $self->cursor_change ($idx, 'replace_ms');
187    
188     $self->commit ('replace_ms');
189     }
190    
191     sub remove {
192     my ($self, $idx) = @_;
193    
194     splice @{$self->{ms}}, $idx, 1;
195     $self->cursor_change ($idx, 'remove_ms');
196    
197     $self->commit ('remove_ms');
198     }
199    
200     sub commit {
201     my ($self, $change) = @_;
202    
203     $change = 'unknown_edit' unless defined $change;
204     $change = $self->{owner} . '#' . $change;
205    
206     my $map = $self->{map};
207     $map = $map->{map};
208    
209     $map->change_begin ($change . ' on ' . $self);
210    
211     $map->change_stack ($self->{x}, $self->{y}, $self->{ms});
212    
213     if (my $changeset = $map->change_end) {
214     my $undo_stack = ($map->{undo_stack} ||= []);
215    
216     my $str_self = "$self";
217    
218     if ($map->{undo_stack_pos} > 0
219     && $undo_stack->[$map->{undo_stack_pos} - 1]->{title}
220     =~ /^attribute_edit:.*? on \Q$str_self\E$/) {
221     $map->{undo_stack_pos}--;
222    
223     warn "merged change ($changeset->{title})\n";
224     } else {
225     warn "added change ($changeset->{title})\n";
226     }
227    
228     splice @{ $map->{undo_stack} ||= [] },
229     $map->{undo_stack_pos}++, 1e6,
230     $changeset;
231     }
232     $self->{ms} = $self->{map}->{map}->get ($self->{x}, $self->{y});
233    
234     $::MAINWIN->broadcast_cursor_changes ($self->{map}, $self->{x}, $self->{y});
235    
236     $self->changed ($change);
237     }
238    
239     sub size {
240     @{$_[0]->{ms} || []}
241     }
242    
243     sub get {
244     my ($self, $z) = @_;
245    
246     if (defined $z) {
247     return undef unless @{$self->{ms}};
248     return $self->{ms}->[$z]
249     } else {
250     return @{$self->{ms} || []}
251     }
252     }
253    
254     sub update_cursor_overlay {
255     my ($self, $owner) = @_;
256    
257     $self->{map}->{map}->{overlay_holder}->{$owner} = "$self";
258    
259     $self->{map}->{map}->overlay ('srovl_' . $owner =>
260     $self->{x} * TILESIZE,
261     $self->{y} * TILESIZE,
262     TILESIZE,
263     TILESIZE,
264     sub {
265     my ($self, $x, $y) = @_;
266    
267     if (!$self->{_conn_upd_curs_gc_fg}->{$owner}) {
268     my $gc
269     = $self->{_conn_upd_curs_gc_fg}->{$owner}
270     = Gtk2::Gdk::GC->new ($self->{window});
271     my $cm = $self->{window}->get_colormap;
272     $gc->set_foreground (
273     gtk2_get_color (
274     $self, $owner eq 'stack_view' ? "green" : "blue"));
275     $gc->set_background (gtk2_get_color ($self, "black"));
276     }
277    
278     $self->{window}->draw_rectangle (
279     $self->{_conn_upd_curs_gc_fg}->{$owner},
280     0,
281     $x + ($owner eq 'stack_view' ? 1 : 0),
282     $y + ($owner eq 'stack_view' ? 1 : 0),
283     TILESIZE - ($owner eq 'stack_view' ? 3 : 1),
284     TILESIZE - ($owner eq 'stack_view' ? 3 : 1),
285     );
286     }
287     );
288     }
289    
290     sub DESTROY {
291     my ($self) = @_;
292     if ($self->{map}->{map}->{overlay_holder}->{'stack_view'} eq "$self") {
293     $self->{map}->{map}->overlay ('srovl_stack_view')
294     }
295     warn "$self StackRef::Map DESTROYED!\n";
296     }
297    
298     package GCE::StackRef::Arch;
299    
300     use Carp;
301     use GCE::Util;
302     use base qw/GCE::StackRef/;
303    
304     sub init {
305     my ($self) = @_;
306     $self->{ref} = $self->{src}->[0];
307     $self->{z} = $self->{src}->[1];
308     }
309    
310     sub get {
311     my ($self, $z) = @_;
312     my $a = $self->{ref}->get ($self->{z});
313     $a = $a->{inventory}->[$z] if defined $z;
314     unless (defined $a) {
315     Carp::confess "GOT UNDEF ARCH IN ARCHREF $self->{ref}, $self->{z}, $z!";
316     }
317     $a
318     }
319    
320     sub push {
321     my ($self, $arch) = @_;
322    
323     my $a = $self->get;
324     push @{$a->{inventory}}, $arch;
325     $self->commit ('push_inv');
326     }
327    
328     sub swap {
329     my ($self, $swapidx, $ownidx) = @_;
330    
331     my $a = $self->get;
332     my $inv = $a->{inventory};
333     ($inv->[$swapidx], $inv->[$ownidx])
334     = ($inv->[$ownidx], $inv->[$swapidx]);
335     $self->commit ('swap_inv');
336     }
337    
338     sub replace {
339     my ($self, $idx, $new) = @_;
340    
341     my $a = $self->get;
342     splice @{$a->{inventory}}, $idx, 1, $new;
343     $self->cursor_change ($idx, 'replace_inv');
344     $self->commit ('replace_inv');
345     }
346    
347     sub remove {
348     my ($self, $idx) = @_;
349    
350     my $a = $self->get;
351     splice @{$a->{inventory}}, $idx, 1;
352     $self->cursor_change ($idx, 'remove_inv', 1);
353    
354     $self->commit ('remove_inv');
355     }
356    
357     sub commit {
358     my ($self, $msg) = @_;
359     $self->{ref}->commit ($msg);
360     $self->changed ($self->{owner} . '#' . $msg);
361     }
362    
363     sub type {
364     my ($self) = @_;
365     Deliantra::arch_attr $self->get;
366     }
367    
368     sub attr {
369     my ($self, $key) = @_;
370     $self->get->{$key}
371     }
372    
373     sub attr_or_arch {
374     my ($self, $key) = @_;
375     def ($self->attr ($key), $self->archetype->{$key})
376     }
377    
378     sub archetype {
379     my ($self) = @_;
380    
381     $Deliantra::ARCH{$self->get->{_name}} || $Deliantra::ARCH{empty_archetype};
382     }
383    
384     sub picker_folder {
385     my ($self) = @_;
386     my $folder = $self->archetype->{editor_folder};
387     my @a = split /\//, $folder;
388     $a[0]
389     }
390    
391     sub longname {
392     my ($self) = @_;
393    
394     my $name = $self->attr ('_name');
395     my $rname = $self->attr ('name');
396     my $t = $self->type;
397    
398     $name . ($rname ? " - $rname" : "") . " ($t->{name})"
399     }
400    
401     sub field_value_is_default {
402     my ($self, $key, $val) = @_;
403     my $al_arch = $self->archetype;
404    
405     # XXX: Was '... and $val', does this fix problems?
406     (defined ($al_arch->{$key}) && $al_arch->{$key} ne $val)
407     || (not (defined $al_arch->{$key}) and $val)
408     }
409    
410     sub reset_to_defaults {
411     my ($self) = @_;
412    
413     my $arch = $self->get;
414    
415     for (keys %$arch) {
416     delete $arch->{$_} if $_ ne '_name'
417     }
418    
419     $self->commit ('reset_to_defaults');
420     }
421    
422     sub attr_set {
423     my ($self, $key, $value, $type, $src) = @_;
424    
425     my $arch = $self->get;
426     my $al_arch = $self->archetype;
427    
428     if (ref $value) {
429     $arch->{$key} = $value;
430    
431     } else {
432     if (not defined $al_arch->{$key}) {
433     if ((not defined $value) || $value eq ''
434     || ($type eq 'bool' && $value eq '0')) {
435     # try to normalize
436     delete $arch->{$key};
437     } else {
438     # try to normalize
439     $arch->{$key} = $value;
440     }
441     } else {
442     if ($al_arch->{$key} ne $value) {
443     $arch->{$key} = $value;
444     } else {
445     # try to normalize
446     delete $arch->{$key};
447     }
448     }
449     }
450    
451     $self->commit ($src . '!attribute_edit:' . $key);
452     }
453    
454     =head1 AUTHOR
455    
456     Marc Lehmann <schmorp@schmorp.de>
457     http://home.schmorp.de/
458    
459     Robin Redeker <elmex@ta-sa.org>
460     http://www.ta-sa.org/
461    
462     =cut
463     1;