File Coverage

blib/lib/Imager/Album/GUI.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package Imager::Album::GUI;
2              
3 1     1   2916 use Imager;
  1         84652  
  1         8  
4 1     1   5915 use Gtk;
  0            
  0            
5             use Gtk::Gdk;
6              
7             use strict;
8              
9             # Where in gods name do I put this!?!?
10              
11             init Gtk;
12             init Gtk::Gdk::Rgb;
13              
14             use vars qw ( $red $blue $yellow $green $gray);
15              
16             $red = Gtk::Gdk::Color->parse_color("red");
17             $blue = Gtk::Gdk::Color->parse_color("blue");
18             $green = Gtk::Gdk::Color->parse_color("green");
19             $yellow = Gtk::Gdk::Color->parse_color("yellow");
20             $gray = Gtk::Gdk::Color->parse_color("gray");
21              
22              
23              
24             sub new {
25             my $class = shift;
26             my $self = {};
27             bless $self, $class;
28              
29             $self->{'parent'} = shift;
30             $self->{'images'} = $self->{'parent'}->{'images'};
31              
32             $self->{'gui_info'} = {}; # store references to labels
33             $self->{'selection'} = {}; # current selection
34              
35             my $window = new Gtk::Window "toplevel";
36             $window->set_usize(700,300);
37             $window->set_title( "Imager::Album v".$Imager::Album::VERSION );
38             $window->signal_connect( "destroy", sub { Gtk->exit( 0 ); } ); # XXX
39             $window->show;
40              
41             my $timg = $self->{'images'};
42             $self->{'ordering'} = $self->{'parent'}->{'ordering'};
43              
44             my $gdkwindow = $window->window;
45             my $gc = Gtk::Gdk::GC->new($gdkwindow);
46              
47             my $imagelist = $self->make_imagelist();
48              
49             my $hbox = new Gtk::HBox 0, 0;
50             my $ltb = new Gtk::VBox 0, 0;
51             $hbox->pack_start( $ltb, 0, 1, 0);
52              
53             $ltb->pack_start( $imagelist, 1, 1, 0);
54              
55             my $commands = $self->make_commands(@{$self->{'parent'}->{'commands'}});
56             $ltb->pack_start( $commands, 0, 1, 0);
57              
58             my $previewer = $self->make_previewer();
59             $hbox->pack_end( $previewer->{'scroller'}, 1, 1, 0);
60             $hbox->show();
61             $ltb->show();
62             $window->add( $hbox );
63             $window->show();
64              
65             $self->{'window'} = $window;
66             $self->{'gdkwindow'} = $gdkwindow;
67             $self->{'gc'} = $gc;
68             $self->{'previewer'} = $previewer;
69             $self->{'imagelist'} = $imagelist;
70             $self->{'parent'}->{'gui'} = $self;
71              
72             # $pixmap->show;
73             $self->previewer_update($previewer);
74             }
75              
76              
77             sub boot {
78             main Gtk;
79             exit( 0 );
80             }
81              
82              
83             sub shutdown {
84             my $self = shift;
85             $self->{'window'}->destroy();
86             }
87              
88              
89             sub get_selection {
90             my $self = shift;
91             my %rev;
92             my @images = @{$self->{'ordering'}};
93             @rev{@images} = 0..$#images;
94             my @selection = keys %{$self->{'selection'}};
95             @selection = sort { $rev{$a}<=>$rev{$b} } @selection;
96             return @selection;
97             }
98              
99             sub make_commands {
100             my $self = shift;
101             my @commands = @_;
102             my $vbox = new Gtk::VBox 0,0;
103              
104             for (@commands) {
105             my $button = new Gtk::Button($_->[0]);
106             my $code = $_->[1];
107             $button->signal_connect('clicked',
108             sub {
109             $code->($self->{'parent'}, $self->get_selection() );
110             $self->previewer_update();
111             });
112             $vbox->pack_start($button, 1, 1, 0);
113             $button->show();
114             }
115              
116             $vbox->show();
117             return $vbox;
118             }
119              
120              
121              
122             sub make_imagelist {
123             my $self = shift;
124             my %images = %{$self->{'images'}};
125              
126             my $scroller = new Gtk::ScrolledWindow(undef, undef);
127             my $list = Gtk::CList->new_with_titles( "File", "Title" );
128              
129             $list->set_reorderable(1);
130             $scroller->set_policy('never', 'always');
131             $scroller->border_width(0);
132              
133             $list->set_column_width( 0, 100 );
134             $list->set_column_width( 1, 100 );
135             $list->set_selection_mode(-extended);
136              
137             $list->signal_connect( "select_row" => sub { $self->set_image(@_); } );
138             $list->signal_connect( "unselect_row" => sub { $self->unset_image(@_); } );
139              
140             $list->signal_connect( "row_move" => sub { my ($list, $f, $t) = @_;
141             my $parent = $self->{'parent'};
142             $parent->change_order($f, $t);
143             $self->previewer_update(); }
144             );
145              
146             $scroller->add($list);
147              
148             $scroller -> show();
149             $list -> show();
150              
151             my @iorder = @{$self->{'ordering'}};
152             for (@iorder) {
153             $list->append( $images{$_}->{'path'}, $images{$_}->{'name'});
154             }
155             return $scroller;
156             }
157              
158              
159             # Called when nothing is removed or added
160              
161             sub imagelist_update_names {
162             my $self = shift;
163             my $list = $self->{'imagelist'}->child();
164             my %images = %{$self->{'images'}};
165              
166             my $rows = $list->rows();
167             my @iorder = @{$self->{'ordering'}};
168              
169             for (0..$rows-1) {
170             $list->set_text($_, 0, $images{$iorder[$_]}->{'path'});
171             $list->set_text($_, 1, $images{$iorder[$_]}->{'name'});
172             }
173             }
174              
175              
176             sub imagelist_update {
177             my $self = shift;
178             my $list = $self->{'imagelist'}->child();
179              
180             $list -> clear();
181              
182             my %images = %{$self->{'images'}};
183              
184             my @iorder = @{$self->{'ordering'}};
185             for (@iorder) {
186             $list->append( $images{$_}->{'path'}, $images{$_}->{'name'});
187             }
188             }
189              
190              
191              
192              
193              
194             sub make_previewer {
195             my $previewer = {};
196             my $scroller = new Gtk::ScrolledWindow(undef, undef);
197              
198             $scroller->set_policy('automatic', 'always');
199             $scroller->border_width(0);
200              
201             my $vbox = new Gtk::VBox 0,0;
202             $scroller -> add_with_viewport($vbox);
203              
204             $vbox -> show();
205             $scroller -> show();
206              
207             $previewer->{scroller} = $scroller;
208             $previewer->{vbox} = $vbox;
209             return $previewer;
210             }
211              
212              
213              
214             sub previewer_update {
215             my $self = shift;
216             my $previewer = $self->{'previewer'};
217             my $album = $self->{'parent'};
218             my $vbox = $previewer->{'vbox'};
219              
220             # This nukes everything in the boxes
221             {
222             my @tt;
223             $vbox->foreach(sub { my $hbox = shift;
224             $hbox->foreach(sub {
225             my @t = ();
226             my $tb = shift;
227             $tb->foreach(sub { push(@t, shift); });
228             $tb->remove($_) for @t;
229             });
230             push(@tt, $hbox);
231             });
232             $vbox->remove($_) for @tt;
233             }
234              
235             my %images = %{$self->{'images'}};
236             my @prelist = @{$self->{'ordering'}};
237              
238             for (@prelist) {
239             if (!$self->{'images'}->{$_}->{'valid'}) {
240             delete $self->{'images'}->{$_}->{'gdk_preview'};
241             }
242             }
243              
244             $album->update_previews();
245              
246             my $rows = int((3+@prelist)/4);
247             my $count = 0;
248             my $row;
249             for $row (0..$rows-1) {
250             my $hbox = new Gtk::HBox 0,0;
251             my $col;
252             for $col (0..3) {
253             next if $count >= @prelist;
254             my $imageno = $prelist[$count];
255             my $image = $album->get_image($imageno);
256             my $gdkim;
257              
258             if (! exists $image->{gdk_preview}) {
259             $gdkim = $self->read_image( $album->get_preview_path($imageno) );
260             $image->{gdk_preview} = $gdkim;
261             } else {
262             $gdkim = $image->{gdk_preview};
263             }
264             $gdkim->show();
265             my $tbox = new Gtk::VBox 0,0;
266             # my $button = new Gtk::Button($imageno." :: ".$image->{'name'});
267             my $button = new Gtk::Button($image->{'name'});
268             $self->{'gui_info'}->{$imageno} = $button;
269             if (exists $self->{'selection'}->{$imageno}) {
270             my $tstyle = $button->get_style()->copy();
271             $tstyle->bg('normal', $yellow);
272             $tstyle->bg('prelight', $yellow);
273             $button->set_style($tstyle);
274             }
275             my $t = $count; # this must be done so closures don't all refer to
276             # same variable
277             $button->signal_connect('clicked', sub {
278             my $list = $self->{'imagelist'}->child();
279             if (exists $self->{'selection'}->{$imageno}) {
280             $list->unselect_row($t,0);
281             } else {
282             $list->select_row($t,0);
283             }
284             });
285             $tbox->pack_start( $gdkim, 0, 0, 0);
286             $tbox->pack_start( $button, 0, 0, 0);
287             $button->show();
288             $hbox->pack_start( $tbox, 0, 0, 3);
289             $tbox->show();
290             $count++;
291             }
292             $vbox->pack_start($hbox, 0, 0, 2);
293             $hbox->show();
294             }
295             $vbox->show();
296             }
297              
298              
299              
300              
301              
302              
303             sub image_to_row {
304             my ($self, $imageno) = @_;
305             my @images = @{$self->{'ordering'}};
306             my %rev;
307             @rev{@images} = 0..$#images;
308             return $rev{$imageno};
309             }
310              
311              
312             sub row_to_image {
313             my ($self, $row) = @_;
314             my @images = @{$self->{'ordering'}};
315             return $images[$row];
316             }
317              
318              
319              
320              
321              
322             sub set_image {
323             my ($self, $list, $row) = @_;
324              
325             my $imageno = $self->row_to_image($row);
326             $self->{'selection'}->{$imageno} = 1;
327              
328             my $button = $self->{'gui_info'}->{$imageno};
329             my $tstyle = $button->get_style()->copy();
330             $tstyle->bg('normal', $yellow);
331             $tstyle->bg('prelight', $yellow);
332              
333             $button->set_style($tstyle);
334             }
335              
336              
337              
338             sub unset_image {
339             my ($self, $list, $row) = @_;
340              
341             my $imageno = $self->row_to_image($row);
342             delete $self->{'selection'}->{$imageno};
343              
344             my $button = $self->{'gui_info'}->{$imageno};
345             my $tstyle = $button->get_style()->copy();
346             $tstyle->bg('normal', $gray);
347             $tstyle->bg('prelight', $gray);
348             $button->set_style($tstyle);
349              
350             }
351              
352              
353              
354             sub remove_images {
355             my $self = shift;
356             my @imagenos = @_;
357             $self->{'parent'}->remove_images(@imagenos);
358             $self->{'selection'} = {};
359             $self->previewer_update();
360             $self->imagelist_update();
361             }
362              
363              
364              
365             sub export_gallery {
366             my $self = shift;
367             $self->export();
368             }
369              
370              
371              
372              
373             sub label {
374             my $self = shift;
375             my @imagenos = @_;
376              
377             # print "@imagenos\n";
378             my @images = map { $self->{'images'}->{$_} } @imagenos;
379              
380             my $window = new Gtk::Window "toplevel";
381             $window->set_usize(600,400);
382             $window->set_title( "Image-Caption" );
383             $window->set_modal( 1 );
384              
385             my $vbox = new Gtk::VBox 0,0;
386              
387             # Image at top
388             my $imbox = new Gtk::HBox 0,0;
389             $vbox->pack_start( $imbox, 0, 1, 0 );
390              
391             # image name
392             my $fname_label = new Gtk::Label "Filename";
393             $vbox->pack_start( $fname_label, 0, 1, 0 );
394              
395             # image name
396             my $namentry = new Gtk::Entry;
397              
398             # caption next
399             my $capentry = new Gtk::Entry;
400              
401             # button strip
402             my $strip = new Gtk::HBox 0,0;
403              
404              
405             $vbox->pack_end( $_, 0, 1, 0 ) for ($strip, $capentry, $namentry);
406              
407             my $prev_image = new Gtk::Button "previous image";
408             $strip->pack_start( $prev_image, 0, 1, 0);
409              
410             my $next_image = new Gtk::Button "next image";
411             $strip->pack_start( $next_image, 0, 1, 0);
412              
413             my $done = new Gtk::Button "done";
414             $strip->pack_start( $done, 0, 1, 0);
415              
416             $window->add($vbox);
417              
418              
419             $_->show() for ($done, $prev_image, $next_image,
420             $strip, $capentry, $namentry,
421             $fname_label, $imbox, $vbox, $window);
422              
423              
424             my $gdkwindow = $window->window;
425              
426             my %scaleopts = (xpixels=>400, ypixels=>300, qtype=>'preview', type=>'min');
427              
428             my $ino = 0;
429              
430             my $imset = sub {
431             my $img = Imager->new();
432             my $ihash = $images[$ino];
433             my %sopts = %scaleopts;
434             $img->read(file=>$ihash->{'path'}) or die $img->errstr;
435              
436             if ($ihash->{'rotated'} % 2) {
437             my $t = $sopts{'xpixels'};
438             $sopts{'xpixels'} = $sopts{'ypixels'};
439             $sopts{'ypixels'} = $t;
440             }
441              
442             $img = $img->scale(%sopts);
443              
444             if ($ihash->{'rotated'}) {
445             $img = $img->rotate(degrees=>$ihash->{'rotated'}*90);
446             }
447              
448             my $gdkim = img_to_pix2($img, $gdkwindow);
449             $gdkim->show;
450              
451             empty_box($imbox);
452             $imbox->pack_start($gdkim, 1, 1, 1);
453              
454              
455             $fname_label->set_text( ($ino+1)."/".@images." :: ".$ihash->{'path'});
456              
457             $namentry->set_text($ihash->{'name'});
458             $capentry->set_text($ihash->{'caption'});
459             };
460              
461             my $imstore = sub {
462             my $ihash = $images[$ino];
463             $ihash->{'name'} = $namentry->get_text();
464             $ihash->{'caption'} = $capentry->get_text();
465             };
466              
467              
468             $imset->();
469              
470              
471             $prev_image->signal_connect('clicked', sub {
472             $imstore->();
473             $ino = ($ino+@images-1)%(0+@images);
474             $imset->();
475             });
476              
477             $next_image->signal_connect('clicked', sub {
478             $imstore->();
479             $ino = ($ino+1)%(0+@images);
480             $imset->();
481             });
482              
483              
484             $done->signal_connect('clicked', sub {
485             $imstore->();
486             $window->destroy();
487             $self->previewer_update();
488             $self->imagelist_update_names();
489             });
490              
491              
492              
493             }
494              
495              
496              
497              
498              
499              
500              
501              
502              
503              
504              
505              
506              
507             sub export {
508             my $self = shift;
509              
510             my $window = new Gtk::Window "toplevel";
511             # $window->set_usize(600,400);
512             $window->set_title( "Image-Caption" );
513             $window->set_modal( 1 );
514              
515             my $vbox = new Gtk::VBox 0,0;
516              
517             # path name
518             my $hbox = new Gtk::HBox 0,0;
519             my $dir_label = new Gtk::Label "Directory to export to:";
520             my $dir_entry = new Gtk::Entry;
521              
522             $hbox->pack_start( $dir_label, 0, 1, 0);
523             $hbox->pack_end( $dir_entry, 0, 1, 0);
524             $vbox->pack_start( $hbox, 0, 1, 0 );
525             $_->show() for ($hbox, $dir_label, $dir_entry);
526              
527             # gallery name
528             $hbox = new Gtk::HBox 0,0;
529             my $gallery_label = new Gtk::Label "Name of gallery:";
530             my $gallery_entry = new Gtk::Entry;
531              
532             $hbox->pack_start( $gallery_label, 0, 1, 0);
533             $hbox->pack_end( $gallery_entry, 0, 1, 0);
534             $vbox->pack_start( $hbox, 0, 1, 0 );
535             $_->show() for ($hbox, $gallery_label, $gallery_entry);
536              
537             # button strip
538             $hbox = new Gtk::HBox 0,0;
539             my $ok = new Gtk::Button "Ok";
540             my $cancel = new Gtk::Button "Cancel";
541              
542             $hbox->pack_start( $ok, 0, 1, 0);
543             $hbox->pack_start( $cancel, 0, 1, 0);
544             $vbox->pack_end( $hbox, 0, 1, 0 );
545             $_->show() for ($hbox, $ok, $cancel);
546              
547             $window->add($vbox);
548              
549             $_->show() for ($vbox, $window);
550              
551             $ok->signal_connect('clicked', sub {
552             my $gallery = $gallery_entry->get_text();
553             my $dir = $dir_entry->get_text();
554             $self->{'parent'}->export($dir, $gallery);
555             $window->destroy();
556             });
557              
558             $cancel->signal_connect('clicked', sub {
559             $window->destroy();
560             });
561             }
562              
563              
564              
565              
566              
567              
568              
569              
570              
571              
572             # Technically these don't have to be methods
573             # but it's just simpler
574              
575             sub read_image {
576             my ($self, $file) = @_;
577              
578             my $img = Imager->new();
579             $img->read(file=>$file) or die $img->errstr;
580             my $pixmap = $self->img_to_pix($img);
581             return $pixmap;
582             }
583              
584              
585              
586              
587             sub img_to_pix {
588             my ($self, $img) = @_;
589              
590             my $gc = $self->{'gc'};
591             my $gdkwindow = $self->{'gdkwindow'};
592              
593             my $width = $img->getwidth;
594             my $height = $img->getheight;
595             my $data = Imager::i_img_getdata($img->{IMG});
596              
597             my ($gdk_pixmap, $gdk_mask) = new Gtk::Gdk::Pixmap($gdkwindow, $width, $height, -1);
598             $gdk_pixmap->draw_rgb_image($gc, 0, 0, $width, $height, 0, $data, $width*3);
599             my $pixmap = new Gtk::Pixmap($gdk_pixmap, $gdk_mask);
600             return $pixmap;
601             }
602              
603              
604              
605             sub img_to_pix2 {
606             my ($img, $gdkwindow) = @_;
607              
608             my $gc = Gtk::Gdk::GC->new($gdkwindow);
609              
610             my $width = $img->getwidth;
611             my $height = $img->getheight;
612             my $data = Imager::i_img_getdata($img->{IMG});
613              
614             my ($gdk_pixmap, $gdk_mask) = new Gtk::Gdk::Pixmap($gdkwindow, $width, $height, -1);
615             $gdk_pixmap->draw_rgb_image($gc, 0, 0, $width, $height, 0, $data, $width*3);
616             my $pixmap = new Gtk::Pixmap($gdk_pixmap, $gdk_mask);
617             return $pixmap;
618             }
619              
620              
621              
622             sub empty_box {
623             my $box = shift;
624             my @widgets;
625             $box->foreach(sub { push @widgets, shift; });
626             $box->remove($_) for @widgets;
627             }
628              
629              
630              
631             1;