File Coverage

blib/lib/Gtk2/Ex/SearchBox.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Gtk2::Ex::SearchBox;
2              
3             our $VERSION = '0.03';
4              
5 1     1   22117 use warnings;
  1         1  
  1         29  
6 1     1   4 use strict;
  1         2  
  1         37  
7 1     1   340 use Gtk2 -init;
  0            
  0            
8             use Glib ':constants';
9             use Data::Dumper;
10             use Gtk2::Gdk::Keysyms;
11             use Gtk2::Ex::PopupWindow;
12              
13             use constant OR_______COLUMN => 0;
14             use constant OPERATOR_COLUMN => 1;
15             use constant MODEL____COLUMN => 2;
16             use constant NAME_____COLUMN => 3;
17              
18             sub new {
19             my ($class, $type, $operatorlist) = @_;
20             my $self = {};
21             my $default_operatorlist = [
22             'contains',
23             'doesn\'t contain',
24             'not equal to',
25             'equals',
26             ];
27             $self->{operatorlist} = $operatorlist || $default_operatorlist;
28             $self->{type} = $type || 'multiple';
29             bless ($self, $class);
30             $self->{widget} = $self->_create_widget();
31             return $self;
32             }
33              
34             sub signal_connect {
35             my ($self, $signal, $callback) = @_;
36             $self->{signals}->{$signal} = $callback;
37             }
38              
39             sub get_model {
40             my ($self) = @_;
41             my @temp;
42             @temp = @{$self->{datamodel}} if $self->{datamodel};
43             push @temp, {
44             'operator' => $self->{operatorlist}->[$self->{operatorcombo}->get_active],
45             'field' => $self->{entry}->get_text,
46             } if $self->{entry}->get_text;
47             return \@temp;
48             }
49              
50             sub set_model {
51             my ($self, $datamodel) = @_;
52             $self->{datamodel} = $datamodel;
53             $self->_populate;
54             $self->_check_list_visibility();
55             }
56              
57             sub to_sql_condition {
58             my ($self, $fieldname, $datamodel) = @_;
59             my @condition;
60             foreach my $x (@$datamodel) {
61             next unless $x;
62             if ($x->{operator} eq 'equals') {
63             push @condition, $fieldname.' = '.'\''.$x->{field}.'\'';
64             } elsif ($x->{operator} eq 'not equal to') {
65             push @condition, $fieldname.' <> '.'\''.$x->{field}.'\'';
66             } elsif ($x->{operator} eq 'contains') {
67             push @condition, $fieldname.' like '.'\'%'.$x->{field}.'%\'';
68             } elsif ($x->{operator} eq 'doesn\'t contain') {
69             push @condition, $fieldname.' not like '.'\'%'.$x->{field}.'%\'';
70             }
71             }
72             my $str = join ' or ', @condition;
73             return $str;
74             }
75              
76             sub attach_popup_to {
77             my ($self, $parent) = @_;
78             my $popupwindow = Gtk2::Ex::PopupWindow->new($parent);
79             $popupwindow->set_move_with_parent(TRUE);
80             my $frame = Gtk2::Frame->new;
81             $frame->add($self->{widget});
82             $self->{popup} = $popupwindow;
83             $popupwindow->{window}->add($frame);
84             return $popupwindow;
85             }
86              
87             sub _create_widget {
88             my ($self) = @_;
89              
90             my @column_types;
91             $column_types[OR_______COLUMN] = 'Glib::String';
92             $column_types[OPERATOR_COLUMN] = 'Glib::String';
93             $column_types[MODEL____COLUMN] = 'Gtk2::ListStore';
94             $column_types[NAME_____COLUMN] = 'Glib::String';
95              
96             my $table = Gtk2::Table->new(2,4,FALSE);
97             my $operatorcombo = Gtk2::ComboBox->new_text;
98             my $entry = Gtk2::Entry->new;
99             my $ok_button = Gtk2::Button->new_from_stock('gtk-ok');
100              
101             my $add_another_button = Gtk2::Button->new('_Another Pattern');
102            
103             my $treemodel = Gtk2::ListStore->new (@column_types);
104             my $treeview= Gtk2::TreeView->new($treemodel);
105             my $scrolledwindow = Gtk2::ScrolledWindow->new;
106            
107             $self->{treeview} = $treeview;
108             $self->{treemodel} = $treemodel;
109             $self->{entry} = $entry;
110             $self->{operatorcombo} = $operatorcombo;
111             $self->{add_another_button} = $add_another_button;
112             $self->{ok_button} = $ok_button;
113              
114             $self->_add_combo;
115             $self->_populate;
116             $treeview->set_headers_visible(FALSE);
117             $treeview->get_selection->set_mode('multiple');
118             $treeview->signal_connect ('key-press-event' =>
119             sub {
120             my ($widget, $event) = @_;
121             if ($event->keyval == $Gtk2::Gdk::Keysyms{'Delete'}) {
122             my @paths = $treeview->get_selection->get_selected_rows;
123             return if $#paths < 0;
124             my @sel = map { $_->to_string } @paths;
125             my $data = $self->{datamodel};
126             foreach my $i (reverse sort @sel) {
127             splice (@$data, $i, 1);
128             }
129             $self->{datamodel} = $data;
130             $self->_populate;
131             $self->_check_list_visibility();
132             &{ $self->{signals}->{'changed'} } if $self->{signals}->{'changed'};
133             }
134             }
135             );
136             $operatorcombo->set_wrap_width(1);
137             my $operatorlist = $self->{operatorlist};
138             foreach my $x (@$operatorlist) {
139             $operatorcombo->append_text($x);
140             }
141             $operatorcombo->set_active(0);
142             $operatorcombo->signal_connect('realize' =>
143             sub {
144             $treeview->get_column(1)->set_min_width(
145             $operatorcombo->size_request->width
146             );
147             $entry->grab_focus;
148             }
149             );
150             $add_another_button->set_sensitive(FALSE);
151             $self->_check_list_visibility();
152             if ($self->{type} eq 'single') {
153             $add_another_button->hide;
154             $add_another_button->set_no_show_all(TRUE);
155             }
156             if ($#{@{$self->{operatorlist}}} <= 0) {
157             $operatorcombo->hide;
158             $operatorcombo->set_no_show_all(TRUE);
159             }
160             $add_another_button->signal_connect('clicked' =>
161             sub {
162             if ($entry->get_text) {
163             my $data = $self->{datamodel};
164             my %hash = map { $_->{field} => 1 } @$data;
165             unless ($hash{$entry->get_text}) {
166             push @$data, {
167             'field' => $entry->get_text,
168             'operator' => $operatorlist->[$operatorcombo->get_active],
169             };
170             $self->{datamodel} = $data;
171             }
172             $self->_populate;
173             $self->_check_list_visibility();
174             $entry->set_text('');
175             $entry->grab_focus;
176             &{ $self->{signals}->{'changed'} } if $self->{signals}->{'changed'};
177             }
178             }
179             );
180             $ok_button->signal_connect('clicked' =>
181             sub {
182             &{ $self->{signals}->{'closed'} } if $self->{signals}->{'closed'};
183             }
184             );
185             $entry->signal_connect( 'changed' =>
186             sub {
187             if ($entry->get_text) {
188             $add_another_button->set_sensitive(TRUE);
189             } else {
190             $add_another_button->set_sensitive(FALSE);
191             }
192             }
193             );
194              
195             $scrolledwindow->set_policy('never', 'automatic');
196             $scrolledwindow->add($treeview);
197             $entry->set_activates_default(TRUE);
198              
199             $ok_button->signal_connect( 'realize' =>
200             sub {
201             $ok_button->set_flags ('can-default');
202             $ok_button->grab_default;
203             }
204             );
205              
206             #$table->set_col_spacings(5);
207             #$table->attach($operatorcombo ,0,1,0,1, 'fill', 'fill', 0, 0);
208             #$table->attach($entry ,1,2,0,1, 'fill' , 'fill', 0, 0);
209             #$table->attach($ok_button ,2,3,0,1, 'fill', 'fill', 0, 0);
210             #$table->attach($add_another_button,3,4,0,1, 'fill', 'fill', 0, 0);
211             #$table->attach($scrolledwindow ,0,2,1,2, 'fill' , 'expand', 0, 0);
212              
213             my $hbox1 = Gtk2::HBox->new (FALSE, 0);
214             $hbox1->pack_start ($operatorcombo, FALSE, TRUE, 0);
215             $hbox1->pack_start ($entry, TRUE, TRUE, 0);
216              
217             my $vbox1 = Gtk2::VBox->new (FALSE, 0);
218             $vbox1->pack_start ($hbox1, FALSE, TRUE, 0);
219             $vbox1->pack_start ($scrolledwindow, TRUE, TRUE, 0);
220              
221             my $hbox2 = Gtk2::HBox->new (FALSE, 0);
222             $hbox2->pack_start ($ok_button, FALSE, TRUE, 0);
223             $hbox2->pack_start ($add_another_button, FALSE, TRUE, 0);
224              
225             my $vbox2 = Gtk2::VBox->new (FALSE, 0);
226             $vbox2->pack_start ($hbox2, FALSE, TRUE, 0);
227             $vbox2->pack_start (Gtk2::Label->new, TRUE, TRUE, 0);
228              
229             my $hbox = Gtk2::HBox->new (FALSE, 0);
230             $hbox->pack_start ($vbox1, TRUE, TRUE, 0);
231             $hbox->pack_start ($vbox2, FALSE, TRUE, 0);
232              
233             return $hbox;
234             }
235              
236              
237              
238             sub _add_combo {
239             my ($self) = @_;
240             my $treeview = $self->{treeview};
241             my $treemodel = $self->{treemodel};
242            
243             my $combo_renderer = Gtk2::CellRendererCombo->new;
244             $combo_renderer->set (
245             text_column => 0, # col in combo model with text to display
246             editable => TRUE, # without this, it's just a text renderer
247             );
248             $combo_renderer->signal_connect (edited =>
249             sub {
250             my ($cell, $text_path, $new_text) = @_;
251             $treemodel->set (
252             $treemodel->get_iter_from_string($text_path),
253             OPERATOR_COLUMN,
254             $new_text
255             );
256             # &{ $self->{signals}->{'changed'} } if $self->{signals}->{'changed'};
257             }
258             );
259             $treeview->insert_column_with_attributes(
260             -1, 'Fields',
261             Gtk2::CellRendererText->new,
262             text => OR_______COLUMN
263             );
264             $treeview->insert_column_with_attributes(
265             -1, 'Operators',
266             $combo_renderer,
267             text => OPERATOR_COLUMN,
268             model => MODEL____COLUMN
269             );
270             $treeview->insert_column_with_attributes(
271             -1, 'Fields',
272             Gtk2::CellRendererText->new,
273             text => NAME_____COLUMN
274             );
275             }
276              
277             sub _populate{
278             my ($self) = @_;
279             my $treeview = $self->{treeview};
280             my $treemodel = $self->{treemodel};
281             my $i = 0;
282             my $operatorlist = $self->{operatorlist};
283             my %lookup_hash = map { $_ => $i++ } @$operatorlist;
284             my $combomodel = Gtk2::ListStore->new('Glib::String');
285             foreach my $key (@$operatorlist) {
286             $combomodel->set($combomodel->append, 0, $key);
287             }
288             $treemodel->clear();
289             return unless $self->{datamodel};
290             my @datamodel = @{$self->{datamodel}};
291             #my $first = shift @datamodel;
292             #$self->{operatorcombo}->set_active($lookup_hash{$first->{'operator'}});
293             #$self->{entry}->set_text($first->{'field'});
294             foreach my $data (@datamodel) {
295             $data->{operator} = $lookup_hash{$data->{operator}};
296             $data->{operator} = $combomodel->get(
297             $combomodel->iter_nth_child (undef, $data->{operator})
298             , 0
299             );
300             $treemodel->set (
301             $treemodel->append,
302             OR_______COLUMN, 'or',
303             NAME_____COLUMN, $data->{field},
304             OPERATOR_COLUMN, $data->{operator},
305             MODEL____COLUMN, $combomodel,
306             );
307             }
308             }
309              
310             sub _check_list_visibility {
311             my ($self) = @_;
312             my $treeview = $self->{treeview};
313             my $data = $self->{datamodel};
314             if ($#{@$data} >= 0) {
315             $treeview->set_sensitive(TRUE);
316             #$treeview->set_no_show_all(FALSE);
317             #$treeview->show;
318             } else {
319             $treeview->set_sensitive(FALSE);
320             #$treeview->hide;
321             #$treeview->set_no_show_all(TRUE);
322             #$window->set_focus($entry);
323             }
324             }
325              
326             1;
327              
328             __END__