ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/gde/GCE/Map.pm
Revision: 1.11
Committed: Sun Feb 5 21:19:41 2006 UTC (18 years, 3 months ago) by root
Branch: MAIN
CVS Tags: HEAD
Changes since 1.10: +0 -0 lines
State: FILE REMOVED
Log Message:
*** empty log message ***

File Contents

# Content
1 =head1 NAME
2
3 Gtk2::CV::Schnauzer - a widget for displaying image collections
4
5 =head1 SYNOPSIS
6
7 use Gtk2::CV::Schnauzer;
8
9 =head1 DESCRIPTION
10
11 =head2 METHODS
12
13 =over 4
14
15 =cut
16
17 package GCE::Map;
18
19 use strict;
20
21 use Gtk2;
22
23 use Crossfire;
24 use Crossfire::Gtk2;
25
26 use Glib::Object::Subclass
27 'Gtk2::DrawingArea';
28
29 use List::Util qw(min max);
30
31 sub INIT_INSTANCE {
32 my ($self) = @_;
33
34 $self->signal_connect (destroy => sub { %{$_[0]} = () });
35 $self->signal_connect (realize => sub {
36 my ($self) = @_;
37
38 $self->{window} = $self->window;
39
40 1
41 });
42
43 $self->set_redraw_on_allocate (0); # nope
44 $self->double_buffered (0);
45
46 $self->signal_connect (size_request => sub {
47 $_[1]->width (20 * TILESIZE);
48 $_[1]->height (20 * TILESIZE);
49
50 1
51 });
52
53 $self->signal_connect (expose_event => sub { $self->expose ($_[1]) });
54
55 $self->signal_connect_after (configure_event => sub {
56 $self->set_viewport ($self->{x}, $self->{y});
57 1
58 });
59
60 $self->signal_connect (button_press_event => sub {
61 my ($self, $event) = @_;
62
63 my ($x, $y) = ($event->x, $event->y);
64
65 if ($_[1]->button == 2) {
66 $_[0]->grab_focus;
67 $self->{in_drag} = [$self->{x}, $self->{y}, $x, $y];
68 return 0;
69 }
70
71 1
72 });
73
74 $self->signal_connect (motion_notify_event => sub {
75 my ($self) = @_;
76
77 my ($x, $y) = $self->get_pointer;
78
79 if (my $di = $self->{in_drag}) {
80 $self->set_viewport (
81 $di->[0] + $di->[2] - $x,
82 $di->[1] + $di->[3] - $y,
83 );
84
85 return 0;
86 }
87
88 1;
89 });
90
91 $self->signal_connect (button_release_event => sub {
92 my ($self) = @_;
93
94 delete $self->{in_drag};
95
96 1
97 });
98
99 # unnecessary redraws...
100 $self->signal_connect (focus_in_event => sub { 1 });
101 $self->signal_connect (focus_out_event => sub { 1 });
102
103 # gtk+ supports no motion compression, a major lacking feature. we have to pay for the
104 # workaround with incorrect behaviour and extra server-turnarounds.
105 $self->add_events ([qw(key_press_mask key_release_mask button_press_mask button_release_mask button-motion-mask scroll_mask pointer-motion-hint-mask)]);
106 $self->can_focus (1);
107
108 # $self->signal_connect (key_press_event => sub { $self->handle_key ($_[1]->keyval, $_[1]->state) });
109 }
110
111 sub set_viewport {
112 my ($self, $x, $y) = @_;
113
114 my $area = $self->allocation;
115
116 $x = max 0, min $self->{width} - $area->width , $x;
117 $y = max 0, min $self->{height} - $area->height, $y;
118
119 $self->window->scroll ($self->{x} - $x, $self->{y} - $y);
120
121 ($self->{x}, $self->{y}) = ($x, $y);
122 }
123
124 sub set_map {
125 my ($self, $map) = @_;
126
127 $self->{map} = $map;
128
129 $self->{width} = $map->{width} * TILESIZE;
130 $self->{height} = $map->{height} * TILESIZE;
131
132 $self->{x} =
133 $self->{y} = 0;
134
135 delete $self->{face};
136 $self->update_map (0, 0, $self->{width}, $self->{height});
137
138 $self->invalidate_all;
139 }
140
141 sub coord {
142 my ($self, $x, $y) = @_;
143
144 (
145 int $self->{x} + $x / TILESIZE,
146 int $self->{y} + $y / TILESIZE,
147 )
148 }
149
150 #sub handle_key {
151 # my ($self, $key, $state) = @_;
152 #
153 # $self->prefetch_cancel;
154 #
155 # if ($state * "control-mask") {
156 # if ($key == $Gtk2::Gdk::Keysyms{g}) {
157 # my @sel = keys %{$self->{sel}};
158 # $self->generate_thumbnails (@sel ? @sel : 0 .. $#{$self->{entry}});
159 # } elsif ($key == $Gtk2::Gdk::Keysyms{a}) {
160 # $self->select_all;
161 # } elsif ($key == $Gtk2::Gdk::Keysyms{A}) {
162 # $self->select_range ($self->{offs}, $self->{offs} + $self->{cols} * $self->{page} - 1);
163 # } elsif ($key == $Gtk2::Gdk::Keysyms{s}) {
164 # $self->rescan;
165 # } elsif ($key == $Gtk2::Gdk::Keysyms{d}) {
166 # $self->unlink (keys %{$self->{sel}});
167 # } elsif ($key == $Gtk2::Gdk::Keysyms{u}) {
168 # my @sel = keys %{$self->{sel}};
169 # $self->update_thumbnails (@sel ? @sel : 0 .. $#{$self->{entry}});
170 #
171 # } elsif ($key == $Gtk2::Gdk::Keysyms{Return}) {
172 # $self->cursor_move (0) unless $self->cursor_valid;
173 # $self->emit_activate ($self->{cursor});
174 # } elsif ($key == $Gtk2::Gdk::Keysyms{space}) {
175 # $self->cursor_move (1) or return 1
176 # if $self->{cursor_current} || !$self->cursor_valid;
177 # $self->emit_activate ($self->{cursor}) if $self->cursor_valid;
178 # $self->prefetch (1);
179 # } elsif ($key == $Gtk2::Gdk::Keysyms{BackSpace}) {
180 # $self->cursor_move (-1) or return 1;
181 # $self->emit_activate ($self->{cursor}) if $self->cursor_valid;
182 # $self->prefetch (-1);
183 #
184 # } else {
185 # return 0;
186 # }
187 # } else {
188 # if ($key == $Gtk2::Gdk::Keysyms{Page_Up}) {
189 # my $value = $self->{adj}->value;
190 # $self->{adj}->set_value ($value >= $self->{page} ? $value - $self->{page} : 0);
191 # $self->clear_cursor;
192 # } elsif ($key == $Gtk2::Gdk::Keysyms{Page_Down}) {
193 # my $value = $self->{adj}->value + $self->{page};
194 # $self->{adj}->set_value ($value <= $self->{maxrow} ? $value : $self->{maxrow});
195 # $self->clear_cursor;
196 #
197 # } elsif ($key == $Gtk2::Gdk::Keysyms{Home}) {
198 # $self->{adj}->set_value (0);
199 # $self->clear_cursor;
200 # } elsif ($key == $Gtk2::Gdk::Keysyms{End}) {
201 # $self->{adj}->set_value ($self->{maxrow});
202 # $self->clear_cursor;
203 #
204 # } elsif ($key == $Gtk2::Gdk::Keysyms{Up}) {
205 # $self->cursor_move (-$self->{cols});
206 # } elsif ($key == $Gtk2::Gdk::Keysyms{Down}) {
207 # $self->cursor_move (+$self->{cols});
208 # } elsif ($key == $Gtk2::Gdk::Keysyms{Left}) {
209 # $self->cursor_move (-1);
210 # } elsif ($key == $Gtk2::Gdk::Keysyms{Right}) {
211 # $self->cursor_move (+1);
212 #
213 # } elsif ($key == $Gtk2::Gdk::Keysyms{Return}) {
214 # $self->cursor_move (0) unless $self->cursor_valid;
215 # $self->emit_activate ($self->{cursor});
216 # } elsif ($key == $Gtk2::Gdk::Keysyms{space}) {
217 # $self->cursor_move (1) or return 1
218 # if $self->{cursor_current} || !$self->cursor_valid;
219 # $self->emit_activate ($self->{cursor}) if $self->cursor_valid;
220 # $self->prefetch (1);
221 # } elsif ($key == $Gtk2::Gdk::Keysyms{BackSpace}) {
222 # $self->cursor_move (-1) or return 1;
223 # $self->emit_activate ($self->{cursor}) if $self->cursor_valid;
224 # $self->prefetch (-1);
225 #
226 # } elsif ($key == ord '^') {
227 # $self->updir if exists $self->{dir};
228 #
229 # } elsif (($key >= (ord '0') && $key <= (ord '9'))
230 # || ($key >= (ord 'a') && $key <= (ord 'z'))) {
231 #
232 # $key = chr $key;
233 #
234 # my ($idx, $cursor) = (0, 0);
235 #
236 # $self->clear_selection;
237 #
238 # for my $entry (@{$self->{entry}}) {
239 # $idx++;
240 # $cursor = $idx if $key gt lcfirst $entry->[1];
241 # }
242 #
243 # if ($cursor < @{$self->{entry}}) {
244 # delete $self->{cursor_current};
245 # $self->{sel}{$cursor} = $self->{entry}[$cursor];
246 # $self->{cursor} = $cursor;
247 #
248 # $self->{adj}->set_value (min $self->{maxrow}, $cursor / $self->{cols});
249 # $self->emit_sel_changed;
250 # $self->invalidate_all;
251 # }
252 # } else {
253 # return 0;
254 # }
255 # }
256 #
257 # 1
258 #}
259
260 sub invalidate {
261 my ($self, $x, $y, $w, $h) = @_;
262
263 return unless $self->{window};
264
265 $self->queue_draw_area (
266 map $_ * TILESIZE, $x - 1 , $y - 1, $w + 2, $h + 2
267 );
268 }
269
270 sub invalidate_all {
271 my ($self) = @_;
272
273 $self->queue_draw;
274 }
275
276 sub update_map {
277 my ($self, $x, $y, $w, $h) = @_;
278
279 delete $self->{overlay};
280
281 my $map = $self->{map}{map};
282 my $ov = $self->{face} ||= [];
283
284 $x = 0; $w = $self->{map}{width};
285 $y = 0; $h = $self->{map}{height};
286
287 my @x = ($x .. $x + $w - 1);
288 my @y = ($y .. $y + $h - 1);
289
290 my $TC = \%Crossfire::Gtk2::TILECACHE;
291
292 my @ov;
293
294 # update overlay map with bigfaces and chained faces
295 for my $x (@x) {
296 my $ass = $self->{map}{map}[$x];
297 my $oss = $ov->[$x] ||= [];
298 for my $y (@y) {
299 my $os = $oss->[$y] = [];
300 for my $a (@{ $ass->[$y] || [] }) {
301 my $o = $ARCH->{$a->{_name}}
302 or (warn "arch '$a->{_name}' not found at ($x|$y)\n"), next;
303
304 my $tile = $TC->{$a->{face} || $o->{face}}
305 or (warn "no gfx found for arch '$a->{_name}' at ($x|$y)\n"), next;
306
307 if ($tile->{w} > 1 || $tile->{h} > 1) {
308 # bigfaces
309 for my $ox (0 .. $tile->{w} - 1) {
310 for my $oy (0 .. $tile->{h} - 1) {
311 push @ov, [$x + $ox, $y + $oy,
312 $tile->{idx} + $ox + $oy * $tile->{w}];
313 }
314 }
315
316 } elsif ($o->{more}) {
317 # linked faces
318 do {
319 my $tile = $TC->{$o->{face}}
320 or (warn "no gfx found for arch '$a->{_name}' at ($x*|$y*)\n"), next;
321 push @ov, [$x + $o->{x}, $y + $o->{y}, $tile->{idx}];
322 } while $o = $o->{more};
323
324 } else {
325 # single face
326 push @$os, $tile->{idx};
327
328 }
329 }
330 }
331 }
332
333 # bigger faces always on top, I don't give a shit to those who think otherwise
334 for (@ov) {
335 my ($x, $y, $idx) = @$_;
336
337 push @{ $ov->[$x][$y] }, $idx;
338 }
339 }
340
341 sub expose {
342 my ($self, $event) = @_;
343
344 no integer;
345
346 my $ox = $self->{x}; my $ix = int $ox / TILESIZE;
347 my $oy = $self->{y}; my $iy = int $oy / TILESIZE;
348
349 # get_rectangles is buggy in older versions
350 for my $area ($Gtk2::VERSION > 1.115 ? $event->region->get_rectangles : $event->area) {
351 my ($x, $y, $w, $h) = $area->values; # x y w h
352
353 my @x = ((int ($ox + $x) / TILESIZE) .. int +($ox + $x + $w + TILESIZE - 1) / TILESIZE);
354 my @y = ((int ($oy + $y) / TILESIZE) .. int +($oy + $y + $h + TILESIZE - 1) / TILESIZE);
355
356 my $PB = $Crossfire::Gtk2::TILECACHE;
357
358 my $window = $self->{window};
359
360 my $pb = new Gtk2::Gdk::Pixbuf 'rgb', 0, 8, TILESIZE * (@x + 1), TILESIZE * (@y + 1);
361 $pb->fill (0x00000000);
362
363 for my $x (@x) {
364 my $dx = ($x - $x[0]) * TILESIZE;
365 my $oss = $self->{face}[$x];
366
367 for my $y (@y) {
368 my $dy = ($y - $y[0]) * TILESIZE;
369
370 for my $idx (@{$oss->[$y]}) {
371 $PB->composite ($pb,
372 $dx, $dy,
373 TILESIZE, TILESIZE,
374 $dx - ($idx % 64) * TILESIZE, $dy - TILESIZE * int $idx / 64,
375 1, 1, 'nearest', 255
376 );
377 }
378 }
379 }
380
381 $pb->render_to_drawable ($window, $self->style->black_gc,
382 0, 0,
383 $x[0] * TILESIZE - $ox, $y[0] * TILESIZE - $oy,
384 TILESIZE * @x, TILESIZE * @y,
385 'max', 0, 0);
386 }
387
388 1
389 }
390
391 =back
392
393 =head1 AUTHOR
394
395 Marc Lehmann <schmorp@schmorp.de>
396
397 =cut
398
399 1
400