File Coverage

Glade/PerlRun.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             package Glade::PerlRun;
2 1     1   6 require 5.000; use strict 'vars', 'refs', 'subs';
  1         1  
  1         103  
3              
4             # Copyright (c) 1999 Dermot Musgrove
5             #
6             # This library is released under the same conditions as Perl, that
7             # is, either of the following:
8             #
9             # a) the GNU General Public License as published by the Free
10             # Software Foundation; either version 1, or (at your option) any
11             # later version.
12             #
13             # b) the Artistic License.
14             #
15             # If you use this library in a commercial enterprise, you are invited,
16             # but not required, to pay what you feel is a reasonable fee to perl.org
17             # to ensure that useful software is available now and in the future.
18             #
19             # (visit http://www.perl.org/ or email donors@perlmongers.org for details)
20              
21             BEGIN {
22 1     1   6 use Exporter qw( );
  1         1  
  1         18  
23 1     1   1045 use POSIX qw( isdigit );
  1         11647  
  1         9  
24 1     1   4327 use Gtk; # For message_box
  0            
  0            
25             use Cwd qw( cwd chdir );
26             use File::Basename;
27             use Data::Dumper;
28             use Text::Wrap qw( wrap $columns ); # in options, diag_print
29             use vars qw( @ISA
30             $AUTOLOAD
31             %fields %stubs
32             @EXPORT @EXPORT_OK %EXPORT_TAGS
33             $PACKAGE $VERSION $AUTHOR $DATE
34             @VARS @METHODS
35             $Glade_Perl
36             $I18N
37             $all_forms
38             $project
39             $widgets
40             $work
41             $seq
42             $data
43             $forms
44             $pixmaps_directory
45             $indent
46             $tab
47             $convert
48             @use_modules
49             %stat
50             $NOFILE
51             $permitted_fields
52             );
53             # Tell interpreter who we are inheriting from
54             @ISA = qw(
55             Exporter
56             );
57              
58             $PACKAGE = __PACKAGE__;
59             $VERSION = q(0.61);
60             $AUTHOR = q(Dermot Musgrove );
61             $DATE = q(Sun Nov 17 03:21:11 GMT 2002);
62             $widgets = {};
63             $all_forms = {};
64             $convert = {};
65             $indent = '';
66             $pixmaps_directory = "pixmaps";
67             $NOFILE = '__NOFILE';
68             $permitted_fields = '_permitted_fields';
69              
70             # These vars are imported by all Glade-Perl modules for consistency
71             @VARS = qw(
72             $Glade_Perl
73             $I18N
74             $indent
75             $tab
76             @use_modules
77             $NOFILE
78             $permitted_fields
79             );
80             @METHODS = qw(
81             _
82             S_
83             D_
84             typeKey
85             QuoteXMLChars
86             keyFormat
87             start_checking_gettext_strings
88             create_image
89             create_pixmap
90             missing_handler
91             message_box
92             message_box_close
93             show_skeleton_message
94             reload_any_altered_modules
95             );
96             # These symbols (globals and functions) are always exported
97             @EXPORT = qw(
98             );
99             # Optionally exported package symbols (globals and functions)
100             @EXPORT_OK = ( @METHODS, @VARS );
101             # Tags (groups of symbols) to export
102             %EXPORT_TAGS = (
103             'METHODS' => [@METHODS] ,
104             'VARS' => [@VARS]
105             );
106             }
107              
108             %fields = (
109             # These are the data fields that you can set/get using the dynamic
110             # calls provided by AUTOLOAD (and their initial values).
111             # eg $class->FORMS($new_value); sets the value of FORMS
112             # $current_value = $class->FORMS; gets the current value of FORMS
113             'app' => {
114             'name' => undef,
115             'author' => undef,
116             'version' => '0.01',
117             'date' => undef,
118             'copying' => # Copying policy to appear in generated source
119             "# Unspecified copying policy, please contact the author
120             # ",
121             'description' => undef, # Description for About box etc.
122             # 'pixmaps_directory' => undef,
123             'logo' => 'Logo.xpm', # Use specified logo for project
124             },
125             'data' => {
126             'directory' => undef,
127             },
128             'diag' => {
129             'verbose' => undef, # Show errors and main diagnostics
130             'wrap_at' => 0, # Max diagnostic line length (approx)
131             'autoflush' => undef, # Dont change the policy
132             'indent' => ' ', # Diagnostics indent to lay out messages
133             'benchmark' => undef, # Dont add time to the diagnostic messages
134             'log' => undef, # Write diagnostics to STDOUT
135             # 'log' => "\&STDOUT",# Write diagnostics to STDOUT
136             # or Filename to write diagnostics to
137             'LANG' => ($ENV{'LANG'} || ''),
138             # Which language we want the diagnostics
139             },
140             'run_options' => {
141             'name' => __PACKAGE__,
142             'version' => $VERSION, # Version of Glade-Perl used
143             'author' => $AUTHOR,
144             'date' => $DATE,
145             'logo' => 'glade2perl_logo.xpm', # Our logo
146             'start_time' => undef, # Time that this run started
147             'mru' => undef,
148             'prune' => undef,
149             'proto' => {
150             'site' => undef,
151             'user' => undef,
152             'project' => undef,
153             'params' => undef,
154             'app_defaults' => undef,
155             'base_defaults' => undef,
156             },
157             'xml' => {
158             'site' => undef,
159             'user' => undef,
160             'project' => undef,
161             'params' => undef,
162             'app_defaults' => "Application defaults",
163             'base_defaults' => __PACKAGE__." defaults",
164             'set_by' => 'DEFAULT', # Who set the options
165             'encoding' => undef, # Character encoding eg ('ISO-8859-1')
166             },
167             },
168             );
169              
170              
171             %stubs = (
172             );
173              
174             my $option_hashes = " ".
175             join(" ",
176             'app',
177             'diag',
178             'glade',
179             'glade2perl',
180             'glade_helper',
181             'source',
182             'xml',
183             'dist',
184             'helper',
185             'test'
186             )." ";
187              
188             sub DESTROY {
189             # This sub will be called on object destruction
190             } # End of sub DESTROY
191              
192             =pod
193              
194             =head1 NAME
195              
196             Glade::PerlRun - Utility methods for Glade-Perl (and generated applications).
197              
198             =head1 SYNOPSIS
199              
200             use vars qw(@ISA);
201             use Glade::PerlRun qw(:METHODS :VARS);
202             @ISA = qw( Glade::PerlRun );
203              
204             # 1) CLASS methods
205             my $Object = Glade::PerlRun->new(%params);
206             $Object->glade->file($supplied_path);
207             $widget = $window->lookup_widget('clist1');
208              
209             # 2) OPTIONS handling
210             $options = Glade::PerlRun->options(%params);
211             $normalised_value = Glade::PerlRun->normalise('True');
212             $new_hash_ref = Glade::PerlRun->merge_into_hash_from(
213             $to_hash_ref, # Hash to be updated
214             $from_hash_ref, # Input data to be merged
215             'set accessors'); # Any value will add AUTOLOAD() accessors
216             # for these keys.
217             $Object->save_app_options($mru_filename);
218             $Object->save_options;
219              
220             my $string = Glade::PerlRun->string_from_file('/path/to/file');
221             Glade::PerlRun->save_file_from_string('/path/to/file', $string);
222              
223             # 3) Diagnostic message printing
224             $Object->start_log('log_filename');
225             $Object->diag_print(2, "This is a diagnostics message");
226             $Object->diag_print(2, $hashref, "Prefix to message");
227             $Object->stop_log;
228              
229             # 4) I18N
230             Glade::PerlRun->load_translations('MyApp', 'fr', '/usr/local/share/locale/',
231             undef, '__S', 'Merge with already loaded translations');
232             sprintf(_("A message '%s'"), $value);
233             sprintf(gettext('__S', "A message '%s'"), $value);
234             Glade::PerlRun->start_checking_gettext_strings("__S");
235             Glade::PerlRun->stop_checking_gettext_strings("__S");
236             Glade::PerlRun->write_missing_gettext_strings('__S');
237              
238             # 5) UI methods
239             my $image = Glade::PerlRun->create_image('new.xpm', ['dir1', 'dir2']);
240             my $pixmap = Glade::PerlRun->create_pixmap($form, 'new.xpm', ['dir1', 'dir2']);
241              
242             Glade::PerlRun->show_skeleton_message(
243             $me, \@_, __PACKAGE__, "$Glade::PerlRun::pixmaps_directory/Logo.xpm");
244             Glade::PerlRun->message_box(
245             $message, # Message to display
246             $title, # Dialog title string
247             [_('Dismiss'), _("Quit")." Program"], # Buttons to show
248             1, # Default button is 1st
249             $pixmap, # pixmap filename
250             [&dismiss, &quit], # Button click handlers
251             $entry_needed); # Whether to show an entry
252             # widget for user data
253              
254             # 6) General methods
255             $path = $Object->full_Path($Object->glade->file, $dir);
256             $path = Glade::PerlRun->relative_Path($relative_path, $directory);
257              
258             $Object->reload_any_altered_modules;
259              
260             =head1 DESCRIPTION
261              
262             Glade::PerlRun provides some utility methods that Glade-Perl modules and
263             also the generated classes need to run. These methods can be inherited and
264             called in any app that use()s Glade::PerlRun and quotes Glade::PerlRun
265             in its @ISA array.
266              
267             Broadly, the utilities are of seven types.
268              
269             1) Class methods
270             2) Options handling
271             3) Diagnostic message printing
272             4) I18N
273             5) UI methods
274             6) General methods
275              
276             =head1 1) CLASS METHODS
277              
278             The class methods provide an object constructor and data accessors.
279              
280             =over 4
281              
282             =cut
283              
284             sub new {
285              
286             =item new(%params)
287              
288             Construct a Glade::PerlRun object
289              
290             e.g. my $Object = Glade::PerlRun->new(%params);
291              
292             =cut
293              
294             my $that = shift;
295             my %params = @_;
296             my $class = ref($that) || $that;
297             # Call our super-class constructor to get an object and reconsecrate it
298             my $self = bless {}, $class;
299              
300             $self->merge_into_hash_from($self, \%fields, (__PACKAGE__." defaults"));
301             $self->run_options->proto->base_defaults(\%fields);
302             $self->merge_into_hash_from($self, \%params, ("$class app defaults"));
303             $self->run_options->proto->app_defaults(\%params);
304              
305             return $self;
306             }
307              
308             sub AUTOLOAD {
309              
310             =item AUTOLOAD()
311              
312             Accesses all class data
313              
314             e.g. my $glade_filename = $Object->glade->file;
315             or $Object->glade->file('path/to/glade/file');
316              
317             =cut
318             my $self = shift;
319             my $class = ref($self)
320             or die "$self is not an object so we cannot '$AUTOLOAD'\n",
321             "We were called from ".join(", ", caller),"\n\n";
322             my $name = $AUTOLOAD;
323             $name =~ s/.*://; # strip fully-qualified portion
324              
325             if (exists $self->{$permitted_fields}->{$name} ) {
326             # This allows dynamic data methods - see %fields above
327             # eg $class->UI('new_value');
328             # or $current_value = $class->UI;
329             if (@_) {
330             return $self->{$name} = shift;
331             } else {
332             return $self->{$name};
333             }
334              
335             } elsif (exists $stubs{$name} ) {
336             # This shows dynamic signal handler stub message_box - see %stubs above
337             __PACKAGE__->show_skeleton_message(
338             $AUTOLOAD."\n ("._("AUTOLOADED by")." ".__PACKAGE__.")",
339             [$self, @_],
340             __PACKAGE__,
341             'pixmaps/Logo.xpm');
342            
343             } elsif ($name ne 'DESTROY'){
344             die "Can't access method `$name' in class $class\n",
345             "We were called from ",join(", ", caller),"\n\n";
346              
347             }
348             }
349              
350             sub lookup_widget {
351              
352             =item lookup_widget($widgetname)
353              
354             Accesses a window or a form's widget by name
355              
356             e.g. my $widget = $window->lookup_widget('clist1');
357              
358             OR my $form = $window->FORM; # or use $form in signal handlers
359             my $widget = $form->lookup_widget('clist1');
360              
361             =cut
362              
363             my $self = shift;
364             my $name = shift;
365             my $hash = {};
366            
367             my $class = ref($self)
368             or die "$self is not an object so we cannot lookup_widget '$name'\n",
369             "We were called from ".join(", ", caller),"\n\n";
370            
371             if (exists $self->{$permitted_fields}->{FORM}) {
372             $hash = $self->FORM;
373              
374             } elsif (exists $self->{TOPLEVEL}) {
375             $hash = $self;
376            
377             } else {
378             print "$self is not a window or form object so we cannot lookup_widget '$name'\n",
379             "We were called from ".join(", ", caller),"\n\n";
380             }
381              
382             if (exists $hash->{$name} ) {
383             return $hash->{$name};
384              
385             } else {
386             print "There is no widget `$name' in class $class\n",
387             "We were called from ",join(", ", caller),"\n\n";
388             return undef;
389             }
390             }
391              
392             #===============================================================================
393             #=========== Options utilities ============
394             #===============================================================================
395              
396             =back
397              
398             =head1 2) OPTIONS HANDLING METHODS
399              
400             These methods will load, merge, reduce and save a hierarchical options
401             structure that is stored in one or more XML files and accessed with
402             AUTOLOAD methods.
403              
404             =over
405              
406             =cut
407              
408             sub options {
409             my ($class, %params) = @_;
410             my $me = $class."->options";
411              
412             =item options(%params)
413              
414             Loads and merges all app options.
415              
416             e.g. Glade::PerlRun->options(%params);
417             my options = $Object->options(%params);
418              
419             =cut
420             my ($self, $global, $type, $key, $defaults, $I18N_name, $log, $report);
421             $global = delete $params{'options_global'} || "\$Glade_Perl";
422             $defaults = delete $params{'options_defaults'} || \%Glade::PerlProject::app_fields;
423             $type = delete $params{'options_key'} || $defaults->{type} || 'glade2perl';
424             $I18N_name = delete $params{'options_I18N_name'} || $type || 'Glade-Perl';
425             $report = delete $params{'options_report'};
426              
427             unless (ref $class eq $class) {
428             # This is first time through so construct object and load options
429             @use_modules = ();
430            
431             $self = bless __PACKAGE__->new(%$defaults), $class;
432              
433             eval "$global = \$self";
434              
435             # Now set element $type to point to our options hash
436             $self->{$type} = $self->{run_options};
437             push @{$self->{$permitted_fields}{$type}}, $me;
438              
439             $self->load_all_options(%params);
440              
441             print "PerlRun defaults ", Dumper(\%fields) if $report;
442             print "App defaults supplied ", Dumper($defaults) if $report;
443             print "App params passed ", Dumper(\%params) if $report;
444             eval "print \"\$report Initial state with app defaults and options loaded \", ".
445             "Dumper($report),\"\n\n\"" if $report;
446              
447             # Merge in all options available
448             foreach $key ('site', 'user', 'project', 'params') {
449             eval "print \"".$self->{$type}->xml->{$key}.
450             " options supplied \", "."Dumper($report),\"\n\n\"" if $report;
451             $self->merge_into_hash_from($self,
452             $self->{$type}->proto->{$key},
453             $self->{$type}->xml->{$key});
454             eval "print \"\$report After \$key options from '".
455             $self->{$type}->xml->{$key}."' merged \", ".
456             "Dumper($report),\"\n\n\"" if $report;
457             }
458              
459             $self->{$type}->start_time($class->get_time);
460             $self->{$type}->name($class);
461             $self->{$type}->version($VERSION);
462             $self->{$type}->author($AUTHOR);
463             $self->{$type}->date($DATE);
464              
465              
466             # Load the diagnostics gettext translations
467             $self->load_translations($I18N_name, $self->diag->LANG, undef,
468             undef, '__D', undef);
469             # $self->load_translations($I18N_name, $self->diag->LANG, undef,
470             # '/home/dermot/Devel/$I18N_name/ppo/en.mo', '__D', undef);
471             # $self->check_gettext_strings("__D");
472              
473             if ($type eq 'glade2perl') {
474             # Find out what versions of software we have
475             unless ($self->{$type}->my_gtk_perl &&
476             ($self->{$type}->my_gtk_perl > $Gtk::VERSION)) {
477             $self->{$type}->my_gtk_perl($Gtk::VERSION);
478             }
479             if ( $self->{$type}->dont_show_UI && !$self->source->write) {
480             die "$me - Much as I like an easy life, please alter options ".
481             "to, at least, show_UI or write_source\n Run abandoned";
482             }
483             $indent = $self->source->indent;
484             $tab = (' ' x $self->source->tabwidth);
485             $self->source->tab($tab);
486             }
487              
488             if ($self->diag->wrap_at == 0) {
489             $columns = 1500;
490             } else {
491             $columns = $self->diag->wrap_at;
492             }
493              
494             } else {
495             $self = $class;
496             $self->{$type}->xml->params(
497             $params{$type}{'xml'}{'set_by'} ||
498             $params{'options_set'} ||
499             $self->{$type}->xml->set_by ||
500             $me);
501             $self->{$type}->proto->params($self->convert_old_options(\%params));
502             $self->merge_into_hash_from($self,
503             $self->{$type}->proto->params,
504             $self->{$type}->xml->params);
505             }
506              
507             $self->diag_print (4, $self->{$type}->proto->params);
508             $self->diag_print (5, $self->{$type}->xml);
509             $self->diag_print (6, $self->{$type});
510             $self->diag_print (7, $self);
511            
512             $self->{$type}->xml->set_by (
513             $self->{$type}->proto->params->{$type}{xml}{set_by} || $me);
514              
515             return $self;
516             }
517              
518             sub load_all_options {
519             my ($class, %params) = @_;
520             my $me = (ref $class || $class)."->load_all_options";
521              
522             my $type = $class->{type} || $params{type};
523             $class->{$type}->xml->encoding(
524             $params{$type}{xml}{encoding} ||
525             $params{$type."_encoding"} ||
526             $params{glade}{encoding} ||
527             $params{glade_encoding} ||
528             'ISO-8859-1'
529             );
530              
531             # PARAMS supplied
532             $class->{$type}->xml->params(
533             $params{$type}{'xml'}{'set_by'} ||
534             $params{'foptions_set'} ||
535             $class->{$type}->xml->set_by ||
536             $me);
537             $class->{$type}->proto->params(
538             $class->convert_old_options(\%params));
539              
540             # USER options file
541             $class->{$type}->xml->user(
542             $class->{$type}->proto->params->{$type}{xml}{user} ||
543             "$ENV{'HOME'}/.$type.xml");
544              
545             $class->{$type}->get_options('user');
546             $class->{$type}->proto->user(
547             $class->convert_old_options($class->{$type}->proto->user,
548             $class->{$type}->xml->user));
549              
550             # PROJECT options file (from user mru if not specified in params)
551             my $base = $class->{$type}->proto->user->{$type}{mru} || '';
552             $base =~ s/(.+)\..*$/$1/;
553             $base =~ s/(.+)\..*$/$1/;
554             $base .= ".$type.xml";
555             $class->{$type}->xml->project(
556             $class->{$type}->xml->project ||
557             $class->{$type}->proto->params->{$type}{xml}{project} ||
558             $base
559             );
560             unless ($class->{$type}->xml->project eq $NOFILE) {
561             $class->{$type}->xml->project(
562             $class->full_Path($class->{$type}->xml->project, `pwd`));
563             }
564              
565             $class->{$type}->get_options('project');
566             $class->{$type}->proto->project(
567             $class->convert_old_options($class->{$type}->proto->project, $me));
568            
569             # SITE options file
570             $class->{$type}->xml->site(
571             $class->{$type}->proto->params->{$type}{xml}{site} ||
572             $class->{$type}->proto->project->{$type}{xml}{site} ||
573             $class->{$type}->proto->user->{$type}{xml}{site} ||
574             "/etc/$type.xml");
575              
576             $class->{$type}->get_options('site');
577             $class->{$type}->proto->site(
578             $class->convert_old_options($class->{$type}->proto->site,
579             $class->{$type}->xml->site));
580              
581             $class->diag_print (5, $class) if ref $class;
582             return $class->{$type};
583             }
584              
585             sub get_options {
586             my ($class, $type, $file) = @_;
587              
588             my $pwd = `pwd`;
589             my ($encoding);
590             $file ||= $class->xml->{$type} || $NOFILE;
591              
592             if ($file eq $NOFILE) {
593             $class->xml->{$type} = $file;
594             $class->proto->{$type} = {};
595             return;
596             }
597             if ($file && -r $file) {
598             ($encoding, $class->proto->{$type}) = $class->simple_Proto_from_File(
599             # ($encoding, $class->proto->{$type}) = Glade::PerlXML->Proto_from_File(
600             $class->xml->{$type},
601             '', $option_hashes,
602             $class->xml->encoding);
603             $class->xml->encoding($encoding);
604              
605             } else {
606             # print "File '$file' could NOT be read\n";
607             $class->proto->{$type} = {};
608             }
609             }
610              
611             sub simple_Proto_from_File {
612             my ($class, $filename, $repeated, $special, $encoding) = @_;
613             my $me = __PACKAGE__."->new_Proto_from_File";
614              
615             my $pos = -1;
616             my $xml = $class->string_from_File($filename);
617             return $class->simple_Proto_from_XML(\$xml, 0, \$pos, $repeated, $special, $encoding);
618             }
619              
620             sub simple_Proto_from_XML {
621             my ($class, $xml, $depth, $pos, $repeated, $special, $encoding) = @_;
622             my $me = __PACKAGE__."->simple_Proto_from_XML";
623              
624             # Loads hash from XML string using regexps (not XML::Parser).
625             my ($self, $tag, $use_tag, $prev_contents, $work);
626             my ($found_encoding, $new_pos);
627             while (($new_pos = index($$xml, "<", $$pos)) > -1) {
628             $prev_contents = substr($$xml, $$pos, $new_pos-$$pos);
629             $$pos = $new_pos;
630             $new_pos = index($$xml, ">", $$pos);
631             $tag = substr($$xml, $$pos+1, $new_pos-$$pos-1);
632             $$pos = $new_pos+1;
633             if ($tag =~ /^\?/) {
634             if ($tag =~ s/\?xml.*\s*encoding\=["'](.*?)['"]\?\n*//) {
635             $found_encoding = $1;
636             } else {
637             $found_encoding = $encoding;
638             }
639             next;
640             }
641             if ($tag =~ s|^/||) {
642             # We are an endtag so return the $prev_contents
643             if (ref $self) {
644             return $self;
645              
646             } else {
647             return &UnQuoteXMLChars($prev_contents);
648             }
649              
650             } else {
651             # We are a starttag so recurse
652             if ($tag =~ s|/$||) {
653             # We are also an endtag (empty eg. so ignore
654             #print "Found empty tag <$tag />\n";
655             } else {
656             $work = $class->simple_Proto_from_XML(
657             $xml, $depth + 1, $pos, $repeated);
658             if (" $repeated " =~ / $tag /) {
659             # Store as a numbered key
660             $use_tag = "~$tag-".sprintf(&keyFormat, $seq++);
661             $self->{$use_tag}{&typeKey} = $tag ;
662             } else {
663             # Store as key
664             $use_tag = $tag;
665             }
666             $self->{$use_tag} = $work;
667             }
668             }
669             }
670              
671             return ($found_encoding, values %$self);
672             }
673              
674             sub typeKey { return ' type'; }
675             #sub keyFormat { if (shift) {return '%04u-%s' } else {return '%04u' } }
676             sub keyFormat { return '%04u' }
677              
678             sub QuoteXMLChars {
679             my $text = shift;
680             # Suggested by Eric Bohlman on perl-xml mailling list
681             my %ents=('&'=>'amp','<'=>'lt','>'=>'gt',"'"=>'apos','"'=>'quot');
682             $text =~ s/([&<>'"])/&$ents{$1};/g;
683             # Uncomment the line below if you don't want to use European characters in
684             # your project options
685             # $text =~ s/([\x80-\xFF])/&XmlUtf8Encode(ord($1))/ge;
686             return $text;
687             }
688              
689             sub UnQuoteXMLChars {
690             my $text = shift;
691             my %ents=('<'=>'<','>'=>'>','''=>"'",'"'=>'"', '&'=>'&');
692             $text =~ s/(<|>|'|"|&)/$ents{$1}/g;
693             return $text;
694             }
695              
696             sub XmlUtf8Encode {
697             # This was ripped from XML::DOM - thanks to
698             # Enno Derksen (official maintainer), enno@att.com
699             # and Clark Cooper, coopercl@sch.ge.com
700             my $n = shift;
701             my $me = "XmlUtf8Encode";
702             if ($n < 0x80) {
703             return chr ($n);
704              
705             } elsif ($n < 0x800) {
706             return pack ("CC", (($n >> 6) | 0xc0),
707             (($n & 0x3f) | 0x80));
708              
709             } elsif ($n < 0x10000) {
710             return pack ("CCC", (($n >> 12) | 0xe0),
711             ((($n >> 6) & 0x3f) | 0x80),
712             (($n & 0x3f) | 0x80));
713              
714             } elsif ($n < 0x110000) {
715             return pack ("CCCC", (($n >> 18) | 0xf0),
716             ((($n >> 12) & 0x3f) | 0x80),
717             ((($n >> 6) & 0x3f) | 0x80),
718             (($n & 0x3f) | 0x80));
719             }
720             __PACKAGE__->diag_print(1,
721             "error Number is too large for Unicode [%s] in %s ", $n, $me);
722             return "#";
723             }
724              
725             sub convert_old_options {
726             my ($class, $old, $file) = @_;
727             my $me = __PACKAGE__."->convert_old_options";
728             my $new = {};
729              
730             my $key;
731             my $converted = 0;
732             for $key (keys %$old) {
733             # Normalise any True/False values to 1/0
734             $old->{$key} = $class->normalise($old->{$key});
735             if ($convert->{$key}) {
736             eval $convert->{$key};
737             die @! if @!;
738             $converted++;
739             } elsif (ref $old->{$key}) {
740             $new->{$key} = $class->merge_into_hash_from(
741             $new->{$key}, $old->{$key}, $file);
742             } else {
743             $new->{$key} = $old->{$key};
744             }
745             }
746              
747             if ($file and $converted and $class->diagnostics(2)) {
748             if (-w $file) {
749             # We can rewrite the options file
750             print sprintf("$me has converted options in file %s\n",
751             $file);
752             $class->write_options($new, $file);
753             } else {
754             print "$me cannot rewrite file '$file'\n".
755             sprintf(
756             "You may want to edit '$file' yourself to read: \n%s\n",
757             $class->XML_from_Proto('', ' ', 'G2P-Options', $new));
758             }
759             }
760             return $new;
761             }
762              
763             sub normalise {
764             my ($class, $value) = @_;
765              
766             =item normalise($value)
767              
768             Return a normalised value ie. convert 'True'|'Yes'|'y'|'On' to 1
769             and 'False'|'No'|'n'|'Off' to 0.
770             The comparisons are case-insensitive.
771              
772             e.g. my $normalised_value = Glade::PerlRun->normalise('True');
773              
774             =cut
775             if (defined $value) {
776             if ($value =~ /^(true|y|yes|on)$/i) {
777             return 1;
778             } elsif ($value =~ /^(false|n|no|off)$/i) {
779             return 0;
780             } else {
781             return $value;
782             }
783             }
784             }
785              
786             sub merge_into_hash_from {
787             my ($class, $to_hash, $from_hash, $autoload) = @_;
788             my $me = $class."->merge_into_hash_from";
789              
790             =item merge_into_hash_from($to_hash, $from_hash, $autoload)
791              
792             Recursively merge a hash into an existing one - overwriting any keys with
793             a defined value. It will also optionally set accessors for the keys to be
794             used via AUTOLOAD().
795              
796             e.g. $new_hash_ref = Glade::PerlRun->merge_into_hash_from(
797             $to_hash_ref, # Hash to be updated
798             $from_hash_ref, # Input data to be merged
799             'set accessors'); # Any value will add AUTOLOAD() accessors
800             # for these keys.
801              
802             =cut
803             my ($key, $value);
804             $autoload ||= '';
805             foreach $key (keys %$from_hash) {
806             next if $key eq $permitted_fields;
807             if (ref $from_hash->{$key} eq 'HASH') {
808             $to_hash->{$key} ||= bless {}, ref $to_hash;
809             $class->merge_into_hash_from(
810             $to_hash->{$key},
811             $from_hash->{$key},
812             $autoload);
813              
814             } else {
815             # Check that we are not overwriting a hash with a scalar
816             unless (ref $to_hash->{$key}) {
817             $to_hash->{$key} = $class->normalise($from_hash->{$key});
818             }
819             }
820             $to_hash->{$permitted_fields}{$key}++ if $autoload;
821             }
822             return $to_hash;
823             }
824              
825             sub save_app_options {
826             my ($class, $mru, %defaults) = @_;
827             my $me = $class."->save_app_options";
828              
829             =item save_app_options($mru, %defaults)
830              
831             Updates mru and saves all app/user options. This will save the mru file
832             in the user options file (if one is named in
833             $class->run_options->xml->user).
834              
835             e.g. Glade::PerlRun->save_app_options($mru_filename);
836              
837             =cut
838             my $type = 'glade2perl';
839             %defaults = %{$Glade_Perl->{$type}->proto->app_defaults}
840             unless keys %defaults;
841              
842             # Store new mru file name and start_time
843             $Glade_Perl->{$type}->proto->user->{$type}->{mru} = $mru;
844             $Glade_Perl->{$type}->proto->user->{$type}->{start_time} =
845             ($Glade_Perl->{$type}->start_time);
846             undef $Glade_Perl->{$type}->{mru};
847              
848             # Save project options
849             $Glade_Perl->diag_print(6, $class, "Options to be saved");
850             $Glade_Perl->save_options(
851             undef,
852             %Glade::PerlRun::fields,
853             %defaults
854             );
855              
856             if ($Glade_Perl->{$type}->xml->user) {
857             # Save new user options
858             $Glade_Perl->write_options(
859             $Glade_Perl->reduce_hash(
860             $Glade_Perl->{$type}->proto->user,
861             {},
862             {},
863             {},
864             {},
865             $Glade_Perl->{$type}->prune
866             ),
867             $Glade_Perl->{$type}->xml->user);
868             }
869             }
870              
871             sub save_options {
872             my ($class, $filename, %app_defaults) = @_;
873             my $me = __PACKAGE__."->save_options";
874              
875             =item save_options($filename, %app_defaults)
876              
877             Reduce and save the supplied options to the file specified.
878              
879             e.g. $Object->save_options;
880              
881             =cut
882             my $type = $class->type;
883             %app_defaults = %{$class->run_options->proto->app_defaults}
884             unless keys %app_defaults;
885            
886             if ($filename) {
887             $class->{$type}->xml->{project} = ($filename);
888             } else {
889             $filename = $class->{$type}->xml->{project};
890             }
891              
892             if ($filename eq $NOFILE) {
893             $class->diag_print(2, "%s- Not saving %s project options",
894             $indent, $type);
895             return;
896             }
897             $class->diag_print(4, $class, "Project options");
898              
899             my $options = $class->reduce_hash(
900             $class,
901             $class->{$type}->proto->user,
902             $class->{$type}->proto->site,
903             \%app_defaults,
904             \%__PACKAGE__::fields,
905             $class->{$type}->prune,
906             );
907              
908             if (ref $options) {
909             bless $options, ref $class;
910             $options->{'type'} = $type;
911             $options->{$type}{start_time} = ($class->{$type}->start_time);
912             $class->write_options($options, $filename);
913             } else {
914             $class->diag_print(2, "%s- No project options need saving",
915             $indent);
916             }
917             }
918              
919             sub write_options {
920             my ($class, $options, $filename) = @_;
921             my $me = __PACKAGE__."->write_options";
922              
923             =item write_options($options, $filename)
924              
925             Write an options hash to XML file.
926              
927             e.g. my options = $Object->write_options($hash_ref, '/path/to/file');
928              
929             =cut
930             my $type = $class->type;
931             my $xml;
932              
933             if ($class->{$type}->xml->encoding) {
934             $xml = "
935             $class->{$type}->xml->encoding."\"?>\n";
936             } else {
937             $xml = "\n";
938             }
939             $xml .= $class->XML_from_Proto('', ' ', "$type-Options", $options);
940            
941             if ($filename eq $NOFILE) {
942             $class->diag_print(2, "%s- Not saving %s options", $indent, $type);
943             $class->diag_print(2, "%s", "$indent- XML would have been\n'$xml'\n");
944             return;
945             }
946             $class->diag_print(5, $xml, 'DONT_TRANSLATE');
947              
948             $class->save_file_from_string($filename, $xml);
949              
950             $class->diag_print(2, "%s- %s options saved to %s",
951             $class->diag->indent, $type, $filename);
952             }
953              
954             sub reduce_hash {
955             my ($class,
956             $all_options, $user_options, $site_options,
957             $app_defaults, $base_defaults,
958             $prune, $hashtypes) = @_;
959             my $me = __PACKAGE__."->reduce_hash";
960              
961             =item reduce_hash($all_options, $user_options, $site_options,
962             $app_defaults, $base_defaults, $prune, $hashtypes)
963              
964             Removes any options that are equivalent to site/user/project options
965             or that are specified to be pruned. We will descend into any hash types
966             specified.
967              
968             e.g. my options = $Object->reduce_hash(
969             $options_to_reduce,
970             $user_options,
971             $site_options,
972             $app_defaults,
973             $base_defaults
974             '*work*proto*',
975             '*My::Class*');
976              
977             =cut
978             my ($key, $default, $from, $return, $reftype);
979             my $verbose = 5;
980             $user_options ||= {};
981             $site_options ||= {};
982             $app_defaults ||= {};
983             $base_defaults ||= {};
984             $prune ||= "*".
985             join("*",
986             $permitted_fields,
987             &typeKey,
988             'run_options',
989             'PARTYPE',
990             'module',
991             'tab',
992             'proto',
993             'gtk_style',
994             'generate',
995             ).
996             "*";
997             $hashtypes ||= "*".join("*",
998             (ref $class || $class),
999             'Glade::PerlGenerate',
1000             'Glade::PerlProject',
1001             'Glade::PerlRun',
1002             )."*";
1003              
1004             $class->diag_print($verbose, "Prune is '$prune'");
1005             $class->diag_print($verbose, "Hashtypes is '$hashtypes'");
1006             foreach $key (keys %{$all_options}) {
1007             $reftype = ref $all_options->{$key};
1008             $class->diag_print($verbose+1, "%s- Reducing %s object '%s'",
1009             $class->diag->indent, $reftype, $key) if $reftype;
1010             if ($reftype and "*ARRAY*" =~ /\*$reftype\*/) {
1011             $class->diag_print($verbose, "--------------------------------");
1012             $all_options->{$key} = join("\n", @{$all_options->{$key}});
1013             $class->diag_print($verbose,
1014             "%s- Joining '%s' object {'%s'} into newline-separated string '%s'",
1015             $class->diag->indent, $reftype, $key, $all_options->{$key});
1016             }
1017             if (!defined $all_options->{$key}) {
1018             $class->diag_print ($verbose,
1019             "%s- Removing option '%s' (%s)",
1020             $class->diag->indent, $key, 'no value defined');
1021              
1022             } elsif ($prune =~ /\*$key\*/) {
1023             # Ignore the specified keys
1024             $class->diag_print ($verbose,
1025             "%s- Removing option '%s' (%s)",
1026             $class->diag->indent, $key, 'pruned');
1027              
1028             } elsif ($reftype and "*HASH*$hashtypes*" =~ /\*$reftype\*/) {
1029             $class->diag_print($verbose, "--------------------------------");
1030             $class->diag_print($verbose, "%s- Descending into '%s' object {'%s'}",
1031             $class->diag->indent, $reftype, $key);
1032             $class->diag_print($verbose+1, $all_options->{$key},
1033             $class->diag->indent."- {'$key'} which is a ");
1034             $class->diag_print($verbose+1, $all_options->{$key},
1035             "Project option element {'$key'}");
1036             $class->diag_print($verbose+1, $user_options->{$key},
1037             "User options element {'$key'}") if $user_options->{$key};
1038             $class->diag_print($verbose+1, $site_options->{$key},
1039             "Site options element {'$key'}") if $site_options->{$key};
1040             $class->diag_print($verbose+1, $app_defaults->{$key},
1041             "App defaults element {'$key'}") if $app_defaults->{$key};
1042             $class->diag_print($verbose+1, $base_defaults->{$key},
1043             __PACKAGE__." defaults element {'$key'}") if $base_defaults->{$key};
1044             $return->{$key} = $class->reduce_hash(
1045             $all_options->{$key},
1046             $user_options->{$key},
1047             $site_options->{$key},
1048             $app_defaults->{$key},
1049             $base_defaults->{$key},
1050             $prune, $hashtypes);
1051             unless (keys %{$return->{$key}}) {
1052             delete $return->{$key};
1053             $class->diag_print($verbose, "%s- Losing empty hash {'%s'}",
1054             $class->diag->indent, $key);
1055             } else {
1056             $class->diag_print($verbose, $return->{$key},
1057             "$me reduced {'$key'} so that");
1058             }
1059              
1060             } else {
1061             if (defined $user_options->{$key}) {
1062             $default = $user_options->{$key};
1063             $from = "user options file";
1064              
1065             } elsif (defined $site_options->{$key}) {
1066             $default = $site_options->{$key};
1067             $from = "site options file";
1068              
1069             } elsif (defined $app_defaults->{$key}) {
1070             $default = $app_defaults->{$key};
1071             $from = (ref $all_options)." app defaults";
1072              
1073             } elsif (defined $base_defaults->{$key}) {
1074             $default = $base_defaults->{$key};
1075             $from = __PACKAGE__." defaults";
1076              
1077             } else {
1078             $default = '__NO_DEFAULT_OPTION_AVAILABLE__';
1079             $from = "no default";
1080             }
1081             if ($all_options->{$key} eq $class->normalise($default)) {
1082             $class->diag_print ($verbose,
1083             "%s- Removing {'%s'} => '$all_options->{$key}' (equals default in %s)",
1084             $class->diag->indent, $key, $from);
1085             } elsif (!$all_options->{$key} and $default eq '__NO_DEFAULT_OPTION_AVAILABLE__') {
1086             $class->diag_print ($verbose,
1087             "%s- Removing option '%s' (no default and no value)",
1088             $class->diag->indent, $key, $from);
1089             } else {
1090             $return->{$key} = $all_options->{$key};
1091             }
1092             }
1093             }
1094             return $return;
1095             }
1096              
1097             sub XML_from_Proto {
1098             # usage my $xmlstring =
1099             # XML::UTIL->XML_from_Proto($prefix, ' ', $tag, $protohashref);
1100             # This proc will compose XML from a proto hash in
1101             # Proto_from_XML's return format
1102             my ($class, $prefix, $tab, $tag, $proto) = @_;
1103             my $me = "$class->XML_from_Proto";
1104             my ($key, $val, $xml, $limit);
1105             my $typekey = &typeKey;
1106             my $prune = "*$typekey*$permitted_fields*";
1107             my $contents = '';
1108             my $newprefix = "$tab$prefix";
1109              
1110             # make up the start tag
1111             foreach $key (sort keys %$proto) {
1112             unless ($prune =~ /\*$key\*/) {
1113             if (ref $proto->{$key} eq 'ARRAY') {
1114             print "error- Key '$key' is an ARRAY !!! and has been ignored\n";
1115             next;
1116             } elsif (ref $proto->{$key}) {
1117             # call ourself to expand nested xml
1118             $contents .= "\n".
1119             $class->XML_from_Proto(
1120             $newprefix, $tab,
1121             ($proto->{$key}{$typekey} || $key),
1122             $proto->{$key}, $prune).
1123             "\n";
1124             } else {
1125             # this is a vanilla string so trim and add to output
1126             if (defined $proto->{$key}) {
1127             $contents .= "\n$newprefix<$key>".
1128             &QuoteXMLChars($proto->{$key})."";
1129             } else {
1130             $contents .= "\n$newprefix<$key>";
1131             # $contents .= "\n$newprefix<$key />";
1132             }
1133             }
1134             }
1135             }
1136              
1137             # make up the string to return
1138             if ($contents eq '') {
1139             if ($tag ne '') {
1140             $xml .= "\n$prefix<$tag />";
1141             }
1142             } else {
1143             if ($tag ne '') {
1144             $xml .= "$prefix<$tag>$contents\n$prefix";
1145             } else {
1146             $xml .= "\n$contents\n";
1147             }
1148             }
1149             return $xml
1150             }
1151            
1152             sub fix_name {
1153              
1154             =item fix_name($name)
1155              
1156             Substitutes illegal characters in a perl name and returns it
1157              
1158             e.g. my $name = Glade::PerlRun->fix_name($name);
1159             OR my $name = Glade::PerlRun->fix_name($name, 'TRANSLATE');
1160              
1161             =cut
1162              
1163             my ($class, $name, $translate) = @_;
1164             my $illegals = '- ./+*!';
1165             my $replaced = 0;
1166             my $new_name = $name;
1167             if ($name =~ /[$illegals]/) {
1168             if ($translate) {
1169             my %ents=('-'=>'MINUS', ' '=>'SPACE', '.'=>'DOT',
1170             '/'=>'SLASH', '+'=>'PLUS', '*'=>'STAR', '!'=>'BANG');
1171             $replaced = $new_name =~ s/([$illegals])/_$ents{$1}_/g;
1172              
1173             } else {
1174             $replaced = $new_name =~ s/([$illegals])//g;
1175              
1176             }
1177             $Glade_Perl->diag_print (1, "warn new name '%s' generated as ".
1178             "original name '%s' contained %s chars [%s] ".
1179             "which are illegal in a Perl name.",
1180             $new_name, $name, $replaced, $illegals);
1181             }
1182             return $new_name;
1183             }
1184              
1185             sub save_file_from_string {
1186             my ($class, $filename, $string) = @_;
1187             my $me = __PACKAGE__."->save_file_from_string";
1188              
1189             =item save_file_from_string($filename, $string)
1190              
1191             Write a string to a file.
1192              
1193             e.g. Glade::PerlRun->save_file_from_string('/path/to/file', $string);
1194              
1195             =cut
1196             $class->diag_print(5, $string, 'DONT_TRANSLATE');
1197              
1198             open OUTPUT, ">".($filename) or
1199             die sprintf("error %s - can't open file '%s' for output",
1200             $me, $filename);
1201             print OUTPUT $string || '';
1202             close OUTPUT or
1203             die sprintf("error %s - can't close file '%s'",
1204             $me, $filename);
1205             $class->diag_print(3, "%s- %s string saved to %s",
1206             $class->diag->indent, $me, $filename);
1207             }
1208              
1209             #===============================================================================
1210             #=========== Diagnostics utilities ============
1211             #===============================================================================
1212              
1213              
1214             =back
1215              
1216             =head1 3) DIAGNOSTIC MESSAGE METHODS
1217              
1218             These methods will start logging diagnostic messages, produce standardised
1219             I18N messages and then stop logging and close any open files.
1220              
1221             =over
1222              
1223             =cut
1224              
1225             sub verbosity { shift->diag->verbose }
1226             sub Writing_to_File { shift->source->write }
1227             sub Building_UI_only {!defined shift->source->write }
1228              
1229             sub diagnostics {
1230             ($_[1] || 1) <= (shift->diag->verbose);
1231             }
1232              
1233             sub diag_print {
1234             my $class = shift;
1235             my $level = shift;
1236             my $message = shift;
1237              
1238             =item diag_print()
1239              
1240             Prints diagnostics message (I18N translated) if verbosity is >= level specified
1241              
1242             e.g. $Object->diag_print(2, "This is a diagnostics message");
1243             $Object->diag_print(2, $hashref, "Prefix to message");
1244              
1245             =cut
1246             return unless $class->diagnostics($level);
1247             my $time='';
1248             if ($class->diag->benchmark) {
1249             my @times = times;
1250             $time = int( $times[0] + $times[1] );
1251             }
1252             unless (ref $message) {
1253             # Make up message from all remaining args
1254             $message = sprintf(D_($message, 2), @_) unless
1255             $_[0] && $_[0] eq 'DONT_TRANSLATE';
1256             print STDOUT wrap($time,
1257             $class->diag->indent.$class->diag->indent, "$message\n");
1258              
1259             } else {
1260             my $prefix = shift || '';
1261             print $class->diag->indent."- $prefix ", Dumper($message);
1262             # $class->diag_ref_print($level, $message, @_);
1263             }
1264             }
1265              
1266             sub diag_ref_print {
1267             my ($class, $level, $message, $desc, $pad) = @_;
1268              
1269             return unless $class->diagnostics($level);
1270             my ($key, $val, $ref);
1271             my $padkey = $pad || 17;
1272             my $title = D_($desc || "");
1273             my @times = times;
1274             my $time='';
1275             $ref = ref $message;
1276             if ($class->diag->benchmark) {
1277             $time = int( $times[0] + $times[1] );
1278             }
1279             unless ($ref) {
1280             print STDOUT wrap($time,
1281             $time.$class->diag->indent.$class->diag->indent, "$message\n");
1282              
1283             } elsif (($ref eq 'HASH') or ( $ref =~ /Glade::/)) {
1284             print STDOUT "$title $ref ",D_("contains"), ":\n";
1285             foreach $key (sort keys %$message) {
1286             my $ref = ref $message->{$key};
1287             if (ref $message->{$key}) {
1288             print STDOUT " {'$key'}".
1289             (' ' x ($padkey-length($key))).
1290             " => ", D_("is a reference to a"), " $ref\n";
1291             } elsif (defined $message->{$key}) {
1292             print STDOUT " {'$key'}".
1293             (' ' x ($padkey-length($key))).
1294             " => '$message->{$key}'\n";
1295             } else {
1296             print STDOUT " {'$key'}\n";
1297             }
1298             $val = (ref ) || $message->{$key} || 'undef';
1299             }
1300              
1301             } elsif ($ref eq 'ARRAY') {
1302             print STDOUT "$title $ref ", D_("contains"), ":\n";
1303             my $im_count = 0;
1304             foreach $val (@$message) {
1305             $key = sprintf "[%d]", $im_count;
1306             $ref = ref $val;
1307             if ($ref) {
1308             print STDOUT " $key".(' ' x ($padkey-length($key))).
1309             " = ", D_("is a reference to a"), " $ref\n";
1310             } elsif (defined $message->[$im_count]) {
1311             print STDOUT " $key".(' ' x ($padkey-length($key))).
1312             " = '$message->[$im_count]'\n";
1313             } else {
1314             print STDOUT " $key\n";
1315             }
1316             $im_count++;
1317             }
1318              
1319             } else {
1320             # Unknown ref type
1321             print STDOUT wrap($time, $time.$class->diag->indent.$class->diag->indent,
1322             D_("Unknown reference type"), " '$ref'\n");
1323             }
1324             }
1325              
1326             sub start_log {
1327             my ($class, $filename) = @_;
1328             my $me = (ref $class || $class)."->start_log";
1329              
1330             =item start_log()
1331              
1332             Opens the log files and starts writing diagnostics
1333              
1334             e.g. $Object->start_log('log_filename');
1335              
1336             =cut
1337             my $type = $class->type;
1338             # Check for log file names
1339             $filename ||=
1340             $class->diag->log ||
1341             $class->{$type}->proto->{params}{$type}{diag}{log} ||
1342             $class->{$type}->proto->{project}{$type}{diag}{log} ||
1343             $class->{$type}->proto->{user}{$type}{diag}{log} ||
1344             $class->{$type}->proto->{site}{$type}{diag}{log} ||
1345             "STDOUT";
1346             $filename = $class->normalise($filename);
1347              
1348             if ($class->diag->autoflush) {
1349             select STDOUT;
1350             $|=1;
1351             }
1352             if ('*STDOUT*1*' =~ /\*$filename\*/) {
1353             $filename = '&STDOUT';
1354             } else {
1355             $class->diag->log($filename);
1356             }
1357             if ($class->diag->verbose == 0 ) {
1358             $class->diag_print (2, "Redirecting output to /dev/null");
1359             open STDOUT, ">/dev/null";
1360              
1361             } elsif ($class->diag->log) {
1362             unless ('*&STDOUT*STDOUT*1*' =~ /\*$filename\*/) {
1363             # Set full paths
1364             $class->diag->log($class->full_Path($class->diag->log, `pwd`));
1365             $class->diag_print (3, "%s- Opening log file '%s'",
1366             $class->diag->indent, $class->diag->log);
1367             open STDOUT, ">".$class->diag->log or
1368             die sprintf("error %s - can't open file '%s' for output",
1369             $me, $class->diag->log);
1370             }
1371             open STDERR, ">&1" or
1372             die sprintf("error %s - can't redirect STDERR to file '%s'",
1373             $me, $class->diag->log);
1374             }
1375             $class->diag_print (2,
1376             "--------------------------------------------------------");
1377             $class->diag_print (2,
1378             "%s DIAGNOSTICS - %s (locale <%s> verbosity %s) ".
1379             "started by %s (version %s)",
1380             $class->diag->indent, $class->run_options->start_time,
1381             $class->diag->LANG, $class->diag->verbose,
1382             $class->run_options->name, $class->run_options->version,
1383             );
1384             }
1385              
1386             sub stop_log {
1387             my ($class, $type) = @_;
1388             my $me = (ref $class || $class)."->stop_log";
1389              
1390             =item stop_log()
1391              
1392             Loads site/user/project/params options
1393              
1394             e.g. $Object->stop_log;
1395              
1396             =cut
1397             $type ||= $class->type;
1398             if ($class->diag->log and $class->diagnostics(2)) {
1399             $class->diag_print (2,
1400             "%s RUN COMPLETED - %s diagnostics stopped by %s (version %s)",
1401             $class->diag->indent, $class->get_time,
1402             $class->{$type}->name, $class->{$type}->version);
1403             $class->diag_print (2,
1404             "-----------------------------------------------------------------------------");
1405             close(STDERR) || die "can't close stderr: $!";
1406             close(STDOUT) || die "can't close stdout: $!" ;
1407             }
1408             }
1409              
1410             #===============================================================================
1411             #=========== Gettext Utilities ====
1412             #=========== 'borrowed' from the gettext dist and recoded to house style ====
1413             #===============================================================================
1414              
1415             =back
1416              
1417             =head1 4) INTERNATIONALISATION (I18N) METHODS
1418              
1419             These methods will load translations, translate messages, check for any
1420             missing translations and write a .pot file containing these missing messages.
1421              
1422             =over
1423              
1424             =cut
1425              
1426             =item _()
1427              
1428             Translate a string into our current language
1429              
1430             e.g. sprintf(_("A message '%s'"), $value);
1431              
1432             =cut
1433             sub _ {gettext(@_)}
1434              
1435             =item gettext()
1436              
1437             Translate into a preloaded language (eg '__S' or '__D')
1438              
1439             e.g. C
1440              
1441             =cut
1442             sub gettext {
1443             defined $I18N->{'__'}{$_[0]} ? $I18N->{'__'}{$_[0]} : $_[0];
1444             }
1445              
1446             # These are defined within a no-warning block to avoid warnings about redefining
1447             # They override the subs in Glade::PerlRun during your development
1448             {
1449             local $^W = 0;
1450             eval "sub x_ {_check_gettext('__', \@_);}";
1451             }
1452              
1453             # Translate string into source language
1454             sub S_ { _check_gettext('__S', @_)}
1455              
1456             # Translate string into diagnostics language
1457             sub D_ { _check_gettext('__D', @_)}
1458              
1459             # Internal utility to note any untranslated strings
1460             sub _check_gettext {
1461             # If check_gettext_strings() has been called and there is no translation
1462             # we store the original string for later output by write_gettext_strings
1463             my ($key, $text, $depth) = @_;
1464              
1465             $depth ||= 1;
1466             if (defined $I18N->{$key}{$text}) {
1467             return $I18N->{$key}{$text};
1468             } else {
1469             if ($I18N->{$key}{'__SAVE_MISSING'}) {
1470             my $called_at =
1471             basename((caller $depth)[1]). ":".(caller $depth)[2];
1472             unless ($I18N->{$key}{'__MISSING_STRINGS'}{$text} &&
1473             $I18N->{$key}{'__MISSING_STRINGS'}{$text} =~ / $called_at /) {
1474             $I18N->{$key}{'__MISSING_STRINGS'}{$text} .= " $called_at ";
1475             }
1476             }
1477             return $text;
1478             }
1479             }
1480              
1481             sub start_checking_gettext_strings {
1482             my ($class, $key, $file) = @_;
1483              
1484             =item start_checking_gettext_strings()
1485              
1486             Start checking and storing missing translations in language type
1487              
1488             eg. $class->start_checking_gettext_strings("__S");
1489              
1490              
1491             =cut
1492             $I18N->{($key || '__')}{'__SAVE_MISSING'} = ($file || "&STDOUT");
1493             }
1494              
1495             sub stop_checking_gettext_strings {
1496             my ($class, $key) = @_;
1497              
1498             =item stop_checking_gettext_strings()
1499              
1500             Stop checking for missing translations in language type
1501              
1502             eg. $class->stop_checking_gettext_strings("__S");
1503              
1504             =cut
1505             undef $I18N->{($key || '__')}{'__SAVE_MISSING'};
1506             }
1507              
1508             sub write_missing_gettext_strings {
1509             # Write out the strings that need to be translated in .pot format
1510             my ($class, $key, $file, $no_header, $copy_to) = @_;
1511              
1512             =item write_missing_gettext_strings()
1513              
1514             Write a .pot file containing any untranslated strings in language type
1515              
1516             eg. $object->write_missing_gettext_strings('__S');
1517              
1518             =cut
1519             $key ||= "__";
1520             my ($string, $called_at);
1521             my $me = __PACKAGE__."->write_translatable_strings";
1522             my $saved = $I18N->{$key}{'__MISSING_STRINGS'};
1523             $key ||= "__";
1524             $file ||= $I18N->{$key}{'__SAVE_MISSING'};
1525             return unless keys %$saved;
1526             open POT, ">$file" or
1527             die sprintf(("error %s - can't open file '%s' for output"),
1528             $me, $file);
1529             my $date = `date +"%Y-%m-%d %H:%M%z"`; chomp $date;
1530             my $year = `date +"%Y"`; chomp $year;
1531             # Print header
1532             print POT "# ".sprintf(S_("These are strings that had no gettext translation in '%s'"), $key)."\n";
1533             print POT "# ".sprintf(S_("Automatically generated by %s"),__PACKAGE__)."\n";
1534             print POT "# ".S_("Date")." ".`date`;
1535             print POT "# ".sprintf(S_("Run from class %s in file %s"), $class->PACKAGE, (caller 0)[1])."\n";
1536             unless ($no_header && $no_header eq "NO_HEADER") {
1537             print POT "
1538             # SOME DESCRIPTIVE TITLE.
1539             # Copyright (C) $year ORGANISATION
1540             # ".$class->AUTHOR.",
1541             #
1542             # , fuzzy
1543             msgid \"\"
1544             msgstr \"\"
1545             \"Project-Id-Version: ".$class->PACKAGE." ".$class->VERSION."\\n\"
1546             \"POT-Creation-Date: $date\\n\"
1547             \"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"
1548             \"Last-Translator: ".$class->AUTHOR."\\n\"
1549             \"Language-Team: LANGUAGE \\\n\"
1550             \"MIME-Version: 1.0\\n\"
1551             \"Content-Type: text/plain; charset=CHARSET\\n\"
1552             \"Content-Transfer-Encoding: ENCODING\\n\"
1553              
1554             #: Generic replacement
1555             msgid \"\%s\"
1556             msgstr \"\%s\"
1557              
1558             "; }
1559              
1560             # Print definition for each string
1561             foreach $string (%$saved) {
1562             next unless $string and $saved->{$string};
1563             print POT wrap("#", "#",$saved->{$string}), "\n";
1564             if ($string =~ s/\n/\\n\"\n\"/g) {$string = "\"\n\"".$string}
1565             print POT "msgid \"$string\"\n";
1566             if ($copy_to && $copy_to eq 'COPY_TO') {
1567             print POT "msgstr \"$string\"\n\n";
1568             } else {
1569             print POT "msgstr \"\"\n\n";
1570             }
1571             }
1572             close POT;
1573             }
1574              
1575             sub load_translations {
1576             my ($class, $domain, $language, $locale_dir, $file, $key, $merge) = @_;
1577              
1578             =item load_translations()
1579              
1580             Load a translation file (.mo) for later use as language type
1581              
1582             e.g. To load translations in current LANG from default locations
1583             $class->load_translations('MyApp');
1584            
1585             OR $class->load_translations('MyApp', 'test', undef,
1586             '/home/dermot/Devel/Glade-Perl/ppo/en.mo');
1587              
1588             OR $class->load_translations('MyApp', 'fr', '/usr/local/share/locale/',
1589             undef, '__D', 'Merge with already loaded translations');
1590              
1591             =cut
1592             my $catalog_filename = $file;
1593             $key ||= '__';
1594             $I18N->{$key} = {} unless $merge and $merge eq "MERGE";;
1595              
1596             $language ||= $ENV{"LANG"};
1597             return unless $language;
1598             $locale_dir ||= "/usr/local/share/locale";
1599             $domain ||= "Glade-Perl";
1600              
1601             for $catalog_filename ( $file ||
1602             ("/usr/local/share/locale/$language/LC_MESSAGES/$domain.mo",
1603             "/usr/share/locale/$language/LC_MESSAGES/$domain.mo")) {
1604             if ($catalog_filename and (-f $catalog_filename)) {
1605             $class->load_mo($catalog_filename, $key);
1606             last;
1607             }
1608             }
1609             }
1610              
1611             sub load_mo {
1612             my ($class, $catalog, $key) = @_;
1613              
1614             my ($reverse, $buffer);
1615             my ($magic, $revision, $nstrings);
1616             my ($orig_tab_offset, $orig_length, $orig_pointer);
1617             my ($trans_length, $trans_pointer, $trans_tab_offset);
1618              
1619             # Slurp in the catalog
1620             my $save = $/;
1621             open CATALOG, $catalog or return;
1622             undef $/;
1623             $buffer = ;
1624             close CATALOG;
1625             $/ = $save;
1626            
1627             # Check magic order
1628             $magic = unpack ("I", $buffer);
1629             if (sprintf ("%x", $magic) eq "de120495") {
1630             $reverse = 1;
1631              
1632             } elsif (sprintf ("%x", $magic) ne "950412de") {
1633             print STDERR "'$catalog' "._("is not a catalog file")."\n";
1634             return;
1635             }
1636              
1637             $revision = &mo_format_value (4, $reverse, $buffer);
1638             $nstrings = &mo_format_value (8, $reverse, $buffer);
1639             $orig_tab_offset = &mo_format_value (12, $reverse, $buffer);
1640             $trans_tab_offset = &mo_format_value (16, $reverse, $buffer);
1641              
1642             while ($nstrings-- > 0) {
1643             $orig_length = &mo_format_value ($orig_tab_offset, $reverse, $buffer);
1644             $orig_pointer = &mo_format_value ($orig_tab_offset + 4, $reverse, $buffer);
1645             $orig_tab_offset += 8;
1646              
1647             $trans_length = &mo_format_value ($trans_tab_offset, $reverse, $buffer);
1648             $trans_pointer = &mo_format_value ($trans_tab_offset + 4,$reverse, $buffer);
1649             $trans_tab_offset += 8;
1650              
1651             $I18N->{$key}{substr ($buffer, $orig_pointer, $orig_length)}
1652             = substr ($buffer, $trans_pointer, $trans_length);
1653             }
1654              
1655             # Allow for translation of really empty strings
1656             $I18N->{$key}{'__MO_HEADER_INFO'} = $I18N->{$key}{''};
1657             $I18N->{$key}{''} = '';
1658             }
1659              
1660             sub mo_format_value {
1661             my ($string, $reverse, $buffer) = @_;
1662              
1663             unpack ("i",
1664             $reverse
1665             ? pack ("c4", reverse unpack ("c4", substr ($buffer, $string, 4)))
1666             : substr ($buffer, $string, 4));
1667             }
1668              
1669             #===============================================================================
1670             #=========== Widget hierarchy Utilities ====
1671             #===============================================================================
1672             sub WH {
1673             my ($class, $new) = @_;
1674             if ($new) {
1675             return $class->FORM->{'__WH'} = $new;
1676             } else {
1677             return $class->FORM->{'__WH'};
1678             }
1679             }
1680              
1681             sub CH {
1682             my ($class, $new) = @_;
1683             if ($new) {
1684             return $class->FORM->{'__CH'} = $new;
1685             } else {
1686             return $class->FORM->{'__CH'};
1687             }
1688             }
1689              
1690             sub W {
1691             my ($class, $proto, $new) = @_;
1692             if ($new) {
1693             return $proto->{'__W'} = $new;
1694             } else {
1695             return $proto->{'__W'};
1696             }
1697             }
1698              
1699             sub C {
1700             my ($class, $proto, @new) = @_;
1701             if ($#new) {
1702             return push @{$proto->{'__C'}}, @new;
1703             } else {
1704             return $proto->{'__C'};
1705             }
1706             }
1707              
1708             #===============================================================================
1709             #=========== UI utilities
1710             #===============================================================================
1711              
1712             =back
1713              
1714             =head1 5) UI METHODS
1715              
1716             These methods will provide some useful UI methods to load pixmaps and
1717             images and show message boxes of various types.
1718              
1719             =over
1720              
1721             =cut
1722              
1723             sub create_pixmap {
1724             my ($class, $widget, $filename, $pixmap_dirs) = @_;
1725             my $me = "$class->create_pixmap";
1726              
1727             =item create_pixmap()
1728              
1729             Create a gdk_pixmap and return it
1730              
1731             e.g. my $pixmap = Glade::PerlRun->create_pixmap(
1732             $form, 'new.xpm', ['dir1', 'dir2']);
1733              
1734             =cut
1735              
1736             my ($work, $gdk_pixmap, $gdk_mask, $testfile, $found_filename, $dir);
1737             # First look in specified $pixmap_dirs
1738             if (-f $filename) {
1739             $found_filename = $testfile;
1740              
1741             } else {
1742             foreach $dir (@{$pixmap_dirs}, $Glade::PerlRun::pixmaps_directory, cwd) {
1743             # Make up full path name and test
1744             $testfile = $class->full_Path($filename, $dir);
1745             if (-f $testfile) {
1746             $found_filename = $testfile;
1747             last;
1748             }
1749             }
1750             }
1751             unless ($found_filename) {
1752             if (-f $filename) {
1753             $found_filename = $filename;
1754             } else {
1755             print STDERR sprintf(_(
1756             "error Pixmap file '%s' does not exist in %s\n"),
1757             $filename, $me);
1758             return undef;
1759             }
1760             }
1761             if (Gtk::Gdk::Pixmap->can('colormap_create_from_xpm')) {
1762             # We have Gtk-Perl after CVS 19990911 so we don't need a realized window
1763             my $colormap = $widget->get_colormap;
1764             return new Gtk::Pixmap(
1765             Gtk::Gdk::Pixmap->colormap_create_from_xpm (
1766             undef, $colormap, undef, $found_filename));
1767              
1768             } else {
1769             # We have an old Gtk-Perl so we need a realized window
1770             $work->{'window'} = $widget->get_toplevel->window ;
1771             $work->{'style'} = Gtk::Widget->get_default_style->bg('normal') ;
1772             unless ($work->{'window'}) {
1773             print STDOUT sprintf(_(
1774             "error Couldn't get_toplevel_window to construct pixmap from '%s' in %s\n"),
1775             $filename, $me);
1776             $work->{'window'} = $widget->window ;
1777             }
1778             return new Gtk::Pixmap(
1779             Gtk::Gdk::Pixmap->create_from_xpm(
1780             $work->{'window'}, $work->{'style'}, $found_filename ) );
1781             }
1782             }
1783              
1784             sub create_image {
1785             my ($class, $filename, $pixmap_dirs) = @_;
1786             my $me = "$class->create_image";
1787              
1788             =item create_image()
1789              
1790             Create and load a gdk_imlibimage and return it
1791              
1792             e.g. my $image = Glade::PerlRun->create_image(
1793             'new.xpm', ['dir1', 'dir2']);
1794              
1795             =cut
1796              
1797             my ($work, $testfile, $found_filename, $dir);
1798             if (-f $filename) {
1799             $found_filename = $testfile;
1800              
1801             } else {
1802             foreach $dir (@{$pixmap_dirs}, $Glade::PerlRun::pixmaps_directory, cwd) {
1803             # Make up full path name and test
1804             $testfile = $class->full_Path($filename, $dir);
1805             if (-f $testfile) {
1806             $found_filename = $testfile;
1807             last;
1808             }
1809             }
1810             }
1811             unless ($found_filename) {
1812             if (-f $filename) {
1813             $found_filename = $filename;
1814             } else {
1815             print STDERR sprintf(_(
1816             "error ImlibImage file '%s' does not exist in %s\n"),
1817             $filename, $me);
1818             return undef;
1819             }
1820             }
1821              
1822             return Gtk::Gdk::ImlibImage->load_image ($found_filename);
1823             }
1824              
1825             sub missing_handler {
1826             my ($class, $widgetname, $signal, $handler, $pixmap) = @_;
1827             my $me = __PACKAGE__."->missing_handler";
1828              
1829             #=item missing_handler()
1830             #
1831             #This method pops up a message while the source code is being generated
1832             #if there is no signal handler to call.
1833             #It shows a pixmap (logo) and buttons to dismiss the box or quit the app
1834             #
1835             # $widgetname the widget that triggered the event
1836             # $signal the signal that was triggered
1837             # $handler the name of the signal handler that is missing
1838             # $pixmap pixmap to show
1839             #
1840             #e.g. Glade::PerlRun->missing_handler(
1841             # $widgetname,
1842             # $signal,
1843             # $handler,
1844             # $pixmap);
1845             #
1846             #=cut
1847             print STDOUT sprintf(_("%s- %s - called with args ('%s')"),
1848             $indent, $me, join("', '", @_)), "\n";
1849             my $message = sprintf("\n"._("%s has been called because\n".
1850             "a signal (%s) was caused by widget (%s).\n".
1851             "When Perl::Generate writes the Perl source to a file \n".
1852             "an AUTOLOADed signal handler sub called '%s'\n".
1853             "will be specified in the ProjectSIGS class file. You can write a sub with\n".
1854             "the same name in another module and it will automatically be called instead.\n"),
1855             $me, $signal, $widgetname, $handler) ;
1856             my $widget = __PACKAGE__->message_box($message,
1857             _("Missing handler")." '$handler' "._("called"),
1858             [_("Dismiss"), _("Quit")." PerlGenerate"], 1, $pixmap);
1859            
1860             # Stop the signal before it triggers the missing one
1861             $class->signal_emit_stop($signal);
1862             return $widget;
1863             }
1864              
1865             sub show_skeleton_message {
1866             my ($class, $caller, $data, $package, $pixmap) = @_;
1867              
1868             =item show_skeleton_message($class, $caller, $data, $package, $pixmap)
1869              
1870             This method pops up a message_box to prove that a stub has been called.
1871             It shows a pixmap (logo) and buttons to dismiss the box or quit the app
1872              
1873             $caller where we were called
1874             $data the args that were supplied to the caller
1875             $package
1876             $pixmap pixmap to show
1877              
1878             e.g. Glade::PerlRun->show_skeleton_message(
1879             $me, \@_, __PACKAGE__, "$Glade::PerlRun::pixmaps_directory/Logo.xpm");
1880              
1881             =cut
1882             $pixmap ||= "$Glade::PerlRun::pixmaps_directory/Logo.xpm";
1883             $package ||= (caller);
1884             $data ||= ['unknown args'];
1885              
1886             $class->message_box(sprintf(_("
1887             A signal handler has just been triggered.
1888              
1889             %s was
1890             called with parameters ('%s')
1891              
1892             Until the sub is fleshed out, I will show you
1893             this box to prove that I have been called
1894             "), $caller, join("', '", @$data)),
1895             $caller,
1896             [_('Dismiss'), _("Quit")." Program"],
1897             1,
1898             $pixmap);
1899             }
1900              
1901             sub message_box {
1902             my ($class, $text, $title, $buttons, $default,
1903             $pixmapfile, $just, $handlers, $entry) = @_;
1904              
1905             =item message_box()
1906              
1907             Show a message box with optional pixmap and entry widget.
1908             After the dialog is closed, the data entered will be in
1909             global $Glade::PerlRun::data.
1910              
1911             e.g. Glade::PerlRun->message_box(
1912             $message, # Message to display
1913             $title, # Dialog title string
1914             [_('Dismiss'), _("Quit")." Program"],
1915             # Buttons to show
1916             1, # Default button is 1st
1917             $pixmap, # pixmap filename
1918             [&dismiss, &quit], # Button click handlers
1919             $entry_needed); # Whether to show an entry
1920             # widget for user data
1921              
1922             =cut
1923             my ($i, $ilimit);
1924             my $justify = $just || 'center';
1925             my $mbno = 1;
1926             # Get a unique toplevel widget structure
1927             while (defined $widgets->{"MessageBox-$mbno"}) {$mbno++;}
1928             #
1929             # Create a GtkDialog called MessageBox
1930             $widgets->{"MessageBox-$mbno"} = new Gtk::Window('toplevel');
1931             $widgets->{"MessageBox-$mbno"}->set_title($title);
1932             $widgets->{"MessageBox-$mbno"}->position('mouse');
1933             $widgets->{"MessageBox-$mbno"}->set_policy('1', '1', '0');
1934             $widgets->{"MessageBox-$mbno"}->border_width('6');
1935             $widgets->{"MessageBox-$mbno"}->set_modal('1');
1936             $widgets->{"MessageBox-$mbno"}->realize;
1937             $widgets->{"MessageBox-$mbno"}{'tooltips'} = new Gtk::Tooltips;
1938             #
1939             # Create a GtkVBox called MessageBox-vbox1
1940             $widgets->{"MessageBox-$mbno"}{'vbox1'} = new Gtk::VBox(0, 0);
1941             $widgets->{"MessageBox-$mbno"}{'vbox1'}->border_width(0);
1942             $widgets->{"MessageBox-$mbno"}->add($widgets->{"MessageBox-$mbno"}{'vbox1'});
1943             $widgets->{"MessageBox-$mbno"}{'vbox1'}->show();
1944             #
1945             # Create a GtkHBox called MessageBox-hbox1
1946             $widgets->{"MessageBox-$mbno"}{'hbox1'} = new Gtk::HBox('0', '0');
1947             $widgets->{"MessageBox-$mbno"}{'hbox1'}->border_width('0');
1948             $widgets->{"MessageBox-$mbno"}{'vbox1'}->add($widgets->{"MessageBox-$mbno"}{'hbox1'});
1949             $widgets->{"MessageBox-$mbno"}{'hbox1'}->show();
1950              
1951             if ($pixmapfile) {
1952             #
1953             # Create a GtkPixmap called pixmap1
1954             $widgets->{"MessageBox-$mbno"}{'pixmap1'} = $class->create_pixmap($widgets->{"MessageBox-$mbno"}{'hbox1'}, $pixmapfile);
1955             if ($widgets->{"MessageBox-$mbno"}{'pixmap1'}) {
1956             $widgets->{"MessageBox-$mbno"}{'pixmap1'}->set_alignment('0.5', '0.5');
1957             $widgets->{"MessageBox-$mbno"}{'pixmap1'}->set_padding('0', '0');
1958             $widgets->{"MessageBox-$mbno"}{'hbox1'}->add($widgets->{"MessageBox-$mbno"}{'pixmap1'});
1959             $widgets->{"MessageBox-$mbno"}{'pixmap1'}->show();
1960             $widgets->{"MessageBox-$mbno"}{'hbox1'}->set_child_packing($widgets->{"MessageBox-$mbno"}{'pixmap1'}, '0', '0', '0', 'start');
1961             }
1962             }
1963              
1964             #
1965             # Create a GtkLabel called MessageBox-label1
1966             $widgets->{"MessageBox-$mbno"}{'label1'} = new Gtk::Label($text);
1967             $widgets->{"MessageBox-$mbno"}{'label1'}->set_justify($justify);
1968             $widgets->{"MessageBox-$mbno"}{'label1'}->set_alignment('0.5', '0.5');
1969             $widgets->{"MessageBox-$mbno"}{'label1'}->set_padding('0', '0');
1970             $widgets->{"MessageBox-$mbno"}{'hbox1'}->add($widgets->{"MessageBox-$mbno"}{'label1'});
1971             $widgets->{"MessageBox-$mbno"}{'label1'}->show();
1972             $widgets->{"MessageBox-$mbno"}{'hbox1'}->set_child_packing($widgets->{"MessageBox-$mbno"}{'label1'}, '1', '1', '10', 'start');
1973             $widgets->{"MessageBox-$mbno"}{'vbox1'}->set_child_packing($widgets->{"MessageBox-$mbno"}{'hbox1'}, '1', '1', '0', 'start');
1974             #
1975             # Create a GtkHBox called MessageBox-action_area1
1976             $widgets->{"MessageBox-$mbno"}{'action_area1'} = new Gtk::HBox('1', '5');
1977             $widgets->{"MessageBox-$mbno"}{'action_area1'}->border_width('10');
1978             $widgets->{"MessageBox-$mbno"}{'vbox1'}->add($widgets->{"MessageBox-$mbno"}{'action_area1'});
1979             $widgets->{"MessageBox-$mbno"}{'action_area1'}->show();
1980             if ($entry) {
1981             #
1982             # Create a GtkEntry called MessageBox-entry
1983             $widgets->{"MessageBox-$mbno"}{'entry'} = new Gtk::Entry;
1984             $widgets->{"MessageBox-$mbno"}{'vbox1'}->add($widgets->{"MessageBox-$mbno"}{'entry'});
1985             $widgets->{"MessageBox-$mbno"}{'entry'}->show( );
1986             $widgets->{"MessageBox-$mbno"}{'entry'}->set_usize('160', '0' );
1987             $widgets->{"MessageBox-$mbno"}{'entry'}->can_focus('1' );
1988             $widgets->{"MessageBox-$mbno"}{'entry'}->set_text('' );
1989             $widgets->{"MessageBox-$mbno"}{'entry'}->set_max_length('0' );
1990             $widgets->{"MessageBox-$mbno"}{'entry'}->set_visibility('1' );
1991             $widgets->{"MessageBox-$mbno"}{'entry'}->set_editable('1' );
1992             $widgets->{"MessageBox-$mbno"}{'entry'}->grab_focus();
1993             }
1994             #
1995             # Create a GtkHButtonBox called MessageBox-hbuttonbox1
1996             $widgets->{"MessageBox-$mbno"}{'hbuttonbox1'} = new Gtk::HButtonBox;
1997             $widgets->{"MessageBox-$mbno"}{'hbuttonbox1'}->set_layout('default_style');
1998             $widgets->{"MessageBox-$mbno"}{'hbuttonbox1'}->set_spacing('10');
1999             $widgets->{"MessageBox-$mbno"}{'action_area1'}->add($widgets->{"MessageBox-$mbno"}{'hbuttonbox1'});
2000             $widgets->{"MessageBox-$mbno"}{'hbuttonbox1'}->show();
2001             #
2002             # Now add all the buttons that were requested (and check for default)
2003             $ilimit = scalar(@$buttons);
2004             for ($i = 0; $i < $ilimit; $i++) {
2005             #
2006             # Create a GtkButton called MessageBox-button2
2007             $widgets->{"MessageBox-$mbno"}{'button'.$i} = new Gtk::Button($buttons->[$i]);
2008             $widgets->{"MessageBox-$mbno"}{'button'.$i}->can_focus('1');
2009             if ($handlers->[$i]) {
2010             $widgets->{"MessageBox-$mbno"}{'button'.$i}->signal_connect('clicked', $handlers->[$i], $mbno, $buttons->[$i]);
2011             } else {
2012             $widgets->{"MessageBox-$mbno"}{'button'.$i}->signal_connect('clicked', __PACKAGE__."::message_box_close", $mbno, $buttons->[$i]);
2013             }
2014             $widgets->{"MessageBox-$mbno"}{'button'.$i}->border_width('0');
2015             $widgets->{"MessageBox-$mbno"}{'hbuttonbox1'}->add($widgets->{"MessageBox-$mbno"}{'button'.$i});
2016             if ($i == ($default-1)) {
2017             $widgets->{"MessageBox-$mbno"}{'button'.$i}->can_default('1');
2018             $widgets->{"MessageBox-$mbno"}{'button'.$i}->grab_default();
2019             }
2020             $widgets->{"MessageBox-$mbno"}{'button'.$i}->show();
2021             }
2022             $widgets->{"MessageBox-$mbno"}{'action_area1'}->set_child_packing($widgets->{"MessageBox-$mbno"}{'hbuttonbox1'}, '1', '1', '0', 'start');
2023             $widgets->{"MessageBox-$mbno"}{'vbox1'}->set_child_packing($widgets->{"MessageBox-$mbno"}{'action_area1'}, '0', '1', '0', 'end');
2024             $widgets->{"MessageBox-$mbno"}->show();
2025             return $widgets->{"MessageBox-$mbno"};
2026             }
2027              
2028             sub message_box_close {
2029             my ($class, $mbno, $button_label) = @_;
2030              
2031             # Close this message_box and tidy up
2032             $widgets->{"MessageBox-$mbno"}->get_toplevel->destroy;
2033             undef $widgets->{"MessageBox-$mbno"};
2034             if (_("*Quit Program*Quit PerlGenerate*Quit UI Build*Close Form*") =~ m/\*$button_label\*/) {
2035             Gtk->main_quit;
2036             }
2037             return $data;
2038             }
2039              
2040             sub destroy_all_forms {
2041             my $class = shift;
2042             my $hashref = shift || $__PACKAGE__::all_forms;
2043             my $myform;
2044             foreach $myform (keys %$hashref) {
2045             $hashref->{$myform}->get_toplevel->destroy;
2046             undef $hashref->{$myform};
2047             }
2048             }
2049              
2050             #===============================================================================
2051             #=========== Utilities ============
2052             #===============================================================================
2053              
2054             =back
2055              
2056             =head1 6) GENERAL METHODS
2057              
2058             These are some general purpose methods that are useful to Glade::PerlGenerate
2059             and generated apps.
2060              
2061             =over
2062              
2063             =cut
2064              
2065             sub get_time {
2066             # FIXME check that this is portable and always works
2067             # why does it give BST interactively but UTC from Glade??
2068             # $key = sprintf(" (%+03d00)", (localtime)[8]);
2069             # $key = (localtime).$key;
2070             my $time = `date`;
2071             chomp $time;
2072             return $time
2073             }
2074              
2075             sub full_Path {
2076             my ($class, $rel_path, $directory, $default) = @_;
2077             my $me = "$class->full_Path";
2078              
2079             =item full_Path()
2080              
2081             Turn a relative path name into an absolute path
2082              
2083             e.g. my $path = Glade::PerlRun->full_Path($relative_path, $directory);
2084              
2085             =cut
2086             my $basename;
2087             my $slash = '/';
2088             my $updir = '/\.\./';
2089             # set to $default if not defined
2090             my $fullname = $rel_path || $default || '';
2091             # add $base unless we are absolute already
2092             if ($fullname !~ /^$slash/ && defined $directory) {
2093             # We are supposed to be relative to a directory so use Cwd->chdir to
2094             # change to specified directory and Cwd->cwd to get full path names
2095             my $save_dir = cwd;
2096             chdir($directory);
2097             my $fulldir = cwd;
2098             # Now change directory to where we were on entry
2099             $fullname = "$fulldir$slash$fullname";
2100             chdir($save_dir);
2101             } else {
2102             # Get the real path (not symlinks)
2103             my $dirname = dirname($fullname);
2104             my $basename = basename($fullname);
2105             my $save_dir = cwd;
2106             chdir($dirname);
2107             my $fulldir = cwd;
2108             # Now change directory to where we were on entry
2109             $fullname = "$fulldir$slash$basename";
2110             chdir($save_dir);
2111             }
2112             # Remove double //s and /./s
2113             $fullname =~ s/$slash\.?$slash/$slash/g;
2114             # Remove /../ relative directories
2115             while ($fullname =~ /$updir/) {
2116             $fullname =~ s/(.+)(?!$updir)$slash.+?$updir/$1$slash/;
2117             }
2118             # Remove trailing /s
2119             $fullname =~ s/$slash$//;
2120             return $fullname;
2121             }
2122              
2123             sub relative_path {
2124             my ($class, $basepath, $path, $root) = @_;
2125             my $me = __PACKAGE__."::relative_path";
2126              
2127             =item relative_Path($basepath, $path, $root)
2128              
2129             Turn an absolute path name into a relative path
2130              
2131             e.g. my $path = Glade::PerlRun->relative_Path($relative_path, $directory);
2132              
2133             =cut
2134             return $path if $path =~ /:/;
2135             my $rel;
2136             # This loop is based on code from Nicolai Langfeldt .
2137             # First we calculate common initial path components length ($li).
2138             my $li = 1;
2139             while (1) {
2140             my $i = index($path, '/', $li);
2141             last if $i < 0 ||
2142             $i != index($basepath, '/', $li) ||
2143             substr($path,$li,$i-$li) ne substr($basepath,$li,$i-$li);
2144             $li=$i+1;
2145             }
2146             # then we nuke it from both paths
2147             substr($path, 0,$li) = '';
2148             substr($basepath,0,$li) = '';
2149              
2150             $rel = "";
2151              
2152             # Add one "../" for each path component left in the base path
2153             $path = ('../' x $basepath =~ tr|/|/|) . $path;
2154             $path = "./" if $path eq "";
2155             $rel = $path;
2156              
2157             return $rel;
2158             }
2159              
2160             sub string_from_file {&string_from_File(@_);}
2161             sub string_from_File {
2162             my ($class, $filename) = @_;
2163             my $me = __PACKAGE__."->string_from_File";
2164              
2165             =item string_from_File()
2166              
2167             Reads (slurps) a file into a string
2168              
2169             e.g. my $string = Glade::PerlRun->string_from_file('/path/to/file');
2170              
2171             =cut
2172             my $save = $/;
2173             undef $/;
2174             open INFILE, $filename or
2175             die sprintf((
2176             "error %s - can't open file '%s' for input"),
2177             $me, $filename);
2178             undef $/;
2179             my $string = ;
2180             close INFILE;
2181             $/ = $save;
2182              
2183             return $string;
2184             }
2185              
2186             sub reload_any_altered_modules {
2187             my ($class) = @_;
2188             my $me = __PACKAGE__."->reload_any_altered_modules";
2189              
2190             =item reload_any_altered_modules()
2191              
2192             Check all loaded modules and reload any that have been altered since the
2193             app started. This saves restarting the app for every change to the signal
2194             handlers or support modules.
2195              
2196             It is impossible to reload the UI module (called something like ProjectUI.pm)
2197             while the app is running without crashing it so don't run glade2perl and then
2198             call this method.
2199             Similarly, any modules that construct objects in their
2200             own namespace will cause unpredictable failures.
2201              
2202             I usually call this in a button's signal handler so that I can edit the
2203             modules and easily reload the edited versions of modules.
2204              
2205             e.g. Glade::PerlRun->reload_any_altered_modules;
2206              
2207             =cut
2208             my $stat = \%stat;
2209             my $reloaded = 0;
2210             my ($prefix, $msg);
2211             if (ref $class) {
2212             $prefix = ($class->{diag}{indent} || $indent);
2213             } else {
2214             $prefix = $indent;
2215             }
2216             $prefix .= "- $me";
2217             while(my($key,$file) = each %INC) {
2218             local $^W = 0;
2219             my $mtime = (stat $file)[9];
2220             # warn and skip the files with relative paths which can't be
2221             # located by applying @INC;
2222             unless (defined $mtime and $mtime) {
2223             print "$prefix - Can't locate $file\n",next
2224             }
2225             unless(defined $stat->{$file}) {
2226             # First time through so log process start time
2227             $stat->{$file} = $^T;
2228             }
2229              
2230             if($mtime > $stat->{$file}) {
2231             delete $INC{$key};
2232             require $key;
2233             $reloaded++;
2234             print "$prefix - Reloading $key in process $$\n";
2235             }
2236             # Log actual stat/checked time
2237             $stat->{$file} = $mtime;
2238             }
2239             return "Reloaded $reloaded module(s) in process $$";
2240             }
2241              
2242             =back
2243              
2244             =head1 SEE ALSO
2245              
2246             Glade::PerlGenerate(3) glade2perl(1)
2247              
2248             =head1 AUTHOR
2249              
2250             Dermot Musgrove
2251              
2252             =cut
2253              
2254             1;
2255              
2256             __END__