File Coverage

blib/lib/Gtk2/Ex/Carp.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             # $Id: Carp.pm,v 1.5 2005/09/23 11:02:41 jodrell Exp $
2             package Gtk2::Ex::Carp;
3              
4             =pod
5              
6             =head1 NAME
7              
8             Gtk2::Ex::Carp - GTK+ friendly C and C functions.
9              
10             =head1 SYNOPSIS
11              
12             use Gtk2::Ex::Carp;
13              
14             # these override the standard Perl functions:
15              
16             warn('i told you not to push that button!');
17              
18             die('an ignominious death');
19              
20              
21             # new functions for showing extended error information:
22              
23             # like warn(), but shows a dialog with extra information
24             # in an expandable text entry:
25             worry($SHORT_MESSAGE, $EXTENDED_INFORMATION);
26              
27             # like worry(), but fatal:
28             wail($SHORT_MESSAGE, $EXTENDED_INFORMATION);
29            
30             =head1 DESCRIPTION
31              
32             This module exports four functions, of which two override the standard
33             C and C functions, and two which allow for extended error
34             reporting. When called, these functions display a user-friendly message
35             dialog window.
36              
37             The C function in this module actually replaces the core C
38             function, so any modules you've loaded may die will use former instead
39             of the latter. C will also print the error message to C and
40             will exit the program (with the appropriate exit code) when the dialog is
41             dismissed.
42              
43             The C function will also print a message to C, but will
44             allow the program to continue running when the dialog is dismissed.
45              
46             =head2 EXTRA FUNCTIONS
47              
48             The C and C functions behave just like C and
49             C, respectively, except that they allow you to provide additional
50             information. A second argument, which can contain additional error information,
51             is used to fill a text box inside an expander.
52              
53             =head2 HANDLING GLIB EXCEPTIONS
54              
55             This module also installs C as a Glib exception handler. Any unhandled
56             exceptions will be presented to the user in a warning dialog.
57              
58             =head2 PROGRAM FLOW
59              
60             Note that all the functions in this module create dialogs and use the C
61             method, so that the standard Glib main loop is blocked until the user responds
62             to the dialog.
63              
64             =head1 LOCALISATION ISSUES
65              
66             The dialogs that are created use the standard GNOME layout, with a bold
67             "title" label above the main message. The text for these labels is taken
68             from two package variables that may be altered to suit your needs:
69              
70             $Gtk2::Ex::Carp::FATAL_ERROR_MESSAGE = 'Fatal Error';
71             $Gtk2::Ex::Carp::WARNING_ERROR_MESSAGE = 'Warning';
72             $Gtk2::Ex::Carp::EXTENDED_EXPANDER_LABEL = 'Details:';
73              
74             However, if the C module is available on the system, and your
75             application uses it, these variables will be automagically translated, as long
76             as these default values are translated in your .mo files.
77              
78             =head1 SEE ALSO
79              
80             L, L, L
81              
82             =head1 AUTHOR
83              
84             Gavin Brown (gavin dot brown at uk dot com)
85              
86             =head1 COPYRIGHT
87              
88             (c) 2005 Gavin Brown. All rights reserved. This program is free software; you
89             can redistribute it and/or modify it under the same terms as Perl itself.
90              
91             =cut
92              
93 1     1   21674 use Carp;
  1         3  
  1         114  
94 1     1   8343 use Gtk2 -init; # init just in case
  0            
  0            
