monotone

monotone Mtn Source Tree

Root/mtn-browse

  • Property mtn:execute set to true
1#!/usr/bin/perl
2##############################################################################
3#
4# File Name - mtn-browse
5#
6# Description - Perl GUI utility for browsing a Monotone database without a
7# workspace.
8#
9# Author - A.E.Cooper.
10#
11# Legal Stuff - Copyright (c) 2007 Anthony Edward Cooper
12# <aecooper@coosoft.plus.com>.
13#
14# This program is free software; you can redistribute it
15# and/or modify it under the terms of the GNU General Public
16# License as published by the Free Software Foundation;
17# either version 3 of the License, or (at your option) any
18# later version.
19#
20# This program is distributed in the hope that it will be
21# useful, but WITHOUT ANY WARRANTY; without even the implied
22# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
23# PURPOSE. See the GNU General Public License for more
24# details.
25#
26# You should have received a copy of the GNU General Public
27# License along with this software; if not, write to the Free
28# Software Foundation, Inc., 59 Temple Place - Suite 330,
29# Boston, MA 02111-1307 USA.
30#
31##############################################################################
32#
33##############################################################################
34#
35# Global Data For This Module
36#
37##############################################################################
38
39
40
41# ***** DIRECTIVES *****
42
43require 5.008;
44
45BEGIN
46{
47 use constant LIB_PATH => "/home/aecoope/perl";
48 use constant MIME_GLOB_FILE => "/usr/share/mime/globs";
49}
50use lib LIB_PATH;
51use locale;
52use strict;
53use warnings;
54
55# ***** REQUIRED PACKAGES *****
56
57# Standard Perl and CPAN modules.
58
59use Data::Dumper;
60use File::Basename;
61use File::Spec;
62use File::Temp qw(tempdir);
63use Glib qw(FALSE TRUE);
64use Gnome2;
65use Gnome2::VFS -init;
66use Gtk2 -init;
67use Gtk2::Gdk::Keysyms;
68use Gtk2::GladeXML;
69use Gtk2::Helper;
70use Gtk2::Pango;
71use Gtk2::SourceView;
72use Locale::TextDomain ("mtn-browse", LIB_PATH . "/share/locale");
73use IO::File;
74use IPC::Open3;
75use POSIX qw(:errno_h :locale_h :sys_wait_h strftime);
76use Symbol qw(gensym);
77use Text::Tabs;
78use Time::Local;
79
80# Monotone AutomateStdio module.
81
82use Monotone::AutomateStdio qw(:capabilities :severities);
83
84# Modules specific to this application.
85
86use Globals qw(:constants :variables);
87use AdvancedFind;
88use Annotate;
89use ChangeLog;
90use ComboAutoCompletion;
91use Common;
92use Completion;
93use FindFiles;
94use FindText;
95use History;
96use Preferences;
97use WindowManager;
98
99# ***** GLOBAL DATA DECLARATIONS *****
100
101# Constants for the columns within the manifest liststore widget.
102
103use constant MLS_ICON_COLUMN => 0;
104use constant MLS_NAME_COLUMN => 1;
105use constant MLS_DATE_COLUMN => 2;
106use constant MLS_AUTHOR_COLUMN => 3;
107use constant MLS_MANIFEST_ENTRY_COLUMN => 4;
108
109# The type of window that is going to be managed by this module.
110
111my $window_type = "main_window";
112
113# The large logo used for the about dialog window.
114
115my $large_logo;
116
117# ***** FUNCTIONAL PROTOTYPES *****
118
119# Private routines.
120
121sub about_activate_cb($$);
122sub advanced_find_button_clicked_cb($$);
123sub annotate_button_clicked_cb($$);
124sub close_toolbutton_clicked_cb($$);
125sub determine_mime_type($$$$);
126sub directory_up_button_clicked_cb($$);
127sub display_file($$);
128sub file_change_history_button_clicked_cb($$);
129sub find_files_button_clicked_cb($$);
130sub get_browser_window(;$$$$$);
131sub help_toolbutton_clicked_cb($$);
132sub main_window_delete_event_cb($$$);
133sub manifest_browser_treeview_cursor_changed_cb($$);
134sub manifest_browser_treeview_row_activated_cb($$$$);
135sub monotone_viz_button_clicked_cb($$);
136sub mtn_db_locked_handler($$);
137sub mtn_error_handler($$);
138sub new_blank_menu_item_clicked_cb($$);
139sub new_toolbutton_clicked_cb($$);
140sub open_toolbutton_clicked_cb($$);
141sub preferences_toolbutton_clicked_cb($$);
142sub quit_activate_cb($$);
143sub reload_toolbutton_clicked_cb($$);
144sub revision_change_history_button_clicked_cb($$);
145sub revision_change_log_button_clicked_cb($$);
146sub save_as_button_clicked_cb($$);
147sub search_text_button_clicked_cb($$);
148sub setup_mtn_object($$);
149sub setup_sigchld_handler($);
150sub show_line_numbers_togglebutton_toggled_cb($$);
151sub sigchld_handler();
152sub update_browser_state($$);
153sub view_button_clicked_cb($$);
154#
155##############################################################################
156#
157# Routine - Main Body Of Code
158#
159# Description - This is the main body of code for the mtn-browse script.
160#
161# Data - @_ : The command line arguments.
162# Return Value : Unix exit code.
163#
164##############################################################################
165
166
167
168{
169
170 my($branch,
171 $browser,
172 $mtn,
173 $revision_id);
174
175 # Initialise stuff.
176
177 Gnome2::Program->init("mtn-browse", 0.51);
178 setup_sigchld_handler(\&sigchld_handler);
179 $glade_file = LIB_PATH . "/UI/mtn-browse.glade";
180 $tooltips = Gtk2::Tooltips->new();
181 $line_image = Gtk2::Gdk::Pixbuf->new_from_file(LIB_PATH . "/UI/line.png");
182
183 # Set up the default database locked and I/O wait handlers for the Monotone
184 # class.
185
186 Monotone::AutomateStdio->
187register_db_locked_handler(\&mtn_db_locked_handler);
188 Monotone::AutomateStdio->register_io_wait_handler
189(sub { WindowManager->instance()->update_gui(); }, 1);
190
191 # Load in user preferences.
192
193 eval
194 {
195$user_preferences = load_preferences();
196$mime_match_table =
197 build_mime_match_table($user_preferences->{mime_table});
198 };
199 if ($@ ne "")
200 {
201chomp($@);
202my $dialog = Gtk2::MessageDialog->new
203 ($browser->{window},
204 ["modal"],
205 "warning",
206 "close",
207 __x("Your preferences cannot be loaded:\n{error_message}",
208 error_message => $@));
209$dialog->run();
210$dialog->destroy();
211exit(1);
212 }
213 $mono_font = Gtk2::Pango::FontDescription->
214from_string($user_preferences->{fixed_font});
215
216 # Create the temporary working directory.
217
218 eval
219 {
220$tmp_dir = tempdir("mtn-browse_XXXXXXXXXX", TMPDIR => 1, CLEANUP => 1);
221 };
222 if ($@ ne "")
223 {
224my $dialog = Gtk2::MessageDialog->new
225 (undef,
226 ["modal"],
227 "error",
228 "close",
229 __x("{error_message}\nThis is fatal, I am going to exit.",
230 error_message => $@));
231$dialog->run();
232$dialog->destroy();
233exit(1);
234 }
235
236 # Open a Monotone database. First attempt to open the current Monotone
237 # workspace's database. If this doesn't work or the default database is to
238 # be used anyway then attempt to open that database instead.
239
240 eval
241 {
242$mtn = Monotone::AutomateStdio->new();
243$mtn = undef;
244while (! -d "_MTN")
245{
246 if (! chdir(".."))
247 {
248my $dialog = Gtk2::MessageDialog->new
249 (undef,
250 ["modal"],
251 "error",
252 "close",
253 __("Started in a workspace but I cannot\n"
254. "find _MTN, I am going to exit."));
255$dialog->run();
256$dialog->destroy();
257exit(1);
258 }
259}
260$mtn = Monotone::AutomateStdio->new();
261if ($user_preferences->{workspace}->{auto_select})
262{
263 $mtn->get_option(\$branch, "branch");
264 $mtn->get_base_revision_id(\$revision_id);
265}
266 };
267 if (! ($user_preferences->{workspace}->{takes_precedence}
268 && defined($mtn))
269&& $user_preferences->{default_mtn_db} ne "")
270 {
271
272# Before opening the default database, make sure we are not in any
273# workspace.
274
275if (defined($mtn))
276{
277 chdir("..");
278 $mtn = $branch = $revision_id = undef;
279}
280
281# Attempt to open the default database.
282
283eval
284{
285 $mtn = Monotone::AutomateStdio->
286new($user_preferences->{default_mtn_db});
287};
288if ($@ ne "")
289{
290 my $dialog = Gtk2::MessageDialog->new
291(undef,
292 ["modal"],
293 "warning",
294 "close",
295 __x("Cannot open database {database_name}.",
296 database_name => $user_preferences->{default_mtn_db}));
297$dialog->run();
298$dialog->destroy();
299}
300
301 }
302
303 # Set up the error handlers for the Monotone class.
304
305 Monotone::AutomateStdio->register_error_handler(MTN_SEVERITY_ALL,
306 \&mtn_error_handler);
307
308 # Create the browser window and display it. Please note that updating the
309 # browser to reflect the current workspace is done in an idle handler so
310 # that control can be handed over to Gtk2 before updating the display.
311
312 $browser = get_browser_window($mtn);
313 if (defined($mtn))
314 {
315setup_mtn_object($mtn, $browser->{window});
316if (defined($branch))
317{
318 Glib::Idle->add
319(sub {
320 my $browser = $_[0];
321
322 return if ($browser->{in_cb});
323 local $browser->{in_cb} = 1;
324
325 $browser->{branch_combo_details}->{preset} = 1;
326 $browser->{branch_combo_details}->{value} = $branch;
327 $browser->{revision_combo_details}->{preset} = 1;
328 $browser->{revision_combo_details}->{value} =
329 $revision_id;
330 $browser->{tagged_checkbutton}->set_active(FALSE);
331 &{$browser->{update_handler}}($browser, ALL_CHANGED);
332
333 return FALSE;
334 },
335 $browser);
336}
337 }
338 $mtn = undef;
339
340 # Hand control over to Gtk2.
341
342 Gtk2->main();
343
344 # Cleanup.
345
346 WindowManager->instance()->cleanup();
347 Gnome2::VFS->shutdown();
348 $SIG{CHLD} = "IGNORE";
349
350 exit(0);
351
352}
353#
354##############################################################################
355#
356# Routine - quit_activate_cb
357#
358# Description - Callback routine called when the user selects the quit file
359# menu option.
360#
361# Data - $widget : The widget object that received the signal.
362# $browser : The browser instance that is associated with
363# this widget.
364#
365##############################################################################
366
367
368
369sub quit_activate_cb($$)
370{
371
372 my($widget, $browser) = @_;
373
374 return if ($browser->{in_cb});
375 local $browser->{in_cb} = 1;
376
377 # Hide all the windows, close all the database handles and then exit Gtk2.
378
379 WindowManager->instance()->cond_find
380(undef,
381 sub {
382 my $instance = $_[0];
383 $instance->{window}->hide() if ($instance->{window}->mapped());
384 $instance->{mtn} = undef if (exists($instance->{mtn}));
385 return;
386 });
387 Gtk2->main_quit();
388
389}
390#
391##############################################################################
392#
393# Routine - about_activate_cb
394#
395# Description - Callback routine called when the user selects the about
396# help menu option.
397#
398# Data - $widget : The widget object that received the signal.
399# $browser : The browser instance that is associated with
400# this widget.
401#
402##############################################################################
403
404
405
406sub about_activate_cb($$)
407{
408
409 my($widget, $browser) = @_;
410
411 return if ($browser->{in_cb});
412 local $browser->{in_cb} = 1;
413
414 $large_logo = Gtk2::Gdk::Pixbuf->new_from_file
415(LIB_PATH . "/UI/mtn-browse-large.png")
416if (! defined($large_logo));
417 Gnome2::About->new
418("mtn-browse",
419 "0.51",
420 __("Copyright \xa9 2007-2009 Anthony Cooper"),
421 __("A graphical front-end browser for Monotone VCS databases"),
422 ["Anthony Cooper <support\@coosoft.plus.com>"],
423 __("TBD"),
424 __("TBD"),
425 $large_logo)->run();
426
427}
428#
429##############################################################################
430#
431# Routine - new_toolbutton_clicked_cb
432#
433# Description - Callback routine called when the user clicks on the new
434# toolbutton in a main browser window.
435#
436# Data - $widget : The widget object that received the signal.
437# $browser : The browser instance that is associated with
438# this widget.
439#
440##############################################################################
441
442
443
444sub new_toolbutton_clicked_cb($$)
445{
446
447 my($widget, $browser) = @_;
448
449 return if ($browser->{in_cb});
450 local $browser->{in_cb} = 1;
451
452 my @revision_ids;
453
454 # Simply get a new/unused browser window and display it.
455
456 if (! defined($browser->{mtn}))
457 {
458get_browser_window();
459 }
460 elsif (! $browser->{branch_combo_details}->{complete})
461 {
462get_browser_window($browser->{mtn});
463 }
464 elsif (! $browser->{revision_combo_details}->{complete})
465 {
466get_browser_window($browser->{mtn},
467 $browser->{branch_combo_details}->{value});
468 }
469 else
470 {
471get_revision_ids($browser, \@revision_ids);
472get_browser_window($browser->{mtn},
473 $browser->{branch_combo_details}->{value},
474 $revision_ids[0]);
475 }
476
477}
478#
479##############################################################################
480#
481# Routine - new_blank_menu_item_clicked_cb
482#
483# Description - Callback routine called when the user clicks on the new
484# blank menu item in a main browser window.
485#
486# Data - $widget : The widget object that received the signal.
487# $browser : The browser instance that is associated with
488# this widget.
489#
490##############################################################################
491
492
493
494sub new_blank_menu_item_clicked_cb($$)
495{
496
497 my($widget, $browser) = @_;
498
499 return if ($browser->{in_cb});
500 local $browser->{in_cb} = 1;
501
502 # Simply get a new/unused browser window and display it.
503
504 get_browser_window();
505
506}
507#
508##############################################################################
509#
510# Routine - open_toolbutton_clicked_cb
511#
512# Description - Callback routine called when the user clicks on the open
513# toolbutton in a main browser window.
514#
515# Data - $widget : The widget object that received the signal.
516# $browser : The browser instance that is associated with
517# this widget.
518#
519##############################################################################
520
521
522
523sub open_toolbutton_clicked_cb($$)
524{
525
526 my($widget, $browser) = @_;
527
528 return if ($browser->{in_cb});
529 local $browser->{in_cb} = 1;
530
531 my $mtn;
532
533 if (open_database($browser->{window}, \$mtn, undef))
534 {
535setup_mtn_object($mtn, $browser->{window});
536$browser->{mtn} = $mtn;
537&{$browser->{update_handler}}($browser, DATABASE_CHANGED);
538 }
539
540}
541#
542##############################################################################
543#
544# Routine - close_toolbutton_clicked_cb
545#
546# Description - Callback routine called when the user clicks on the close
547# toolbutton in a main browser window.
548#
549# Data - $widget : The widget object that received the signal.
550# $browser : The browser instance that is associated with
551# this widget.
552#
553##############################################################################
554
555
556
557sub close_toolbutton_clicked_cb($$)
558{
559
560 my($widget, $browser) = @_;
561
562 return if ($browser->{in_cb});
563 local $browser->{in_cb} = 1;
564
565 # Simply reset the browser's Monotone instance and update its display.
566
567 $browser->{mtn} = undef;
568 $browser->{appbar}->clear_stack();
569 &{$browser->{update_handler}}($browser, DATABASE_CHANGED);
570
571}
572#
573##############################################################################
574#
575# Routine - reload_toolbutton_clicked_cb
576#
577# Description - Callback routine called when the user clicks on the
578# reload toolbutton in a main browser window.
579#
580# Data - $widget : The widget object that received the signal.
581# $browser : The browser instance that is associated with
582# this widget.
583#
584##############################################################################
585
586
587
588sub reload_toolbutton_clicked_cb($$)
589{
590
591 my($widget, $browser) = @_;
592
593 return if ($browser->{in_cb});
594 local $browser->{in_cb} = 1;
595
596 # Simply refresh the entire browser.
597
598 if ($browser->{branch_combo_details}->{complete})
599 {
600$browser->{branch_combo_details}->{preset} = 1;
601if ($browser->{revision_combo_details}->{complete})
602{
603 $browser->{revision_combo_details}->{preset} = 1;
604 if ($browser->{directory_combo_details}->{complete})
605 {
606$browser->{directory_combo_details}->{preset} = 1;
607if (exists($browser->{file_being_viewed}->{manifest_entry}))
608{
609 $browser->{file_being_viewed_preset_value} =
610$browser->{file_being_viewed}->{short_name};
611}
612 }
613}
614 }
615 $browser->{mtn}->closedown();
616 $browser->{appbar}->clear_stack();
617 &{$browser->{update_handler}}($browser, ALL_CHANGED);
618
619}
620#
621##############################################################################
622#
623# Routine - preferences_toolbutton_clicked_cb
624#
625# Description - Callback routine called when the user clicks on the
626# preferences toolbutton in a main browser window.
627#
628# Data - $widget : The widget object that received the signal.
629# $browser : The browser instance that is associated with
630# this widget.
631#
632##############################################################################
633
634
635
636sub preferences_toolbutton_clicked_cb($$)
637{
638
639 my($widget, $browser) = @_;
640
641 return if ($browser->{in_cb});
642 local $browser->{in_cb} = 1;
643
644 if (preferences($browser))
645 {
646$user_preferences = load_preferences();
647$mime_match_table =
648 build_mime_match_table($user_preferences->{mime_table});
649 }
650
651}
652#
653##############################################################################
654#
655# Routine - help_toolbutton_clicked_cb
656#
657# Description - Callback routine called when the user clicks on the
658# help toolbutton in a main browser window.
659#
660# Data - $widget : The widget object that received the signal.
661# $browser : The browser instance that is associated with
662# this widget.
663#
664##############################################################################
665
666
667
668sub help_toolbutton_clicked_cb($$)
669{
670
671 my($widget, $browser) = @_;
672
673 return if ($browser->{in_cb});
674 local $browser->{in_cb} = 1;
675
676 # Gnome2::Help->display("mtn-browse.xml");
677
678 my $dialog = Gtk2::MessageDialog->new($browser->{window},
679 ["modal"],
680 "info",
681 "close",
682 __("Not implemented."));
683 $dialog->run();
684 $dialog->destroy();
685
686}
687#
688##############################################################################
689#
690# Routine - advanced_find_button_clicked_cb
691#
692# Description - Callback routine called when the user clicks on the
693# advanced find button in a main browser window.
694#
695# Data - $widget : The widget object that received the signal.
696# $browser : The browser instance that is associated with
697# this widget.
698#
699##############################################################################
700
701
702
703sub advanced_find_button_clicked_cb($$)
704{
705
706 my($widget, $browser) = @_;
707
708 return if ($browser->{in_cb});
709 local $browser->{in_cb} = 1;
710
711 my(@branches,
712 $preset_branch,
713 $revision_id,
714 $state);
715
716 if (advanced_find($browser, \$revision_id, \@branches))
717 {
718
719# Preset branch name. If we already have a selected branch then try and
720# match branch names, if that fails then just pick the first name.
721
722$preset_branch = 1;
723$state = BRANCH_CHANGED;
724if ($browser->{branch_combo_details}->{complete})
725{
726 foreach my $name (@branches)
727 {
728if ($name eq $browser->{branch_combo_details}->{value})
729{
730 $preset_branch = 0;
731 last;
732}
733 }
734}
735if ($preset_branch)
736{
737 $browser->{branch_combo_details}->{preset} = 1;
738 $browser->{branch_combo_details}->{value} =
739(scalar(@branches) > 0) ? $branches[0] : "";
740 $state = ALL_CHANGED;
741}
742
743# Preset revision id.
744
745$browser->{revision_combo_details}->{preset} = 1;
746$browser->{revision_combo_details}->{value} = $revision_id;
747
748# A revision id is what is returned so switch off the listing of tag
749# names.
750
751$browser->{tagged_checkbutton}->set_active(FALSE);
752
753$browser->{appbar}->clear_stack();
754&{$browser->{update_handler}}($browser, $state);
755
756 }
757
758}
759#
760##############################################################################
761#
762# Routine - revision_change_history_button_clicked_cb
763#
764# Description - Callback routine called when the user clicks on the
765# revision change history button in a main browser window.
766#
767# Data - $widget : The widget object that received the signal.
768# $browser : The browser instance that is associated with
769# this widget.
770#
771##############################################################################
772
773
774
775sub revision_change_history_button_clicked_cb($$)
776{
777
778 my($widget, $browser) = @_;
779
780 return if ($browser->{in_cb});
781 local $browser->{in_cb} = 1;
782
783 my(@revision_ids,
784 $tag);
785
786 get_revision_ids($browser, \@revision_ids, \$tag);
787 display_revision_change_history($browser->{mtn}, $tag, $revision_ids[0]);
788
789}
790#
791##############################################################################
792#
793# Routine - revision_change_log_button_clicked_cb
794#
795# Description - Callback routine called when the user clicks on the
796# revision change log button in a main browser window.
797#
798# Data - $widget : The widget object that received the signal.
799# $browser : The browser instance that is associated with
800# this widget.
801#
802##############################################################################
803
804
805
806sub revision_change_log_button_clicked_cb($$)
807{
808
809 my($widget, $browser) = @_;
810
811 return if ($browser->{in_cb});
812 local $browser->{in_cb} = 1;
813
814 my(@revision_ids,
815 $tag);
816
817 # Get the currently selected revision id and then display its change log.
818
819 get_revision_ids($browser, \@revision_ids, \$tag);
820 display_change_log($browser->{mtn}, $revision_ids[0], "", $tag);
821
822}
823#
824##############################################################################
825#
826# Routine - monotone_viz_button_clicked_cb
827#
828# Description - Callback routine called when the user clicks on the
829# monotone-viz button in a main browser window.
830#
831# Data - $widget : The widget object that received the signal.
832# $browser : The browser instance that is associated with
833# this widget.
834#
835##############################################################################
836
837
838
839sub monotone_viz_button_clicked_cb($$)
840{
841
842 my($widget, $browser) = @_;
843
844 return if ($browser->{in_cb});
845 local $browser->{in_cb} = 1;
846
847 my($cmd,
848 $db_name,
849 @revision_ids);
850
851 # Build up the monotone-viz command, we need the database name and then
852 # optionally the branch and revision ids.
853
854 $cmd = "monotone-viz ";
855 if (! defined($db_name = $browser->{mtn}->get_db_name()))
856 {
857$browser->{mtn}->get_option(\$db_name, "database");
858 }
859 $cmd .= $db_name;
860 if ($browser->{branch_combo_details}->{complete})
861 {
862$cmd .= " " . $browser->{branch_combo_details}->{value};
863if ($browser->{revision_combo_details}->{complete})
864{
865 get_revision_ids($browser, \@revision_ids);
866 $cmd .= " " . $revision_ids[0];
867}
868 }
869
870 # Launch Monotone-Viz.
871
872 system($cmd . " &");
873
874}
875#
876##############################################################################
877#
878# Routine - directory_up_button_clicked_cb
879#
880# Description - Callback routine called when the user clicks on the up
881# directory button in a main browser window.
882#
883# Data - $widget : The widget object that received the signal.
884# $browser : The browser instance that is associated with
885# this widget.
886#
887##############################################################################
888
889
890
891sub directory_up_button_clicked_cb($$)
892{
893
894 my($widget, $browser) = @_;
895
896 return if ($browser->{in_cb});
897 local $browser->{in_cb} = 1;
898
899 my($len,
900 $old_len,
901 $value);
902
903 # Simply go up one directory level in the manifest if we aren't already at
904 # the top.
905
906 $value = $browser->{directory_combo_details}->{value};
907 if ($value ne "")
908 {
909$old_len = length($value);
910$value = dirname($value);
911$value = "" if ($value eq ".");
912$browser->{directory_combo_details}->{value} = $value;
913$browser->{directory_combo_details}->{complete} = 1;
914$len = length($value);
915if ($len < $old_len)
916{
917 $browser->{directory_comboboxentry}->get_model()->clear();
918 foreach my $item (@{$browser->{directory_combo_details}->{list}})
919 {
920$browser->{directory_comboboxentry}->append_text($item)
921 if ($value eq substr($item, 0, $len));
922 }
923}
924$browser->{directory_comboboxentry}->child()->set_text($value);
925$browser->{appbar}->clear_stack();
926&{$browser->{update_handler}}($browser, DIRECTORY_CHANGED);
927 }
928
929}
930#
931##############################################################################
932#
933# Routine - find_files_button_clicked_cb
934#
935# Description - Callback routine called when the user clicks on the find
936# files button in a main browser window.
937#
938# Data - $widget : The widget object that received the signal.
939# $browser : The browser instance that is associated with
940# this widget.
941#
942##############################################################################
943
944
945
946sub find_files_button_clicked_cb($$)
947{
948
949 my($widget, $browser) = @_;
950
951 return if ($browser->{in_cb});
952 local $browser->{in_cb} = 1;
953
954 my(@revision_ids,
955 $tag);
956
957 get_revision_ids($browser, \@revision_ids, \$tag);
958 display_find_files($browser->{mtn},
959 $tag,
960 $revision_ids[0],
961 $browser->{manifest},
962 $browser->{directory_combo_details}->{value});
963
964}
965#
966##############################################################################
967#
968# Routine - show_line_numbers_togglebutton_toggled_cb
969#
970# Description - Callback routine called when the user toggles the show line
971# numbers button in a main browser window.
972#
973# Data - $widget : The widget object that received the signal.
974# $browser : The browser instance that is associated with
975# this widget.
976#
977##############################################################################
978
979
980
981sub show_line_numbers_togglebutton_toggled_cb($$)
982{
983
984 my($widget, $browser) = @_;
985
986 return if ($browser->{in_cb});
987 local $browser->{in_cb} = 1;
988
989 $browser->{file_view_sv}->set_show_line_numbers($widget->get_active());
990
991}
992#
993##############################################################################
994#
995# Routine - search_text_button_clicked_cb
996#
997# Description - Callback routine called when the user clicks on the search
998# text button in a main browser window.
999#
1000# Data - $widget : The widget object that received the signal.
1001# $browser : The browser instance that is associated with
1002# this widget.
1003#
1004##############################################################################
1005
1006
1007
1008sub search_text_button_clicked_cb($$)
1009{
1010
1011 my($widget, $browser) = @_;
1012
1013 return if ($browser->{in_cb});
1014 local $browser->{in_cb} = 1;
1015
1016 find_text($browser->{window}, $browser->{file_view_sv});
1017
1018}
1019#
1020##############################################################################
1021#
1022# Routine - save_as_button_clicked_cb
1023#
1024# Description - Callback routine called when the user clicks on the save as
1025# button in a main browser window.
1026#
1027# Data - $widget : The widget object that received the signal.
1028# $browser : The browser instance that is associated with
1029# this widget.
1030#
1031##############################################################################
1032
1033
1034
1035sub save_as_button_clicked_cb($$)
1036{
1037
1038 my($widget, $browser) = @_;
1039
1040 return if ($browser->{in_cb});
1041 local $browser->{in_cb} = 1;
1042
1043 my $data;
1044
1045 $browser->{mtn}->get_file(\$data,
1046 $browser->{file_being_viewed}->{manifest_entry}->
1047 {file_id});
1048 save_as_file($browser->{window},
1049 $browser->{file_being_viewed}->{short_name},
1050 \$data);
1051
1052}
1053#
1054##############################################################################
1055#
1056# Routine - view_button_clicked_cb
1057#
1058# Description - Callback routine called when the user clicks on the view
1059# button in a main browser window.
1060#
1061# Data - $widget : The widget object that received the signal.
1062# $browser : The browser instance that is associated with
1063# this widget.
1064#
1065##############################################################################
1066
1067
1068
1069sub view_button_clicked_cb($$)
1070{
1071
1072 my($widget, $browser) = @_;
1073
1074 return if ($browser->{in_cb});
1075 local $browser->{in_cb} = 1;
1076
1077 my($app,
1078 $data,
1079 $fh,
1080 $file_name,
1081 $helper,
1082 $mime_details,
1083 $mime_obj,
1084 $mime_type);
1085
1086 # Generate temporary disk file name.
1087
1088 if (! defined($file_name =
1089 generate_tmp_path($browser->{file_being_viewed}->
1090 {short_name})))
1091 {
1092my $dialog = Gtk2::MessageDialog->new
1093 ($browser->{window},
1094 ["modal"],
1095 "warning",
1096 __x("Cannot generate temporary file name:\n{error_message}.",
1097 error_message => $!));
1098$dialog->run();
1099$dialog->destroy();
1100return;
1101 }
1102
1103 # Attempt to save the contents to the file.
1104
1105 if (! defined($fh = IO::File->new($file_name, "w")))
1106 {
1107my $dialog = Gtk2::MessageDialog->new
1108 ($browser->{window},
1109 ["modal"],
1110 "warning",
1111 "close",
1112 __x("{error_message}.", error_message => $!));
1113$dialog->run();
1114$dialog->destroy();
1115return;
1116 }
1117 $browser->{mtn}->get_file(\$data,
1118 $browser->{file_being_viewed}->
1119 {manifest_entry}->{file_id});
1120 $fh->print($data);
1121 $fh->close();
1122 $fh = undef;
1123
1124 # Get the user preference settings for this type of file.
1125
1126 determine_mime_type($browser->{file_being_viewed}->{short_name},
1127\$data,
1128\$mime_type,
1129\$mime_details);
1130
1131 # If the user has specified a helper application then use that to view the
1132 # file, otherwise use the default desktop settings.
1133
1134 if (defined($mime_details) && $mime_details->{helper_application} ne "")
1135 {
1136
1137# Use the specified helper application, replacing `{file}' with the
1138# real file name.
1139
1140$helper = $mime_details->{helper_application};
1141if ($helper =~ m/\{file\}/)
1142{
1143 $helper =~ s/\{file\}/$file_name/g;
1144}
1145else
1146{
1147 $helper .= " " . $file_name;
1148}
1149
1150# Launch it.
1151
1152system($helper . " &");
1153
1154 }
1155 else
1156 {
1157
1158# Use the desktop to load the file.
1159
1160# Use the appropriate application for this type of file, defaulting to
1161# Vi if necessary.
1162
1163if (! defined($mime_type =
1164 Gnome2::VFS->get_mime_type("file://" . $file_name)))
1165{
1166 my $dialog = Gtk2::MessageDialog->new
1167($browser->{window},
1168 ["modal"],
1169 "warning",
1170 "close",
1171 __("Unknown file type, not viewing."));
1172 $dialog->run();
1173 $dialog->destroy();
1174 return;
1175}
1176$app = $mime_obj->get_default_application()
1177 if (defined($mime_obj = Gnome2::VFS::Mime::Type->new($mime_type)));
1178if (defined($app))
1179{
1180 $app->launch("file://" . $file_name);
1181}
1182else
1183{
1184 my $dialog = Gtk2::MessageDialog->new
1185($browser->{window},
1186 ["modal"],
1187 "info",
1188 "close",
1189 __x("No application is associated with\n"
1190 . "Mime type {mime_type},\nusing Vi instead.",
1191 mime_type => $mime_type));
1192 system("xterm -e vi " . $file_name . " &");
1193}
1194
1195 }
1196
1197}
1198#
1199##############################################################################
1200#
1201# Routine - annotate_button_clicked_cb
1202#
1203# Description - Callback routine called when the user clicks on the
1204# annotate button in a main browser window.
1205#
1206# Data - $widget : The widget object that received the signal.
1207# $browser : The browser instance that is associated with
1208# this widget.
1209#
1210##############################################################################
1211
1212
1213
1214sub annotate_button_clicked_cb($$)
1215{
1216
1217 my($widget, $browser) = @_;
1218
1219 return if ($browser->{in_cb});
1220 local $browser->{in_cb} = 1;
1221
1222 my @revision_ids;
1223
1224 get_revision_ids($browser, \@revision_ids);
1225 display_annotation($browser->{mtn},
1226 $revision_ids[0],
1227 $browser->{file_being_viewed}->{manifest_entry}->
1228 {name});
1229
1230}
1231#
1232##############################################################################
1233#
1234# Routine - file_change_history_button_clicked_cb
1235#
1236# Description - Callback routine called when the user clicks on the file
1237# change history button in a main browser window.
1238#
1239# Data - $widget : The widget object that received the signal.
1240# $browser : The browser instance that is associated with
1241# this widget.
1242#
1243##############################################################################
1244
1245
1246
1247sub file_change_history_button_clicked_cb($$)
1248{
1249
1250 my($widget, $browser) = @_;
1251
1252 return if ($browser->{in_cb});
1253 local $browser->{in_cb} = 1;
1254
1255 my @revision_ids;
1256
1257 get_revision_ids($browser, \@revision_ids);
1258 display_file_change_history($browser->{mtn},
1259$revision_ids[0],
1260$browser->{file_being_viewed}->
1261 {manifest_entry}->{name});
1262
1263}
1264#
1265##############################################################################
1266#
1267# Routine - manifest_browser_treeview_cursor_changed_cb
1268#
1269# Description - Callback routine called when the user selects an entry in
1270# the manifest treeview in a main browser window.
1271#
1272# Data - $widget : The widget object that received the signal.
1273# $browser : The browser instance that is associated with
1274# this widget.
1275#
1276##############################################################################
1277
1278
1279
1280sub manifest_browser_treeview_cursor_changed_cb($$)
1281{
1282
1283 my($widget, $browser) = @_;
1284
1285 return if ($browser->{in_cb});
1286 local $browser->{in_cb} = 1;
1287
1288 my($manifest_entry,
1289 $short_name);
1290
1291 # Get the manifest entry details for the item that was selected.
1292
1293 $widget->get_selection()->selected_foreach
1294(sub {
1295 my($model, $path, $iter) = @_;
1296 $short_name = $model->get($iter, MLS_NAME_COLUMN);
1297 $manifest_entry = $model->get($iter, MLS_MANIFEST_ENTRY_COLUMN);
1298 });
1299
1300 # If the item is a file then display its contents, otherwise if it is a
1301 # directory then just ignore it.
1302
1303 if (defined($manifest_entry) && $manifest_entry->{type} eq "file")
1304 {
1305$browser->{file_being_viewed} = {short_name => $short_name,
1306 manifest_entry => $manifest_entry};
1307$browser->{appbar}->clear_stack();
1308&{$browser->{update_handler}}($browser, FILE_CHANGED);
1309 }
1310
1311}
1312#
1313##############################################################################
1314#
1315# Routine - manifest_browser_treeview_row_activated_cb
1316#
1317# Description - Callback routine called when the user double clicks on an
1318# entry in the manifest treeview in a main browser window.
1319#
1320# Data - $widget : The widget object that received the
1321# signal.
1322# $tree_path : A Gtk2::TreePath object for the
1323# selected item.
1324# $tree_view_column : A Gtk2::TreeViewColumn object for the
1325# selected item.
1326# $browser : The browser instance that is associated
1327# with this widget.
1328#
1329##############################################################################
1330
1331
1332
1333sub manifest_browser_treeview_row_activated_cb($$$$)
1334{
1335
1336 my($widget, $tree_path, $tree_view_column, $browser) = @_;
1337
1338 return if ($browser->{in_cb});
1339 local $browser->{in_cb} = 1;
1340
1341 my $manifest_entry;
1342
1343 # Get the manifest entry details for the item that was double-clicked.
1344
1345 $widget->get_selection()->selected_foreach
1346(sub {
1347 my($model, $path, $iter) = @_;
1348 $manifest_entry = $model->get($iter, MLS_MANIFEST_ENTRY_COLUMN);
1349 });
1350
1351 # If the item is a directory then change to it, otherwise if it is a file
1352 # then just ignore it.
1353
1354 if (defined($manifest_entry) && $manifest_entry->{type} eq "directory")
1355 {
1356$browser->{directory_combo_details}->{value} = $manifest_entry->{name};
1357$browser->{directory_combo_details}->{complete} = 1;
1358$browser->{directory_comboboxentry}->child()->
1359 set_text($manifest_entry->{name});
1360$browser->{appbar}->clear_stack();
1361&{$browser->{update_handler}}($browser, DIRECTORY_CHANGED);
1362 }
1363
1364}
1365#
1366##############################################################################
1367#
1368# Routine - main_window_delete_event_cb
1369#
1370# Description - Callback routine called when a main window is about to be
1371# dismissed.
1372#
1373# Data - $widget : The widget object that received the signal.
1374# $event : A Gtk2::Gdk::Event object describing the
1375# event that has occurred.
1376# $browser : The browser instance that is associated with
1377# this widget.
1378# Return Value : TRUE if the event has been handled and needs
1379# no further handling, otherwise FALSE if the
1380# event should carry on through the remaining
1381# event handling.
1382#
1383##############################################################################
1384
1385
1386
1387sub main_window_delete_event_cb($$$)
1388{
1389
1390 my($widget, $event, $browser) = @_;
1391
1392 return TRUE if ($browser->{in_cb});
1393 local $browser->{in_cb} = 1;
1394
1395 my $others_mapped;
1396
1397 # Only exit if this is the only browser window currently being shown.
1398
1399 $others_mapped = 0;
1400 WindowManager->instance()->cond_find
1401($window_type,
1402 sub {
1403 my $instance = $_[0];
1404 if ($instance != $browser && $instance->{window}->mapped())
1405 {
1406 $others_mapped = 1;
1407 return 1;
1408 }
1409 return;
1410 });
1411
1412 hide_find_text($browser->{file_view_sv});
1413 $browser->{window}->hide();
1414 $browser->{mtn} = undef;
1415 $browser->{branch_combo_details}->{preset} = 0;
1416 $browser->{revision_combo_details}->{preset} = 0;
1417 $browser->{directory_combo_details}->{preset} = 0;
1418 $browser->{file_being_viewed_preset_value} = "";
1419 &{$browser->{update_handler}}($browser, DATABASE_CHANGED);
1420
1421 Gtk2->main_quit() unless ($others_mapped);
1422
1423 return TRUE;
1424
1425}
1426#
1427##############################################################################
1428#
1429# Routine - get_browser_window
1430#
1431# Description - Creates or prepares an existing browser window for use.
1432#
1433# Data - $mtn : The Monotone::AutomateStdio object that is
1434# to be used for the browser window. If it is
1435# undef then no database is used and a blank
1436# browser window is displayed.
1437# $branch : The branch name that is to be preselected in
1438# the browser window. This is optional unless
1439# any of the following arguments are
1440# specified.
1441# $revision_id : The revision id that is to be preselected in
1442# the browser window. This is optional unless
1443# any of the following arguments are
1444# specified.
1445# $directory : The directory that is to be preselected in
1446# the browser window. This is optional unless
1447# the following argument is specified.
1448# $file : The file that is to be displayed in the
1449# browser window. This is optional.
1450# Return Value : A reference to the newly created or unused
1451# browser instance record.
1452#
1453##############################################################################
1454
1455
1456
1457sub get_browser_window(;$$$$$)
1458{
1459
1460 my($mtn, $branch, $revision_id, $directory, $file) = @_;
1461
1462 my $browser;
1463 my $wm = WindowManager->instance();
1464
1465 # Create a new browser window if an unused one wasn't found, otherwise
1466 # reuse an existing unused one.
1467
1468 if (! defined($browser = $wm->find_unused($window_type)))
1469 {
1470
1471my($image,
1472 $renderer,
1473 $tv_column);
1474
1475$browser = {};
1476$browser->{mtn} = $mtn;
1477$browser->{glade} = Gtk2::GladeXML->new($glade_file, $window_type);
1478
1479# Flag to stop recursive calling of callbacks.
1480
1481$browser->{in_cb} = 0;
1482
1483# Connect Glade registered signal handlers.
1484
1485glade_signal_autoconnect($browser->{glade}, $browser);
1486
1487# Link in the update handler for the browser.
1488
1489$browser->{update_handler} = \&update_browser_state;
1490
1491# Get the widgets that we are interested in.
1492
1493$browser->{window} = $browser->{glade}->get_widget($window_type);
1494foreach my $widget ("appbar",
1495 "main_vbox",
1496 "browser_hpaned",
1497 "close_toolbutton",
1498 "properties_toolbutton",
1499 "branch_comboboxentry",
1500 "revision_comboboxentry",
1501 "tagged_checkbutton",
1502 "directory_comboboxentry",
1503 "directory_up_button",
1504 "find_files_button",
1505 "show_line_numbers_togglebutton",
1506 "manifest_browser_treeview",
1507 "file_view_scrolledwindow",
1508 "file_name_value_label",
1509 "file_id_value_label",
1510 "last_update_value_label",
1511 "file_revision_id_value_label",
1512 "file_button_vbox",
1513 "database_name_value_label",
1514 "date_value_label",
1515 "author_value_label",
1516 "change_log_value_label")
1517{
1518 $browser->{$widget} = $browser->{glade}->get_widget($widget);
1519}
1520
1521# Setup button sensitivity groups.
1522
1523$browser->{text_file_sensitive_group} = [];
1524foreach my $item ("search_text", "annotate")
1525{
1526 push(@{$browser->{text_file_sensitive_group}},
1527 $browser->{glade}->get_widget($item . "_button"));
1528}
1529$browser->{revision_sensitive_group} = [];
1530foreach my $item ("revision_change_history", "revision_change_log")
1531{
1532 push(@{$browser->{revision_sensitive_group}},
1533 $browser->{glade}->get_widget($item . "_button"));
1534}
1535
1536# Setup the comboboxes.
1537
1538$browser->{branch_comboboxentry}->
1539 set_model(Gtk2::ListStore->new("Glib::String"));
1540$browser->{branch_comboboxentry}->set_text_column(0);
1541$browser->{branch_comboboxentry}->set_wrap_width(2);
1542$browser->{directory_comboboxentry}->
1543 set_model(Gtk2::ListStore->new("Glib::String"));
1544$browser->{directory_comboboxentry}->set_text_column(0);
1545$browser->{directory_comboboxentry}->set_wrap_width(2);
1546$browser->{revision_comboboxentry}->
1547 set_model(Gtk2::ListStore->new("Glib::String"));
1548$browser->{revision_comboboxentry}->set_text_column(0);
1549$browser->{revision_comboboxentry}->set_wrap_width(2);
1550
1551# Setup the tree view manifest file browser.
1552
1553$browser->{manifest_liststore} = Gtk2::ListStore->new("Glib::String",
1554 "Glib::String",
1555 "Glib::String",
1556 "Glib::String",
1557 "Glib::Scalar");
1558$browser->{manifest_browser_treeview}->
1559 set_model($browser->{manifest_liststore});
1560
1561$tv_column = Gtk2::TreeViewColumn->new();
1562$image = Gtk2::Image->new_from_stock("gtk-file", "menu");
1563$image->show_all();
1564$tv_column->set_widget($image);
1565$tv_column->set_resizable(FALSE);
1566$tv_column->set_sizing("fixed");
1567$tv_column->set_fixed_width(25);
1568$tv_column->set_sort_column_id(MLS_ICON_COLUMN);
1569$renderer = Gtk2::CellRendererPixbuf->new();
1570$tv_column->pack_start($renderer, TRUE);
1571$tv_column->set_attributes($renderer, "stock-id" => MLS_ICON_COLUMN);
1572$browser->{manifest_browser_treeview}->append_column($tv_column);
1573
1574$tv_column = Gtk2::TreeViewColumn->new();
1575$tv_column->set_title(__("File Name"));
1576$tv_column->set_resizable(TRUE);
1577$tv_column->set_sizing("fixed");
1578$tv_column->set_fixed_width(180);
1579$tv_column->set_sort_column_id(MLS_NAME_COLUMN);
1580$renderer = Gtk2::CellRendererText->new();
1581$tv_column->pack_start($renderer, FALSE);
1582$tv_column->set_attributes($renderer, "text" => MLS_NAME_COLUMN);
1583$browser->{manifest_browser_treeview}->append_column($tv_column);
1584
1585# Only set up the remaining columns if that is what the user wants.
1586
1587if ($user_preferences->{show_file_details})
1588{
1589 $tv_column = Gtk2::TreeViewColumn->new();
1590 $tv_column->set_title(__("Last Update"));
1591 $tv_column->set_resizable(TRUE);
1592 $tv_column->set_sizing("grow-only");
1593 $tv_column->set_sort_column_id(MLS_DATE_COLUMN);
1594 $renderer = Gtk2::CellRendererText->new();
1595 $tv_column->pack_start($renderer, FALSE);
1596 $tv_column->set_attributes($renderer, "text" => MLS_DATE_COLUMN);
1597 $browser->{manifest_browser_treeview}->append_column($tv_column);
1598
1599 $tv_column = Gtk2::TreeViewColumn->new();
1600 $tv_column->set_title(__("Author"));
1601 $tv_column->set_resizable(TRUE);
1602 $tv_column->set_sizing("grow-only");
1603 $tv_column->set_sort_column_id(MLS_AUTHOR_COLUMN);
1604 $renderer = Gtk2::CellRendererText->new();
1605 $tv_column->pack_start($renderer, FALSE);
1606 $tv_column->set_attributes($renderer, "text" => MLS_AUTHOR_COLUMN);
1607 $browser->{manifest_browser_treeview}->append_column($tv_column);
1608
1609}
1610$browser->{show_file_details} = $user_preferences->{show_file_details};
1611
1612$browser->{manifest_browser_treeview}->
1613 set_search_column(MLS_NAME_COLUMN);
1614
1615# Setup the file file viewer (with syntax highlighting).
1616
1617$browser->{file_view_svbuffer} = Gtk2::SourceView::Buffer->new(undef);
1618$browser->{file_view_svbuffer}->set_max_undo_levels(0);
1619$browser->{file_view_svbuffer}->begin_not_undoable_action();
1620$browser->{file_view_svlangmgr} =
1621 Gtk2::SourceView::LanguagesManager->new();
1622$browser->{file_view_sv} = Gtk2::SourceView::View->
1623 new_with_buffer($browser->{file_view_svbuffer});
1624$browser->{file_view_sv}->modify_font($mono_font);
1625$browser->{file_view_sv}->set_cursor_visible(FALSE);
1626$browser->{file_view_sv}->set_editable(FALSE);
1627$browser->{file_view_scrolledwindow}->add($browser->{file_view_sv});
1628$browser->{file_view_sv_populate_popup_handler} =
1629 $browser->{file_view_sv}->signal_connect
1630 ("populate_popup",
1631 \&find_text_textview_populate_popup_cb,
1632 $browser);
1633$browser->{file_view_sv_key_press_handler} =
1634 $browser->{file_view_sv}->signal_connect
1635 ("key_press_event",
1636 \&find_text_textview_key_press_event_cb,
1637 $browser);
1638$browser->{file_view_sv}->show_all();
1639
1640local $browser->{in_cb} = 1;
1641$browser->{window}->show_all();
1642
1643# Register the window for management.
1644
1645$wm->manage($browser,
1646 $window_type,
1647 $browser->{window},
1648 undef,
1649 $browser->{file_view_sv}->get_window("text"));
1650
1651# Update the browser's internal state.
1652
1653$browser->{branch_combo_details}->{preset} = 0;
1654$browser->{revision_combo_details}->{preset} = 0;
1655$browser->{directory_combo_details}->{preset} = 0;
1656$browser->{file_being_viewed_preset_value} = "";
1657&{$browser->{update_handler}}($browser, ALL_CHANGED);
1658
1659 }
1660 else
1661 {
1662
1663my($height,
1664 $width);
1665
1666$browser->{in_cb} = 0;
1667local $browser->{in_cb} = 1;
1668
1669# Reset the browser's state.
1670
1671($width, $height) = $browser->{window}->get_default_size();
1672$browser->{window}->resize($width, $height);
1673$browser->{browser_hpaned}->set_position(300);
1674$browser->{tagged_checkbutton}->set_active(FALSE);
1675$browser->{appbar}->set_progress_percentage(0);
1676$browser->{appbar}->clear_stack();
1677$browser->{branch_combo_details}->{preset} = 0;
1678$browser->{revision_combo_details}->{preset} = 0;
1679$browser->{directory_combo_details}->{preset} = 0;
1680$browser->{file_being_viewed_preset_value} = "";
1681$browser->{show_line_numbers_togglebutton}->set_active(FALSE);
1682$browser->{file_view_sv}->set_show_line_numbers(FALSE);
1683&{$browser->{update_handler}}($browser, ALL_CHANGED);
1684$browser->{window}->show_all();
1685
1686# Now update with the details of the specified database.
1687
1688if (defined($mtn))
1689{
1690 $browser->{mtn} = $mtn;
1691 &{$browser->{update_handler}}($browser, DATABASE_CHANGED);
1692}
1693
1694 }
1695
1696 # Now deal with any presetting that is required.
1697
1698 if (defined($branch))
1699 {
1700local $browser->{in_cb} = 1;
1701$browser->{branch_combo_details}->{preset} = 1;
1702$browser->{branch_combo_details}->{value} = $branch;
1703if (defined($revision_id))
1704{
1705 $browser->{revision_combo_details}->{preset} = 1;
1706 $browser->{revision_combo_details}->{value} = $revision_id;
1707 if (defined($directory))
1708 {
1709$browser->{directory_combo_details}->{preset} = 1;
1710$browser->{directory_combo_details}->{value} = $directory;
1711if (defined($file))
1712{
1713 $browser->{file_being_viewed_preset_value} = $file;
1714}
1715 }
1716}
1717&{$browser->{update_handler}}($browser, ALL_CHANGED);
1718 }
1719
1720 # Make sure that the branch comboboxentry has the focus and not the bonobo
1721 # dock.
1722
1723 $browser->{branch_comboboxentry}->child()->grab_focus();
1724 $browser->{branch_comboboxentry}->child()->set_position(-1);
1725
1726 return $browser;
1727
1728}
1729#
1730##############################################################################
1731#
1732# Routine - update_browser_state
1733#
1734# Description - Update the display of the specified browser instance
1735# according to the specified state.
1736#
1737# Data - $browser : The browser instance that is to have its state
1738# updated.
1739# $changed : What the user has changed.
1740#
1741##############################################################################
1742
1743
1744
1745sub update_browser_state($$)
1746{
1747
1748 my($browser, $changed) = @_;
1749
1750 my $wm = WindowManager->instance();
1751
1752 $wm->make_busy($browser, 1);
1753 $browser->{appbar}->push($browser->{appbar}->get_status()->get_text());
1754 $wm->update_gui();
1755
1756 # The database has changed.
1757
1758 if ($changed == DATABASE_CHANGED)
1759 {
1760
1761my $db_name;
1762
1763if (! defined($browser->{mtn}))
1764{
1765
1766 # Disable the browser as no database is associated with it.
1767
1768 $browser->{close_toolbutton}->set_sensitive(FALSE);
1769 $browser->{properties_toolbutton}->set_sensitive(FALSE);
1770 $browser->{main_vbox}->set_sensitive(FALSE);
1771 set_label_value($browser->{database_name_value_label}, "");
1772
1773}
1774else
1775{
1776
1777 # Enable the browser as there is a database associated with it.
1778
1779 $browser->{close_toolbutton}->set_sensitive(TRUE);
1780 $browser->{properties_toolbutton}->set_sensitive(TRUE);
1781 $browser->{main_vbox}->set_sensitive(TRUE);
1782 if (! defined($browser->{mtn}->get_db_name()))
1783 {
1784$browser->{mtn}->get_option(\$db_name, "database");
1785set_label_value($browser->{database_name_value_label},
1786__x("<WorkSpace> ({database_name})",
1787 database_name => $db_name));
1788 }
1789 else
1790 {
1791set_label_value($browser->{database_name_value_label},
1792$browser->{mtn}->get_db_name());
1793 }
1794
1795 # Make sure that the branch comboboxentry has the focus and not the
1796 # bonobo dock.
1797
1798 $browser->{branch_comboboxentry}->child()->grab_focus();
1799 $browser->{branch_comboboxentry}->child()->set_position(-1);
1800
1801}
1802
1803 }
1804
1805 # The list of available branches has changed.
1806
1807 if ($changed & BRANCH)
1808 {
1809
1810my @branch_list;
1811
1812# Reset the branch selection.
1813
1814$browser->{branch_combo_details}->{completion} = undef;
1815if ($browser->{branch_combo_details}->{preset})
1816{
1817 $browser->{branch_combo_details}->{complete} = 1;
1818 $browser->{branch_combo_details}->{preset} = 0;
1819}
1820else
1821{
1822 $browser->{branch_combo_details}->{complete} = 0;
1823 $browser->{branch_combo_details}->{value} = "";
1824}
1825
1826# Get the new list of branches.
1827
1828$browser->{appbar}->set_status(__("Fetching branch list"));
1829$wm->update_gui();
1830$browser->{mtn}->branches(\@branch_list) if (defined($browser->{mtn}));
1831$browser->{branch_combo_details}->{list} = \@branch_list;
1832
1833# Update the branch list combobox.
1834
1835$browser->{appbar}->set_status(__("Populating branch list"));
1836$wm->update_gui();
1837my $counter = 1;
1838$browser->{branch_comboboxentry}->get_model()->clear();
1839foreach my $branch (@branch_list)
1840{
1841 $browser->{branch_comboboxentry}->append_text($branch);
1842 if (($counter % 10) == 0)
1843 {
1844$browser->{appbar}->set_progress_percentage
1845 ($counter / scalar(@branch_list));
1846$wm->update_gui();
1847 }
1848 ++ $counter;
1849}
1850$browser->{appbar}->set_progress_percentage(1);
1851$wm->update_gui();
1852$browser->{branch_comboboxentry}->child()->
1853 set_text($browser->{branch_combo_details}->{value});
1854$browser->{branch_comboboxentry}->child()->set_position(-1);
1855$browser->{appbar}->set_progress_percentage(0);
1856$browser->{appbar}->set_status("");
1857$wm->update_gui();
1858
1859 }
1860
1861 # The list of available revisions has changed.
1862
1863 if ($changed & REVISION)
1864 {
1865
1866my @revision_list;
1867
1868# If auto-selection of the head revision is wanted by the user and it
1869# is appropriate then preset the revision id.
1870
1871if ($user_preferences->{auto_select_head}
1872 && ! $browser->{tagged_checkbutton}->get_active()
1873 && $browser->{branch_combo_details}->{complete}
1874 && ! $browser->{revision_combo_details}->{preset})
1875{
1876 my @revision_ids;
1877 $browser->{appbar}->set_status(__("Auto selecting head revision"));
1878 $wm->update_gui();
1879 $browser->{mtn}->select
1880(\@revision_ids,
1881 "h:" . $browser->{branch_combo_details}->{value});
1882 if (scalar(@revision_ids) == 1)
1883 {
1884$browser->{revision_combo_details}->{preset} = 1;
1885$browser->{revision_combo_details}->{value} = $revision_ids[0];
1886$browser->{tagged_checkbutton}->set_active(FALSE);
1887 }
1888 elsif (scalar(@revision_ids) > 1)
1889 {
1890my $message;
1891$message =
1892 __x("The `{branch}' branch has multiple heads\nand "
1893 . "<i>Auto select head revision</i> is switched "
1894 . "on.\nPlease manually select the revision.\n"
1895 . "The head revision ids are:",
1896branch => $browser->{branch_combo_details}->{value});
1897foreach my $item (@revision_ids)
1898{
1899 $message .= "\n" . Glib::Markup::escape_text($item);
1900}
1901my $dialog = Gtk2::MessageDialog->new_with_markup
1902 ($browser->{window}, ["modal"], "info", "close", $message);
1903$wm->allow_input(sub { $dialog->run(); });
1904$dialog->destroy();
1905 }
1906 $browser->{appbar}->set_status("");
1907 $wm->update_gui();
1908}
1909
1910# Reset the revision selection.
1911
1912$browser->{revision_combo_details}->{completion} = undef;
1913if ($browser->{revision_combo_details}->{preset})
1914{
1915 $browser->{revision_combo_details}->{complete} = 1;
1916 $browser->{revision_combo_details}->{preset} = 0;
1917}
1918else
1919{
1920 $browser->{revision_combo_details}->{complete} = 0;
1921 $browser->{revision_combo_details}->{value} = "";
1922}
1923
1924# Get the new list of revisions.
1925
1926if ($browser->{branch_combo_details}->{complete})
1927{
1928 $browser->{appbar}->set_status(__("Fetching revision list"));
1929 $wm->update_gui();
1930 get_branch_revisions($browser->{mtn},
1931 $browser->{branch_combo_details}->{value},
1932 $browser->{tagged_checkbutton}->get_active(),
1933 $browser->{appbar},
1934 \@revision_list);
1935}
1936$browser->{revision_combo_details}->{list} = \@revision_list;
1937
1938# Update the revision list combobox.
1939
1940$browser->{appbar}->set_progress_percentage(0);
1941$browser->{appbar}->set_status(__("Populating revision list"));
1942$wm->update_gui();
1943my $counter = 1;
1944$browser->{revision_comboboxentry}->get_model()->clear();
1945foreach my $revision (@revision_list)
1946{
1947 $browser->{revision_comboboxentry}->append_text($revision);
1948 if (($counter % 10) == 0)
1949 {
1950$browser->{appbar}->set_progress_percentage
1951 ($counter / scalar(@revision_list));
1952$wm->update_gui();
1953 }
1954 ++ $counter;
1955}
1956$browser->{appbar}->set_progress_percentage(1);
1957$wm->update_gui();
1958$browser->{revision_comboboxentry}->child()->
1959 set_text($browser->{revision_combo_details}->{value});
1960$browser->{appbar}->set_progress_percentage(0);
1961$browser->{appbar}->set_status("");
1962$wm->update_gui();
1963
1964 }
1965
1966 # The list of available files and directories has changed.
1967
1968 if ($changed & DIRECTORY)
1969 {
1970
1971my(@directory_list,
1972 @manifest_list);
1973
1974# Reset the directory combo.
1975
1976$browser->{directory_combo_details}->{completion} = undef;
1977if ($browser->{directory_combo_details}->{preset})
1978{
1979 $browser->{directory_combo_details}->{complete} = 1;
1980 $browser->{directory_combo_details}->{preset} = 0;
1981}
1982else
1983{
1984 $browser->{directory_combo_details}->{complete} = 1;
1985 $browser->{directory_combo_details}->{value} = "";
1986}
1987set_label_value($browser->{date_value_label}, "");
1988set_label_value($browser->{author_value_label}, "");
1989set_label_value($browser->{change_log_value_label}, "");
1990
1991# Reset the name of the file being viewed.
1992
1993$browser->{file_being_viewed} = {};
1994
1995# Get the new manifest.
1996
1997$browser->{appbar}->set_status(__("Fetching manifest"));
1998$wm->update_gui();
1999if ($browser->{revision_combo_details}->{complete})
2000{
2001 my @revision_ids;
2002 get_revision_ids($browser, \@revision_ids);
2003 if (scalar(@revision_ids) > 1)
2004 {
2005my $message;
2006$message =
2007 __x("The `{tag_name}' tag is not unique on this branch.\n"
2008 . "Please either select the revision by its id\n"
2009 . "or use the <i>Advanced Find</i> feature.\n"
2010 . "The matching revision ids are:",
2011tag_name =>
2012 $browser->{revision_combo_details}->{value});
2013foreach my $item (@revision_ids)
2014{
2015 $message .= "\n" . Glib::Markup::escape_text($item);
2016}
2017my $dialog = Gtk2::MessageDialog->new_with_markup
2018 ($browser->{window}, ["modal"], "info", "close", $message);
2019$wm->allow_input(sub { $dialog->run(); });
2020$dialog->destroy();
2021$browser->{revision_combo_details}->{complete} = 0;
2022$browser->{revision_combo_details}->{value} = "";
2023$browser->{revision_comboboxentry}->child()->set_text("");
2024 }
2025 else
2026 {
2027my($author,
2028 @certs_list,
2029 $change_log,
2030 $date);
2031$browser->{mtn}->get_manifest_of(\@manifest_list,
2032 $revision_ids[0]);
2033$browser->{mtn}->certs(\@certs_list, $revision_ids[0]);
2034$author = $change_log = $date = "";
2035foreach my $cert (@certs_list)
2036{
2037 if ($cert->{name} eq "author")
2038 {
2039$author = $cert->{value};
2040 }
2041 elsif ($cert->{name} eq "changelog")
2042 {
2043$change_log = $cert->{value};
2044$change_log =~ s/\s+$//s;
2045 }
2046 elsif ($cert->{name} eq "date")
2047 {
2048$date = $cert->{value};
2049$date =~ s/T/ /;
2050 }
2051}
2052set_label_value($browser->{date_value_label}, $date);
2053set_label_value($browser->{author_value_label}, $author);
2054set_label_value($browser->{change_log_value_label},
2055$change_log);
2056 }
2057}
2058$browser->{manifest} = \@manifest_list;
2059
2060# Generate a simple list of directories for auto completion.
2061
2062$browser->{appbar}->set_progress_percentage(0.5);
2063$wm->update_gui();
2064foreach my $item (@manifest_list)
2065{
2066 push(@directory_list, $item->{name})
2067if ($item->{type} eq "directory");
2068}
2069$browser->{directory_combo_details}->{list} = \@directory_list;
2070$browser->{appbar}->set_progress_percentage(1);
2071$wm->update_gui();
2072
2073# Update the directory list combobox.
2074
2075$browser->{appbar}->set_progress_percentage(0);
2076$browser->{appbar}->set_status(__("Populating directory list"));
2077$wm->update_gui();
2078my $counter = 1;
2079$browser->{directory_comboboxentry}->get_model()->clear();
2080foreach my $item (@directory_list)
2081{
2082 $browser->{directory_comboboxentry}->append_text($item);
2083 if (($counter % 10) == 0)
2084 {
2085$browser->{appbar}->set_progress_percentage
2086 ($counter / scalar(@directory_list));
2087$wm->update_gui();
2088 }
2089 ++ $counter;
2090}
2091$browser->{appbar}->set_progress_percentage(1);
2092$wm->update_gui();
2093$browser->{directory_comboboxentry}->child()->
2094 set_text($browser->{directory_combo_details}->{value});
2095$browser->{appbar}->set_progress_percentage(0);
2096$browser->{appbar}->set_status("");
2097foreach my $widget (@{$browser->{revision_sensitive_group}})
2098{
2099 $widget->
2100set_sensitive($browser->{revision_combo_details}->{complete}
2101 ? TRUE : FALSE);
2102}
2103$wm->update_gui();
2104
2105 }
2106
2107 # The list of displayed files and directories has changed.
2108
2109 if ($changed & DIRECTORY_VIEW)
2110 {
2111
2112my($author,
2113 $counter,
2114 @directory_entry_list,
2115 $last_update,
2116 @revision_ids,
2117 $taking_our_time);
2118
2119# Reset the manifest tree view.
2120
2121$browser->{manifest_liststore}->clear();
2122
2123# If we are currently at a valid directory then get its contents and
2124# enable the find files button.
2125
2126if ($browser->{revision_combo_details}->{complete}
2127 && $browser->{directory_combo_details}->{complete})
2128{
2129 get_dir_contents($browser->{directory_combo_details}->{value},
2130 $browser->{manifest},
2131 \@directory_entry_list);
2132 $browser->{find_files_button}->set_sensitive(TRUE);
2133}
2134else
2135{
2136 $browser->{find_files_button}->set_sensitive(FALSE);
2137}
2138
2139# Disable the directory up button if we are already at the top level,
2140# otherwise make sure it is enabled.
2141
2142$browser->{directory_up_button}->set_sensitive
2143 (($browser->{directory_combo_details}->{value} eq "")
2144 ? FALSE : TRUE);
2145
2146# Update the directory tree view.
2147
2148$browser->{appbar}->set_status(__("Populating file details"));
2149$counter = 1;
2150$taking_our_time = 0;
2151get_revision_ids($browser, \@revision_ids);
2152foreach my $item (@directory_entry_list)
2153{
2154
2155 # Get the latest modification time and the author if that is what
2156 # the user wants and the entry is a file (caching the result in the
2157 # manifest for future reference if we have to work it out).
2158
2159 if ($browser->{show_file_details}
2160&& $item->{manifest_entry}->{type} eq "file")
2161 {
2162if (! exists($item->{manifest_entry}->{author}))
2163{
2164 $taking_our_time = 1;
2165 cache_extra_file_info($browser->{mtn},
2166 $revision_ids[0],
2167 $item->{manifest_entry});
2168}
2169$author = $item->{manifest_entry}->{author};
2170$last_update = $item->{manifest_entry}->{last_update};
2171$last_update =~ s/T/ /;
2172 }
2173 else
2174 {
2175$author = "";
2176$last_update = "";
2177 }
2178
2179 # Put the entry into the liststore.
2180
2181 $browser->{manifest_liststore}->
2182set($browser->{manifest_liststore}->append(),
2183 MLS_ICON_COLUMN,
2184 ($item->{manifest_entry}->{type} eq "directory")
2185 ? "gtk-open" : "gtk-file",
2186 MLS_NAME_COLUMN, $item->{name},
2187 MLS_DATE_COLUMN, $last_update,
2188 MLS_AUTHOR_COLUMN, $author,
2189 MLS_MANIFEST_ENTRY_COLUMN, $item->{manifest_entry});
2190
2191 if ($browser->{file_being_viewed_preset_value} ne ""
2192&& $browser->{file_being_viewed_preset_value} eq $item->{name})
2193 {
2194$browser->{file_being_viewed} =
2195 {short_name => $item->{name},
2196 manifest_entry => $item->{manifest_entry}};
2197$browser->{file_being_viewed_preset_value} = "";
2198 }
2199
2200 if ($taking_our_time && ($counter % 10) == 0)
2201 {
2202$browser->{appbar}->set_progress_percentage
2203 ($counter / scalar(@directory_entry_list));
2204$wm->update_gui();
2205 }
2206 ++ $counter;
2207
2208}
2209if ($taking_our_time)
2210{
2211 $browser->{appbar}->set_progress_percentage(1);
2212 $wm->update_gui();
2213}
2214
2215$browser->{manifest_browser_treeview}->scroll_to_point(0, 0)
2216 if ($browser->{manifest_browser_treeview}->realized());
2217
2218$browser->{appbar}->set_progress_percentage(0);
2219$browser->{appbar}->set_status("");
2220$wm->update_gui();
2221
2222 }
2223
2224 # The displayed file contents has changed.
2225
2226 if ($changed & DISPLAY_OF_FILE)
2227 {
2228
2229# Load up the selected file's contents into the file viewer.
2230
2231if (exists($browser->{file_being_viewed}->{manifest_entry}))
2232{
2233
2234 my $manifest_entry =
2235$browser->{file_being_viewed}->{manifest_entry};
2236
2237 # Only do anything if the selected file has changed.
2238
2239 if ($browser->{file_id_value_label}->get_text()
2240ne $manifest_entry->{file_id})
2241 {
2242
2243my($last_update,
2244 $textual_data);
2245
2246# Enable the file buttons, keeping the ones relating to text
2247# files disabled, and reset any associated find text window.
2248
2249$browser->{file_button_vbox}->set_sensitive(TRUE);
2250foreach my $widget (@{$browser->{text_file_sensitive_group}})
2251{
2252 $widget->set_sensitive(FALSE);
2253}
2254reset_find_text($browser->{file_view_sv});
2255enable_find_text($browser->{file_view_sv}, 0);
2256
2257# Display the selected file's contents.
2258
2259display_file($browser, \$textual_data);
2260
2261# If we have just displayed a text file then enable the file
2262# buttons applicable to text files and enable any associated
2263# find text window that may be displayed.
2264
2265if ($textual_data)
2266{
2267 foreach my $widget
2268(@{$browser->{text_file_sensitive_group}})
2269 {
2270$widget->set_sensitive(TRUE);
2271 }
2272 enable_find_text($browser->{file_view_sv}, 1);
2273}
2274
2275# Update the file details labels.
2276
2277if (! exists($manifest_entry->{last_changed_revision}))
2278{
2279 my @revision_ids;
2280 get_revision_ids($browser, \@revision_ids);
2281 cache_extra_file_info($browser->{mtn},
2282 $revision_ids[0],
2283 $manifest_entry);
2284}
2285$last_update = $manifest_entry->{last_update};
2286$last_update =~ s/T/ /;
2287set_label_value($browser->{file_name_value_label},
2288$manifest_entry->{name});
2289set_label_value($browser->{file_id_value_label},
2290$manifest_entry->{file_id});
2291set_label_value($browser->{last_update_value_label},
2292$last_update);
2293set_label_value($browser->{file_revision_id_value_label},
2294$manifest_entry->{last_changed_revision});
2295 }
2296
2297}
2298else
2299{
2300
2301 # Reset the file view buffer and the associated find text window.
2302
2303 $browser->{file_button_vbox}->set_sensitive(FALSE);
2304 $browser->{file_view_svbuffer}->
2305place_cursor($browser->{file_view_svbuffer}->get_start_iter());
2306 $browser->{file_view_svbuffer}->set_text("");
2307 $browser->{file_view_svbuffer}->set("highlight", FALSE);
2308 set_label_value($browser->{file_name_value_label}, "");
2309 set_label_value($browser->{file_id_value_label}, "");
2310 set_label_value($browser->{last_update_value_label}, "");
2311 set_label_value($browser->{file_revision_id_value_label}, "");
2312 enable_find_text($browser->{file_view_sv}, 0);
2313
2314}
2315
2316 }
2317
2318 $browser->{appbar}->pop();
2319 $wm->make_busy($browser, 0);
2320
2321}
2322#
2323##############################################################################
2324#
2325# Routine - display_file
2326#
2327# Description - Display the currenty selected file in the sourceview
2328# textview.
2329#
2330# Data - $browser : The browser instance that is to display the
2331# file.
2332# $text_data : A reference to a variable that is to contain a
2333# boolean indicator as to whether the displayed
2334# file contains textual or binary data.
2335#
2336##############################################################################
2337
2338
2339
2340sub display_file($$)
2341{
2342
2343 my($browser, $textual_data) = @_;
2344
2345 my($contents,
2346 $iter,
2347 $lang,
2348 $mime_details,
2349 $mime_type);
2350
2351 $$textual_data = 0;
2352
2353 # Reset the file view buffer.
2354
2355 $browser->{file_view_svbuffer}->
2356place_cursor($browser->{file_view_svbuffer}->get_start_iter());
2357 $browser->{file_view_svbuffer}->set_text("");
2358 $browser->{file_view_svbuffer}->set("highlight", FALSE);
2359
2360 # Get contents.
2361
2362 $browser->{mtn}->get_file(\$contents,
2363 $browser->{file_being_viewed}->{manifest_entry}->
2364 {file_id});
2365
2366 # Try and work out the MIME type.
2367
2368 determine_mime_type($browser->{file_being_viewed}->{short_name},
2369\$contents,
2370\$mime_type,
2371\$mime_details);
2372
2373 # Only attempt to render the file's contents if requested to do so.
2374
2375 if (! defined($mime_type)
2376|| (defined($mime_details) && ! $mime_details->{display_internally}))
2377 {
2378
2379# The user doesn't want to display this type of file.
2380
2381$browser->{file_view_svbuffer}->
2382 set_text("<"
2383 . (defined($mime_type)
2384? $mime_type : __("Unknown Contents"))
2385 . ">");
2386
2387 }
2388 else
2389 {
2390
2391# The user wants to display this type of file.
2392
2393# Image/non-image data?
2394
2395if ($mime_type =~ m/^image\/.+$/)
2396{
2397
2398 # Image data.
2399
2400 my $exception;
2401 my $loader = Gtk2::Gdk::PixbufLoader->new();
2402 eval
2403 {
2404$loader->write($contents);
2405 };
2406 $exception = $@;
2407 eval
2408 {
2409$loader->close();
2410 };
2411 $exception = $@ if ($@ ne "");
2412 if ($exception eq "")
2413 {
2414$browser->{file_view_svbuffer}->insert_pixbuf
2415 ($browser->{file_view_svbuffer}->get_start_iter(),
2416 $loader->get_pixbuf());
2417 }
2418 else
2419 {
2420$browser->{file_view_svbuffer}->
2421 set_text("<" . $mime_type . ">");
2422 }
2423
2424}
2425else
2426{
2427
2428 # Non-image data.
2429
2430 # Binary/text data?
2431
2432 if (data_is_binary(\$contents))
2433 {
2434
2435# Binary data.
2436
2437# We have been asked to display this data. The only thing we
2438# can do is to hex dump it out.
2439
2440$browser->{file_view_svbuffer}->
2441 set_text("<" . $mime_type . "> " . __("Hex dump:\n"));
2442$browser->{file_view_svbuffer}->
2443 insert($browser->{file_view_svbuffer}->get_end_iter(),
2444 ${hex_dump(\$contents)});
2445
2446 }
2447 else
2448 {
2449
2450# Text data.
2451
2452$$textual_data = 1;
2453
2454# Enable syntax highlighting if the user wants it on and it is
2455# available for this type of file.
2456
2457if ((! defined($mime_details)
2458 || (defined($mime_details)
2459 && $mime_details->{syntax_highlight}))
2460 && defined($lang =
2461 $browser->{file_view_svlangmgr}->
2462 get_language_from_mime_type($mime_type)))
2463{
2464 $browser->{file_view_svbuffer}->set("highlight", TRUE);
2465 $browser->{file_view_svbuffer}->set_language($lang);
2466}
2467
2468# Load in the contents.
2469
2470$browser->{file_view_svbuffer}->set_text($contents);
2471
2472 }
2473
2474 # Delete the trailing newline.
2475
2476 $iter = $browser->{file_view_svbuffer}->get_end_iter();
2477 $browser->{file_view_svbuffer}->delete
2478($iter, $browser->{file_view_svbuffer}->get_end_iter())
2479if ($iter->backward_char());
2480
2481}
2482
2483 }
2484
2485 # Scroll back up to the top left.
2486
2487 $browser->{file_view_svbuffer}->
2488place_cursor($browser->{file_view_svbuffer}->get_start_iter());
2489 if ($browser->{file_view_scrolledwindow}->realized())
2490 {
2491$browser->{file_view_scrolledwindow}->get_vadjustment()->set_value(0);
2492$browser->{file_view_scrolledwindow}->get_hadjustment()->set_value(0);
2493 }
2494
2495}
2496#
2497##############################################################################
2498#
2499# Routine - determine_mime_type
2500#
2501# Description - Given a file name and its contents, determine and return
2502# its MIME type and the related entry in the MIME details
2503# table.
2504#
2505# Data - $file_name : The name of the file.
2506# $contents : A reference to a variable containing the
2507# contents of the file.
2508# $mime_type : A reference to a variable that is to
2509# contain the MIME type. This will be undef
2510# if no match can be found.
2511# $mime_details : A reference to a variable that is to
2512# contain a refernce to the related entry in
2513# the MIME information table. This will be
2514# undef if no match can be found.
2515#
2516##############################################################################
2517
2518
2519
2520sub determine_mime_type($$$$)
2521{
2522
2523 my($file_name, $contents, $mime_type, $mime_details) = @_;
2524
2525 $$mime_type = $$mime_details = undef;
2526
2527 # Try and work out the MIME type through file name pattern matching.
2528
2529 foreach my $entry (@$mime_match_table)
2530 {
2531if ($file_name =~ m/$entry->{re}/)
2532{
2533 $$mime_type = $entry->{details}->{name};
2534 $$mime_details = $entry->{details};
2535 return;
2536}
2537 }
2538
2539 # If that didn't work then try determining the MIME type based upon its
2540 # contents.
2541
2542 $$mime_type = Gnome2::VFS->get_mime_type_for_data($contents);
2543
2544 # If we now have a MIME type then do another lookup with it but by matching
2545 # MIME type names and not file name patterns.
2546
2547 if (defined($$mime_type))
2548 {
2549foreach my $entry (@{$user_preferences->{mime_table}})
2550{
2551 if ($$mime_type eq $entry->{name})
2552 {
2553$$mime_details = $entry;
2554return;
2555 }
2556}
2557 }
2558
2559}
2560#
2561##############################################################################
2562#
2563# Routine - setup_mtn_object
2564#
2565# Description - Sets up a new Monotone::AutomateStdio object so that it can
2566# be used in this application.
2567#
2568# Data - $mtn : The Monotone::AutomateStdio object that is to be
2569# setup.
2570# $parent : The parent window widget that is to be used for
2571# assorted dialog windows that may appear.
2572#
2573##############################################################################
2574
2575
2576
2577sub setup_mtn_object($$)
2578{
2579
2580 my($mtn, $parent) = @_;
2581
2582 $mtn->register_db_locked_handler(\&mtn_db_locked_handler, $parent);
2583 if ($user_preferences->{show_suspended})
2584 {
2585if ($mtn->can(MTN_IGNORE_SUSPEND_CERTS))
2586{
2587 $mtn->ignore_suspend_certs(1);
2588}
2589else
2590{
2591 my $dialog = Gtk2::MessageDialog->
2592new($parent,
2593 ["modal"],
2594 "info",
2595 "close",
2596 __("Your version of Monotone does not support\n"
2597 . "suspend certificates. I will adjusted your\n"
2598 . "preferences accordingly."));
2599 $dialog->run();
2600 $dialog->destroy();
2601 $user_preferences->{show_suspended} = 0;
2602 eval
2603 {
2604save_preferences($user_preferences);
2605 };
2606 if ($@ ne "")
2607 {
2608chomp($@);
2609my $dialog = Gtk2::MessageDialog->new
2610 (undef,
2611 ["modal"],
2612 "warning",
2613 "close",
2614 __("Your preferences could not be saved:\n") . $@);
2615$dialog->run();
2616$dialog->destroy();
2617 }
2618}
2619 }
2620
2621}
2622#
2623##############################################################################
2624#
2625# Routine - mtn_db_locked_handler
2626#
2627# Description - This routine is called when ever a locked database
2628# condition is detected.
2629#
2630# Data - $mtn : The Monotone::AutomateStdio object that
2631# encountered the locked database.
2632# $parent : The parent window for any dialogs that are
2633# to be displayed.
2634# Return Value : True if the command is to be retried,
2635# otherwise false if the locked database
2636# should be reported as an error.
2637#
2638##############################################################################
2639
2640
2641
2642sub mtn_db_locked_handler($$)
2643{
2644
2645 my($mtn, $parent) = @_;
2646
2647 my $dialog;
2648 my $wm = WindowManager->instance();
2649
2650 $dialog = Gtk2::MessageDialog->new
2651($parent,
2652 ["modal"],
2653 "info",
2654 "ok",
2655 __("The Monotone database is currently locked, please\n"
2656 . "dismiss this dialog when this is no longer the case."));
2657 $wm->allow_input(sub { $dialog->run(); });
2658 $dialog->destroy();
2659
2660 return 1;
2661
2662}
2663#
2664##############################################################################
2665#
2666# Routine - mtn_error_handler
2667#
2668# Description - This routine is called when ever there is a problem with
2669# Monotone.
2670#
2671# Data - $severity : The severity of the error.
2672# $message : The error message.
2673#
2674##############################################################################
2675
2676
2677
2678sub mtn_error_handler($$)
2679{
2680
2681 my($severity, $message) = @_;
2682
2683 my $dialog;
2684 my $wm = WindowManager->instance();
2685
2686 if ($severity == MTN_SEVERITY_WARNING)
2687 {
2688$dialog = Gtk2::MessageDialog->new_with_markup
2689 (undef,
2690 ["modal"],
2691 "warning",
2692 "close",
2693 __x("Problem with monotone request, got:\n"
2694 . "<b><i>{error_message}</i></b>\n"
2695 . "This should not be happening!",
2696 error_message => Glib::Markup::escape_text($message)));
2697$wm->allow_input(sub { $dialog->run(); });
2698$dialog->destroy();
2699$wm->reset_state();
2700die($message);
2701 }
2702 else
2703 {
2704$dialog = Gtk2::MessageDialog->new_with_markup
2705 (undef,
2706 ["modal"],
2707 "error",
2708 "close",
2709 __x("Monotone process unexpectedly exiting with:\n"
2710 . "<b><i>{error_message}</i></b>\n"
2711 . "This is fatal, I am going to exit.",
2712 error_message => Glib::Markup::escape_text($message)));
2713$wm->allow_input(sub { $dialog->run(); });
2714$dialog->destroy();
2715Gtk2->main_quit() unless (Gtk2->main_level() == 0);
2716die($message);
2717 }
2718
2719}
2720#
2721##############################################################################
2722#
2723# Routine - sigchld_handler
2724#
2725# Description - This routine is called when ever a subprocess exits.
2726#
2727# Data - None.
2728#
2729##############################################################################
2730
2731
2732
2733sub sigchld_handler()
2734{
2735
2736 my($pid,
2737 $status);
2738
2739 my $wm = WindowManager->instance();
2740
2741 while (($pid = waitpid(-1, WNOHANG)) > 0)
2742 {
2743$status = $?;
2744if (WIFEXITED($status) || WIFSIGNALED($status))
2745{
2746
2747 # If it is an mtn process then close down the relevant object so
2748 # that it will automatically restart when needed.
2749
2750 WindowManager->instance()->cond_find
2751(undef,
2752 sub {
2753 my $instance = $_[0];
2754 if (exists($instance->{mtn}) && defined($instance->{mtn})
2755 && $instance->{mtn}->get_pid() == $pid)
2756 {
2757 my $message;
2758 $instance->{mtn}->closedown();
2759 if (WIFSIGNALED($status))
2760 {
2761 $message = __x("terminated by signal {number}",
2762 number => WTERMSIG($status));
2763 }
2764 else
2765 {
2766 $message = __x("exited with status {number}",
2767 number => WEXITSTATUS($status));
2768 }
2769 my $dialog = Gtk2::MessageDialog->new
2770 (undef,
2771 ["modal"],
2772 "warning",
2773 "close",
2774 __x("The mtn subprocess just unexpectedly\n"
2775 . "exited ({error_message}).\n"
2776 . "This should not be happening!\n"
2777 . "It will be restarted when needed.",
2778 error_message => $message));
2779 $wm->allow_input(sub { $dialog->run(); });
2780 $dialog->destroy();
2781 $wm->reset_state();
2782 return 1;
2783 }
2784 return;
2785 });
2786
2787}
2788 }
2789 warn(__x("waitpid failed: {error_message}", error_message => $!))
2790if ($pid < 0 && $! != ECHILD);
2791
2792}
2793#
2794##############################################################################
2795#
2796# Routine - setup_sigchld_handler
2797#
2798# Description - This routine sets up the handler for SIGCHLD signals.
2799#
2800# Data - $handler - A reference to the SIGCHLD handler routine.
2801#
2802##############################################################################
2803
2804
2805
2806sub setup_sigchld_handler($)
2807{
2808
2809 my $handler = $_[0];
2810
2811 my($reader,
2812 $writer);
2813
2814 # Basically set up a SIGCHLD handler that simply writes a character down an
2815 # anonymous pipe in order to wake up the actual handler that is registered
2816 # with Gtk2 as a file activity handler. This is efficient and safer than
2817 # some alternatives.
2818
2819 pipe($reader, $writer)
2820or die(__x("pipe failed: {error_message}", error_message => $!));
2821 $SIG{CHLD} = sub { syswrite($writer, "\n", 1); };
2822 Gtk2::Helper->add_watch(fileno($reader), "in",
2823 sub {
2824my $buffer;
2825sysread($reader, $buffer, 1);
2826&$handler();
2827return 1; });
2828
2829}

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status