# # # patch "AdvancedFind.pm" # from [4057f374a82a5720b9654a9a3ab11fa5c8e08d7b] # to [9ecb0053b5bb0b7e821572fa740b04a014c9dbc8] # # patch "Globals.pm" # from [a24f4acd16f3849fa802bbfdc496d61483b22ac3] # to [210c3971b3ca55744f8e3ae4c40f42395e326d77] # # patch "History.pm" # from [ee85354b0b069da7197afafbfa22e1307e389ea6] # to [0a0f559ca522cea54e20bdcbf6eccbaf983bb936] # # patch "Preferences.pm" # from [592b6d26869b985da23d12a7cf365c9aeea1fe9a] # to [9582cfdd83a9f9ef8af46a0c1fc17a6d460119d4] # # patch "Utilities.pm" # from [458875b2f3623c910d08ea3aa72d8729b4fec91b] # to [d4930c03d430af5ac4a2d0dcf1cb3d7d5a898c27] # # patch "mtn-browse" # from [e0a9d3e52bf31b902b4b351e04438ec745d22ac6] # to [88920cff112f114139d36a0ba7cecbc14b305bca] # # patch "mtn-browse.glade" # from [7e503af1903439bbf47468d917f692db3bea8ec4] # to [e108526a6ed91bc84238ac6586567a7c53b196ea] # ============================================================ --- AdvancedFind.pm 4057f374a82a5720b9654a9a3ab11fa5c8e08d7b +++ AdvancedFind.pm 9ecb0053b5bb0b7e821572fa740b04a014c9dbc8 @@ -791,45 +791,13 @@ sub update_advanced_find_state($$) { $advanced_find->{appbar}->set_status("Fetching revision list"); gtk2_update(); - - # Get either a list of tags or revision ids depending upon what the - # user has chosen. - - if ($advanced_find->{tagged_checkbutton}->get_active()) - { - my(%dup_list, - @list); - $advanced_find->{mtn}-> - tags(address@hidden, - $advanced_find->{branch_combo_details}->{value}); - $advanced_find->{appbar}->set_progress_percentage(0.5); - gtk2_update(); - foreach my $item (@list) - { - if (! exists($dup_list{$item->{tag}})) - { - push(@revision_list, $item->{tag}); - $dup_list{$item->{tag}} = 1; - } - } - } - else - { - $advanced_find->{mtn}-> - select(address@hidden, - "b:" . $advanced_find->{branch_combo_details}-> - {value}); - $advanced_find->{appbar}->set_progress_percentage(0.33); - gtk2_update(); - $advanced_find->{mtn}->toposort(address@hidden, - @revision_list); - $advanced_find->{appbar}->set_progress_percentage(0.66); - gtk2_update(); - splice(@revision_list, 0, scalar(@revision_list) - 100); - @revision_list = reverse(@revision_list); - } - $advanced_find->{appbar}->set_progress_percentage(1); - gtk2_update(); + get_branch_revisions($advanced_find->{mtn}, + $advanced_find->{branch_combo_details}-> + {value}, + $advanced_find->{tagged_checkbutton}-> + get_active(), + $advanced_find->{appbar}, + address@hidden); } $advanced_find->{revision_combo_details}->{list} = address@hidden; ============================================================ --- Globals.pm a24f4acd16f3849fa802bbfdc496d61483b22ac3 +++ Globals.pm 210c3971b3ca55744f8e3ae4c40f42395e326d77 @@ -96,55 +96,13 @@ our $tmp_dir; our $tmp_dir; -# Text viewable application mime types. +# The user's preferences data. -our @text_viewable_app_mime_types = - qw(postscript - rtf - x-awk - x-cgi - x-csh - x-glade - x-java - x-javascript - x-jbuilder-project - x-perl - x-php - x-python - x-shellscript - x-troff-man - x-troff - xhtml+xml); +our $user_preferences; -# Supported text mime types (used for syntax highlighting). +# The MIME type file name pattern match table. -our @text_mime_types = - ( - { - pattern => qr/.*\.c$/o, - type => "text/x-csrc" - }, - { - pattern => qr/.*\.(C)|(cc)|(cp)|(cpp)|(CPP)|(cxx)|(c\+\+)$/o, - type => "text/x-c++src" - }, - { - pattern => qr/.*\.(h)|(hh)|(H)$/o, - type => "text/x-c++hdr" - }, - { - pattern => qr/.*\.h$/o, - type => "text/x-chdr" - }, - { - pattern => qr/(^[Mm]akefile(\.[^.]+)?)|(.*\.mk)$/o, - type => "text/x-makefile" - }, - { - pattern => qr/.*/o, - type => "text/plain" - } - ); +our $mime_match_table; # ***** PACKAGE INFORMATION ***** @@ -167,11 +125,11 @@ our %EXPORT_TAGS = (constants => [qw(ALL SELECTED_REVISION_CHANGED)], variables => [qw($glade_file $line_image + $mime_match_table $mono_font - @text_mime_types - @text_viewable_app_mime_types $tmp_dir - $tooltips)]); + $tooltips + $user_preferences)]); our @EXPORT = qw(); Exporter::export_ok_tags(qw(constants variables)); our $VERSION = 0.1; ============================================================ --- History.pm ee85354b0b069da7197afafbfa22e1307e389ea6 +++ History.pm 0a0f559ca522cea54e20bdcbf6eccbaf983bb936 @@ -705,8 +705,8 @@ sub compare_revisions($$$;$) gtk2_update(); $instance->{mtn} = $mtn; - $instance->{red_revision_id} = $revision_id_1; - $instance->{green_revision_id} = $revision_id_2; + $instance->{revision_id_1} = $revision_id_1; + $instance->{revision_id_2} = $revision_id_2; # Get Monotone to do the comparison. @@ -792,7 +792,7 @@ sub compare_revisions($$$;$) $instance->{comparison_buffer}->insert_with_tags_by_name ($instance->{comparison_buffer}->get_end_iter(), $line . "\n", - "compare-first-file-info"); + "compare-file-info-1"); # Store the file name and the starting line number so that the user # can later jump straight to it using the file combobox. @@ -811,7 +811,7 @@ sub compare_revisions($$$;$) $instance->{comparison_buffer}->insert_with_tags_by_name ($instance->{comparison_buffer}->get_end_iter(), $line . "\n", - "compare-second-file-info"); + "compare-file-info-2"); } } @@ -835,7 +835,7 @@ sub compare_revisions($$$;$) $instance->{comparison_buffer}->insert_with_tags_by_name ($instance->{comparison_buffer}->get_end_iter(), $line . "\n", - "compare-first-file"); + "compare-file-1"); } # Deal with + change lines. @@ -846,7 +846,7 @@ sub compare_revisions($$$;$) $instance->{comparison_buffer}->insert_with_tags_by_name ($instance->{comparison_buffer}->get_end_iter(), $line . "\n", - "compare-second-file"); + "compare-file-2"); } # Print out the rest. @@ -975,15 +975,15 @@ sub coloured_revision_change_log_button_ # Work out what revision id to use. - if ($widget == $instance->{red_revision_change_log_button}) + if ($widget == $instance->{revision_change_log_1_button}) { - $revision_id = $instance->{red_revision_id}; - $colour = "red"; + $revision_id = $instance->{revision_id_1}; + $colour = "compare-1"; } else { - $revision_id = $instance->{green_revision_id}; - $colour = "green"; + $revision_id = $instance->{revision_id_2}; + $colour = "compare-2"; } # Display the full revision change log. @@ -1240,7 +1240,9 @@ sub get_revision_comparison_window() "file_comparison_combobox", "comparison_textview", "comparison_scrolledwindow", - "red_revision_change_log_button") + "revision_change_log_1_button", + "revision_change_log_1_button_label", + "revision_change_log_2_button_label") { $instance->{$widget} = $instance->{glade}->get_widget($widget); } @@ -1281,6 +1283,17 @@ sub get_revision_comparison_window() create_format_tags($instance->{comparison_buffer}); $instance->{comparison_textview}->modify_font($mono_font); + # Setup the revision log button coloured labels. + + $instance->{revision_change_log_1_button_label}-> + set_markup("Revision Change Log"); + $instance->{revision_change_log_2_button_label}-> + set_markup("Revision Change Log"); + # Register the window for management. $wm->manage($instance, $window_type, $instance->{stop_button}); ============================================================ --- Preferences.pm 592b6d26869b985da23d12a7cf365c9aeea1fe9a +++ Preferences.pm 9582cfdd83a9f9ef8af46a0c1fc17a6d460119d4 @@ -61,10 +61,31 @@ use constant PREFERENCES_FORMAT_VERSION use constant PREFERENCES_FORMAT_VERSION => 1; +# Text viewable application mime types. + +my @text_viewable_app_mime_types = + qw(postscript + rtf + x-awk + x-cgi + x-csh + x-glade + x-java + x-javascript + x-jbuilder-project + x-perl + x-php + x-python + x-shellscript + x-troff-man + x-troff + xhtml+xml); + # ***** FUNCTIONAL PROTOTYPES FOR THIS FILE ***** # Public routines. +sub build_mime_match_table($); sub load_preferences(); sub preferences($); sub save_preferences($); @@ -93,8 +114,10 @@ sub save_preferences_from_gui($); # Description - Displays the preferences dialog window and then lets the # user change the application preferences. # -# Data - $browser : The browser instance that called up the -# preferences dialog window. +# Data - $browser : The browser instance that called up the +# preferences dialog window. +# Return Value : True if the preferences were changed, +# otherwise false if they were left unaltered. # ############################################################################## @@ -171,6 +194,8 @@ sub preferences($) $instance->{file_name_patterns_liststore}->clear(); $instance->{preferences} = undef; + return $instance->{preferences_changed}; + } # ############################################################################## @@ -237,12 +262,12 @@ sub load_preferences() bg => "SteelBlue"}, annotate_text_2 => {fg => "MidnightBlue", bg => "SkyBlue"}, - cmp_revision_1 => {fg => "DarkGreen", + cmp_revision_1 => {fg => "DarkRed", + bg => "MistyRose1", + hl => "IndianRed1"}, + cmp_revision_2 => {fg => "DarkGreen", bg => "DarkSeaGreen1", - hl => "SpringGreen1"}, - cmp_revision_2 => {fg => "DarkRed", - bg => "MistyRose1", - hl => "IndianRed1"}}, + hl => "SpringGreen1"}}, mime_table => $mime_table); } @@ -293,6 +318,46 @@ sub save_preferences($) # ############################################################################## # +# Routine - build_mime_match_table +# +# Description - Build a regular expression pattern matching table for +# working out the MIME type of a file from its file name. +# +# Data - $mime_info_table : A reference to the MIME information +# table on which the pattern matching +# table is to be based. +# Return Value : A reference to the newly created pattern +# matching table. +# +############################################################################## + + + +sub build_mime_match_table($) +{ + + my $mime_info_table = $_[0]; + + my($re_str, + @table); + + foreach my $entry (@$mime_info_table) + { + foreach my $file_glob (@{$entry->{file_name_patterns}}) + { + $re_str = file_glob_to_regexp($file_glob); + push(@table, + {re => qr/$re_str/, + details => $entry}); + } + } + + return address@hidden; + +} +# +############################################################################## +# # Routine - database_browse_button_clicked_cb # # Description - Callback routine called when the user clicks on the browse @@ -315,92 +380,11 @@ sub database_browse_button_clicked_cb($$ return if ($instance->{in_cb}); local $instance->{in_cb} = 1; - my($chooser_dialog, - $done); + my $file_name; - $chooser_dialog = Gtk2::FileChooserDialog->new("Open Database", - $instance->{window}, - "open", - "gtk-cancel" => "cancel", - "gtk-open" => "ok"); + $instance->{database_entry}->set_text($file_name) + if (open_database($instance->{window}, undef, \$file_name)); - do - { - if ($chooser_dialog->run() eq "ok") - { - - my ($err, - $fh, - $file_name, - $mtn); - - $file_name = $chooser_dialog->get_filename(); - - # The user has selected a file. First make sure we can open it for - # reading (I know I could use the -r test but this takes care of - # any other unforeseen access problems as well). - - if (! defined($fh = IO::File->new($file_name, "r"))) - { - my $dialog = Gtk2::MessageDialog->new - ($instance->{window}, - ["modal"], - "warning", - "close", - $! . "."); - $dialog->run(); - $dialog->destroy(); - } - else - { - - $fh->close(); - $fh = undef; - - # Ok it is a readable file, try and open it but deal with any - # errors in a nicer way than normal. - - Monotone::AutomateStdio->register_error_handler("both"); - eval - { - $mtn = Monotone::AutomateStdio->new($file_name); - }; - $err = $@; - Monotone::AutomateStdio->register_error_handler - ("both", \&mtn_error_handler); - if ($err ne "") - { - my $dialog = Gtk2::MessageDialog->new - ($instance->{window}, - ["modal"], - "warning", - "close", - "Not a valid Monotone database."); - $dialog->run(); - $dialog->destroy(); - } - else - { - - # Seems to be ok so update the related entry widget. - - $instance->{database_entry}->set_text($file_name); - $done = 1; - - } - - } - - } - else - { - $done = 1; - } - } - while (! $done); - - $chooser_dialog->destroy(); - } # ############################################################################## @@ -607,7 +591,7 @@ sub add_file_name_pattern_button_clicked foreach my $entry (@{$instance->{preferences}->{mime_table}}) { - if (scalar(grep(/\Q$pattern\E/, @{$entry->{file_name_patterns}})) > 0) + if (grep(/\Q$pattern\E/, @{$entry->{file_name_patterns}}) > 0) { $match = $entry->{name}; last; @@ -755,8 +739,10 @@ sub get_preferences_window($$) "auto_select_checkbutton", "tagged_lists_limit_spinbutton", "tagged_lists_sort_cronologically_radiobutton", + "tagged_lists_sort_by_name_radiobutton", "id_lists_limit_spinbutton", "id_lists_sort_cronologically_radiobutton", + "id_lists_sort_by_id_radiobutton", # Appearance pane widgets. @@ -908,7 +894,7 @@ sub get_preferences_window($$) } $instance->{done} = 0; - $instance->{preferences_changed} = 0; + $instance->{preferences_changed} = undef; $instance->{selected_file_name_pattern} = undef; $instance->{selected_mime_types_entry} = undef; $instance->{selected_mime_types_path} = undef; @@ -947,14 +933,26 @@ sub load_preferences_into_gui($) TRUE : FALSE); $instance->{tagged_lists_limit_spinbutton}-> set_value($instance->{preferences}->{query}->{tagged}->{limit}); - $instance->{tagged_lists_sort_cronologically_radiobutton}-> - set_active($instance->{preferences}->{query}->{tagged}-> - {sort_cronologically} ? TRUE : FALSE); + if ($instance->{preferences}->{query}->{tagged}->{sort_cronologically}) + { + $instance->{tagged_lists_sort_cronologically_radiobutton}-> + set_active(TRUE); + } + else + { + $instance->{tagged_lists_sort_by_name_radiobutton}->set_active(TRUE); + } $instance->{id_lists_limit_spinbutton}-> set_value($instance->{preferences}->{query}->{id}->{limit}); - $instance->{id_lists_sort_cronologically_radiobutton}-> - set_active($instance->{preferences}->{query}->{id}-> - {sort_cronologically} ? TRUE : FALSE); + if ($instance->{preferences}->{query}->{id}->{sort_cronologically}) + { + $instance->{id_lists_sort_cronologically_radiobutton}-> + set_active(TRUE); + } + else + { + $instance->{id_lists_sort_by_id_radiobutton}->set_active(TRUE); + } # Do the appearance pane. @@ -1150,10 +1148,13 @@ sub initialise_mime_info_table() sub initialise_mime_info_table() { - my($pattern, + my($display_internally, + $pattern, $globs_file, $line, %lookup, + $part, + $syntax_highlight, @table, $type); @@ -1183,10 +1184,26 @@ sub initialise_mime_info_table() } else { + $display_internally = $syntax_highlight = 0; + if ($type =~ m/^application\/.+$/o) + { + ($part) = ($type =~ m/^application\/(.+)$/o); + $display_internally = $syntax_highlight = 1 + if (grep(/\Q$part\E/, @text_viewable_app_mime_types) + > 0); + } + elsif ($type =~ m/^image\/.+$/o) + { + $display_internally = 1; + } + elsif ($type =~ m/^text\/.+$/o) + { + $display_internally = $syntax_highlight = 1; + } $lookup{$type} = {name => $type, file_name_patterns => [$pattern], - display_internally => 0, - syntax_highlight => 0, + display_internally => $display_internally, + syntax_highlight => $syntax_highlight, helper_application => ""}; push(@table, $lookup{$type}); } @@ -1334,6 +1351,8 @@ sub file_glob_to_regexp($) } } + $re_text .= "\$"; + return $re_text; } ============================================================ --- Utilities.pm 458875b2f3623c910d08ea3aa72d8729b4fec91b +++ Utilities.pm d4930c03d430af5ac4a2d0dcf1cb3d7d5a898c27 @@ -50,12 +50,16 @@ sub create_format_tags($); sub colour_to_string($); sub create_format_tags($); +sub data_is_binary($); sub generate_revision_report($$$$;$); sub generate_tmp_path($); +sub get_branch_revisions($$$$$); sub get_dir_contents($$$); sub get_revision_ids($$); sub glade_signal_autoconnect($$); sub gtk2_update(); +sub hex_dump($); +sub open_database($$$); sub run_command($@); sub set_label_value($$); # @@ -512,97 +516,321 @@ sub get_dir_contents($$$) # ############################################################################## # -# Routine - get_revision_ids +# Routine - open_database # -# Description - Return the currently selected revision id, whether this is -# specified via a tag or as a revision id. +# Description - Allows the user to select a Monotone Database and then +# opens it, making sure that it is a valid database or +# dealing with the consequences if it isn't. # -# Data - $instance : The window instance. -# $revision_ids : The list of selected revision ids. Normally -# the list will have at most one element but -# may contain more if the tag isn't unique on -# the current branch. +# Data - $parent : The parent window for any dialogs that are +# to be displayed. +# $mtn : A reference to a variable that is to contain +# the newly created Monotone::AutomateStdio +# object. This parameter can be undef if the +# object is not required. +# $file_name : A reference to a variable that is to contain +# the full file name of the selected database. +# This parameter can be undef if the file name +# is not required. +# Return Value : True on success, otherwise false on +# cancellation. # ############################################################################## -sub get_revision_ids($$) +sub open_database($$$) { - my($instance, $revision_ids) = @_; + my($parent, $mtn, $file_name) = @_; - @$revision_ids=(); - return unless ($instance->{revision_combo_details}->{complete}); - if ($instance->{tagged_checkbutton}->get_active()) + my($chooser_dialog, + $done, + $ret_val); + + $chooser_dialog = Gtk2::FileChooserDialog->new("Open Database", + $parent, + "open", + "gtk-cancel" => "cancel", + "gtk-open" => "ok"); + + do { - $instance->{mtn}-> - select($revision_ids, - "t:" . $instance->{revision_combo_details}->{value}); + if ($chooser_dialog->run() eq "ok") + { + + my ($err, + $fh, + $fname, + $mtn_obj); + + $fname = $chooser_dialog->get_filename(); + + # The user has selected a file. First make sure we can open it for + # reading (I know I could use the -r test but this takes care of + # any other unforeseen access problems as well). + + if (! defined($fh = IO::File->new($fname, "r"))) + { + my $dialog = Gtk2::MessageDialog->new + ($parent, + ["modal"], + "warning", + "close", + $! . "."); + $dialog->run(); + $dialog->destroy(); + } + else + { + + $fh->close(); + $fh = undef; + + # Ok it is a readable file, try and open it but deal with any + # errors in a nicer way than normal. + + Monotone::AutomateStdio->register_error_handler("both"); + eval + { + $mtn_obj = Monotone::AutomateStdio->new($fname); + }; + $err = $@; + Monotone::AutomateStdio->register_error_handler + ("both", \&mtn_error_handler); + if ($err ne "") + { + my $dialog = Gtk2::MessageDialog->new + ($parent, + ["modal"], + "warning", + "close", + "Not a valid Monotone database."); + $dialog->run(); + $dialog->destroy(); + } + else + { + + # Seems to be ok so tell the caller. + + $$mtn = $mtn_obj if (defined($mtn)); + $$file_name = $fname if (defined($file_name)); + $done = $ret_val = 1; + + } + + } + + } + else + { + $done = 1; + } } - else - { - push(@$revision_ids, $instance->{revision_combo_details}->{value}); - } + while (! $done); + $chooser_dialog->destroy(); + + return $ret_val; + } # ############################################################################## # -# Routine - glade_signal_autoconnect +# Routine - get_branch_revisions # -# Description - This routine uses the Glade library to connect up all the -# registered signal handlers to their related widgets. +# Description - Get a list of revision ids or tags for the specified branch +# that take into account the user's preferences for ordering +# and the maximum number of revisions to display. # -# Data - $glade : The Glade object describing the widgets that -# are to have their signal handlers -# registered. -# $client_data : The client data that is to be passed into -# each callback routine when it is called. +# Data - $mtn : The Monotone database handle that is to be +# used. +# $branch : The name of the branch that revisions are to +# be found for. +# $tags : True if the list of revisions are to be tags, +# otherwise false if they are to be ids. +# $appbar : If defined, the application progress bar +# widget that is to be updated with the progress +# of this operation. It is assumed that the +# progress is set at 0 and will end up being set +# to 1. +# $revisions : A reference to a list that is to contain the +# resultant list of sorted revision tags or ids. # ############################################################################## -sub glade_signal_autoconnect($$) +sub get_branch_revisions($$$$$) { - my($glade, $client_data) = @_; + my($mtn, $branch, $tags, $appbar, $revisions) = @_; - $glade->signal_autoconnect - (sub { - my($callback_name, $widget, $signal_name, $signal_data, - $connect_object, $after, $user_data) = @_; - my $func = $after ? "signal_connect_after" : "signal_connect"; - $widget->$func($signal_name, - $callback_name, - $connect_object ? $connect_object : $user_data); }, - $client_data); + @$revisions = (); + if ($tags) + { + + my(@certs, + @list, + %seen); + + # Get the list of revision tags. + + $mtn->tags(address@hidden, $branch); + $appbar->set_progress_percentage(0.5) if (defined($appbar)); + gtk2_update(); + + # Dedupe it. + + @list = grep({ ! $seen{$_->{tag}} ++ } @list); + + # Sort it by date if necessary (because it needs to be truncated or + # that's how the user wants it sorted). + + if (($user_preferences->{query}->{tagged}->{limit} > 0 + && scalar(@list) > $user_preferences->{query}->{tagged}->{limit}) + || $user_preferences->{query}->{tagged}->{sort_cronologically}) + { + @list = sort({ + foreach my $rec ($a, $b) + { + if (! exists($rec->{date})) + { + $mtn->certs(address@hidden, + $rec->{revision_id}); + foreach my $cert (@certs) + { + if ($cert->{name} eq "date") + { + $rec->{date} = $cert->{value}; + last; + } + } + } + } + $b->{date} cmp $a->{date}; + } + @list); + } + + # Truncate the list if necessary. + + if ($user_preferences->{query}->{tagged}->{limit} > 0 + && scalar(@list) > $user_preferences->{query}->{tagged}->{limit}) + { + splice(@list, $user_preferences->{query}->{tagged}->{limit}); + } + + # Extract the list of tags. + + @$revisions = map({ $_->{tag} } @list); + + # Sort alphabetically if required. + + @$revisions = sort(@$revisions) + if (! $user_preferences->{query}->{tagged}->{sort_cronologically}); + + } + else + { + + # Get the list of revision ids. + + $mtn->select($revisions, "b:" . $branch); + + # Does it need truncating? + + if ($user_preferences->{query}->{id}->{limit} == 0 + || scalar(@$revisions) + <= $user_preferences->{query}->{id}->{limit}) + { + + # No so simply sort it. + + if ($user_preferences->{query}->{id}->{sort_cronologically}) + { + $appbar->set_progress_percentage(0.33) if (defined($appbar)); + gtk2_update(); + $mtn->toposort($revisions, @$revisions); + $appbar->set_progress_percentage(0.66) if (defined($appbar)); + gtk2_update(); + @$revisions = reverse(@$revisions); + } + else + { + $appbar->set_progress_percentage(0.5) if (defined($appbar)); + gtk2_update(); + @$revisions = sort(@$revisions); + } + + } + else + { + + # Yes so truncate and then sort it. + + $appbar->set_progress_percentage(0.33) if (defined($appbar)); + gtk2_update(); + $mtn->toposort($revisions, @$revisions); + $appbar->set_progress_percentage(0.66) if (defined($appbar)); + splice(@$revisions, + 0, + scalar(@$revisions) + - $user_preferences->{query}->{id}->{limit}); + if ($user_preferences->{query}->{id}->{sort_cronologically}) + { + @$revisions = reverse(@$revisions); + } + else + { + @$revisions = sort(@$revisions); + } + + } + + } + + $appbar->set_progress_percentage(1) if (defined($appbar)); + gtk2_update(); + } # ############################################################################## # -# Routine - gtk2_update +# Routine - get_revision_ids # -# Description - Process all outstanding Gtk2 toolkit events. This is used -# to update the GUI whilst the application is busy doing -# something. +# Description - Return the currently selected revision id, whether this is +# specified via a tag or as a revision id. # -# Data - None. +# Data - $instance : The window instance. +# $revision_ids : The list of selected revision ids. Normally +# the list will have at most one element but +# may contain more if the tag isn't unique on +# the current branch. # ############################################################################## -sub gtk2_update() +sub get_revision_ids($$) { - return if (Gtk2->main_level() == 0); - while (Gtk2->events_pending()) + my($instance, $revision_ids) = @_; + + @$revision_ids=(); + return unless ($instance->{revision_combo_details}->{complete}); + if ($instance->{tagged_checkbutton}->get_active()) { - Gtk2->main_iteration(); + $instance->{mtn}-> + select($revision_ids, + "t:" . $instance->{revision_combo_details}->{value}); } + else + { + push(@$revision_ids, $instance->{revision_combo_details}->{value}); + } } # @@ -625,6 +853,8 @@ sub create_format_tags($) my $text_buffer = $_[0]; + my $colours = $user_preferences->{colours}; + # Normal Black text, assorted styles, on a white background. $text_buffer->create_tag("normal", "weight" => PANGO_WEIGHT_NORMAL); @@ -635,83 +865,142 @@ sub create_format_tags($) "weight" => PANGO_WEIGHT_BOLD, "style" => "italic"); - # Green text, assorted styles, on a white background. + # Set up the colour and style schemes for file comparison and annotation. - $text_buffer->create_tag("green", "foreground" => "DarkGreen"); - $text_buffer->create_tag("bold-green", - "weight" => PANGO_WEIGHT_BOLD, - "foreground" => "DarkGreen"); - $text_buffer->create_tag("italics-green", - "style" => "italic", - "foreground" => "DarkGreen"); - $text_buffer->create_tag("bold-italics-green", - "weight" => PANGO_WEIGHT_BOLD, - "style" => "italic", - "foreground" => "DarkGreen"); + foreach my $i (1 .. 2) + { + my $clr = $user_preferences->{colours}->{"cmp_revision_" . $i}; + $text_buffer->create_tag("compare-" . $i, + "foreground" => $clr->{fg}); + $text_buffer->create_tag("bold-compare-" . $i, + "weight" => PANGO_WEIGHT_BOLD, + "foreground" => $clr->{fg}); + $text_buffer->create_tag("italics-compare-" . $i, + "style" => "italic", + "foreground" => $clr->{fg}); + $text_buffer->create_tag("bold-italics-compare-" . $i, + "weight" => PANGO_WEIGHT_BOLD, + "style" => "italic", + "foreground" => $clr->{fg}); + $text_buffer->create_tag("compare-file-" . $i, + "foreground" => $clr->{fg}, + "background" => $clr->{bg}); + $text_buffer->create_tag("compare-file-info-" . $i, + "weight" => PANGO_WEIGHT_BOLD, + "foreground" => $clr->{hl}, + "background" => "DarkSlateGrey"); + foreach my $prefix ("annotate_prefix_", "annotate_text_") + { + my $tag = $prefix; + $tag =~ s/_/-/go; + $clr = $user_preferences->{colours}->{$prefix . $i}; + $text_buffer->create_tag($tag . $i, + "foreground" => $clr->{fg}, + "background" => $clr->{bg}); + } + } - # Red text, assorted styles, on a white background. - - $text_buffer->create_tag("red", "foreground" => "DarkRed"); - $text_buffer->create_tag("bold-red", - "weight" => PANGO_WEIGHT_BOLD, - "foreground" => "DarkRed"); - $text_buffer->create_tag("italics-red", - "style" => "italic", - "foreground" => "DarkRed"); - $text_buffer->create_tag("bold-italics-red", - "weight" => PANGO_WEIGHT_BOLD, - "style" => "italic", - "foreground" => "DarkRed"); - # Yellow text on a grey background. $text_buffer->create_tag("compare-info", "foreground" => "Yellow", "background" => "LightSlateGrey"); - # Red text, assorted styles, on pink and grey backgrounds. +} +# +############################################################################## +# +# Routine - hex_dump +# +# Description - Generates a hexadecimal dump of the specified data. +# +# Data - $data : A reference to the data that is to be hex +# dumped. +# Return Value : A reference to the resultant hex dump as a +# string. +# +############################################################################## - $text_buffer->create_tag("compare-first-file", - "foreground" => "DarkRed", - "background" => "MistyRose1"); - $text_buffer->create_tag("compare-first-file-info", - "weight" => PANGO_WEIGHT_BOLD, - "foreground" => "IndianRed1", - "background" => "DarkSlateGrey"); - # Green text, assorted styles, on light green and grey backgrounds. - $text_buffer->create_tag("compare-second-file", - "foreground" => "DarkGreen", - "background" => "DarkSeaGreen1"); - $text_buffer->create_tag("compare-second-file-info", - "weight" => PANGO_WEIGHT_BOLD, - "foreground" => "SpringGreen1", - "background" => "DarkSlateGrey"); +sub hex_dump($) +{ - # Blue text, assorted shades, on assorted blue backgrounds. + my $data = $_[0]; - $text_buffer->create_tag("annotate-prefix-1", - "foreground" => "AliceBlue", - "background" => "CadetBlue"); - $text_buffer->create_tag("annotate-text-1", - "foreground" => "MidnightBlue", - "background" => "PaleTurquoise"); + my ($buffer, + $counter, + @line); - # Blue text, assorted shades, on assorted blue backgrounds (slightly darker - # than the previous group). + $counter = 0; + foreach my $byte (split(//, $$data)) + { + ++ $counter; + push(@line, $byte); + $buffer .= sprintf("%02X ", ord($byte)); + $buffer .= " " if (($counter % 8) == 0); + if (($counter % 16) == 0) + { + foreach my $byte2 (@line) + { + $buffer .= ($byte2 =~ m/[[:print:]]/) ? (" " . $byte2) : " ."; + } + $buffer .= "\n"; + @line = (); + } + } - $text_buffer->create_tag("annotate-prefix-2", - "foreground" => "AliceBlue", - "background" => "SteelBlue"); - $text_buffer->create_tag("annotate-text-2", - "foreground" => "MidnightBlue", - "background" => "SkyBlue"); + # If the last line is incomplete then finish it off. + if (scalar(@line) > 0) + { + $buffer .= " " x (16 - scalar(@line)); + $buffer .= " " if (scalar(@line) < 8); + $buffer .= " "; + foreach my $byte2 (@line) + { + $buffer .= ($byte2 =~ m/[[:print:]]/) ? (" " . $byte2) : " ."; + } + $buffer .= "\n"; + } + + return \$buffer; + } # ############################################################################## # +# Routine - data_is_binary +# +# Description - Determines whether the specified string contains binary +# data. +# +# Data - $data : A reference to the data that is to be +# tested. +# Return Value : True if the data is binary, otherwise false +# if it is predominantly textual. +# +############################################################################## + + + +sub data_is_binary($) +{ + + my $data = $_[0]; + + my $non_printable; + + $non_printable = grep(/[^[:print:][:space:]]/, split(//, $$data)); + + return 1 if (((100 * $non_printable) / length($$data)) > 20); + + return; + +} +# +############################################################################## +# # Routine - colour_to_string # # Description - Returns a string representing the specified @@ -762,5 +1051,64 @@ sub set_label_value($$) $tooltips->set_tip($widget->parent(), $value); } +# +############################################################################## +# +# Routine - glade_signal_autoconnect +# +# Description - This routine uses the Glade library to connect up all the +# registered signal handlers to their related widgets. +# +# Data - $glade : The Glade object describing the widgets that +# are to have their signal handlers +# registered. +# $client_data : The client data that is to be passed into +# each callback routine when it is called. +# +############################################################################## + + +sub glade_signal_autoconnect($$) +{ + + my($glade, $client_data) = @_; + + $glade->signal_autoconnect + (sub { + my($callback_name, $widget, $signal_name, $signal_data, + $connect_object, $after, $user_data) = @_; + my $func = $after ? "signal_connect_after" : "signal_connect"; + $widget->$func($signal_name, + $callback_name, + $connect_object ? $connect_object : $user_data); }, + $client_data); + +} +# +############################################################################## +# +# Routine - gtk2_update +# +# Description - Process all outstanding Gtk2 toolkit events. This is used +# to update the GUI whilst the application is busy doing +# something. +# +# Data - None. +# +############################################################################## + + + +sub gtk2_update() +{ + + return if (Gtk2->main_level() == 0); + while (Gtk2->events_pending()) + { + Gtk2->main_iteration(); + } + +} + 1; ============================================================ --- mtn-browse e0a9d3e52bf31b902b4b351e04438ec745d22ac6 +++ mtn-browse 88920cff112f114139d36a0ba7cecbc14b305bca @@ -118,6 +118,7 @@ sub directory_up_button_clicked_cb($$); sub annotate_button_clicked_cb($$); sub close_toolbutton_clicked_cb($$); sub directory_up_button_clicked_cb($$); +sub display_file($$); sub file_change_history_button_clicked_cb($$); sub get_browser_window(;$$$$$); sub help_toolbutton_clicked_cb($$); @@ -167,9 +168,32 @@ sub view_button_clicked_cb($$); setup_sigchld_handler(\&sigchld_handler); $glade_file = LIB_PATH . "/UI/mtn-browse.glade"; $tooltips = Gtk2::Tooltips->new(); - $mono_font = Gtk2::Pango::FontDescription->from_string("monospace 10"); $line_image = Gtk2::Gdk::Pixbuf->new_from_file(LIB_PATH . "/UI/line.png"); + # Load in user preferences. + + eval + { + $user_preferences = load_preferences(); + $mime_match_table = + build_mime_match_table($user_preferences->{mime_table}); + }; + if ($@ ne "") + { + chomp($@); + my $dialog = Gtk2::MessageDialog->new + ($browser->{window}, + ["modal"], + "warning", + "close", + sprintf("Your preferences cannot be loaded:\n%s", $@)); + $dialog->run(); + $dialog->destroy(); + exit(1); + } + $mono_font = Gtk2::Pango::FontDescription-> + from_string($user_preferences->{fixed_font}); + # Create the temporary working directory. eval @@ -191,9 +215,9 @@ sub view_button_clicked_cb($$); exit(1); } - # Attempt to create an mtn handle, if it fails then assume that we are not - # inside a workspace. However if it does work then move to the workspace's - # root and then pre-load the browser with the relevant branch and revision. + # Open a Monotone database. First attempt to open the current Monotone + # workspace's database. If this doesn't work or the default database is to + # be used anyway then attempt to open that database instead. eval { @@ -216,10 +240,48 @@ sub view_button_clicked_cb($$); } } $mtn = Monotone::AutomateStdio->new(); - $mtn->get_option(\$branch, "branch"); - $mtn->get_base_revision_id(\$revision_id); + if ($user_preferences->{workspace}->{auto_select}) + { + $mtn->get_option(\$branch, "branch"); + $mtn->get_base_revision_id(\$revision_id); + } }; + if (! ($user_preferences->{workspace}->{takes_precedence} + && defined($mtn)) + && $user_preferences->{default_mtn_db} ne "") + { + # Before opening the default database, make sure we are not in any + # workspace. + + if (defined($mtn)) + { + chdir(".."); + $mtn = $branch = $revision_id = undef; + } + + # Attempt to open the default database. + + eval + { + $mtn = Monotone::AutomateStdio-> + new($user_preferences->{default_mtn_db}); + }; + if ($@ ne "") + { + my $dialog = Gtk2::MessageDialog->new + (undef, + ["modal"], + "warning", + "close", + sprintf("Cannot open database `%s'.", + $user_preferences->{default_mtn_db})); + $dialog->run(); + $dialog->destroy(); + } + + } + # Set up the error handlers for the Monotone library. Monotone::AutomateStdio->register_error_handler("both", @@ -230,7 +292,7 @@ sub view_button_clicked_cb($$); # that control can be handed over to Gtk2 before updating the display. $browser = get_browser_window($mtn); - if (defined($mtn)) + if (defined($mtn) && defined($branch)) { Glib::Idle->add (sub { @@ -249,8 +311,8 @@ sub view_button_clicked_cb($$); return FALSE; }, $browser); - $mtn = undef; } + $mtn = undef; # Hand control over to Gtk2. @@ -345,8 +407,8 @@ sub about_activate_cb($$) # # Routine - new_toolbutton_clicked_cb # -# Description - Callback routine called when the user clicks on the -# new toolbutton in a main browser window. +# Description - Callback routine called when the user clicks on the new +# toolbutton in a main browser window. # # Data - $widget : The widget object that received the signal. # $browser : The browser instance that is associated with @@ -374,8 +436,8 @@ sub new_toolbutton_clicked_cb($$) # # Routine - open_toolbutton_clicked_cb # -# Description - Callback routine called when the user clicks on the -# open toolbutton in a main browser window. +# Description - Callback routine called when the user clicks on the open +# toolbutton in a main browser window. # # Data - $widget : The widget object that received the signal. # $browser : The browser instance that is associated with @@ -393,102 +455,22 @@ sub open_toolbutton_clicked_cb($$) return if ($browser->{in_cb}); local $browser->{in_cb} = 1; - my($chooser_dialog, - $done); + my $mtn; - $chooser_dialog = Gtk2::FileChooserDialog->new("Open Database", - $browser->{window}, - "open", - "gtk-cancel" => "cancel", - "gtk-open" => "ok"); - - do + if (open_database($browser->{window}, \$mtn, undef)) { - if ($chooser_dialog->run() eq "ok") - { - - my ($err, - $fh, - $file_name, - $mtn); - - $file_name = $chooser_dialog->get_filename(); - - # The user has selected a file. First make sure we can open it for - # reading (I know I could use the -r test but this takes care of - # any other unforeseen access problems as well). - - if (! defined($fh = IO::File->new($file_name, "r"))) - { - my $dialog = Gtk2::MessageDialog->new - ($browser->{window}, - ["modal"], - "warning", - "close", - $! . "."); - $dialog->run(); - $dialog->destroy(); - } - else - { - - $fh->close(); - $fh = undef; - - # Ok it is a readable file, try and open it but deal with any - # errors in a nicer way than normal. - - Monotone::AutomateStdio->register_error_handler("both"); - eval - { - $mtn = Monotone::AutomateStdio->new($file_name); - }; - $err = $@; - Monotone::AutomateStdio->register_error_handler - ("both", \&mtn_error_handler); - if ($err ne "") - { - my $dialog = Gtk2::MessageDialog->new - ($browser->{window}, - ["modal"], - "warning", - "close", - "Not a valid Monotone database."); - $dialog->run(); - $dialog->destroy(); - } - else - { - - # Seems to be ok so update the browser with the new - # database. - - $browser->{mtn} = $mtn; - &{$browser->{update_handler}}($browser, DATABASE_CHANGED); - $done = 1; - - } - - } - - } - else - { - $done = 1; - } + $browser->{mtn} = $mtn; + &{$browser->{update_handler}}($browser, DATABASE_CHANGED); } - while (! $done); - $chooser_dialog->destroy(); - } # ############################################################################## # # Routine - close_toolbutton_clicked_cb # -# Description - Callback routine called when the user clicks on the -# close toolbutton in a main browser window. +# Description - Callback routine called when the user clicks on the close +# toolbutton in a main browser window. # # Data - $widget : The widget object that received the signal. # $browser : The browser instance that is associated with @@ -569,7 +551,12 @@ sub preferences_toolbutton_clicked_cb($$ return if ($browser->{in_cb}); local $browser->{in_cb} = 1; - preferences($browser); + if (preferences($browser)) + { + $user_preferences = load_preferences(); + $mime_match_table = + build_mime_match_table($user_preferences->{mime_table}); + } } # @@ -1033,7 +1020,8 @@ sub view_button_clicked_cb($$) $data, $fh, $file_name, - $mime, + $helper, + $mime_obj, $mime_type); if (! defined($file_name = @@ -1072,39 +1060,80 @@ sub view_button_clicked_cb($$) $fh->close(); $fh = undef; - # Load into the appropriate application for this type of file, defaulting - # to Vi if necessary. + # Get the user preference settings for this type of file. - if (! defined($mime_type = - Gnome2::VFS->get_mime_type("file://" . $file_name))) + foreach my $entry (@$mime_match_table) { - my $dialog = Gtk2::MessageDialog->new - ($browser->{window}, - ["modal"], - "warning", - "close", - "Unknown file type, not viewing."); - $dialog->run(); - $dialog->destroy(); - return; + if ($browser->{file_being_viewed}->{short_name} =~ m/$entry->{re}/) + { + $helper = $entry->{details}->{helper_application}; + last; + } } - $app = $mime->get_default_application() - if (defined($mime = Gnome2::VFS::Mime::Type->new($mime_type))); - if (defined($app)) + + # If the user has specified a helper application then use that to view the + # file, otherwise use the default desktop settings. + + if (defined($helper) && $helper ne "") { - $app->launch("file://" . $file_name); + + # Use the specified helper application, replacing `{file}' with the + # real file name. + + if ($helper =~ m/\{file\}/) + { + $helper =~ s/\{file\}/$file_name/g; + } + else + { + $helper .= " " . $file_name; + } + + # Launch it. + + system($helper . " &"); + } else { - my $dialog = Gtk2::MessageDialog->new - ($browser->{window}, - ["modal"], - "info", - "close", - sprintf("No application is associated with\n" - . "Mime type `%s',\nusing Vi instead.", - $mime_type)); - system("xterm -e vi " . $file_name . " &"); + + # Use the desktop to load the file. + + # Use the appropriate application for this type of file, defaulting to + # Vi if necessary. + + if (! defined($mime_type = + Gnome2::VFS->get_mime_type("file://" . $file_name))) + { + my $dialog = Gtk2::MessageDialog->new + ($browser->{window}, + ["modal"], + "warning", + "close", + "Unknown file type, not viewing."); + $dialog->run(); + $dialog->destroy(); + return; + } + $app = $mime_obj->get_default_application() + if (defined($mime_obj = Gnome2::VFS::Mime::Type->new($mime_type))); + if (defined($app)) + { + $app->launch("file://" . $file_name); + } + else + { + my $dialog = Gtk2::MessageDialog->new + ($browser->{window}, + ["modal"], + "info", + "close", + sprintf("No application is associated with\n" + . "Mime type `%s',\nusing Vi instead.", + $mime_type)); + system("xterm -e vi " . $file_name . " &"); + } + } } @@ -1565,7 +1594,7 @@ sub get_browser_window(;$$$$$) $browser->{revision_combo_details}->{preset} = 0; $browser->{directory_combo_details}->{preset} = 0; $browser->{file_being_viewed_preset_value} = ""; - &{$browser->{update_handler}}($browser, DATABASE_CHANGED); + &{$browser->{update_handler}}($browser, ALL_CHANGED); } else @@ -1589,7 +1618,7 @@ sub get_browser_window(;$$$$$) $browser->{revision_combo_details}->{preset} = 0; $browser->{directory_combo_details}->{preset} = 0; $browser->{file_being_viewed_preset_value} = ""; - &{$browser->{update_handler}}($browser, DATABASE_CHANGED); + &{$browser->{update_handler}}($browser, ALL_CHANGED); $browser->{window}->show_all(); # Now update with the details of the specified database. @@ -1664,7 +1693,7 @@ sub update_browser_state($$) # The database has changed. - if ($changed & BRANCH) + if ($changed == DATABASE_CHANGED) { my $db_name; @@ -1786,42 +1815,11 @@ sub update_browser_state($$) { $browser->{appbar}->set_status("Fetching revision list"); gtk2_update(); - - # Get either a list of tags or revision ids depending upon what the - # user has chosen. - - if ($browser->{tagged_checkbutton}->get_active()) - { - my(%dup_list, - @list); - $browser->{mtn}-> - tags(address@hidden, $browser->{branch_combo_details}->{value}); - $browser->{appbar}->set_progress_percentage(0.5); - gtk2_update(); - foreach my $item (@list) - { - if (! exists($dup_list{$item->{tag}})) - { - push(@revision_list, $item->{tag}); - $dup_list{$item->{tag}} = 1; - } - } - } - else - { - $browser->{mtn}-> - select(address@hidden, - "b:" . $browser->{branch_combo_details}->{value}); - $browser->{appbar}->set_progress_percentage(0.33); - gtk2_update(); - $browser->{mtn}->toposort(address@hidden, @revision_list); - $browser->{appbar}->set_progress_percentage(0.66); - gtk2_update(); - splice(@revision_list, 0, scalar(@revision_list) - 100); - @revision_list = reverse(@revision_list); - } - $browser->{appbar}->set_progress_percentage(1); - gtk2_update(); + get_branch_revisions($browser->{mtn}, + $browser->{branch_combo_details}->{value}, + $browser->{tagged_checkbutton}->get_active(), + $browser->{appbar}, + address@hidden); } $browser->{revision_combo_details}->{list} = address@hidden; @@ -1861,7 +1859,7 @@ sub update_browser_state($$) if ($browser->{directory_combo_details}->{preset}) { $browser->{directory_combo_details}->{complete} = 1; - $browser->{revision_combo_details}->{preset} = 0; + $browser->{directory_combo_details}->{preset} = 0; } else { @@ -2114,8 +2112,7 @@ sub update_browser_state($$) if (exists($browser->{file_being_viewed}->{manifest_entry})) { - my $manifest_entry; - $manifest_entry = + my $manifest_entry = $browser->{file_being_viewed}->{manifest_entry}; # Only do anything if the selected file has changed. @@ -2124,170 +2121,37 @@ sub update_browser_state($$) ne $manifest_entry->{file_id}) { - my($contents, - $lang, - $mime_type, - $scrolled_window); + my $textual_data; - # Reset the file view buffer and the associated find text - # window. + # Enable the file buttons, keeping the ones relating to text + # files disabled, and reset any associated find text window. $browser->{file_button_vbox}->set_sensitive(TRUE); foreach my $widget (@{$browser->{text_file_sensitive_group}}) { $widget->set_sensitive(FALSE); } - $browser->{file_view_svbuffer}-> - place_cursor($browser->{file_view_svbuffer}-> - get_start_iter()); - $browser->{file_view_svbuffer}->set_text(""); - $browser->{file_view_svbuffer}->set("highlight", FALSE); reset_find_text($browser->{file_view_sv}); disable_find_text($browser->{file_view_sv}, 1); - # Get contents. + # Display the selected file's contents. - $browser->{mtn}->get_file(\$contents, - $manifest_entry->{file_id}); + display_file($browser, \$textual_data); - # Try and work out the mime type, first based on contents and - # then based on the file name extension. + # If we have just displayed a text file then enable the file + # buttons applicable to text files and enable any associated + # find text window that may be displayed. - if (! defined($mime_type = - Gnome2::VFS->get_mime_type_for_data($contents)) - || $mime_type eq "text/plain") + if ($textual_data) { - my $name = $browser->{file_being_viewed}->{short_name}; - foreach my $item (@text_mime_types) + foreach my $widget + (@{$browser->{text_file_sensitive_group}}) { - if ($name =~ m/$item->{pattern}/) - { - $mime_type = $item->{type}; - last; - } + $widget->set_sensitive(TRUE); } + disable_find_text($browser->{file_view_sv}, 0); } - # Override some mis-identified types. - - $mime_type = "image/svg+xml" - if ($mime_type eq "text/xml" - && $browser->{file_being_viewed}->{short_name} - =~ m/.*\.svg$/o); - - # If it's image data then attempt to render it. - - if ($mime_type =~ m/^image\/.+$/o) - { - - # Image data. - - eval - { - my $loader = Gtk2::Gdk::PixbufLoader->new(); - $loader->write($contents); - $loader->close(); - $browser->{file_view_svbuffer}->insert_pixbuf - ($browser->{file_view_svbuffer}->get_start_iter(), - $loader->get_pixbuf()); - }; - $browser->{file_view_svbuffer}-> - set_text("<" . $mime_type . ">") if ($@ ne ""); - - } - else - { - - # Non-image data. - - my $ok_to_render = 0; - - # Attempt to syntax highlight the file if it looks safe. - - if ($mime_type =~ m/^application\/.+$/o) - { - my $part; - ($part) = ($mime_type =~ m/^application\/(.+)$/o); - foreach my $item (@text_viewable_app_mime_types) - { - if ($part eq $item) - { - $ok_to_render = 1; - last; - } - } - } - - if ($mime_type =~ m/^text\/.+$/o || $ok_to_render) - { - - # Contents is displayable text. - - my $iter; - - # Enable syntax highlighting if it is available for - # this type of text file. - - if (defined($lang = $browser->{file_view_svlangmgr}-> - get_language_from_mime_type($mime_type))) - { - $browser->{file_view_svbuffer}-> - set("highlight", TRUE); - $browser->{file_view_svbuffer}-> - set_language($lang); - } - - # Load in the contents and then delete the trailing - # newline. - - $browser->{file_view_svbuffer}->set_text($contents); - $iter = $browser->{file_view_svbuffer}->get_end_iter(); - $browser->{file_view_svbuffer}->delete - ($iter, - $browser->{file_view_svbuffer}->get_end_iter()) - if ($iter->backward_char()); - - # Enable the file buttons applicable to text files. - - foreach my $widget - (@{$browser->{text_file_sensitive_group}}) - { - $widget->set_sensitive(TRUE); - } - - # Enable any associated find text window that may be - # displayed. - - disable_find_text($browser->{file_view_sv}, 0); - - } - else - { - - # Display mime type for undisplayable file contents. - - $browser->{file_view_svbuffer}-> - set("highlight", FALSE); - $browser->{file_view_svbuffer}-> - set_text("<" . $mime_type . ">"); - - } - - } - - # Scroll back up to the top left. - - $browser->{file_view_svbuffer}-> - place_cursor($browser->{file_view_svbuffer}-> - get_start_iter()); - if ($browser->{file_view_scrolledwindow}->realized()) - { - $browser->{file_view_scrolledwindow}-> - get_vadjustment()->set_value(0); - $browser->{file_view_scrolledwindow}-> - get_hadjustment()->set_value(0); - } - # Update the file details labels. if (! exists($manifest_entry->{last_changed_revision})) @@ -2353,6 +2217,178 @@ sub update_browser_state($$) # ############################################################################## # +# Routine - display_file +# +# Description - Display the currenty selected file in the sourceview +# textview. +# +# Data - $browser : The browser instance that is to display the +# file. +# $text_data : A reference to a variable that is to contain a +# boolean indicator as to whether the displayed +# file contains textual or binary data. +# +############################################################################## + + + +sub display_file($$) +{ + + my($browser, $textual_data) = @_; + + my($contents, + $iter, + $lang, + $mime_details, + $mime_type); + + $$textual_data = 0; + + # Reset the file view buffer. + + $browser->{file_view_svbuffer}-> + place_cursor($browser->{file_view_svbuffer}->get_start_iter()); + $browser->{file_view_svbuffer}->set_text(""); + $browser->{file_view_svbuffer}->set("highlight", FALSE); + + # Get contents. + + $browser->{mtn}->get_file(\$contents, + $browser->{file_being_viewed}->{manifest_entry}-> + {file_id}); + + # Try and work out the MIME type through file name pattern matching. + + foreach my $entry (@$mime_match_table) + { + if ($browser->{file_being_viewed}->{short_name} =~ m/$entry->{re}/) + { + $mime_type = $entry->{details}->{name}; + $mime_details = $entry->{details}; + last; + } + } + + # If that didn't work then try determining the MIME type based upon the + # file's contents. + + $mime_type = Gnome2::VFS->get_mime_type_for_data($contents) + if (! defined($mime_type)); + + # Only attempt to render the file's contents if requested to do so. + + if (! defined($mime_type) + || (defined($mime_details) && ! $mime_details->{display_internally})) + { + + # The user doesn't want to display this type of file. + + $browser->{file_view_svbuffer}-> + set_text("<" + . (defined($mime_type) ? $mime_type : "Unknown Contents") + . ">"); + + } + else + { + + # The user wants to display this type of file. + + # Image/non-image data? + + if ($mime_type =~ m/^image\/.+$/o) + { + + # Image data. + + eval + { + my $loader = Gtk2::Gdk::PixbufLoader->new(); + $loader->write($contents); + $loader->close(); + $browser->{file_view_svbuffer}->insert_pixbuf + ($browser->{file_view_svbuffer}->get_start_iter(), + $loader->get_pixbuf()); + }; + $browser->{file_view_svbuffer}->set_text("<" . $mime_type . ">") + if ($@ ne ""); + + } + else + { + + # Non-image data. + + # Binary/text data? + + if (data_is_binary(\$contents)) + { + + # Binary data. + + # We have been asked to display this data. The only thing we + # can do is to hex dump it out. + + $browser->{file_view_svbuffer}-> + set_text("<" . $mime_type . "> Hex dump:\n"); + $browser->{file_view_svbuffer}-> + insert($browser->{file_view_svbuffer}->get_end_iter(), + ${hex_dump(\$contents)}); + + } + else + { + + # Text data. + + $$textual_data = 1; + + # Enable syntax highlighting if the user wants it on and it is + # available for this type of file. + + if ((! defined($mime_details) + || (defined($mime_details) + && $mime_details->{syntax_highlight})) + && defined($lang = + $browser->{file_view_svlangmgr}-> + get_language_from_mime_type($mime_type))) + { + $browser->{file_view_svbuffer}->set("highlight", TRUE); + $browser->{file_view_svbuffer}->set_language($lang); + } + + # Load in the contents. + + $browser->{file_view_svbuffer}->set_text($contents); + + } + + # Delete the trailing newline. + + $iter = $browser->{file_view_svbuffer}->get_end_iter(); + $browser->{file_view_svbuffer}->delete + ($iter, $browser->{file_view_svbuffer}->get_end_iter()) + if ($iter->backward_char()); + + } + + } + + # Scroll back up to the top left. + + $browser->{file_view_svbuffer}-> + place_cursor($browser->{file_view_svbuffer}->get_start_iter()); + if ($browser->{file_view_scrolledwindow}->realized()) + { + $browser->{file_view_scrolledwindow}->get_vadjustment()->set_value(0); + $browser->{file_view_scrolledwindow}->get_hadjustment()->set_value(0); + } + +} +# +############################################################################## +# # Routine - mtn_error_handler # # Description - This routine is called when ever there is a problem with ============================================================ --- mtn-browse.glade 7e503af1903439bbf47468d917f692db3bea8ec4 +++ mtn-browse.glade e108526a6ed91bc84238ac6586567a7c53b196ea @@ -3971,7 +3971,7 @@ file version that is to be compared0 - + True View the change log of the revision that had the red highlighted text @@ -3982,9 +3982,9 @@ that had the red highlighted text - + True - <span foreground="DarkRed">Revision Change Log</span> + False True GTK_JUSTIFY_LEFT @@ -4000,7 +4000,7 @@ that had the red highlighted text - + True View the change log of the revision that had the green highlighted text @@ -4011,9 +4011,9 @@ that had the green highlighted text - + True - <span foreground="SpringGreen4">Revision Change Log</span> + False True GTK_JUSTIFY_LEFT @@ -4767,7 +4767,8 @@ of tagged revisions in Combo lists True The maximum number of tags -to be displayed (0 - unlimited) +to be displayed (starting from +the most recent, 0 = unlimited) True False @@ -4801,7 +4802,8 @@ to be displayed (0 - unlimited) True The maximum number of ids -to be displayed (0 - unlimited) +to be displayed (starting from +the most recent, 0 = unlimited) True False @@ -6561,10 +6563,11 @@ 2) The optional token {file} is with this file type is used 2) The optional token {file} is -replaced with the file's full file -name, if not present then the -file name is just appended to -the end of the command +replaced with the file's full path +name, if this token is not +present then the file name is +simply appended to the end of +the command True False