File Coverage

blib/lib/Gtk2/Ex/Splash.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             # Copyright 2011 Kevin Ryde
2              
3             # This file is part of Gtk2-Ex-Splash.
4             #
5             # Gtk2-Ex-Splash is free software; you can redistribute it and/or modify
6             # it under the terms of the GNU General Public License as published by the
7             # Free Software Foundation; either version 3, or (at your option) any later
8             # version.
9             #
10             # Gtk2-Ex-Splash is distributed in the hope that it will be useful, but
11             # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12             # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13             # for more details.
14             #
15             # You should have received a copy of the GNU General Public License along
16             # with Gtk2-Ex-Splash. If not, see .
17              
18              
19             package Gtk2::Ex::Splash;
20 1     1   634 use 5.008;
  1         4  
  1         40  
21 1     1   5 use strict;
  1         2  
  1         29  
22 1     1   4 use warnings;
  1         5  
  1         28  
23 1     1   3423 use Gtk2;
  0            
  0            
24             use List::Util 'max';
25             use Scalar::Util;
26              
27             our $VERSION = 52;
28              
29             # uncomment this to run the ### lines
30             #use Smart::Comments;
31              
32             use Glib::Object::Subclass 'Gtk2::Window',
33             signals => { realize => \&_do_realize,
34             map => \&_do_map,
35             unmap => \&_do_flush,
36             unrealize => \&_do_flush,
37             expose_event => \&_do_expose_event },
38              
39             properties =>
40             [ Glib::ParamSpec->object ('pixmap',
41             (do {
42             my $str = 'Pixmap';
43             eval { require Locale::Messages;
44             Locale::Messages::dgettext('gtk20-properties',$str)
45             } || $str }),
46             'Blurb.',
47             'Gtk2::Gdk::Pixmap',
48             Glib::G_PARAM_READWRITE),
49              
50             Glib::ParamSpec->object ('pixbuf',
51             (do {
52             my $str = 'Pixbuf';
53             eval { require Locale::Messages;
54             Locale::Messages::dgettext('gtk20-properties',$str)
55             } || $str }),
56             'Blurb.',
57             'Gtk2::Gdk::Pixbuf',
58             Glib::G_PARAM_READWRITE),
59              
60             Glib::ParamSpec->scalar ('filename',
61             (do {
62             # as from GtkFileSelection and
63             # GtkRecentManager
64             my $str = 'Filename';
65             eval { require Locale::Messages;
66             Locale::Messages::dgettext('gtk20-properties',$str)
67             } || $str }),
68             'Blurb.',
69             Glib::G_PARAM_READWRITE),
70             ];
71              
72             sub new {
73             my $class = shift;
74             return $class->SUPER::new (type => 'popup', @_);
75             }
76              
77             my %instances;
78              
79             sub INIT_INSTANCE {
80             my ($self) = @_;
81             ### Splash INIT_INSTANCE()
82             $self->set_app_paintable (0);
83             $self->set_double_buffered(0);
84             $self->can_focus (0);
85             Scalar::Util::weaken ($instances{Scalar::Util::refaddr($self)} = $self);
86             }
87             sub FINALIZE_INSTANCE {
88             my ($self) = @_;
89             delete $instances{Scalar::Util::refaddr($self)};
90             }
91              
92             sub SET_PROPERTY {
93             my ($self, $pspec, $newval) = @_;
94             my $pname = $pspec->get_name;
95             ### Splash SET_PROPERTY: $pname
96              
97             if ($pname eq 'filename' && defined $newval) {
98             # stringize to copy possible object passed in instead of plain string
99             $newval = "$newval";
100             }
101             $self->{$pname} = $newval;
102             _update_pixmap ($self);
103             if ($self->mapped) {
104             _flush($self);
105             }
106             }
107              
108             sub _do_expose_event {
109             # my $self = shift;
110             ### Splash _do_expose(), no chain to default
111              
112             # don't chain as don't want the GtkWindow expose handler
113             # gtk_window_expose() to draw the style background colour ...
114             }
115              
116             sub _do_realize {
117             my $self = shift;
118             ### Splash _do_realize()
119              
120             $self->signal_chain_from_overridden();
121             my $window = $self->window;
122             $window->set_override_redirect (1);
123             if ($window->can('set_type_hint')) { # new in Gtk 2.10
124             $window->set_type_hint ('splashscreen');
125             }
126             ### xwininfo: do { $self->get_display->flush; $self->window && system "xwininfo -events -id ".$self->window->XID }
127              
128             _update_pixmap ($self);
129              
130             ### Splash _do_realize() finished
131             }
132              
133             my $unmap_id;
134              
135             # widget "unmap" signal emission hook, run after normal signal connections
136             sub _do_unmap_emission_hook {
137             my ($invocation_hint, $parameters) = @_;
138             my ($widget) = @$parameters;
139             ### Splash _do_unmap_emission_hook()
140              
141             # ->clear() any Splash instances on the same screen as $widget
142             my $keep = 0;
143             foreach my $instance (values %instances) {
144             if ($instance # perhaps weakened away in global destruction
145             && (my $window = $instance->window) # when realized
146             && $instance->mapped) { # and mapped
147             $keep = 1;
148             if (! $widget->can('get_screen') # not in Gtk 2.0
149             || $widget->get_screen == $instance->get_screen) {
150             ### clear other instance: "$instance"
151             $window->clear;
152             _flush ($instance);
153             }
154             }
155             }
156             if (! $keep) {
157             undef $unmap_id; # to be reconnected in _do_map()
158             }
159             ### _do_unmap_emission_hook() finished
160             ### $keep
161             ### $unmap_id
162             return $keep; # stay connected, or not
163             }
164              
165             sub _do_map {
166             my $self = shift;
167             ### Splash _do_map()
168             $self->signal_chain_from_overridden ();
169              
170             # something fishy requires a clear, the background isn't drawn just by a map
171             $self->window->clear;
172             _flush ($self);
173              
174             $unmap_id ||= Gtk2::Widget->signal_add_emission_hook
175             (unmap => \&_do_unmap_emission_hook);
176             ### $unmap_id
177              
178             ### _do_map() finished
179             }
180              
181             # "unmap" and "unrealize" class handler
182             # flush so as to immediately pop down the splash
183             # $splash->unrealize() doesn't call the unmap handler, hence unrealize handler
184             # $splash->destroy() does unmap then unrealize, so it gets both in fact
185             #
186             sub _do_flush {
187             ### Splash _do_flush()
188             my $self = shift;
189             $self->signal_chain_from_overridden (@_);
190             _flush ($self);
191             }
192              
193             # set_back_pixmap() or set_background() according to the current properties
194             # clear the window to make the change show up too, if mapped
195             # if no window yet (unrealized) then do nothing
196             sub _update_pixmap {
197             my ($self) = @_;
198             ### _update_pixmap()
199             ### pixmap: $self->{'pixmap'}
200             ### pixbuf: $self->{'pixbuf'}
201             ### filename: $self->{'filename'}
202              
203             my $window = $self->window || return;
204              
205             my $pixmap = $self->{'pixmap'};
206             if (! $pixmap) {
207             my $pixbuf = $self->{'pixbuf'};
208             if (! $pixbuf
209             && defined (my $filename = $self->{'filename'})) {
210             ### $filename
211             $pixbuf = Gtk2::Gdk::Pixbuf->new_from_file ($filename);
212             }
213              
214             ### $pixbuf
215             if ($pixbuf) {
216             ### state: $self->state
217             ### style: $self->get_style
218             ### bg-gc: $self->get_style->bg_gc($self->state)
219             ### bg-color: $self->get_style->bg($self->state)->to_string
220              
221             my $width = $pixbuf->get_width;
222             my $height = $pixbuf->get_height;
223             $pixmap = Gtk2::Gdk::Pixmap->new ($window, $width,$height, -1);
224              
225             # my $bg_color = $self->get_style->bg($self->state);
226             # my $gc = Gtk2::Gdk::GC->new($pixmap, { foreground => $bg_color });
227              
228             my $gc = $self->get_style->bg_gc($self->state);
229             $pixmap->draw_rectangle ($gc,
230             1, # filled
231             0,0,
232             $width,$height);
233             $pixbuf->render_to_drawable ($pixmap,
234             $gc,
235             0,0,
236             0,0,
237             $width, $height,
238             'none', # dither
239             0,0);
240             }
241             }
242             ### $pixmap
243              
244             my ($width, $height) = ($pixmap ? $pixmap->get_size : (100,100));
245             $self->resize ($width, $height);
246             ### resize to: "$width, $height"
247              
248             my $root = ($self->can('get_root_window') # new in Gtk 2.2
249             ? $self->get_root_window
250             : Gtk2::Gdk->default_root_window);
251             my ($root_width, $root_height) = $root->get_size;
252             my $x = max (0, int (($root_width - $width) / 2));
253             my $y = max (0, int (($root_height - $height) / 2));
254             ### move to: "$x,$y"
255             $self->move ($x, $y);
256              
257             # the size is normally only applied under ->map(), or some such, force here
258             $window->move_resize ($x, $y, $width, $height);
259              
260             ### Splash set_back_pixmap(): $pixmap
261             $window->set_back_pixmap ($pixmap);
262             if (! $pixmap) {
263             # fallback to the style bg if no pixmap etc set, just "normal" as not to
264             # bother following the $self->state() for this fallback
265             $window->set_background ($self->get_style->bg('normal'));
266             }
267             if ($self->mapped) {
268             $window->clear;
269             }
270             }
271              
272             # flush the X request queue on the display of $self
273             sub _flush {
274             my ($self) = @_;
275             if ($self->can('get_display')) { # new in Gtk 2.2
276             ### get_display flush
277             $self->get_display->flush;
278             } else {
279             ### flush() is XSync in Gtk 2.0.x
280             Gtk2::Gdk->flush;
281             }
282             }
283              
284             1;
285             __END__