File Coverage

blib/lib/App/Chart/Gtk2/GUI.pm
Criterion Covered Total %
statement 18 20 90.0
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 25 27 92.5


line stmt bran cond sub pod time code
1             # GUI shared functions.
2              
3             # Copyright 2007, 2008, 2009, 2010, 2011 Kevin Ryde
4              
5             # This file is part of Chart.
6             #
7             # Chart is free software; you can redistribute it and/or modify it under the
8             # terms of the GNU General Public License as published by the Free Software
9             # Foundation; either version 3, or (at your option) any later version.
10             #
11             # Chart is distributed in the hope that it will be useful, but WITHOUT ANY
12             # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13             # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14             # details.
15             #
16             # You should have received a copy of the GNU General Public License along
17             # with Chart. If not, see <http://www.gnu.org/licenses/>.
18              
19             package App::Chart::Gtk2::GUI;
20 1     1   442 use 5.006;
  1         3  
21 1     1   4 use strict;
  1         2  
  1         17  
22 1     1   5 use warnings;
  1         1  
  1         28  
23 1     1   4 use Carp;
  1         2  
  1         53  
24 1     1   5 use List::Util qw(min max);
  1         2  
  1         50  
25 1     1   314 use Locale::TextDomain ('App-Chart');
  1         17961  
  1         6  
26 1     1   5802 use Glib;
  0            
  0            
