File Coverage

blib/lib/Gtk2/Ex/ToolItem/ComboEnum.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 2010, 2011 Kevin Ryde
2              
3             # This file is part of Gtk2-Ex-ComboBoxBits.
4             #
5             # Gtk2-Ex-ComboBoxBits is free software; you can redistribute it and/or
6             # modify it under the terms of the GNU General Public License as published
7             # by the Free Software Foundation; either version 3, or (at your option) any
8             # later version.
9             #
10             # Gtk2-Ex-ComboBoxBits 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-ComboBoxBits. If not, see .
17              
18             package Gtk2::Ex::ToolItem::ComboEnum;
19 1     1   1777 use 5.008;
  1         4  
  1         41  
20 1     1   6 use strict;
  1         2  
  1         31  
21 1     1   5 use warnings;
  1         1  
  1         31  
22 1     1   785 use Gtk2;
  0            
  0            
23             use Gtk2::Ex::ContainerBits;
24             use Gtk2::Ex::ComboBox::Enum 5; # v.5 for get_active_nick(),set_active_nick()
25              
26             # uncomment this to run the ### lines
27             #use Smart::Comments;
28              
29             our $VERSION = 32;
30              
31             use Glib::Object::Subclass
32             'Gtk2::ToolItem',
33             interfaces => [
34             # Gtk2::Buildable new in Gtk 2.12, omit if not available
35             Gtk2::Widget->isa('Gtk2::Buildable') ? ('Gtk2::Buildable') : ()
36             ],
37             signals => { create_menu_proxy => \&_do_create_menu_proxy,
38             },
39             properties => [
40             # FIXME: default enum-type is undef but
41             # Glib::ParamSpec->string() doesn't allow that until
42             # Perl-Glib 1.240, in which case have
43             # Glib::ParamSpec->gtype().
44             #
45             (Glib::Param->can('gtype')
46             ?
47             # new in Glib 2.10 and Perl-Glib 1.240
48             Glib::ParamSpec->gtype
49             ('enum-type',
50             'Enum type',
51             'The enum class to display.',
52             'Glib::Enum',
53             Glib::G_PARAM_READWRITE)
54             :
55             Glib::ParamSpec->string
56             ('enum-type',
57             'Enum type',
58             'The enum class to display.',
59             '',
60             Glib::G_PARAM_READWRITE)),
61              
62             Glib::ParamSpec->string
63             ('active-nick',
64             'Active nick',
65             'The selected enum value, as its nick.',
66             (eval {Glib->VERSION(1.240);1}
67             ? undef # default
68             : ''), # no undef/NULL before Perl-Glib 1.240
69             Glib::G_PARAM_READWRITE),
70              
71             Glib::ParamSpec->string
72             ('overflow-mnemonic',
73             'Overflow menu mnemonic',
74             'A mnemonic label string to show in the overflow menu. Default is some mangling of the enum-type.',
75             (eval {Glib->VERSION(1.240);1}
76             ? undef # default
77             : ''), # no undef/NULL before Perl-Glib 1.240
78             Glib::G_PARAM_READWRITE),
79             ];
80              
81             sub INIT_INSTANCE {
82             my ($self) = @_;
83             my $combobox = Gtk2::Ex::ComboBox::Enum->new;
84             $combobox->show;
85             $combobox->signal_connect ('notify::active-nick' => \&_do_combobox_notify);
86             $combobox->signal_connect ('notify::enum-type' => \&_do_combobox_notify);
87             $combobox->signal_connect ('notify::add-tearoffs'
88             => \&_do_combobox_notify_add_tearoffs);
89             $self->add ($combobox);
90             }
91              
92             sub FINALIZE_INSTANCE {
93             my ($self) = @_;
94             if (my $menuitem = delete $self->{'menuitem'}) {
95             $menuitem->destroy; # destroy circular MenuItem<->AccelLabel
96             }
97             }
98              
99             sub GET_PROPERTY {
100             my ($self, $pspec) = @_;
101             my $pname = $pspec->get_name;
102             ### ComboEnum GET_PROPERTY: $pname
103              
104             if ($pname eq 'enum_type' || $pname eq 'active_nick') {
105             ### fetch from combobox: $self->get_child && $self->get_child->get($pname)
106             my $combobox;
107             return (($combobox = $self->get_child) # no child when destroyed, maybe
108             && $combobox->get($pname));
109             }
110              
111             return $self->{$pname};
112             }
113              
114             sub SET_PROPERTY {
115             my ($self, $pspec, $newval) = @_;
116             my $pname = $pspec->get_name;
117             ### ComboEnum SET_PROPERTY: $pname, $newval
118              
119             if ($pname eq 'enum_type' || $pname eq 'active_nick') {
120             foreach my $target
121             ($self->get_child,
122             ($self->{'menuitem'} && $self->{'menuitem'}->get_submenu)) {
123              
124             if ($target) { # no children when destroyed maybe
125             ### ComboEnum propagate to: "$target"
126             $target->set ($pname => $newval);
127             }
128             }
129             } else {
130             $self->{$pname} = $newval;
131             }
132              
133             if ($pname eq 'overflow_mnemonic'
134             || $pname eq 'enum_type') { # for when showing enum-type as fallback
135             _update_overflow_mnemonic($self);
136             }
137             }
138              
139             sub _do_combobox_notify {
140             my ($combobox, $pspec) = @_;
141             ### ComboEnum _do_combobox_notify()
142             if (my $self = $combobox->parent) { # in case unparented maybe
143             $self->notify($pspec->get_name);
144             }
145             }
146             sub _do_combobox_notify_add_tearoffs {
147             my ($combobox, $pspec) = @_;
148             ### ComboEnum _do_combobox_notify_add_tearoffs()
149             if (my $self = $combobox->parent) { # in case unparented maybe
150             _update_overflow_tearoff ($self);
151             }
152             }
153              
154             sub _do_create_menu_proxy {
155             my ($self) = @_;
156             ### _do_create_menu_proxy()
157             my $combobox = $self->get_child || return 0; # if being destroyed maybe
158              
159             if (! $self->{'menuitem'}) {
160             require Gtk2::Ex::Menu::EnumRadio;
161             require Glib::Ex::ConnectProperties;
162             my $menu = Gtk2::Ex::Menu::EnumRadio->new;
163             # enum-type first otherwise active-nick not settable
164             Glib::Ex::ConnectProperties->new ([$self,'enum-type'],
165             [$menu,'enum-type']);
166             Glib::Ex::ConnectProperties->new ([$self,'active-nick'],
167             [$menu,'active-nick']);
168             # ComboBox tearoff-title new in 2.10, but Menu tearoff-title always present
169             if ($combobox->find_property('tearoff-title')) {
170             Glib::Ex::ConnectProperties->new ([$combobox,'tearoff-title'],
171             [$menu,'tearoff-title']);
172             }
173             ### initial menu enum-type: $menu->get_active_nick
174             ### initial menu active-nick: $menu->get_active_nick
175              
176             # prompt store to $self->{'menuitem'} for safe destroy if any error
177             my $menuitem = $self->{'menuitem'} = Gtk2::MenuItem->new_with_mnemonic ('');
178             $menuitem->set_submenu ($menu);
179             _update_overflow_mnemonic ($self); # initial label
180             _update_overflow_tearoff ($self); # initial tearoff
181             }
182              
183             $self->set_proxy_menu_item (__PACKAGE__, $self->{'menuitem'});
184             return 1;
185             }
186              
187             sub _update_overflow_mnemonic {
188             my ($self) = @_;
189             ### ComboEnum _update_overflow_mnemonic()
190              
191             my $menuitem = $self->{'menuitem'} || return;
192             my $label = $menuitem->get_child || return; # just in case gone
193              
194             my $str = $self->{'overflow_mnemonic'};
195             if (! defined $str) {
196             if (my $combobox = $self->get_child) {
197             if (defined ($str = $combobox->get('enum-type'))) {
198             $str =~ s/.*:://;
199             } else {
200             $str = $combobox->get_name;
201             }
202             } else {
203             $str = $self->get_name;
204             }
205             require Glib::Ex::EnumBits;
206             $str = Glib::Ex::EnumBits::to_display_default (undef, $str);
207             require Gtk2::Ex::MenuBits;
208             $str = Gtk2::Ex::MenuBits::mnemonic_escape($str);
209             }
210             ### $str
211             $label->set_label ($str);
212             }
213              
214             sub _update_overflow_tearoff {
215             my ($self) = @_;
216             ### ComboEnum _update_overflow_tearoff()
217             my $combobox = $self->get_child || return 0; # if being destroyed maybe
218             my $menuitem = $self->{'menuitem'} || return;
219             my $menu = $menuitem->get_submenu || return; # if being destroyed maybe
220             $combobox->find_property('add-tearoffs') || return; # not in Gtk 2.4
221             if ($combobox->get('add-tearoffs')) {
222             ### tearoff wanted
223             unless (List::Util::first
224             {$_->isa('Gtk2::TearoffMenuItem')}
225             $menu->get_children) {
226             ### add new TearoffMenuItem
227             $menu->prepend (Gtk2::TearoffMenuItem->new);
228             }
229             } else {
230             ### tearoff not wanted
231             Gtk2::Ex::ContainerBits::remove_widgets
232             ($menu,
233             grep {$_->isa('Gtk2::TearoffMenuItem')} $menu->get_children);
234             }
235             }
236              
237             #------------------------------------------------------------------------------
238             # Gtk2::Buildable interface
239              
240             sub GET_INTERNAL_CHILD {
241             my ($self, $builder, $name) = @_;
242             if ($name eq 'combobox') {
243             return $self->get_child;
244             }
245             return undef;
246             }
247              
248             1;
249             __END__