File Coverage

blib/lib/Tk/ComboEntry.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package Tk::ComboEntry;
2              
3 1     1   2635 use Tk::Listbox;
  0            
  0            
4             use Tk::Entry;
5             use Tk;
6              
7             use base qw (Tk::Derived Tk::Frame);
8             use vars qw ($VERSION);
9             use strict;
10              
11             $VERSION = '0.03';
12              
13             *listheight = \&Tk::ComboEntry::ListHeight;
14             *state = \&Tk::ComboEntry::SelectionState;
15             *itemlist = \&Tk::ComboEntry::SelectionList;
16             *list = \&Tk::ComboEntry::SelectionList;
17             *listfont = \&Tk::ComboEntry::ListFont;
18             *invoke = \&Tk::ComboEntry::Invoke;
19              
20             Tk::Widget->Construct ('ComboEntry');
21              
22             sub ClassInit
23             {
24             $_[1]->bind ($_[0], '', 'Configure');
25             $_[1]->bind ($_[0], '', 'Configure');
26             return $_[0];
27             }
28              
29             sub Populate
30             {
31             my $this = shift;
32              
33             eval
34             {
35             my $l_Bitmask = pack
36             (
37             "b8"x8,
38             "..........",
39             ".11111111.",
40             "..111111..",
41             "..111111..",
42             "...1111...",
43             "...1111...",
44             "....11....",
45             "....11....",
46             );
47              
48             $this->toplevel->DefineBitmap
49             (
50             'downtriangle' => 8, 8, $l_Bitmask
51             );
52             };
53              
54             my $l_Entry = $this->Component
55             (
56             'Entry' => 'Entry',
57             '-highlightthickness' => 1,
58             '-borderwidth' => 0,
59             '-relief' => 'flat',
60             '-takefocus' => 1,
61             '-width' => 0,
62             );
63              
64             my $l_Button = $this->Component
65             (
66             'Button' => 'Button',
67             '-bitmap' => 'downtriangle',
68             '-command' => sub {$this->ButtonPressed();},
69             '-highlightthickness' => 1,
70             '-relief' => 'raised',
71             '-borderwidth' => 1,
72             '-takefocus' => 1,
73             '-width' => 0,
74             );
75              
76             my $l_Popup = $this->Component
77             (
78             'Toplevel' => 'Popup',
79             '-relief' => 'raised',
80             '-borderwidth' => 1,
81             );
82              
83             my $l_ListBox = $l_Popup->Scrolled
84             (
85             'Listbox',
86             '-cursor' => 'top_left_arrow',
87             '-highlightthickness' => 1,
88             '-selectmode' => 'browse',
89             '-scrollbars' => 'osoe',
90             '-relief' => 'flat',
91             '-takefocus' => 1,
92             );
93              
94             my $l_ActualListBox = $this->{'m_ListBox'} = $l_ListBox->Subwidget
95             (
96             'scrolled',
97             );
98              
99             ($this->{'m_ScrollBarY'} = $l_ListBox->Subwidget ('yscrollbar'))->configure
100             (
101             '-borderwidth' => 1,
102             );
103              
104             $l_ActualListBox->selection
105             (
106             'set',
107             '0',
108             );
109              
110             $l_Entry->bind
111             (
112             '' => sub {$this->DoInvokeCallback();},
113             );
114              
115             $l_Popup->bind
116             (
117             '' => sub {$this->AutoHide ($l_Popup);},
118             );
119              
120             $l_Button->bind
121             (
122             '' => sub {$l_Button->invoke();},
123             );
124              
125             $l_ActualListBox->bind
126             (
127             '' => sub {$this->Hide();},
128             );
129              
130             $l_ActualListBox->bind
131             (
132             '' => sub {$this->Select();},
133             );
134              
135             $l_ActualListBox->bind
136             (
137             '' => sub {$this->MenuSelect();},
138             );
139              
140             $l_ActualListBox->bind
141             (
142             '' => sub {$this->Traverse (@_);},
143             );
144              
145             $l_ActualListBox->bind
146             (
147             '' => [sub {$this->KeySeek (@_);}, Ev ('A')],
148             );
149              
150             $l_ActualListBox->bind
151             (
152             '' => sub {$this->Select();},
153             );
154              
155             $l_ListBox->pack
156             (
157             '-expand' => 'true',
158             '-fill' => 'both',
159             '-padx' => 0,
160             '-pady' => 0,
161             );
162              
163             $l_Entry->pack
164             (
165             '-expand' => 'true',
166             '-fill' => 'both',
167             '-anchor' => 'nw',
168             '-side' => 'left',
169             '-ipadx' => 0,
170             '-ipady' => 0,
171             '-padx' => 0,
172             '-pady' => 0,
173             );
174              
175             $l_Button->pack
176             (
177             '-side' => 'right',
178             '-anchor' => 'ne',
179             '-fill' => 'y',
180             '-ipadx' => 0,
181             '-ipady' => 0,
182             '-padx' => 0,
183             '-pady' => 0,
184             );
185              
186             $this->ConfigSpecs
187             (
188             '-background' => [['SELF', 'METHOD', $l_Entry, $l_ListBox], 'background', 'Background', 'white'],
189             '-listfont' => ['METHOD', 'font', 'Font', '-*-Times-Bold-R-Normal--*-120-*-*-*-*-*-*'],
190             '-scrollbarwidth' => ['METHOD', 'scrollbarwidth', 'ScrollbarWidth', undef],
191             '-borderwidth' => [['SELF', $l_Button], 'borderwidth', 'BorderWidth', 1],
192             '-popupwidth' => ['METHOD', 'popupwidth', 'PopupWidth', undef],
193             '-listheight' => ['METHOD', 'listheight', 'ListHeight', 90],
194             '-showmenu' => ['PASSIVE', 'showmenu', 'ShowMenu', 1],
195             '-state' => ['METHOD', 'state', 'State', 'normal'],
196             '-selectmode' => [$l_ListBox],
197             '-itemlist' => ['METHOD'],
198             '-invoke' => ['METHOD'],
199             '-list' => ['METHOD'],
200             '-bg' => '-background',
201             );
202              
203             $this->configure ('-relief' => 'sunken');
204             $this->ConfigSpecs ("DEFAULT" => [$l_Entry]);
205             $this->Delegates (DEFAULT => $l_Entry);
206             $this->SUPER::Populate (@_);
207             $this->Hide();
208             return $this;
209             }
210              
211             sub Configure
212             {
213             my $this = shift;
214              
215             $this->Subwidget ('Entry')->configure
216             (
217             '-state' => $this->SelectionState,
218             );
219              
220             $this->Subwidget ('Button')->configure
221             (
222             '-width' => $this->height() - ($this->cget ('-borderwidth') * 4),
223             );
224             }
225              
226             sub ButtonPressed
227             {
228             $_[0]->{'m_Visible'} ? $_[0]->Hide() : $_[0]->Show();
229             }
230              
231             sub SelectionList
232             {
233             $_[0]->{m_ListBox}->delete ('0', 'end');
234              
235             foreach my $l_Entry (sort (ref ($_[1]) eq 'ARRAY' ? @{$_[1]} : @_))
236             {
237             chomp $l_Entry;
238             $_[0]->{m_ListBox}->insert ('end', $l_Entry);
239             }
240             }
241              
242             sub ListHeight
243             {
244             return ($_[0]->{'m_ListHeight'} = (defined ($_[1] && $_[1] > 2) ? $_[1] : $_[0]->{m_ListHeight}));
245             }
246              
247             sub ListFont
248             {
249             $_[0]->{m_ListBox}->configure ('-font' => $_[1]) if defined ($_[1]);
250             return $_[0]->{m_ListBox}->cget ('-font');
251             }
252              
253             sub Invoke
254             {
255             return (defined ($_[1]) ? $_[0]->{m_Invoke} = $_[1] : $_[0]->{m_Invoke});
256             }
257              
258             sub SelectionState
259             {
260             return ($_[0]->{m_SelectionState}) unless (defined ($_[1]));
261             $_[0]->Subwidget ('Entry')->configure ('-state' => ($_[0]->{m_SelectionState} = $_[1]));
262             return ($_[0]->{m_SelectionState});
263             }
264              
265             sub Hide
266             {
267             my $this = shift;
268             my $l_Popup = $this->Subwidget ('Popup');
269             $l_Popup->overrideredirect (1);
270             $l_Popup->transient();
271             $l_Popup->withdraw();
272             $l_Popup->grabRelease();
273             $this->{m_Visible} = 0;
274             $this->Subwidget ('Button')->focus();
275             }
276              
277             sub Show
278             {
279             my $this = shift;
280              
281             my ($l_Popup, $l_Entry) =
282             (
283             $this->Subwidget ('Popup'),
284             $this->Subwidget ('Entry'),
285             );
286              
287             my $l_Geometry =
288             (
289             ($this->cget ('-popupwidth') || $this->width()).
290             'x'.
291             ($this->{m_ListHeight} || 40).
292             '+'.
293             $l_Entry->rootx().
294             '+'.
295             ($this->rooty() + $this->height())
296             );
297              
298             $l_Popup->geometry ($l_Geometry);
299             $l_Popup->deiconify();
300             $l_Popup->transient();
301             $l_Popup->raise();
302             $l_Popup->grabGlobal();
303              
304             $this->{m_ListBox}->focus();
305             $this->{m_Visible} = 1;
306             }
307              
308             sub Select
309             {
310             my $this = shift;
311             my $l_Entry = $this->Subwidget ('Entry');
312             my $l_ListBox = $this->{m_ListBox};
313             my @l_Array = ();
314              
315             $l_Entry->configure ('-state' => 'normal');
316             $l_Entry->delete ('0', 'end');
317              
318             foreach my $l_Row ($l_ListBox->curselection())
319             {
320             push (@l_Array, $l_ListBox->get ($l_Row));
321             }
322              
323             $l_Entry->insert ('0', join (',', @l_Array));
324             $l_Entry->configure ('-state' => $this->{m_SelectionState});
325             $this->Hide();
326             $this->DoInvokeCallback();
327             }
328              
329             sub MenuSelect
330             {
331             my $this = shift;
332              
333             return unless $this->cget ('-showmenu');
334              
335             my $l_Entry = $this->Subwidget ('Entry');
336             my $l_ListBox = $this->{m_ListBox};
337              
338             return unless Exists ($l_ListBox);
339              
340             my $l_Event = $l_ListBox->XEvent();
341             my $l_Menu = $this->toplevel()->Subwidget ('ComboEntryMenu');
342              
343             $l_ListBox->activate
344             (
345             $l_ListBox->nearest ($l_Event->y())
346             );
347              
348             unless (Exists ($l_Menu))
349             {
350             $l_Menu = $this->toplevel()->Component
351             (
352             'Menu' => 'ComboEntryMenu',
353             '-tearoff' => 0,
354             );
355              
356             $l_Menu->add
357             (
358             'command',
359             '-label' => 'Enlarge',
360              
361             '-command' => sub
362             {
363             $this->configure ('-listheight' => $this->cget ('-listheight') + 10);
364             $this->Show();
365             },
366             );
367              
368             $l_Menu->add
369             (
370             'command',
371             '-label' => 'Reduce',
372              
373             '-command' => sub
374             {
375             $this->configure ('-listheight' => $this->cget ('-listheight') - 10);
376             $this->Show();
377             },
378             );
379              
380             $l_Menu->add
381             (
382             'command',
383             '-label' => 'Delete',
384             '-command' => sub {$l_ListBox->delete ($l_ListBox->index ('active'));},
385             );
386             }
387              
388             if (Exists ($l_Menu))
389             {
390             $this->Subwidget ('Popup')->grabRelease();
391              
392             $l_Menu->Popup() if ($Tk::VERSION < 800.005);
393              
394             $l_Menu->post
395             (
396             $l_Event->x() + $l_ListBox->rootx(),
397             $l_Event->y() + $l_ListBox->rooty(),
398             );
399             }
400             }
401              
402             sub DoInvokeCallback
403             {
404             if (ref ($_[0]->{'m_Invoke'}) eq 'CODE' || ref ($_[0]->{'m_Invoke'}) eq 'Tk::Callback')
405             {
406             $_[0]->afterIdle ([$_[0]->{m_Invoke}, $_[0]]);
407             }
408             }
409              
410             sub Traverse
411             {
412             $_[0]->{m_ListBox}->activate
413             (
414             $_[0]->{m_ListBox}->nearest ($_[0]->{m_ListBox}->XEvent()->y())
415             );
416             }
417              
418             sub AutoHide
419             {
420             my ($l_X, $l_Y, $l_RootX, $l_RootY, $l_Width, $l_Height) =
421             (
422             $_[1]->pointerx(),
423             $_[1]->pointery(),
424             $_[1]->rootx(),
425             $_[1]->rooty(),
426             $_[1]->width(),
427             $_[1]->height(),
428             );
429              
430             return unless
431             (
432             $l_X >= $l_RootX + $l_Width ||
433             $l_Y >= $l_RootY + $l_Height ||
434             $l_X <= $l_RootX ||
435             $l_Y <= $l_RootY
436             );
437              
438             $_[0]->Hide();
439             }
440              
441             sub background
442             {
443             my ($this, $p_Color) = @_;
444              
445             return ($this->{m_BackgroundColor}) unless (defined ($p_Color));
446              
447             my $l_Button = $this->Subwidget ('Button');
448             my $l_Entry = $this->Subwidget ('Entry');
449              
450             $this->{m_BackgroundColor} = $p_Color;
451              
452             $l_Button->configure
453             (
454             '-activebackground' => $l_Button->cget ('-background'),
455             '-highlightbackground' => $p_Color,
456             );
457              
458             $l_Entry->configure
459             (
460             '-highlightbackground' => $p_Color,
461             );
462              
463             return ($p_Color);
464             }
465              
466             sub scrollbarwidth
467             {
468             $_[0]->{'m_ScrollBarY'}->configure ('width' => $_[1]) if ($_[1] > 1);
469             return $_[0]->{'m_ScrollBarY'}->cget ('width');
470             }
471              
472             sub popupwidth
473             {
474             $_[0]->{'Configure'}{'-popupwidth'} = $_[1] if ($_[1] > 1 && $_[1] < 256);
475             return $_[0]->{'Configure'}{'-popupwidth'};
476             }
477              
478             sub KeySeek
479             {
480             my ($this, $p_ListBox, $p_Key) = @_;
481             my $l_Index = $p_ListBox->size() - 1;
482             my $p_Key = ord ($p_Key);
483              
484             return unless ($p_Key > 32);
485              
486             while ($l_Index && ord (substr ($p_ListBox->get ($l_Index), 0, 1)) > $p_Key)
487             {
488             --$l_Index;
489             }
490              
491             $p_ListBox->selectionClear (0, 'end');
492             $p_ListBox->selectionSet ($l_Index, $l_Index);
493             $p_ListBox->see ($l_Index);
494             }
495              
496             1;
497              
498             __END__