27             use Gtk2;
28             use Gtk2::Pango;
29              
30             use App::Chart;
31              
32             # uncomment this to run the ### lines
33             #use Smart::Comments;
34              
35             Glib::set_application_name (__('Chart'));
36             Gtk2::Window->set_default_icon_from_file
37             (App::Chart::datafilename ('chart.xpm'));
38             Gtk2->CHECK_VERSION(2,12,0)
39             or die "App::Chart needs Gtk 2.12 or higher";
40              
41              
42             #------------------------------------------------------------------------------
43              
44             # Perl-Gtk 1.221 no undef
45             sub menu_set_screen {
46             my ($menu, $screen) = @_;
47             # $screen ||= do {
48             # my $display;
49             # ($display = Gtk2::Gdk::Display->get_default)
50             # && $display->get_default_screen;
51             # };
52             if ($screen) {
53             $menu->set_screen ($screen);
54             }
55             }
56              
57             #------------------------------------------------------------------------------
58              
59             sub string_width {
60             my ($widget_or_layout, $str) = @_;
61             ### string_width(): "$widget_or_layout"
62             ### $str
63             my $layout;
64             if ($widget_or_layout->can ('create_pango_layout')) {
65             # if widget instead of layout
66             $layout = $widget_or_layout->create_pango_layout ($str);
67             } else {
68             $layout = $widget_or_layout;
69             $layout->set_text ($str);
70             }
71             my ($width, $height) = $layout->get_pixel_size;
72             return $width;
73             }
74              
75              
76             #------------------------------------------------------------------------------
77              
78             # FIXME: really want to clip to the region, not an enclosing rect. The
79             # style paint might be some nice drawing, but the default is a pretty simple
80             # gdk_draw_layout().
81             #
82             sub draw_text_centred {
83             my ($widget, $region_or_event, $str) = @_;
84             my $win = $widget->window || return; # unrealized
85              
86             my $clip_rect = $region_or_event && do {
87             if (my $func = $region_or_event->can('get_clipbox')) {
88             $func->($region_or_event);
89             } else {
90             $region_or_event->area;
91             }
92             };
93              
94             my $alloc = $widget->allocation;
95             my $layout = $widget->create_pango_layout ($str);
96             $layout->set_wrap ('word-char');
97             $layout->set_width ($alloc->width * Gtk2::Pango::PANGO_SCALE);
98             my ($str_width, $str_height) = $layout->get_pixel_size;
99             my $x = max (0, ($alloc->width - $str_width) / 2);
100             my $y = max (0, ($alloc->height - $str_height) / 2);
101             if ($widget->get_flags & 'no-window') {
102             $x += $alloc->x;
103             $y += $alloc->y;
104             }
105             my $style = $widget->get_style;
106             $style->paint_layout ($win,
107             $widget->state,
108             1, # use text gc
109             $clip_rect,
110             $widget,
111             'centred-text',
112             $x, $y, $layout);
113             }
114              
115              
116             #------------------------------------------------------------------------------
117              
118             sub chart_style_class {
119             my ($class) = @_;
120             _chart_style_parse();
121             $class =~ s/:/_/g;
122             Gtk2::Rc->parse_string
123             ("class \"$class\" style:gtk \"Chart_style\"");
124             }
125             sub chart_style_widget {
126             my ($name) = @_;
127             _chart_style_parse();
128             Gtk2::Rc->parse_string
129             ("widget \"*.$name\" style:application \"Chart_style\"");
130             }
131             use constant::defer _chart_style_parse => sub {
132             Gtk2::Rc->parse_string (<<'HERE');
133             style "Chart_style" {
134             # white on black
135             fg[ACTIVE] = { 1.0, 1.0, 1.0 }
136             fg[NORMAL] = { 1.0, 1.0, 1.0 }
137             fg[PRELIGHT] = { 1.0, 1.0, 1.0 }
138             fg[SELECTED] = { 1.0, 1.0, 1.0 }
139             fg[INSENSITIVE] = { 1.0, 1.0, 1.0 }
140             text[ACTIVE] = { 1.0, 1.0, 1.0 }
141             text[NORMAL] = { 1.0, 1.0, 1.0 }
142             text[PRELIGHT] = { 1.0, 1.0, 1.0 }
143             text[SELECTED] = { 1.0, 1.0, 1.0 }
144             text[INSENSITIVE] = { 1.0, 1.0, 1.0 }
145              
146             bg[ACTIVE] = { 0, 0, 0 }
147             bg[NORMAL] = { 0, 0, 0 }
148             bg[PRELIGHT] = { 0, 0, 0 }
149             bg[SELECTED] = { 0, 0, 0 }
150             bg[INSENSITIVE] = { 0, 0, 0 }
151             base[ACTIVE] = { 0, 0, 0 }
152             base[NORMAL] = { 0, 0, 0 }
153             base[PRELIGHT] = { 0, 0, 0 }
154             base[SELECTED] = { 0, 0, 0 }
155             base[INSENSITIVE] = { 0, 0, 0 }
156             }
157             HERE
158             return; # nothing
159             };
160              
161              
162             #------------------------------------------------------------------------------
163              
164             # $uri is either a string or a URI object
165             sub browser_open {
166             my ($uri, $parent_widget) = @_;
167             $uri = "$uri"; # stringize URI object
168             ### browser_open(): $uri
169              
170             if (Gtk2->can('show_uri')) { # new in Gtk 2.14
171             my $screen = $parent_widget && $parent_widget->get_screen;
172             if (eval { Gtk2::show_uri ($screen, $uri); 1 }) {
173             return;
174             }
175             # possible Glib::Error "operation not supported" on http urls
176             ### show_uri() error: $@
177             }
178              
179             # The quoting, or lack thereof, expected of the url in openURL is in
180             # mozilla XRemoteService.cpp. It looks for ( ) delims, then the last ","
181             # is the last arg to take off new-window, new-tab, noraise, etc.
182             {
183             my @command = ('mozilla', '-remote', "openURL($uri,new-window)");
184             ### run: @command
185             if (system (@command) == 0) {
186             return;
187             }
188             ### run status: $?
189             ### error: "$!"
190             }
191             {
192             my @command = ('sensible-browser', $uri);
193             if (_spawn (@command)) { return }
194             }
195             {
196             my @command = ('mozilla', $uri);
197             if (_spawn (@command)) { return }
198             }
199             warn "Cannot run browser: none of show_uri, sensible-browser or mozilla work";
200             }
201             sub _spawn {
202             my @command = @_;
203             ### spawn(): @command
204             require Proc::SyncExec;
205             my $pid = Proc::SyncExec::sync_exec (\&_spawn_detach, @command);
206             if (! defined $pid) {
207             ### cannot run: $!
208             return 0;
209             }
210             if (waitpid ($pid, 0) != $pid) {
211             warn "Error waiting spawned $pid: $!\n";
212             }
213             return 1;
214             }
215             sub _spawn_detach {
216             if (my $pid = Proc::SyncExec::fork_retry()) {
217             POSIX::_exit (0); # parent
218             } else {
219             ## no critic (RequireCheckingReturnValueOfEval)
220             eval { POSIX::setsid() };
221             return 1; # ok, child continues
222             }
223             }
224              
225             #-----------------------------------------------------------------------------
226              
227             sub color_object {
228             my ($widget, $colour_str) = @_;
229             if (defined $colour_str) {
230             require App::Chart::Gtk2::Ex::GdkColorAlloc;
231             return ($colour_str,
232             $widget->{'color'}->{$colour_str}
233             ||= App::Chart::Gtk2::Ex::GdkColorAlloc->new (widget => $widget,
234             color => $colour_str));
235             } else {
236             return ('fg', $widget->style->fg($widget->state));
237             }
238             }
239              
240             sub gc_for_colour {
241             my ($widget, $colour_str) = @_;
242             ($colour_str, my $color_obj) = color_object ($widget, $colour_str);
243             return ($widget->{'gc_solid'}->{$colour_str} ||= do {
244             require App::Chart::Gtk2::Ex::GtkGCBits;
245             App::Chart::Gtk2::Ex::GtkGCBits->get_for_widget
246             ($widget, { foreground => $color_obj,
247             line_style => 'solid',
248             line_width => 0 })
249             });
250             }
251             sub gc_for_colour_dashed {
252             my ($widget, $colour_str) = @_;
253             ($colour_str, my $color_obj) = color_object ($widget, $colour_str);
254             return ($widget->{'gc_dash'}->{$colour_str} ||= do {
255             require App::Chart::Gtk2::Ex::GtkGCBits;
256             App::Chart::Gtk2::Ex::GtkGCBits->get_for_widget
257             ($widget, { foreground => $color_obj,
258             line_style => 'on_off_dash',
259             line_width => 0 })
260             });
261             }
262              
263             1;
264             __END__
265              
266             # =for stopwords Pango undef Gtk ListStore TreeStore renderer TreeViewColumn TreeView
267             #
268             # =head1 NAME
269             #
270             # App::Chart::Gtk2::GUI -- miscellaneous graphical interface functions
271             #
272             # =head1 SYNOPSIS
273             #
274             # use App::Chart::Gtk2::GUI;
275             #
276             # =head1 FUNCTIONS
277             #
278             # =over 4
279             #
280             # =cut
281             #
282             # =item App::Chart::Gtk2::GUI::string_width ($widget_or_layout, $str)
283             #
284             # Return the width in pixels of C<$str> in the font of the given widget or
285             # layout (either a C<Gtk2::Widget> or a C<Gtk2::Pango::Layout> object).
286             #
287             # =cut
288             #
289             # =item C<< App::Chart::Gtk2::GUI::draw_text_centred ($widget, $region_or_event, $string) >>
290             #
291             # Draw C<string> centred in the window of C<widget>. If C<widget> isn't
292             # realized yet then do nothing. Pango C<word-char> wrapping is enabled, so
293             # the string is not truncated if it's wider than the window.
294             #
295             # Both windowed and no-window widgets can be given here. C<$region_or_event>
296             # gives a region to clip to, or undef to draw everything.
297             #
298             # =cut
299             #
300             # =item App::Chart::Gtk2::GUI::chart_style_class ($class)
301             #
302             # =item App::Chart::Gtk2::GUI::chart_style_widget ($widgetname)
303             #
304             # Setup package C<$class> or widget C<$widgetname> (per
305             # C<< $widget->set_name >>) to get the Chart graph style settings, which means
306             # black background and white foreground and text.
307             #
308             # C<$class> can include colons like C<"App::Chart::Gtk2::HAxis"> and they're turned
309             # into underscores like C<"App__Chart__Gtk2__HAxis"> which is the Gtk class
310             # name.
311             #
312             # =cut
313             #
314             # =back