File Coverage

blib/lib/Gtk2/Ex/GroupBy.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::GroupBy;
2              
3             our $VERSION = '0.02';
4              
5 1     1   39839 use warnings;
  1         2  
  1         26  
6 1     1   5 use strict;
  1         2  
  1         36  
7 1     1   401 use Gtk2 -init;
  0            
  0            
8             use Glib ':constants';
9             use Gtk2::Ex::Simple::List;
10             use Data::Dumper;
11              
12             use constant ORDER_COLUMN => 0;
13             use constant MODEL_COLUMN => 1;
14             use constant NAME__COLUMN => 2;
15              
16             sub new {
17             my ($class, $slist) = @_;
18             my $self = {};
19             bless ($self, $class);
20             $self->{widget} = $self->_create_widget();
21             return $self;
22             }
23              
24             sub signal_connect {
25             my ($self, $signal, $callback) = @_;
26             $self->{signals}->{$signal} = $callback;
27             }
28              
29             sub set_model {
30             my ($self, $data) = @_;
31             # Add explicitly or else TiedRows are gonna bite you
32             foreach my $x (@{$data->{'groupby'}->[0]}) {
33             push @{$self->{groupby_list1}->{data}}, [$x];
34             }
35             foreach my $x (@{$data->{'groupby'}->[1]}) {
36             push @{$self->{groupby_list2}->{data}}, [$x];
37             }
38             _populate($self->{formula_treeview1}, $self->{formula_model1}, $data->{'formula'}->[0]);
39             _populate($self->{formula_treeview2}, $self->{formula_model2}, $data->{'formula'}->[1]);
40             $self->_check_visibility;
41             }
42              
43             sub get_model {
44             my ($self) = @_;
45             $self->_update_model;
46             return $self->{model};
47             }
48              
49             sub get_widget {
50             my ($self) = @_;
51             return $self->{widget};
52             }
53              
54             sub _create_widget {
55             my ($self) = @_;
56             my $groupby_list1 = Gtk2::Ex::Simple::List->new (
57             'Group By Fields' => 'text',
58             );
59             my $groupby_list2 = Gtk2::Ex::Simple::List->new (
60             'Choose Group By Fields from' => 'text',
61             );
62              
63             my @column_types;
64             $column_types[0] = 'Glib::String';
65             $column_types[NAME__COLUMN] = 'Glib::String';
66             $column_types[ORDER_COLUMN] = 'Glib::String';
67             $column_types[MODEL_COLUMN] = 'Gtk2::ListStore';
68              
69             my $formula_model1 = Gtk2::ListStore->new (@column_types);
70             my $formula_treeview1= Gtk2::TreeView->new($formula_model1);
71             $self->_add_combo($formula_treeview1, $formula_model1, 'Fields', 'Aggregation Formula');
72              
73             my $formula_model2 = Gtk2::ListStore->new (@column_types);
74             my $formula_treeview2= Gtk2::TreeView->new($formula_model2);
75             $self->_add_combo($formula_treeview2, $formula_model2, 'Choose Fields from', 'Choose Formula from');
76              
77             $self->{groupby_list1} = $groupby_list1;
78             $self->{groupby_list2} = $groupby_list2;
79             $self->{formula_treeview1} = $formula_treeview1;
80             $self->{formula_treeview2} = $formula_treeview2;
81             $self->{formula_model1} = $formula_model1;
82             $self->{formula_model2} = $formula_model2;
83            
84             ($groupby_list1->get_column(0)->get_cell_renderers)[0]->set(xalign => 0.5);
85             ($groupby_list2->get_column(0)->get_cell_renderers)[0]->set(xalign => 0.5);
86              
87             $groupby_list1->get_column(0)->set_expand(TRUE);
88             $groupby_list2->get_column(0)->set_expand(TRUE);
89              
90             $groupby_list1->get_column(0)->set_alignment(0.5);
91             $groupby_list2->get_column(0)->set_alignment(0.5);
92              
93             ($formula_treeview1->get_column(0)->get_cell_renderers)[0]->set(xalign => 0.5);
94             ($formula_treeview2->get_column(0)->get_cell_renderers)[0]->set(xalign => 0.5);
95             ($formula_treeview1->get_column(1)->get_cell_renderers)[0]->set(xalign => 0.5);
96             ($formula_treeview2->get_column(1)->get_cell_renderers)[0]->set(xalign => 0.5);
97              
98             $formula_treeview1->get_column(0)->set_expand(TRUE);
99             $formula_treeview2->get_column(0)->set_expand(TRUE);
100             $formula_treeview1->get_column(1)->set_expand(TRUE);
101             $formula_treeview2->get_column(1)->set_expand(TRUE);
102              
103             $formula_treeview1->get_column(0)->set_alignment(0.5);
104             $formula_treeview2->get_column(0)->set_alignment(0.5);
105             $formula_treeview1->get_column(1)->set_alignment(0.5);
106             $formula_treeview2->get_column(1)->set_alignment(0.5);
107              
108             # $formula_treeview2->get_column(0)->set_visible(FALSE);
109              
110             $groupby_list1->set_reorderable(TRUE);
111             $formula_treeview1->set_reorderable(TRUE);
112            
113             $groupby_list1->get_selection->set_mode('multiple');
114             $formula_treeview1->get_selection->set_mode('multiple');
115             $groupby_list2->get_selection->set_mode('multiple');
116             $formula_treeview2->get_selection->set_mode('multiple');
117              
118             _populate($formula_treeview1, $formula_model1, $self->{model}->{'formula'}->[0]);
119             _populate($formula_treeview2, $formula_model2, $self->{model}->{'formula'}->[1]);
120            
121             my $buttonbox = $self->_pack_buttons;
122              
123             my $groupby_list1_none_label = Gtk2::Label->new;
124             $groupby_list1_none_label->set_line_wrap(TRUE);
125              
126             $groupby_list1_none_label->set_markup('(Please add from the list Below)');
127             $self->{groupby_list1_none_label} = $groupby_list1_none_label;
128              
129             my $vbox11 = Gtk2::VBox->new(FALSE);
130             $vbox11->pack_start ($groupby_list1, TRUE, TRUE, 0);
131             $vbox11->pack_start ($groupby_list1_none_label, FALSE, FALSE, 0);
132            
133             my $groupby_list2_none_label = Gtk2::Label->new;
134             $groupby_list2_none_label->set_markup('(Please add from the list Below)');
135             $self->{groupby_list2_none_label} = $groupby_list2_none_label;
136              
137             my $vbox12 = Gtk2::VBox->new(FALSE);
138             $vbox12->pack_start ($formula_treeview1, TRUE, TRUE, 0);
139             $vbox12->pack_start ($groupby_list2_none_label, FALSE, FALSE, 0);
140              
141             my $vbox21 = Gtk2::VBox->new(FALSE);
142             $vbox21->pack_start ($groupby_list2, TRUE, TRUE, 0);
143            
144             my $vbox22 = Gtk2::VBox->new(FALSE);
145             $vbox22->pack_start ($formula_treeview2, TRUE, TRUE, 0);
146            
147             my $hbox1 = Gtk2::HBox->new(TRUE);
148             $hbox1->pack_start ($vbox11, TRUE, TRUE, 0);
149             $hbox1->pack_start ($vbox12, TRUE, TRUE, 0);
150              
151             my $hbox2 = Gtk2::HBox->new(TRUE);
152             $hbox2->pack_start ($vbox21, TRUE, TRUE, 0);
153             $hbox2->pack_start ($vbox22, TRUE, TRUE, 0);
154              
155             my $vbox = Gtk2::VBox->new(FALSE);
156             $vbox->pack_start ($hbox1, TRUE, TRUE, 0);
157             $vbox->pack_start ($buttonbox, FALSE, TRUE, 0);
158             $vbox->pack_start (_frame_it($hbox2), TRUE, TRUE, 0);
159            
160             return $vbox;
161             }
162              
163             sub _frame_it {
164             my ($widget) = @_;
165             my $frame = Gtk2::Frame->new;
166             $frame->add($widget);
167             return $frame;
168             }
169              
170             sub _pack_buttons {
171             my ($self) = @_;
172            
173             my $removebutton = Gtk2::Button->new;
174             my $removebuttonlabel = Gtk2::HBox->new (FALSE, 0);
175             $removebuttonlabel->pack_start (Gtk2::Label->new(' Remove '), TRUE, TRUE, 0);
176             $removebuttonlabel->pack_start (Gtk2::Image->new_from_stock('gtk-go-down', 'GTK_ICON_SIZE_BUTTON'), FALSE, FALSE, 0);
177             $removebutton->add($removebuttonlabel);
178              
179             my $okbutton = Gtk2::Button->new_from_stock('gtk-ok');
180              
181             my $addbutton = Gtk2::Button->new;
182             my $addbuttonlabel = Gtk2::HBox->new (FALSE, 0);
183             $addbuttonlabel->pack_start (Gtk2::Label->new(' Add '), TRUE, TRUE, 0);
184             $addbuttonlabel->pack_start (Gtk2::Image->new_from_stock('gtk-go-up', 'GTK_ICON_SIZE_BUTTON'), FALSE, FALSE, 0);
185             $addbutton->add($addbuttonlabel);
186              
187             my $clearbutton = Gtk2::Button->new_from_stock('gtk-clear');
188              
189             $removebuttonlabel->set_sensitive(FALSE);
190             $addbuttonlabel->set_sensitive(FALSE);
191             $removebutton->set_sensitive(FALSE);
192             $addbutton->set_sensitive(FALSE);
193            
194             my $triggered = FALSE;
195            
196             $self->{groupby_list1}->get_selection->signal_connect ('changed' =>
197             sub {
198             return if $triggered;
199             $triggered = TRUE;
200             $removebuttonlabel->set_sensitive(TRUE);
201             $addbuttonlabel->set_sensitive(FALSE);
202             $removebutton->set_sensitive(TRUE);
203             $addbutton->set_sensitive(FALSE);
204             $self->{groupby_list2}->get_selection->unselect_all;
205             $self->{formula_treeview1}->get_selection->unselect_all;
206             $self->{formula_treeview2}->get_selection->unselect_all;
207             $triggered = FALSE;
208             }
209             );
210              
211             $self->{groupby_list2}->get_selection->signal_connect ('changed' =>
212             sub {
213             return if $triggered;
214             $triggered = TRUE;
215             $removebuttonlabel->set_sensitive(FALSE);
216             $addbuttonlabel->set_sensitive(TRUE);
217             $removebutton->set_sensitive(FALSE);
218             $addbutton->set_sensitive(TRUE);
219             $self->{groupby_list1}->get_selection->unselect_all;
220             $self->{formula_treeview1}->get_selection->unselect_all;
221             $self->{formula_treeview2}->get_selection->unselect_all;
222             $triggered = FALSE;
223             }
224             );
225              
226             $self->{formula_treeview1}->get_selection->signal_connect ('changed' =>
227             sub {
228             return if $triggered;
229             $triggered = TRUE;
230             $removebuttonlabel->set_sensitive(TRUE);
231             $addbuttonlabel->set_sensitive(FALSE);
232             $removebutton->set_sensitive(TRUE);
233             $addbutton->set_sensitive(FALSE);
234             $self->{groupby_list1}->get_selection->unselect_all;
235             $self->{groupby_list2}->get_selection->unselect_all;
236             $self->{formula_treeview2}->get_selection->unselect_all;
237             $triggered = FALSE;
238             }
239             );
240              
241             $self->{formula_treeview2}->get_selection->signal_connect ('changed' =>
242             sub {
243             return if $triggered;
244             $triggered = TRUE;
245             $removebuttonlabel->set_sensitive(FALSE);
246             $removebutton->set_sensitive(FALSE);
247             if ($#{@{$self->{groupby_list1}->{data}}} >= 0) {
248             $addbuttonlabel->set_sensitive(TRUE);
249             $addbutton->set_sensitive(TRUE);
250             } else {
251             $addbuttonlabel->set_sensitive(FALSE);
252             $addbutton->set_sensitive(FALSE);
253             }
254             $self->{groupby_list1}->get_selection->unselect_all;
255             $self->{groupby_list2}->get_selection->unselect_all;
256             $self->{formula_treeview1}->get_selection->unselect_all;
257             $triggered = FALSE;
258             }
259             );
260              
261             $addbutton->signal_connect ('button-release-event' =>
262             sub {
263             my $indices = _get_selected_indices($self->{formula_treeview2});
264             if ($indices) {
265             my @removeorder = reverse sort @$indices;
266             my $data1 = _get_data($self->{formula_treeview1});
267             my $data2 = _get_data($self->{formula_treeview2});
268             foreach my $x (@removeorder) {
269             push @$data1, splice (@$data2, $x, 1);
270             }
271             $self->{formula_treeview1}->set_no_show_all(FALSE);
272             $self->{formula_treeview1}->show_all;
273             _populate($self->{formula_treeview1}, $self->{formula_model1}, $data1);
274             _populate($self->{formula_treeview2}, $self->{formula_model2}, $data2);
275             } else {
276             my @selected = $self->{groupby_list2}->get_selected_indices;
277             my @removeorder = reverse sort @selected;
278             foreach my $x (@removeorder) {
279             push @{$self->{groupby_list1}->{data}},
280             splice (@{$self->{groupby_list2}->{data}}, $x, 1);
281             }
282             }
283             $self->_update_model;
284             &{ $self->{signals}->{'changed'} } if $self->{signals}->{'changed'};
285             $removebuttonlabel->set_sensitive(FALSE);
286             $addbuttonlabel->set_sensitive(FALSE);
287             $removebutton->set_sensitive(FALSE);
288             $addbutton->set_sensitive(FALSE);
289             return FALSE;
290             }
291             );
292              
293             $clearbutton->signal_connect ('clicked' =>
294             sub {
295             my $removeorder = [0..$#{@{$self->{groupby_list1}->{data}}}];
296             foreach my $x (reverse sort @$removeorder) {
297             push @{$self->{groupby_list2}->{data}},
298             splice (@{$self->{groupby_list1}->{data}}, $x, 1);
299             }
300             if ($#{@{$self->{groupby_list1}->{data}}} < 0) {
301             my $data1 = _get_data($self->{formula_treeview1});
302             my $data2 = _get_data($self->{formula_treeview2});
303             push @$data2, @$data1 if $data1;
304             $data1 = undef;
305             _populate($self->{formula_treeview1}, $self->{formula_model1}, $data1);
306             _populate($self->{formula_treeview2}, $self->{formula_model2}, $data2);
307             }
308             $self->_update_model;
309             &{ $self->{signals}->{'changed'} } if $self->{signals}->{'changed'};
310             return FALSE;
311             }
312             );
313            
314             $removebutton->signal_connect ('clicked' =>
315             sub {
316             my $indices = _get_selected_indices($self->{formula_treeview1});
317             if ($indices) {
318             my @removeorder = reverse sort @$indices;
319             my $data1 = _get_data($self->{formula_treeview1});
320             my $data2 = _get_data($self->{formula_treeview2});
321             foreach my $x (@removeorder) {
322             push @$data2, splice (@$data1, $x, 1);
323             }
324             _populate($self->{formula_treeview1}, $self->{formula_model1}, $data1);
325             _populate($self->{formula_treeview2}, $self->{formula_model2}, $data2);
326             } else {
327             my @selected = $self->{groupby_list1}->get_selected_indices;
328             my @removeorder = reverse sort @selected;
329             foreach my $x (@removeorder) {
330             push @{$self->{groupby_list2}->{data}},
331             splice (@{$self->{groupby_list1}->{data}}, $x, 1);
332             }
333             if ($#{@{$self->{groupby_list1}->{data}}} < 0) {
334             my $data1 = _get_data($self->{formula_treeview1});
335             my $data2 = _get_data($self->{formula_treeview2});
336             push @$data2, @$data1 if $data1;
337             $data1 = undef;
338             _populate($self->{formula_treeview1}, $self->{formula_model1}, $data1);
339             _populate($self->{formula_treeview2}, $self->{formula_model2}, $data2);
340             }
341             }
342             $self->_update_model;
343             &{ $self->{signals}->{'changed'} } if $self->{signals}->{'changed'};
344             $removebuttonlabel->set_sensitive(FALSE);
345             $addbuttonlabel->set_sensitive(FALSE);
346             $removebutton->set_sensitive(FALSE);
347             $addbutton->set_sensitive(FALSE);
348             return FALSE;
349             }
350             );
351              
352             $okbutton->signal_connect ('clicked' =>
353             sub {
354             &{ $self->{signals}->{'closed'} } if $self->{signals}->{'closed'};
355             }
356             );
357              
358             $self->{addbutton} = $addbutton;
359             $self->{removebutton} = $removebutton;
360             $self->{okbutton} = $okbutton;
361             $self->{addbuttonlabel} = $addbuttonlabel;
362             $self->{removebuttonlabel} = $removebuttonlabel;
363            
364             my $hbox = Gtk2::HBox->new (TRUE, 0);
365             $hbox->pack_start ($addbutton, TRUE, TRUE, 0);
366             $hbox->pack_start ($okbutton, TRUE, TRUE, 0);
367             $hbox->pack_start ($clearbutton, TRUE, TRUE, 0);
368             $hbox->pack_start ($removebutton, TRUE, TRUE, 0);
369            
370             return $hbox;
371             }
372              
373             sub _update_model {
374             my ($self) = @_;
375             my @data1;
376             my @data2;
377             foreach my $x (@{$self->{groupby_list1}->{data}}) {
378             push @data1, $x->[0];
379             }
380             foreach my $x (@{$self->{groupby_list2}->{data}}) {
381             push @data2, $x->[0];
382             }
383             $self->{model}->{'groupby'}->[0] = $#data1 >= 0 ? \@data1 : undef;
384             $self->{model}->{'groupby'}->[1] = $#data2 >= 0 ? \@data2 : undef;
385             $self->{model}->{'formula'}->[0] = _get_data($self->{formula_treeview1});
386             $self->{model}->{'formula'}->[1] = _get_data($self->{formula_treeview2});
387             $self->_check_visibility;
388             }
389              
390             sub _get_selected_indices {
391             my ($treeview) = @_;
392             my @indices;
393             my (@paths) = $treeview->get_selection->get_selected_rows;
394             foreach my $path (@paths) {
395             push @indices, $path->to_string;
396             }
397             return undef if $#indices < 0;
398             return \@indices;
399             }
400              
401             sub _get_data {
402             my ($treeview) = @_;
403             my $model = $treeview->get_model;
404             my @data;
405             for my $i(0..$model->iter_n_children-1) {
406             my $col0 = $model->get($model->get_iter_from_string($i), 0);
407             my $col1 = $model->get($model->get_iter_from_string($i), 2);
408             push @data, { formula => $col0, field => $col1 };
409             }
410             return undef if $#data < 0;
411             return \@data;
412             }
413              
414             sub _add_combo {
415             my ($self, $treeview, $model, $groupby_header, $formula_header) = @_;
416            
417             my $combo_renderer = Gtk2::CellRendererCombo->new;
418             $combo_renderer->set (
419             text_column => 0, # col in combo model with text to display
420             editable => TRUE, # without this, it's just a text renderer
421             );
422             $combo_renderer->signal_connect (edited =>
423             sub {
424             my ($cell, $text_path, $new_text) = @_;
425             $model->set (
426             $model->get_iter_from_string($text_path),
427             ORDER_COLUMN,
428             $new_text
429             );
430             &{ $self->{signals}->{'changed'} } if $self->{signals}->{'changed'};
431             }
432             );
433             $treeview->insert_column_with_attributes(
434             -1, $formula_header,
435             $combo_renderer,
436             text => ORDER_COLUMN,
437             model => MODEL_COLUMN
438             );
439             $treeview->insert_column_with_attributes(
440             -1, $groupby_header,
441             Gtk2::CellRendererText->new,
442             text => NAME__COLUMN
443             );
444             }
445              
446             sub _check_visibility {
447             my ($self) = @_;
448             if ($#{@{$self->{groupby_list1}->{data}}} >= 0) {
449             $self->{groupby_list1_none_label}->hide;
450             $self->{groupby_list1_none_label}->set_no_show_all(TRUE);
451             } else {
452             $self->{groupby_list1_none_label}->set_no_show_all(FALSE);
453             $self->{groupby_list1_none_label}->show;
454             }
455             if ($self->{formula_treeview1}->get_model->iter_n_children > 0) {
456             $self->{groupby_list2_none_label}->hide;
457             $self->{groupby_list2_none_label}->set_no_show_all(TRUE);
458             } else {
459             $self->{groupby_list2_none_label}->set_no_show_all(FALSE);
460             $self->{groupby_list2_none_label}->show;
461             }
462             }
463              
464             sub _populate{
465             my ($treeview, $model, $temp) = @_;
466             my $lookup_hash = {
467             'COUNT of' => 0,
468             'MAX of' => 1,
469             'MIN of' => 2,
470             'SUM of' => 3,
471             'AVG of' => 4,
472             'STDDEV of' => 5,
473             };
474             my $combomodel = Gtk2::ListStore->new('Glib::String');
475             $combomodel->set($combomodel->append, 0, 'COUNT of');
476             $combomodel->set($combomodel->append, 0, 'MAX of');
477             $combomodel->set($combomodel->append, 0, 'MIN of');
478             $combomodel->set($combomodel->append, 0, 'SUM of');
479             $combomodel->set($combomodel->append, 0, 'AVG of');
480             $combomodel->set($combomodel->append, 0, 'STDDEV of');
481             $model->clear();
482             my $i = 0;
483             foreach my $data (@$temp) {
484             $data->{formula} = $lookup_hash->{$data->{formula}};
485             $data->{formula} = $combomodel->get(
486             $combomodel->iter_nth_child (undef, $data->{formula})
487             , 0
488             );
489             $model->set (
490             $model->append,
491             NAME__COLUMN, $data->{field},
492             ORDER_COLUMN, $data->{formula},
493             MODEL_COLUMN, $combomodel
494             );
495             }
496             }
497              
498             1;
499              
500             __END__