95             use Gtk2::Pango;
96             use Exporter;
97             use File::Spec;
98             use base qw(Exporter);
99             use vars qw($VERSION @EXPORT $FATAL_ERROR_MESSAGE $WARNING_ERROR_MESSAGE $EXTENDED_EXPANDER_LABEL);
100             no warnings;
101             use strict;
102              
103             our $VERSION = '0.01';
104             our @EXPORT = qw(die warn wail worry);
105             our $FATAL_ERROR_MESSAGE = 'Fatal Error';
106             our $WARNING_ERROR_MESSAGE = 'Warning';
107             our $EXTENDED_EXPANDER_LABEL = 'Details:';
108              
109             BEGIN {
110             # nicked from CGI::Carp:
111             *CORE::GLOBAL::die = \&__PACKAGE__::die;
112              
113             eval {
114             require Locale::gettext;
115             };
116             if (defined($Locale::gettext::VERSION)) {
117             $FATAL_ERROR_MESSAGE = Locale::gettext::gettext($FATAL_ERROR_MESSAGE);
118             $WARNING_ERROR_MESSAGE = Locale::gettext::gettext($WARNING_ERROR_MESSAGE);
119             $EXTENDED_EXPANDER_LABEL = Locale::gettext::gettext($EXTENDED_EXPANDER_LABEL);
120             }
121              
122             Glib->install_exception_handler(\&warn);
123             }
124              
125             # nicked from CGI::Carp:
126             sub id {
127             my $level = shift;
128             my ($pack, $file, $line, $sub) = caller($level);
129             my ($dev, $dirs, $id) = File::Spec->splitpath($file);
130             return ($file, $line, $id);
131             }
132              
133             # nicked from CGI::Carp:, checks to see if the error was raised in an eval()
134             # context. In this case we want to croak so that the developer can catch the
135             # exception:
136             sub in_eval { Carp::longmess =~ /eval \{/ }
137              
138             sub exit_ok {
139             exit($! == 0 ? ($? >> 8) : (($? >> 8) == 0 ? 255 : 1));
140             }
141              
142             sub die {
143             my $message = join('', @_);
144             chomp($message);
145              
146             if (in_eval) {
147             Carp::croak($message);
148              
149             } else {
150             my ($file, $line, $id) = id(1);
151             printf(STDERR "%s at %s line %d\n", $message, $file, $line);
152             _mkdialog('error', $FATAL_ERROR_MESSAGE, sprintf("%s\n\nFile %s, line %d", $message, $file, $line), \&exit_ok)->run;
153              
154             }
155              
156             return 1;
157             }
158              
159             sub warn {
160             my $message = join('', @_);
161             chomp($message);
162             my ($file, $line, $id) = id(1);
163             printf(STDERR "%s at %s line %d\n", $message, $file, $line);
164             _mkdialog('warning', $WARNING_ERROR_MESSAGE, sprintf("%s\n\nFile %s, line %d", $message, $file, $line), sub { shift()->destroy })->run;
165              
166             return 1;
167             }
168              
169             sub worry {
170             my ($error, $extended) = @_;
171             _extended_dialog(
172             'warning',
173             $WARNING_ERROR_MESSAGE,
174             $error,
175             $extended,
176             sub { shift()->destroy },
177             );
178             return 1;
179             }
180              
181             sub wail {
182             my ($error, $extended) = @_;
183             _extended_dialog(
184             'error',
185             $FATAL_ERROR_MESSAGE,
186             $error,
187             $extended,
188             \&exit_ok,
189             );
190             return 1;
191             }
192              
193             sub _extended_dialog {
194             my ($type, $title, $error, $extended, $callback) = @_;
195             chomp($extended);
196             my ($file, $line, $id) = id(2);
197             my $dialog = Gtk2::Ex::Carp::ExtendedErrorDialog->new(
198             $type,
199             $title,
200             $error,
201             sprintf("%s\nFile %s line %d", $extended, $file, $line),
202             );
203             $dialog->signal_connect('response', $callback);
204             $dialog->signal_connect('close', $callback);
205             $dialog->signal_connect('delete_event', $callback);
206             $dialog->run;
207             }
208              
209             sub _mkdialog {
210             my ($type, $primary, $secondary, $callback) = @_;
211              
212             my $dialog = Gtk2::MessageDialog->new(
213             undef,
214             'modal',
215             $type,
216             'ok',
217             '',
218             );
219             $dialog->set_markup(sprintf('%s', $primary));
220             $dialog->format_secondary_text($secondary);
221             $dialog->signal_connect('response', $callback);
222             $dialog->signal_connect('close', $callback);
223             $dialog->signal_connect('delete_event', $callback);
224              
225             return $dialog;
226             }
227              
228             package Gtk2::Ex::Carp::ExtendedErrorDialog;
229             use Gtk2;
230             use base qw(Gtk2::Dialog);
231             use strict;
232              
233             sub new {
234             my ($package, $type, $title, $message, $extended) = @_;
235              
236             my $textview = Gtk2::TextView->new;
237             $textview->modify_font(Gtk2::Pango::FontDescription->from_string('monospace'));
238             $textview->get_buffer->set_text($extended);
239             $textview->set_editable(0);
240              
241             my $scrwin = Gtk2::ScrolledWindow->new;
242             $scrwin->set_policy('automatic', 'automatic');
243             $scrwin->set_shadow_type('in');
244             $scrwin->add($textview);
245              
246             my $expander = Gtk2::Expander->new;
247             $expander->set_label($Gtk2::Ex::Carp::EXTENDED_EXPANDER_LABEL);
248             $expander->add($scrwin);
249              
250             my $primary_label = Gtk2::Label->new;
251             $primary_label->set_use_markup(1);
252             $primary_label->set_markup(sprintf('%s', $title));
253             $primary_label->set_justify('left');
254             $primary_label->set_alignment(0, 0);
255              
256             my $secondary_label = Gtk2::Label->new;
257             $secondary_label->set_text($message);
258             $secondary_label->set_selectable(1);
259             $secondary_label->set_line_wrap(1);
260             $secondary_label->set_justify('left');
261             $secondary_label->set_alignment(0, 0);
262              
263             my $vbox = Gtk2::VBox->new;
264             $vbox->set_spacing(12);
265             $vbox->pack_start($primary_label, 0, 0, 0);
266             $vbox->pack_start($secondary_label, 0, 0, 0);
267             $vbox->pack_start($expander, 0, 0, 0);
268              
269             my $image = Gtk2::Image->new_from_stock('gtk-dialog-'.$type, 'dialog');
270             $image->set_alignment(0, 0);
271              
272             my $hbox = Gtk2::HBox->new;
273             $hbox->set_border_width(6);
274             $hbox->set_spacing(12);
275             $hbox->pack_start($image, 0, 0, 0);
276             $hbox->pack_start($vbox, 1, 1, 0);
277             $hbox->show_all;
278              
279             my $dialog = Gtk2::Dialog->new;
280             $dialog->set_size_request(400, -1);
281             $dialog->set_resizable(0);
282             $dialog->set_position('center');
283             $dialog->set_modal(1);
284             $dialog->set_title($title);
285             $dialog->add_button('gtk-ok', 'ok');
286             $dialog->vbox->add($hbox);
287              
288             $expander->signal_connect('activate', sub { $dialog->set_size_request(400, -1) });
289              
290             bless($dialog, $package);
291              
292             return $dialog;
293             }
294              
295             1;