File Coverage

blib/lib/Gtk2/Ex/SyncCall.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             # Copyright 2008, 2009, 2010, 2011, 2012 Kevin Ryde
2              
3             # This file is part of Gtk2-Ex-WidgetBits.
4             #
5             # Gtk2-Ex-WidgetBits 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-WidgetBits 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-WidgetBits. If not, see .
17              
18             package Gtk2::Ex::SyncCall;
19 1     1   879 use 5.008;
  1         4  
  1         42  
20 1     1   7 use strict;
  1         2  
  1         51  
21 1     1   6 use warnings;
  1         7  
  1         38  
22 1     1   5 use Carp;
  1         2  
  1         81  
23 1     1   1031 use Gtk2;
  0            
  0            
24              
25             # uncomment this to run the ### lines
26             #use Smart::Comments;
27              
28             # version 2 was in with Gtk2-Ex-Dragger ...
29             our $VERSION = 48;
30              
31              
32             my $sync_call_atom;
33              
34             my $get_display = (Gtk2::Widget->can('get_display')
35             ? 'get_display'
36             : do {
37             my $dummy_display = {};
38             sub { $dummy_display }
39             });
40              
41             sub sync {
42             my ($class, $widget, $callback, $userdata) = @_;
43             ### SyncCall sync()
44              
45             my $display = $widget->$get_display;
46             my $data = ($display->{(__PACKAGE__)} ||= do {
47             $widget->add_events ('property-change-mask');
48              
49             ### widget add_events gives: $widget->window && $widget->window->get_events
50             #### window XID: $widget->window && $widget->window->can('XID') && $widget->window->XID
51              
52             require Glib::Ex::SignalIds;
53             # hash of data
54             ({ sync_list => [],
55             signal_ids => Glib::Ex::SignalIds->new
56             ($widget,
57             $widget->signal_connect (property_notify_event =>
58             \&_do_property_notify),
59             $widget->signal_connect (unrealize => \&_do_widget_destroy),
60             $widget->signal_connect (destroy => \&_do_widget_destroy)) })
61             });
62              
63             $widget = $data->{'signal_ids'}->object;
64             my $win = $widget->window
65             || croak __PACKAGE__.'->sync(): widget not realized';
66              
67             # HACK: in gtk 2.18.4 and 2.18.5 property-change-event's aren't delivered
68             # to a non-toplevel widget unless you call $gdkwin->XID on it
69             # (ie. gdk_x11_drawable_get_xid()). This is bizarre and would have to be
70             # a bug, but this workaround at least makes SyncCall work (and its
71             # dependents like Gtk2::Ex::CrossHair).
72             #
73             if ($win->can('XID')) { $win->XID; }
74              
75             my $self = { display => $display,
76             callback => $callback,
77             userdata => $userdata };
78             my $aref = $data->{'sync_list'};
79             push @$aref, $self;
80              
81             if (@$aref == 1) {
82             # first entry in sync_list initiates the sync
83             $sync_call_atom ||= Gtk2::Gdk::Atom->intern (__PACKAGE__);
84             ### property_change of: $sync_call_atom
85             $win->property_change ($sync_call_atom,
86             Gtk2::Gdk::Atom->intern('STRING'),
87             Gtk2::Gdk::CHARS, 'append', '');
88             }
89             return $self;
90             }
91              
92             # 'property-notify-event' signal on sync widget
93             sub _do_property_notify {
94             my ($widget, $event) = @_;
95             ### SyncCall property-notify handler: $event->atom
96              
97             # note, no overloaded != until Gtk2-Perl 1.183, only == prior to that
98             if ($event->atom == $sync_call_atom) {
99             my $display = $widget->$get_display;
100             my $data = $display->{(__PACKAGE__)};
101             _call_all ($data);
102             }
103             # even though $sync_call_atom is supposed to be for us alone, propagate it
104             # anyway in case someone else is monitoring what happens
105             return 0; # Gtk2::EVENT_PROPAGATE
106             }
107              
108             # 'unrealize' or 'destroy' signal on the sync widget
109             sub _do_widget_destroy {
110             my ($widget) = @_;
111             my $display = $widget->$get_display;
112             if (my $data = delete $display->{(__PACKAGE__)}) {
113             _call_all ($data);
114             }
115             }
116              
117             sub _call_all {
118             my ($data) = @_;
119             my $aref = $data->{'sync_list'};
120             $data->{'sync_list'} = [];
121             foreach my $self (@$aref) {
122             $self->{'callback'}->($self->{'userdata'});
123             }
124             }
125              
126             1;
127             __END__