ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/DC/MapWidget.pm
Revision: 1.72
Committed: Wed Jul 5 01:53:24 2006 UTC (17 years, 10 months ago) by root
Branch: MAIN
Changes since 1.71: +5 -5 lines
Log Message:
minor fixes and improvements

File Contents

# User Rev Content
1 root 1.1 package CFClient::MapWidget;
2    
3     use strict;
4 root 1.14 use utf8;
5 root 1.1
6     use List::Util qw(min max);
7    
8 root 1.4 use CFClient::OpenGL;
9 root 1.1
10     our @ISA = CFClient::UI::Base::;
11    
12 root 1.61 my $magicmap_tex =
13     new_from_file CFClient::Texture CFClient::find_rcfile "magicmap.png",
14     mipmap => 1, wrap => 0, internalformat => GL_ALPHA;
15    
16 root 1.1 sub new {
17     my $class = shift;
18    
19 root 1.20 my $self = $class->SUPER::new (
20 root 1.1 z => -1,
21     can_focus => 1,
22 root 1.4 list => glGenList,
23 root 1.51
24     smooth_matrix => [
25     0.05, 0.13, 0.05,
26     0.13, 0.30, 0.13,
27     0.05, 0.13, 0.05,
28     ],
29    
30 root 1.1 @_
31 root 1.20 );
32    
33 root 1.36 $self->{completer} = new CFClient::MapWidget::Command::
34     command => $self->{command},
35     can_focus => 1,
36 root 1.37 tooltip => "<b>The Command Completer</b>\n\n"
37     . "This is your central interface to send text commands to the server. "
38     . "To enter a verbatim command to send to the server, just type the command, "
39     . "followed by a space, and press return. "
40     . "Typing the initial letters of words (or just any letters) displays guesses "
41     . "for commands you might want to use.\n"
42     . "You can use the cursor-up and cursor-down keys to select between those guesses.\n"
43 root 1.64 . "<b>Right-Click</b> opens a menu where you cna select further options, sich as redefining key bindings.",
44 root 1.36 ;
45    
46 root 1.20 $self
47 root 1.1 }
48    
49 root 1.36 sub add_command {
50     my ($self, $command, $tooltip, $widget, $cb) = @_;
51    
52     (my $data = $command) =~ s/\\//g;
53    
54     $tooltip =~ s/^\s+//;
55     $tooltip = "<big>$data</big>\n\n$tooltip";
56     $tooltip =~ s/\s+$//;
57    
58     $self->{completer}{command}{$command} = [$data, $tooltip, $widget, $cb, ++$self->{command_id}];
59     }
60 root 1.4
61 root 1.36 sub clr_commands {
62     my ($self) = @_;
63 root 1.4
64 root 1.36 %{$self->{completer}{command}} = ();
65 root 1.4 }
66    
67 root 1.63 sub invoke_button_down {
68 root 1.1 my ($self, $ev, $x, $y) = @_;
69    
70 root 1.71 if ($ev->{button} == 1) {
71     $self->grab_focus;
72     return unless $::CONN;
73 root 1.1
74 root 1.66 my $x = 1 + CFClient::floor +($ev->{x} - $self->{sx0}) / $self->{tilesize} - $self->{sx};
75     my $y = 1 + CFClient::floor +($ev->{y} - $self->{sy0}) / $self->{tilesize} - $self->{sy};
76 root 1.49
77 root 1.60 $x -= int 0.5 * $self->{sw};
78     $y -= int 0.5 * $self->{sh};
79 root 1.54
80 root 1.56 $::CONN->lookat ($x, $y)
81 root 1.53 if $::CONN;
82 root 1.49
83     } elsif ($ev->{button} == 2) {
84 root 1.71 $self->grab_focus;
85     return unless $::CONN;
86    
87 root 1.5 my ($ox, $oy) = ($ev->{x}, $ev->{y});
88 root 1.1 my ($bw, $bh) = ($::CFG->{map_shift_x}, $::CFG->{map_shift_y});
89    
90     $self->{motion} = sub {
91     my ($ev, $x, $y) = @_;
92    
93 root 1.5 ($x, $y) = ($ev->{x}, $ev->{y});
94 root 1.1
95     $::CFG->{map_shift_x} = $bw + $x - $ox;
96     $::CFG->{map_shift_y} = $bh + $y - $oy;
97    
98     $self->update;
99     };
100 root 1.71 } elsif ($ev->{button} == 3) {
101     (new CFClient::UI::Menu
102     items => [
103 root 1.72 ["Help Browser…\t(F1)", sub { $::HELP_WINDOW->toggle_visibility }],
104     ["Stats &amp; Skills…\t(F2)", sub { ::toggle_player_page ($::STATS_PAGE) }],
105     ["Inventory…\t(F3)", sub { ::toggle_player_page ($::INVENTORY_PAGE) }],
106     ["Spells…\t(F4)", sub { ::toggle_player_page ($::SPELL_PAGE) }],
107     ["Setup… \t(F5)", sub { $::SETUP_DIALOG->toggle_visibility }],
108 root 1.71 ["Server Messages…", sub { $::MESSAGE_WINDOW->toggle_visibility }],
109     [
110     $::PICKUP_ENABLE->{state}
111     ? "Disable automatic pickup"
112     : "Enable automatic pickup",
113     sub { $::PICKUP_ENABLE->toggle }
114     ],
115     ],
116     )->popup ($ev);
117 root 1.1 }
118 root 1.47
119     1
120 root 1.1 }
121    
122 root 1.63 sub invoke_button_up {
123 root 1.1 my ($self, $ev, $x, $y) = @_;
124    
125     delete $self->{motion};
126 root 1.47
127     1
128 root 1.1 }
129    
130 root 1.63 sub invoke_mouse_motion {
131 root 1.1 my ($self, $ev, $x, $y) = @_;
132    
133 root 1.47 if ($self->{motion}) {
134     $self->{motion}->($ev, $x, $y);
135     } else {
136     return 0;
137     }
138    
139     1
140 root 1.1 }
141    
142     sub size_request {
143     (
144 root 1.66 32 * CFClient::ceil $::WIDTH / 32,
145     32 * CFClient::ceil $::HEIGHT / 32,
146 root 1.1 )
147     }
148    
149     sub update {
150     my ($self) = @_;
151    
152     $self->{need_update} = 1;
153     $self->SUPER::update;
154     }
155    
156 root 1.36 my %DIR = (
157     CFClient::SDLK_KP8, [1, "north"],
158     CFClient::SDLK_KP9, [2, "northeast"],
159     CFClient::SDLK_KP6, [3, "east"],
160     CFClient::SDLK_KP3, [4, "southeast"],
161     CFClient::SDLK_KP2, [5, "south"],
162     CFClient::SDLK_KP1, [6, "southwest"],
163     CFClient::SDLK_KP4, [7, "west"],
164     CFClient::SDLK_KP7, [8, "northwest"],
165    
166     CFClient::SDLK_UP, [1, "north"],
167     CFClient::SDLK_RIGHT, [3, "east"],
168     CFClient::SDLK_DOWN, [5, "south"],
169     CFClient::SDLK_LEFT, [7, "west"],
170     );
171    
172 root 1.63 sub invoke_key_down {
173 root 1.36 my ($self, $ev) = @_;
174    
175     my $mod = $ev->{mod};
176     my $sym = $ev->{sym};
177     my $uni = $ev->{unicode};
178    
179 root 1.71 $mod &= CFClient::KMOD_CTRL | CFClient::KMOD_ALT | CFClient::KMOD_SHIFT;
180    
181     if ($uni == ord "\t") {
182     $::PL_WINDOW->toggle_visibility;
183     } elsif ($sym == CFClient::SDLK_F1 && !$mod) {
184     $::HELP_WINDOW->toggle_visibility;
185     } elsif ($sym == CFClient::SDLK_F2 && !$mod) {
186     ::toggle_player_page ($::STATS_PAGE);
187     } elsif ($sym == CFClient::SDLK_F3 && !$mod) {
188     ::toggle_player_page ($::INVENTORY_PAGE);
189     } elsif ($sym == CFClient::SDLK_F4 && !$mod) {
190     ::toggle_player_page ($::SPELL_PAGE);
191     } elsif ($sym == CFClient::SDLK_F5 && !$mod) {
192     $::SETUP_DIALOG->toggle_visibility;
193     } elsif ($sym == CFClient::SDLK_INSERT && $mod & CFClient::KMOD_CTRL) {
194     $::BIND_EDITOR->set_binding (undef, undef, [],
195     sub {
196     my ($mod, $sym, $cmds) = @_;
197     $::BIND_EDITOR->cfg_bind ($mod, $sym, $cmds);
198     });
199     $::BIND_EDITOR->start;
200     $::BIND_EDITOR->show;
201     } elsif ($sym == CFClient::SDLK_INSERT && not ($mod & CFClient::KMOD_CTRL)) {
202     $::BIND_EDITOR->stop;
203     $::BIND_EDITOR->ask_for_bind_and_commit;
204     $::BIND_EDITOR->hide;
205     } elsif (!$::CONN) {
206     return 0; # bindings further down need a valid connection
207    
208     } elsif ($sym == CFClient::SDLK_KP5 && !$mod) {
209 root 1.36 $::CONN->user_send ("stay fire");
210     } elsif ($uni == ord ",") {
211     $::CONN->user_send ("take");
212     } elsif ($uni == ord " ") {
213     $::CONN->user_send ("apply");
214 root 1.37 } elsif ($uni == ord ".") {
215     $::CONN->user_send ($self->{completer}{last_command})
216     if exists $self->{completer}{last_command};
217 root 1.71 } elsif (my $bind_cmd = $::CFG->{profile}{default}{bindings}{$mod}{$sym}) {
218     $::CONN->user_send ($_) for @$bind_cmd;
219     } elsif (($sym == CFClient::SDLK_KP_PLUS && !$mod) || $uni == ord "+") {
220 root 1.36 $::CONN->user_send ("rotateshoottype +");
221 root 1.71 } elsif (($sym == CFClient::SDLK_KP_MINUS && !$mod) || $uni == ord "-") {
222 root 1.36 $::CONN->user_send ("rotateshoottype -");
223     } elsif ($uni == ord '"') {
224     $self->{completer}->set_prefix ("$::CFG->{say_command} ");
225     $self->{completer}->show;
226     } elsif ($uni == ord "'") {
227     $self->{completer}->set_prefix ("");
228     $self->{completer}->show;
229     } elsif (exists $DIR{$sym}) {
230     if ($mod & CFClient::KMOD_SHIFT) {
231     $self->{shft}++;
232 elmex 1.58 if ($DIR{$sym}[0] != $self->{fire_dir}) {
233     $::CONN->user_send ("fire $DIR{$sym}[0]");
234     }
235     $self->{fire_dir} = $DIR{$sym}[0];
236 root 1.36 } elsif ($mod & CFClient::KMOD_CTRL) {
237     $self->{ctrl}++;
238     $::CONN->user_send ("run $DIR{$sym}[0]");
239     } else {
240     $::CONN->user_send ("$DIR{$sym}[1]");
241     }
242 root 1.42 } elsif ((ord 'a') <= $uni && $uni <= (ord 'z')) {
243 root 1.65 $self->{completer}->invoke_key_down ($ev);
244 root 1.36 $self->{completer}->show;
245 root 1.47 } else {
246     return 0;
247 root 1.36 }
248 root 1.47
249     1
250 root 1.36 }
251    
252 root 1.63 sub invoke_key_up {
253 root 1.36 my ($self, $ev) = @_;
254    
255 root 1.47 my $res = 0;
256 root 1.36 my $mod = $ev->{mod};
257     my $sym = $ev->{sym};
258    
259 elmex 1.58 if ($::CFG->{shift_fire_stop}) {
260     if (!($mod & CFClient::KMOD_SHIFT) && delete $self->{shft}) {
261     $::CONN->user_send ("fire_stop");
262     delete $self->{fire_dir};
263     $res = 1;
264     }
265     } else {
266     if (exists $DIR{$sym} && delete $self->{shft}) {
267     $::CONN->user_send ("fire_stop");
268     delete $self->{fire_dir};
269     $res = 1;
270     } elsif (($sym == CFClient::SDLK_LSHIFT || $sym == CFClient::SDLK_RSHIFT) && delete $self->{shft}) { # XXX: is RSHIFT ok?
271     $::CONN->user_send ("fire_stop");
272     delete $self->{fire_dir};
273     $res = 1;
274     }
275 root 1.36 }
276 root 1.47
277 root 1.36 if (!($mod & CFClient::KMOD_CTRL ) && delete $self->{ctrl}) {
278     $::CONN->user_send ("run_stop");
279 root 1.47 $res = 1;
280 root 1.36 }
281 root 1.47
282     $res
283 root 1.36 }
284    
285 root 1.61 sub set_magicmap {
286     my ($self, $w, $h, $x, $y, $data) = @_;
287    
288 root 1.62 $x -= $::MAP->ox + int 0.5 * $::MAP->w;
289     $y -= $::MAP->oy + int 0.5 * $::MAP->h;
290 root 1.61
291     $self->{magicmap} = [$x, $y, $w, $h, $data];
292    
293     $self->update;
294     }
295    
296 root 1.1 sub draw {
297     my ($self) = @_;
298    
299 root 1.60 return unless $::MAP;
300    
301 root 1.36 my $focused = $CFClient::UI::FOCUS == $self
302     || $CFClient::UI::FOCUS == $self->{completer}{entry};
303    
304 root 1.26 return
305 root 1.60 unless $focused || !$::FAST;
306 root 1.26
307 root 1.1 if (delete $self->{need_update}) {
308 root 1.60 my $tilesize = $self->{tilesize} = int 32 * $::CFG->{map_scale};
309    
310     my $sx = $self->{sx} = CFClient::ceil $::CFG->{map_shift_x} / $tilesize;
311     my $sy = $self->{sy} = CFClient::ceil $::CFG->{map_shift_y} / $tilesize;
312    
313     my $sx0 = $self->{sx0} = $::CFG->{map_shift_x} - $sx * $tilesize;
314     my $sy0 = $self->{sy0} = $::CFG->{map_shift_y} - $sy * $tilesize;
315 root 1.59
316 root 1.61 my $sw = $self->{sw} = 1 + CFClient::ceil $self->{w} / $tilesize;
317     my $sh = $self->{sh} = 1 + CFClient::ceil $self->{h} / $tilesize;
318 root 1.60
319     if ($::CFG->{fow_enable}) {
320     my ($w, $h, $data) = $::MAP->fow_texture ($sx, $sy, 0, 0, $sw, $sh);
321    
322     if ($::CFG->{fow_smooth} && $CFClient::OpenGL::GL_VERSION >= 1.2) { # smooth fog of war
323     glConvolutionParameter (GL_CONVOLUTION_2D, GL_CONVOLUTION_BORDER_MODE, GL_CONSTANT_BORDER);
324     glConvolutionFilter2D (
325     GL_CONVOLUTION_2D,
326     GL_ALPHA,
327     3, 3,
328     GL_ALPHA, GL_FLOAT,
329     (pack "f*", @{ $self->{smooth_matrix} }),
330     );
331     glEnable GL_CONVOLUTION_2D;
332     }
333    
334     $self->{fow_texture} = new CFClient::Texture
335     w => $w,
336     h => $h,
337     data => $data,
338     internalformat => GL_ALPHA,
339     format => GL_ALPHA;
340 root 1.1
341 root 1.60 glDisable GL_CONVOLUTION_2D if $::CFG->{fow_smooth};
342     } else {
343     delete $self->{fow_texture};
344     }
345 root 1.1
346 root 1.60 glNewList $self->{list};
347 root 1.31
348 root 1.60 glPushMatrix;
349     glTranslate $sx0, $sy0;
350     glScale $::CFG->{map_scale}, $::CFG->{map_scale};
351 root 1.1
352 root 1.60 $::MAP->draw ($sx, $sy, 0, 0, $sw, $sh);
353 root 1.1
354 root 1.61 glScale 32, 32;
355    
356 root 1.60 if (my $tex = $self->{fow_texture}) {
357     glEnable GL_TEXTURE_2D;
358     glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE;
359 root 1.1
360 root 1.60 glColor +($::CFG->{fow_intensity}) x 3, 0.9;
361     $self->{fow_texture}->draw_quad_alpha (0, 0);
362 root 1.1
363 root 1.60 glDisable GL_TEXTURE_2D;
364 root 1.1 }
365    
366 root 1.61 if ($self->{magicmap}) {
367     my ($x, $y, $w, $h, $data) = @{ $self->{magicmap} };
368    
369 root 1.62 $x += $::MAP->ox - $sx + int 0.5 * ($::MAP->w - $sw + 1);
370     $y += $::MAP->oy - $sy + int 0.5 * ($::MAP->h - $sh + 1);
371 root 1.61
372     glTranslate - $x - 1, - $y - 1;
373     glBindTexture GL_TEXTURE_2D, $magicmap_tex->{name};
374     $::MAP->draw_magicmap ($x, $y, $w, $h, $data);
375     }
376    
377 root 1.60 glPopMatrix;
378 root 1.1 glEndList;
379     }
380    
381     glCallList $self->{list};
382    
383 root 1.29 # TNT2 emulates logops in software (or worse :)
384 root 1.60 unless ($focused) {
385 root 1.32 glColor 0.4, 0.2, 0.2, 0.6;
386 root 1.29 glEnable GL_BLEND;
387     glBlendFunc GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA;
388 root 1.1 glBegin GL_QUADS;
389     glVertex 0, 0;
390     glVertex 0, $::HEIGHT;
391     glVertex $::WIDTH, $::HEIGHT;
392     glVertex $::WIDTH, 0;
393     glEnd;
394 root 1.29 glDisable GL_BLEND;
395 root 1.1 }
396     }
397    
398 root 1.36 sub DESTROY {
399     my $self = shift;
400 root 1.3
401 root 1.36 glDeleteList $self->{list};
402 root 1.1
403 root 1.36 $self->SUPER::DESTROY;
404 root 1.8 }
405    
406 root 1.18 package CFClient::MapWidget::MapMap;
407    
408 root 1.19 our @ISA = CFClient::UI::Base::;
409 root 1.18
410     use Time::HiRes qw(time);
411     use CFClient::OpenGL;
412    
413     sub size_request {
414     ($::HEIGHT * 0.25, $::HEIGHT * 0.25)
415     }
416    
417 root 1.63 sub invoke_size_allocate {
418 root 1.18 my ($self, $w, $h) = @_;
419    
420     $self->update;
421 root 1.63
422     1
423 root 1.18 }
424    
425     sub update {
426     my ($self) = @_;
427    
428     delete $self->{texture_atime};
429     $self->SUPER::update;
430     }
431    
432     sub _draw {
433     my ($self) = @_;
434    
435     $::MAP or return;
436    
437     my ($w, $h) = @$self{qw(w h)};
438    
439     my $sw = int $::WIDTH / (32 * $::CFG->{map_scale}) + 0.99;
440     my $sh = int $::HEIGHT / (32 * $::CFG->{map_scale}) + 0.99;
441    
442     my $sx = int $::CFG->{map_shift_x} / 32;
443     my $sy = int $::CFG->{map_shift_y} / 32;
444    
445     my $ox = 0.5 * ($w - $sw);
446     my $oy = 0.5 * ($h - $sh);
447    
448     glEnable GL_BLEND;
449     glBlendFunc GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA;
450     glEnable GL_TEXTURE_2D;
451     glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE;
452    
453     if ($self->{texture_atime} < time) {
454     $self->{texture_atime} = time + 1/3;
455    
456     $self->{texture} =
457     new CFClient::Texture
458     w => $w,
459     h => $h,
460     data => $::MAP->mapmap (-$ox, -$oy, $w, $h),
461     type => $CFClient::GL_VERSION >= 1.2 ? GL_UNSIGNED_INT_8_8_8_8_REV : GL_UNSIGNED_BYTE;
462     }
463    
464     $self->{texture}->draw_quad (0, 0);
465    
466     glDisable GL_TEXTURE_2D;
467    
468     glTranslate 0.375, 0.375;
469    
470     #TODO: map scale is completely borked
471    
472     my $x0 = int $ox - $sx + 0.5;
473     my $y0 = int $oy - $sy + 0.5;
474    
475     glColor 1, 1, 0, 1;
476     glBegin GL_LINE_LOOP;
477     glVertex $x0 , $y0 ;
478     glVertex $x0 , $y0 + $sh;
479     glVertex $x0 + $sw, $y0 + $sh;
480     glVertex $x0 + $sw, $y0 ;
481     glEnd;
482    
483     glDisable GL_BLEND;
484     }
485    
486 root 1.8 package CFClient::MapWidget::Command;
487    
488     use strict;
489    
490     use CFClient::OpenGL;
491    
492 root 1.23 our @ISA = CFClient::UI::Frame::;
493 root 1.8
494     sub new {
495     my $class = shift;
496    
497     my $self = $class->SUPER::new (
498 root 1.23 bg => [0, 0, 0, 0.8],
499 root 1.8 @_,
500 root 1.23 );
501    
502     $self->add ($self->{vbox} = new CFClient::UI::VBox);
503    
504     $self->{label} = [
505     map
506 root 1.8 CFClient::UI::Label->new (
507 root 1.25 can_hover => 1,
508     can_events => 1,
509     tooltip_width => 0.33,
510     fontsize => $_,
511 root 1.27 ), (0.8) x 16
512 root 1.23 ];
513    
514     $self->{entry} = new CFClient::UI::Entry
515 root 1.40 on_changed => sub {
516 root 1.23 $self->update_labels;
517 root 1.63 0
518 root 1.36 },
519 elmex 1.57 on_button_down => sub {
520     my ($entry, $ev, $x, $y) = @_;
521    
522     if ($ev->{button} == 3) {
523     (new CFClient::UI::Menu
524     items => [
525 root 1.69 ["bind <i>" . (CFClient::UI::Label::escape $self->{select}) . "</i> to a key"
526     => sub { $::BIND_EDITOR->do_quick_binding ([$self->{select}], sub { $entry->grab_focus }) }]
527 elmex 1.57 ],
528     )->popup ($ev);
529     return 1;
530     }
531     0
532     },
533 root 1.40 on_key_down => sub {
534 root 1.36 my ($entry, $ev) = @_;
535    
536     my $self = $entry->{parent}{parent};
537    
538     if ($ev->{sym} == 13) {
539     if (exists $self->{select}) {
540 root 1.37 $self->{last_command} = $self->{select};
541 root 1.36 $::CONN->user_send ($self->{select});
542 elmex 1.52
543 elmex 1.68 unshift @{$self->{history}}, $self->{entry}->get_text;
544 elmex 1.52 $self->{hist_ptr} = 0;
545    
546 root 1.36 $self->hide;
547     }
548     } elsif ($ev->{sym} == 27) {
549 elmex 1.52 $self->{hist_ptr} = 0;
550 root 1.36 $self->hide;
551     } elsif ($ev->{sym} == CFClient::SDLK_DOWN) {
552 elmex 1.52 if ($self->{hist_ptr} > 1) {
553     $self->{hist_ptr}--;
554     $self->{entry}->set_text ($self->{history}->[$self->{hist_ptr} - 1]);
555     } elsif ($self->{hist_ptr} > 0) {
556     $self->{hist_ptr}--;
557     $self->{entry}->set_text ($self->{hist_saveback});
558     } else {
559     ++$self->{select_offset}
560     if $self->{select_offset} < $#{ $self->{last_match} || [] };
561     }
562 root 1.36 $self->update_labels;
563     } elsif ($ev->{sym} == CFClient::SDLK_UP) {
564 elmex 1.52 if ($self->{select_offset}) {
565     --$self->{select_offset}
566     } else {
567     unless ($self->{hist_ptr}) {
568     $self->{hist_saveback} = $self->{entry}->get_text;
569     }
570     if ($self->{hist_ptr} <= $#{$self->{history}}) {
571     $self->{hist_ptr}++;
572     }
573     $self->{entry}->set_text ($self->{history}->[$self->{hist_ptr} - 1])
574     if exists $self->{history}->[$self->{hist_ptr} - 1];
575     }
576 root 1.36 $self->update_labels;
577     } else {
578     return 0;
579     }
580    
581     1
582     }
583     ;
584 root 1.23
585     $self->{vbox}->add (
586     $self->{entry},
587     @{$self->{label}},
588 root 1.8 );
589    
590     $self
591     }
592    
593 root 1.36 sub set_prefix {
594     my ($self, $prefix) = @_;
595    
596     $self->{entry}->set_text ($prefix);
597     $self->show;
598     }
599    
600 root 1.63 sub invoke_size_allocate {
601 root 1.8 my ($self, $w, $h) = @_;
602    
603 root 1.44 $self->move_abs (($::WIDTH - $w) * 0.5, ($::HEIGHT - $h) * 0.6, 10);
604 root 1.63
605     $self->SUPER::invoke_size_allocate ($w, $h)
606 root 1.8 }
607    
608 root 1.36 sub show {
609     my ($self) = @_;
610    
611     $self->SUPER::show;
612 root 1.63 $self->{entry}->grab_focus;
613 root 1.36 }
614    
615     sub hide {
616     my ($self) = @_;
617    
618     $self->SUPER::hide;
619     $self->{entry}->set_text ("");
620     }
621    
622 root 1.63 sub invoke_key_down {
623 root 1.23 my ($self, $ev) = @_;
624    
625 root 1.65 $self->{entry}->emit (key_down => $ev)
626 root 1.23 }
627    
628 root 1.8 sub update_labels {
629     my ($self) = @_;
630    
631 root 1.23 my $text = $self->{entry}->get_text;
632    
633     length $text
634 root 1.36 or return $self->hide;
635 root 1.23
636     my ($cmd, $arg) = $text =~ /^\s*([^[:space:]]*)(.*)$/;
637    
638 root 1.36 if ($text ne $self->{last_search}) {
639     my @match;
640 root 1.23
641 root 1.36 if ($text =~ /^(.*?)\s+$/) {
642     @match = [$cmd, "(appended whitespace suppresses completion)"];
643     } else {
644     my $regexp = do {
645     my ($beg, @chr) = split //, lc $cmd;
646 root 1.23
647 root 1.36 # the following regex is used to match our "completion entry"
648     # to an actual command - the parentheses match kind of "overhead"
649     # - the more characters the parentheses match, the less attractive
650     # is the match.
651     my $regexp = "^\Q$beg\E"
652     . join "", map "(?:.*?[ \\\\]\Q$_\E|(.*?)\Q$_\E)", @chr;
653     qr<$regexp>
654     };
655    
656     my @penalty;
657    
658     for (keys %{$self->{command}}) {
659     if (@penalty = $_ =~ $regexp) {
660     push @match, [$_, length join "", map "::$_", grep defined, @penalty];
661     }
662 root 1.23 }
663 root 1.36
664     @match = map $self->{command}{$_->[0]},
665     sort {
666     $a->[1] <=> $b->[1]
667     or $self->{command}{$a->[0]}[4] <=> $self->{command}{$b->[0]}[4]
668 root 1.39 or (length $b->[0]) <=> (length $a->[0])
669 root 1.36 } @match;
670 root 1.8 }
671 root 1.23
672 root 1.39 $self->{last_search} = $text;
673 root 1.23 $self->{last_match} = \@match;
674    
675     $self->{select_offset} = 0;
676 root 1.8 }
677    
678 root 1.23 my @labels = @{ $self->{label} };
679     my @matches = @{ $self->{last_match} || [] };
680 root 1.8
681 root 1.23 if ($self->{select_offset}) {
682     splice @matches, 0, $self->{select_offset}, ();
683 root 1.8
684 root 1.23 my $label = shift @labels;
685     $label->set_text ("...");
686     $label->set_tooltip ("Use Cursor-Up to view previous matches");
687 root 1.8 }
688    
689 root 1.23 for my $label (@labels) {
690     $label->{fg} = [1, 1, 1, 1];
691     $label->{bg} = [0, 0, 0, 0];
692     }
693    
694     if (@matches) {
695     $self->{select} = "$matches[0][0]$arg";
696    
697     $labels[0]->{fg} = [0, 0, 0, 1];
698     $labels[0]->{bg} = [1, 1, 1, 0.8];
699     } else {
700 root 1.24 $self->{select} = "$cmd$arg";
701 root 1.23 }
702    
703     for my $match (@matches) {
704     my $label = shift @labels;
705    
706     if (@labels) {
707     $label->set_text ("$match->[0]$arg");
708     $label->set_tooltip ($match->[1]);
709     } else {
710     $label->set_text ("...");
711     $label->set_tooltip ("Use Cursor-Down to view more matches");
712     last;
713     }
714     }
715 root 1.8
716 root 1.23 for my $label (@labels) {
717     $label->set_text ("");
718     $label->set_tooltip ("");
719 root 1.8 }
720    
721 root 1.23 $self->update;
722 root 1.8 }
723    
724 root 1.23 sub _draw {
725     my ($self) = @_;
726 root 1.8
727 root 1.23 # hack
728     local $CFClient::UI::FOCUS = $self->{entry};
729 root 1.10
730 root 1.23 $self->SUPER::_draw;
731 root 1.2 }
732    
733 root 1.1 1