#
#
# add_file "AdvancedFind.pm"
# content [def35842dde7df7a16b069185cdb148945dd2efa]
#
# add_file "ChangeLog.pm"
# content [bbaeb9fe833b6c05b752ed17023c943946ed0837]
#
# add_file "ComboAutoCompletion.pm"
# content [a45bbf62e7f2efa0377247431714280e5cfd48cd]
#
# add_file "Completion.pm"
# content [1054100fadcf594ae9902d61f490abd7e7d718fd]
#
# add_file "Globals.pm"
# content [6b2a79a7d7abec36a2b78963676c17f65661b130]
#
# add_file "History.pm"
# content [b73d263a8bef77075f30a740a1da06bf21207277]
#
# add_file "Utilities.pm"
# content [13b6314e75888c8e6431a0bdf11d12478c31a3b5]
#
# add_file "line.png"
# content [824953bb8f5b100ff8a27235c97337998c35d73c]
#
# patch "mtn-browse"
# from [2f9cc86df7157a7eb26bb354b04a351da03c4054]
# to [900690ff1257eaa6bddc465bbc66c96cd85ed282]
#
# set "line.png"
# attr "mtn:manual_merge"
# value "true"
#
============================================================
--- AdvancedFind.pm def35842dde7df7a16b069185cdb148945dd2efa
+++ AdvancedFind.pm def35842dde7df7a16b069185cdb148945dd2efa
@@ -0,0 +1,1053 @@
+##############################################################################
+#
+# File Name - AdvancedFind.pm
+#
+# Description - The advanced find module for the mtn-browse application.
+# This module contains all the routines for implementing the
+# advanced find dialog.
+#
+# Author - A.E.Cooper.
+#
+# Legal Stuff - Copyright (c) 2007 Anthony Edward Cooper
+#
.
+#
+# This program is free software; you can redistribute it
+# and/or modify it under the terms of the GNU General Public
+# License as published by the Free Software Foundation;
+# either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be
+# useful, but WITHOUT ANY WARRANTY; without even the implied
+# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public
+# License along with this software; if not, write to the Free
+# Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307 USA.
+#
+##############################################################################
+#
+##############################################################################
+#
+# GLOBAL DATA FOR THIS MODULE
+#
+##############################################################################
+
+
+
+# ***** DIRECTIVES *****
+
+require 5.008;
+
+use strict;
+
+# ***** FUNCTIONAL PROTOTYPES FOR THIS FILE *****
+
+# Public routines.
+
+sub advanced_find($$$);
+
+# Private routines.
+
+sub create_advanced_find_window();
+sub execute_button_clicked_cb($$);
+sub populate_button_clicked_cb($$);
+sub revisions_treeview_cursor_changed_cb($$);
+sub revisions_treeview_row_activated_cb($$$$);
+sub simple_query_radiobutton_toggled_cb($$);
+sub term_combobox_changed_cb($$);
+sub update_advanced_find_state($$);
+#
+##############################################################################
+#
+# Routine - advanced_find
+#
+# Description - Displays the advanced find dialog window and then gets the
+# user to select the revision they want.
+#
+# Data - $browser : The browser instance that started the
+# advanced find.
+# $revision_id : A reference to a variable that is to contain
+# the selected revision id.
+# $branches : A reference to a list that is to contain the
+# list of branches that the selected revision
+# is on.
+# Return Value : True if a revision has been selected,
+# otherwise false.
+#
+##############################################################################
+
+
+
+sub advanced_find($$$)
+{
+
+ my($browser, $revision_id, $branches) = @_;
+
+ my($advanced_find,
+ $height,
+ $new,
+ $width);
+
+ # Look for an unused window first.
+
+ foreach my $window (@windows)
+ {
+ if ($window->{type} eq "advanced_find_window"
+ && ! $window->{window}->mapped())
+ {
+ $advanced_find = $window;
+ last;
+ }
+ }
+
+ # Create a new advanced find window if an unused one wasn't found,
+ # otherwise reuse the existing unused one.
+
+ if (defined($advanced_find))
+ {
+ $new = 0;
+ }
+ else
+ {
+ $advanced_find = create_advanced_find_window();
+ $new = 1;
+ }
+
+ $advanced_find->{mtn} = $browser->{mtn};
+
+ # Update the window's internal state.
+
+ {
+ local $advanced_find->{in_cb} = 1;
+ $advanced_find->{selected} = 0;
+
+ # Reset the window contents, then show it.
+
+ $advanced_find->{window}->set_transient_for($browser->{window});
+ $advanced_find->{branch_combo_details}->{preset} = 0;
+ $advanced_find->{revision_combo_details}->{preset} = 0;
+ $advanced_find->{appbar}->set_progress_percentage(0);
+ $advanced_find->{appbar}->clear_stack();
+ &{$advanced_find->{update_handler}}($advanced_find, NEW_FIND);
+ ($width, $height) = $advanced_find->{window}->get_default_size();
+ $advanced_find->{window}->resize($width, $height);
+ $advanced_find->{window}->show_all();
+
+ # If necessary, setup the list of windows that can be made busy for
+ # this application window.
+
+ if ($new)
+ {
+ $advanced_find->{busy_windows} = [];
+ push(@{$advanced_find->{busy_windows}},
+ $advanced_find->{window}->window());
+ push(@{$advanced_find->{busy_windows}},
+ $advanced_find->{details_textview}->get_window("text"));
+
+ push(@windows, $advanced_find);
+ }
+
+ # Now actually update it with any preset values.
+
+ $advanced_find->{branch_combo_details}->{preset} = 1;
+ $advanced_find->{branch_combo_details}->{completed} =
+ $browser->{branch_combo_details}->{completed};
+ $advanced_find->{branch_combo_details}->{value} =
+ $browser->{branch_combo_details}->{value};
+
+ $advanced_find->{revision_combo_details}->{preset} = 1;
+ $advanced_find->{revision_combo_details}->{completed} =
+ $browser->{revision_combo_details}->{completed};
+ $advanced_find->{revision_combo_details}->{value} =
+ $browser->{revision_combo_details}->{value};
+
+ $advanced_find->{tagged_tick}->
+ set_active($browser->{tagged_tick}->get_active());
+
+ &{$advanced_find->{update_handler}}($advanced_find, NEW_FIND);
+ }
+
+ # Handle all events until the dialog is dismissed.
+
+ $advanced_find->{done} = 0;
+ while (! $advanced_find->{done})
+ {
+ Gtk2->main_iteration();
+ }
+ $advanced_find->{window}->hide();
+
+ # Deal with the result.
+
+ @$branches = ();
+ $$revision_id = "";
+ if ($advanced_find->{selected})
+ {
+ my($branch_list,
+ @certs_list,
+ $found);
+
+ $$revision_id = $advanced_find->{revisions_treeview_details}->{value};
+
+ # Build up a list of branches that the selected revision is on, putting
+ # the branch named in the branch combo box at the head if it is still
+ # applicable.
+
+ $advanced_find->{mtn}->certs(address@hidden, $$revision_id);
+ $found = 0;
+ foreach my $cert (@certs_list)
+ {
+ if ($cert->{name} eq "branch")
+ {
+ if ($cert->{value}
+ ne $advanced_find->{branch_combo_details}->{value})
+ {
+ push(@$branches, $cert->{value});
+ }
+ else
+ {
+ $found = 1;
+ }
+ }
+ }
+ unshift(@$branches, $advanced_find->{branch_combo_details}->{value})
+ if ($found);
+ push(@$branches, "") if (scalar(@$branches) == 0);
+
+ return 1;
+ }
+ else
+ {
+ return;
+ }
+
+}
+#
+##############################################################################
+#
+# Routine - create_advanced_find_window
+#
+# Description - Creates the advanced find dialog window.
+#
+# Data - Return Value : A reference to the newly created advanced
+# find instance record.
+#
+##############################################################################
+
+
+
+sub create_advanced_find_window()
+{
+
+ my(@branch_list,
+ $font,
+ $instance,
+ $renderer,
+ $tv_column);
+
+ $instance = {};
+ $instance->{type} = "advanced_find_window";
+ $instance->{glade} =
+ Gtk2::GladeXML->new("../mtn-browse.glade", "advanced_find_window");
+
+ # Flag to stop recursive calling of callbacks.
+
+ $instance->{in_cb} = 0;
+
+ # Connect Glade registered signal handlers.
+
+ $instance->{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); },
+ $instance);
+
+ # Link in the update handler for the advanced find window.
+
+ $instance->{update_handler} = \&update_advanced_find_state;
+
+ # Get the widgets that we are interested in.
+
+ $instance->{window} =
+ $instance->{glade}->get_widget("advanced_find_window");
+ $instance->{window}->set_icon($app_icon);
+ $instance->{appbar} = $instance->{glade}->get_widget("appbar");
+ $instance->{simple_query_radiobutton} =
+ $instance->{glade}->get_widget("simple_query_radiobutton");
+ $instance->{simple_frame} = $instance->{glade}->get_widget("simple_frame");
+ $instance->{advanced_frame} =
+ $instance->{glade}->get_widget("advanced_frame");
+ $instance->{branch_combo} =
+ $instance->{glade}->get_widget("branch_comboboxentry");
+ $instance->{revision_combo} =
+ $instance->{glade}->get_widget("revision_comboboxentry");
+ $instance->{tagged_tick} =
+ $instance->{glade}->get_widget("tagged_checkbutton");
+ $instance->{search_term_combo} =
+ $instance->{glade}->get_widget("search_term_comboboxentry");
+ $instance->{term_combo} = $instance->{glade}->get_widget("term_combobox");
+ $instance->{argument_entry} =
+ $instance->{glade}->get_widget("argument_entry");
+ $instance->{date_dateedit} =
+ $instance->{glade}->get_widget("date_dateedit");
+ $instance->{revisions_treeview} =
+ $instance->{glade}->get_widget("revisions_treeview");
+ $instance->{details_textview} =
+ $instance->{glade}->get_widget("details_textview");
+ $instance->{details_scrolledwindow} =
+ $instance->{glade}->get_widget("details_scrolledwindow");
+ $instance->{selected_branch_label} =
+ $instance->{glade}->get_widget("selected_branch_value_label");
+ $instance->{selected_revision_label} =
+ $instance->{glade}->get_widget("selected_revision_value_label");
+ $instance->{ok_button} = $instance->{glade}->get_widget("ok_button");
+
+ # Setup the advanced find window deletion handlers.
+
+ $instance->{window}->signal_connect
+ ("delete_event",
+ sub { $_[2]->{done} = 1 unless ($_[2]->{in_cb}); return TRUE; },
+ $instance);
+ $instance->{glade}->get_widget("cancel_button")->signal_connect
+ ("clicked", sub { $_[1]->{done} = 1; }, $instance);
+ $instance->{glade}->get_widget("ok_button")->signal_connect
+ ("clicked", sub { $_[1]->{done} = $_[1]->{selected} = 1; }, $instance);
+
+ # Setup the comboboxentry key release signal handlers.
+
+ $instance->{branch_combo}->child()->
+ signal_connect("key_release_event",
+ \&combo_key_release_event_cb,
+ $instance);
+ $instance->{revision_combo}->child()->
+ signal_connect("key_release_event",
+ \&combo_key_release_event_cb,
+ $instance);
+
+ # Setup the comboboxes.
+
+ $instance->{branch_combo}->set_model(Gtk2::ListStore->new("Glib::String"));
+ $instance->{branch_combo}->set_text_column(0);
+ $instance->{branch_combo}->set_wrap_width(2);
+ $instance->{revision_combo}->
+ set_model(Gtk2::ListStore->new("Glib::String"));
+ $instance->{revision_combo}->set_text_column(0);
+ $instance->{revision_combo}->set_wrap_width(2);
+ $instance->{search_term_combo}->
+ set_model(Gtk2::ListStore->new("Glib::String"));
+ $instance->{search_term_combo}->set_text_column(0);
+ $instance->{query_history} = [];
+ $instance->{term_combo}->set_active(0);
+
+ # Setup the revisions list browser.
+
+ $instance->{revisions_liststore} = Gtk2::ListStore->new("Glib::String");
+ $instance->{revisions_treeview}->
+ set_model($instance->{revisions_liststore});
+ $tv_column = Gtk2::TreeViewColumn->new();
+ $tv_column->set_title("Matching Revision Ids");
+ $tv_column->set_sort_column_id(0);
+ $renderer = Gtk2::CellRendererText->new();
+ $tv_column->pack_start($renderer, FALSE);
+ $tv_column->set_attributes($renderer, "text" => 0);
+ $instance->{revisions_treeview}->append_column($tv_column);
+ $instance->{revisions_treeview}->set_search_column(0);
+
+ # Setup the revision details viewer.
+
+ $instance->{details_buffer} = $instance->{details_textview}->get_buffer();
+ create_format_tags($instance->{details_textview}->get_buffer());
+ $font = Gtk2::Pango::FontDescription->from_string("monospace 10");
+ $instance->{details_textview}->modify_font($font) if (defined($font));
+
+ return $instance;
+
+}
+#
+##############################################################################
+#
+# Routine - simple_query_radiobutton_toggled_cb
+#
+# Description - Callback routine called when the user changes the advanced
+# find mode radio button.
+#
+# Data - $widget : The widget object that received the
+# signal.
+# $advanced_find : The advanced find dialog window instance
+# that is associated with this widget.
+#
+##############################################################################
+
+
+
+sub simple_query_radiobutton_toggled_cb($$)
+{
+
+ my($widget, $advanced_find) = @_;
+
+ return if ($advanced_find->{in_cb});
+ local $advanced_find->{in_cb} = 1;
+
+ my($len,
+ $value);
+
+ # Simply enable the relevant find widgets depending upon whether simple or
+ # advanced mode is selected.
+
+ if ($advanced_find->{simple_query_radiobutton}->get_active())
+ {
+ $advanced_find->{simple_frame}->set_sensitive(TRUE);
+ $advanced_find->{advanced_frame}->set_sensitive(FALSE);
+ }
+ else
+ {
+ $advanced_find->{simple_frame}->set_sensitive(FALSE);
+ $advanced_find->{advanced_frame}->set_sensitive(TRUE);
+ }
+
+}
+#
+##############################################################################
+#
+# Routine - execute_button_clicked_cb
+#
+# Description - Callback routine called when the user clicks on the execute
+# query button in the advanced find window.
+#
+# Data - $widget : The widget object that received the
+# signal.
+# $advanced_find : The advanced find dialog window instance
+# that is associated with this widget.
+#
+##############################################################################
+
+
+
+sub execute_button_clicked_cb($$)
+{
+
+ my($widget, $advanced_find) = @_;
+
+ return if ($advanced_find->{in_cb});
+ local $advanced_find->{in_cb} = 1;
+
+ # Simply let the update handler deal with it.
+
+ &{$advanced_find->{update_handler}}($advanced_find, REVISION_CHANGED);
+
+}
+#
+##############################################################################
+#
+# Routine - populate_button_clicked_cb
+#
+# Description - Callback routine called when the user clicks on the
+# populate selector button in the advanved find window.
+#
+# Data - $widget : The widget object that received the
+# signal.
+# $advanced_find : The advanced find dialog window instance
+# that is associated with this widget.
+#
+##############################################################################
+
+
+
+sub populate_button_clicked_cb($$)
+{
+
+ my($widget, $advanced_find) = @_;
+
+ return if ($advanced_find->{in_cb});
+ local $advanced_find->{in_cb} = 1;
+
+ my($arg,
+ $pos,
+ $selector,
+ $time_val,
+ $to_insert);
+
+ # Simply get the currently selected selector and then insert it into the
+ # user's query string.
+
+ $selector = $advanced_find->{term_combo}->get_model()->get
+ ($advanced_find->{term_combo}->get_active_iter(), 0);
+ $arg = $advanced_find->{argument_entry}->get_text();
+ $time_val = strftime("%Y-%m-%dT%H:%M:%S",
+ localtime($advanced_find->{date_dateedit}->
+ get_time()));
+ $to_insert = "";
+ if ($selector eq "Author")
+ {
+ $to_insert = "a:" . (($arg eq "") ? "" : $arg);
+ }
+ elsif ($selector eq "Branch")
+ {
+ $to_insert = "b:" . (($arg eq "") ? "" : $arg);
+ }
+ elsif ($selector eq "Cert")
+ {
+ $to_insert = "c:" . (($arg eq "") ? "" : $arg);
+ }
+ elsif ($selector eq "Date (=)")
+ {
+ $to_insert = "d:" . $time_val;
+ }
+ elsif ($selector eq "Date (<=)")
+ {
+ $to_insert = "e:" . $time_val;
+ }
+ elsif ($selector eq "Date (>)")
+ {
+ $to_insert = "l:" . $time_val;
+ }
+ elsif ($selector eq "Head Revision")
+ {
+ $to_insert = "h:";
+ }
+ elsif ($selector eq "Identifier")
+ {
+ $to_insert = "i:" . (($arg eq "") ? "" : $arg);
+ }
+ elsif ($selector eq "Parent")
+ {
+ $to_insert = "p:" . (($arg eq "") ? "" : $arg);
+ }
+ elsif ($selector eq "Separator")
+ {
+ $to_insert = "/";
+ }
+ elsif ($selector eq "Tag")
+ {
+ $to_insert = "t:" . (($arg eq "") ? "" : $arg);
+ }
+
+ $pos = $advanced_find->{search_term_combo}->child()->get_position();
+ $advanced_find->{search_term_combo}->child()->insert_text
+ ($to_insert, $pos);
+ $advanced_find->{search_term_combo}->child()->set_position
+ ($pos + length($to_insert));
+
+}
+#
+##############################################################################
+#
+# Routine - term_combobox_changed_cb
+#
+# Description - Callback routine called when the user changes the value of
+# the term combobox by selecting an entry from its pulldown
+# list in the advanced find window.
+#
+# Data - $widget : The widget object that received the signal.
+# $instance : The window instance that is associated with
+# this widget.
+#
+##############################################################################
+
+
+
+sub term_combobox_changed_cb($$)
+{
+
+ my($widget, $advanced_find) = @_;
+
+ return if ($advanced_find->{in_cb});
+ local $advanced_find->{in_cb} = 1;
+
+ my($arg,
+ $pos,
+ $selector,
+ $time_val,
+ $to_insert);
+
+ # Simply get the currently selected term and then enable/disable the text
+ # entry and date entry widgets accordingly.
+
+ $selector = $advanced_find->{term_combo}->get_model()->get
+ ($advanced_find->{term_combo}->get_active_iter(), 0);
+
+ if ($selector =~ m/^Date .*$/o)
+ {
+ $advanced_find->{argument_entry}->set_sensitive(FALSE);
+ $advanced_find->{date_dateedit}->set_sensitive(TRUE);
+ }
+ elsif ($selector eq "Head" || $selector eq "Separator")
+ {
+ $advanced_find->{argument_entry}->set_sensitive(FALSE);
+ $advanced_find->{date_dateedit}->set_sensitive(FALSE);
+ }
+ else
+ {
+ $advanced_find->{argument_entry}->set_sensitive(TRUE);
+ $advanced_find->{date_dateedit}->set_sensitive(FALSE);
+ }
+
+}
+#
+##############################################################################
+#
+# Routine - revisions_treeview_cursor_changed_cb
+#
+# Description - Callback routine called when the user selects an entry in
+# the revisions treeview in the advanced find window.
+#
+# Data - $widget : The widget object that received the
+# signal.
+# $advanced_find : The advanced find dialog window instance
+# that is associated with this widget.
+#
+##############################################################################
+
+
+
+sub revisions_treeview_cursor_changed_cb($$)
+{
+
+ my($widget, $advanced_find) = @_;
+
+ return if ($advanced_find->{in_cb});
+ local $advanced_find->{in_cb} = 1;
+
+ my $revision_id;
+
+ # Get the selected revision id.
+
+ $widget->get_selection()->selected_foreach
+ (sub {
+ my($model, $path, $iter) = @_;
+ $revision_id = $model->get($iter, 0); });
+
+ if (defined($revision_id)
+ && $revision_id
+ ne $advanced_find->{revisions_treeview_details}->{value})
+ {
+ $advanced_find->{revisions_treeview_details}->{value} = $revision_id;
+ $advanced_find->{appbar}->clear_stack();
+ &{$advanced_find->{update_handler}}($advanced_find,
+ SELECTED_REVISION_CHANGED);
+ }
+
+}
+#
+##############################################################################
+#
+# Routine - revisions_treeview_row_activated_cb
+#
+# Description - Callback routine called when the user double clicks on an
+# entry in the revisions treeview in the advanced find
+# window.
+#
+# Data - $widget : The widget object that received the
+# signal.
+# $tree_path : A Gtk2::TreePath object for the
+# selected item.
+# $tree_view_column : A Gtk2::TreeViewColumn object for the
+# selected item.
+# $advanced_find : The advanced find dialog window
+# instance that is associated with this
+# widget.
+#
+##############################################################################
+
+
+
+sub revisions_treeview_row_activated_cb($$$$)
+{
+
+ my($widget, $tree_path, $tree_view_column, $advanced_find) = @_;
+
+ return if ($advanced_find->{in_cb});
+ local $advanced_find->{in_cb} = 1;
+
+ my $revision_id;
+
+ # Get the selected revision id.
+
+ $widget->get_selection()->selected_foreach
+ (sub {
+ my($model, $path, $iter) = @_;
+ $revision_id = $model->get($iter, 0); });
+
+ if (defined($revision_id))
+ {
+ $advanced_find->{revisions_treeview_details}->{value} = $revision_id;
+ $advanced_find->{appbar}->clear_stack();
+ $advanced_find->{selected} = 1;
+ $advanced_find->{done} = 1;
+ }
+
+}
+#
+##############################################################################
+#
+# Routine - update_advanced_find_state
+#
+# Description - Update the display of the specified advanced find dialog
+# window instance according to the specified state.
+#
+# Data - $advanced_find : The advanced find dialog window instance
+# that is to have its state updated.
+# $changed : What the user has changed.
+#
+##############################################################################
+
+
+
+sub update_advanced_find_state($$)
+{
+
+ my($advanced_find, $changed) = @_;
+
+ my $made_busy = 0;
+
+ if ($advanced_find->{window}->realized())
+ {
+ $made_busy = 1;
+ make_busy($advanced_find, 1);
+ }
+ $advanced_find->{appbar}->push("");
+ gtk2_update();
+
+ # The list of available branches has changed.
+
+ if ($changed & BRANCH)
+ {
+
+ my @branch_list;
+
+ # Reset the query mode.
+
+ $advanced_find->{simple_query_radiobutton}->set_active(TRUE);
+ $advanced_find->{simple_frame}->set_sensitive(TRUE);
+ $advanced_find->{advanced_frame}->set_sensitive(FALSE);
+
+ # Reset the branch selection.
+
+ $advanced_find->{branch_combo_details}->{completion} = undef;
+ if (! $advanced_find->{branch_combo_details}->{preset})
+ {
+ $advanced_find->{branch_combo_details}->{completed} = 0;
+ $advanced_find->{branch_combo_details}->{value} = "";
+ }
+ $advanced_find->{branch_combo_details}->{preset} = 0;
+
+ # Get the new list of branches.
+
+ $advanced_find->{appbar}->set_status("Fetching branch list");
+ gtk2_update();
+ $advanced_find->{mtn}->branches(address@hidden)
+ if (defined($advanced_find->{mtn}));
+ $advanced_find->{branch_combo_details}->{list} = address@hidden;
+
+ # Update the branch list combobox.
+
+ $advanced_find->{appbar}->set_status("Populating branch list");
+ gtk2_update();
+ my $counter = 1;
+ $advanced_find->{branch_combo}->get_model()->clear();
+ foreach my $branch (@branch_list)
+ {
+ $advanced_find->{branch_combo}->append_text($branch);
+ $advanced_find->{appbar}->set_progress_percentage
+ ($counter ++ / scalar(@branch_list));
+ gtk2_update();
+ }
+ $advanced_find->{branch_combo}->child()->
+ set_text($advanced_find->{branch_combo_details}->{value});
+ $advanced_find->{appbar}->set_progress_percentage(0);
+ $advanced_find->{appbar}->set_status("");
+ gtk2_update();
+
+ }
+
+ # The list of available revisions has changed.
+
+ if ($changed & REVISION)
+ {
+
+ my @revision_list;
+
+ # Reset the revision selection.
+
+ $advanced_find->{revision_combo_details}->{completion} = undef;
+ if (! $advanced_find->{revision_combo_details}->{preset})
+ {
+ $advanced_find->{revision_combo_details}->{completed} = 0;
+ $advanced_find->{revision_combo_details}->{value} = "";
+ }
+ $advanced_find->{revision_combo_details}->{preset} = 0;
+
+ # Get the new list of revisions.
+
+ if ($advanced_find->{branch_combo_details}->{completed})
+ {
+ $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_tick}->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();
+ }
+ $advanced_find->{revision_combo_details}->{list} = address@hidden;
+
+ # Update the revision list combobox.
+
+ $advanced_find->{appbar}->set_progress_percentage(0);
+ $advanced_find->{appbar}->set_status("Populating revision list");
+ gtk2_update();
+ my $counter = 1;
+ $advanced_find->{revision_combo}->get_model()->clear();
+ foreach my $revision (@revision_list)
+ {
+ $advanced_find->{revision_combo}->append_text($revision);
+ $advanced_find->{appbar}->set_progress_percentage
+ ($counter ++ / scalar(@revision_list));
+ gtk2_update();
+ }
+ $advanced_find->{revision_combo}->child()->
+ set_text($advanced_find->{revision_combo_details}->{value});
+ $advanced_find->{appbar}->set_progress_percentage(0);
+ $advanced_find->{appbar}->set_status("");
+ gtk2_update();
+
+ }
+
+ # The list of displayed revisions has changed.
+
+ if ($changed & REVISION_LIST)
+ {
+
+ my($counter,
+ @revision_ids);
+
+ # Reset the revisions tree view.
+
+ $advanced_find->{revisions_liststore}->clear();
+ $advanced_find->{revisions_treeview_details}->{value} = "";
+
+ # Get the list of matching revisions.
+
+ $advanced_find->{appbar}->set_status("Finding revisions");
+ gtk2_update();
+ if ($advanced_find->{simple_query_radiobutton}->get_active())
+ {
+ if ($advanced_find->{revision_combo_details}->{completed})
+ {
+ get_revision_ids($advanced_find, address@hidden);
+ }
+ }
+ else
+ {
+ my $query;
+ $query = $advanced_find->{search_term_combo}->child()->get_text();
+
+ # Remember the user can type in any old rubbish with advanced
+ # queries! So protect ourselves.
+
+ Monotone::AutomateStdio->register_error_handler
+ ("both",
+ sub {
+ my($severity, $message) = @_;
+ my $dialog;
+ $dialog = Gtk2::MessageDialog->new_with_markup
+ ($advanced_find->{window},
+ ["modal"],
+ "warning",
+ "close",
+ sprintf("Problem with your query, Monotone "
+ . "gave:\n%s",
+ Glib::Markup::escape_text($message)));
+ $dialog->run();
+ $dialog->destroy();
+ die("Bad query"); });
+ eval
+ {
+ $advanced_find->{mtn}->select(address@hidden, $query);
+ };
+
+ # If the query was valid the store it in the history.
+
+ if (! $@)
+ {
+ my $found;
+ if (scalar(@revision_ids) == 0)
+ {
+ my $dialog;
+ $dialog = Gtk2::MessageDialog->new
+ ($advanced_find->{window},
+ ["modal"],
+ "info",
+ "close",
+ "No revisions matched your query");
+ $dialog->run();
+ $dialog->destroy();
+ }
+ $found = 0;
+ foreach my $entry (@{$advanced_find->{query_history}})
+ {
+ if ($entry eq $query)
+ {
+ $found = 1;
+ last;
+ }
+ }
+ if (! $found)
+ {
+ if (unshift(@{$advanced_find->{query_history}}, $query)
+ > 20)
+ {
+ pop(@{$advanced_find->{query_history}});
+ }
+ $advanced_find->{search_term_combo}->get_model()->clear();
+ foreach my $entry (@{$advanced_find->{query_history}})
+ {
+ $advanced_find->{search_term_combo}->
+ append_text($entry);
+ }
+ }
+ }
+ Monotone::AutomateStdio->register_error_handler
+ ("both", \&mtn_error_handler);
+
+ }
+ $advanced_find->{mtn}->toposort(address@hidden, @revision_ids);
+ @revision_ids = reverse(@revision_ids);
+
+ # Update the revisions tree view.
+
+ $advanced_find->{appbar}->set_status("Populating revision details");
+ $counter = 1;
+ foreach my $item (@revision_ids)
+ {
+ $advanced_find->{revisions_liststore}->
+ set($advanced_find->{revisions_liststore}->append(),
+ 0, $item);
+ $advanced_find->{appbar}->set_progress_percentage
+ ($counter ++ / scalar(@revision_ids));
+ gtk2_update();
+ }
+ $advanced_find->{revisions_treeview}->scroll_to_point(0, 0)
+ if ($advanced_find->{revisions_treeview}->realized());
+
+ $advanced_find->{appbar}->set_progress_percentage(0);
+ $advanced_find->{appbar}->set_status("");
+ gtk2_update();
+
+ }
+
+ # The selected revision has changed.
+
+ if ($changed & REVISION_DETAILS)
+ {
+
+ if ($advanced_find->{revisions_treeview_details}->{value} ne "")
+ {
+ if ($advanced_find->{selected_revision_label}->get_text()
+ ne $advanced_find->{revisions_treeview_details}->{value})
+ {
+ my ($branch,
+ @certs_list,
+ @revision_details);
+
+ $advanced_find->{details_buffer}->set_text("");
+ $advanced_find->{mtn}->certs
+ (address@hidden,
+ $advanced_find->{revisions_treeview_details}->{value});
+ $advanced_find->{mtn}->get_revision
+ (address@hidden,
+ $advanced_find->{revisions_treeview_details}->{value});
+ generate_revision_report
+ ($advanced_find->{details_buffer},
+ $advanced_find->{revisions_treeview_details}->{value},
+ address@hidden,
+ "",
+ address@hidden);
+
+ # Scroll back up to the top left.
+
+ if ($advanced_find->{details_scrolledwindow}->realized())
+ {
+ $advanced_find->{details_scrolledwindow}->
+ get_vadjustment()->set_value(0);
+ $advanced_find->{details_scrolledwindow}->
+ get_hadjustment()->set_value(0);
+ }
+
+ # Update the selected branch and revision labels.
+
+ $branch = "";
+ foreach my $cert (@certs_list)
+ {
+ if ($cert->{name} eq "branch")
+ {
+ $branch = $cert->{value};
+ last;
+ }
+ }
+ set_label_value($advanced_find->{selected_branch_label},
+ $branch);
+ set_label_value($advanced_find->{selected_revision_label},
+ $advanced_find->{revisions_treeview_details}->
+ {value});
+
+ $advanced_find->{ok_button}->set_sensitive(TRUE);
+ }
+ }
+ else
+ {
+ $advanced_find->{ok_button}->set_sensitive(FALSE);
+ $advanced_find->{details_buffer}->set_text("");
+ set_label_value($advanced_find->{selected_branch_label}, "");
+ set_label_value($advanced_find->{selected_revision_label}, "");
+ }
+
+ }
+
+ $advanced_find->{appbar}->pop();
+ make_busy($advanced_find, 0) if ($made_busy);
+
+}
+
+1;
============================================================
--- ChangeLog.pm bbaeb9fe833b6c05b752ed17023c943946ed0837
+++ ChangeLog.pm bbaeb9fe833b6c05b752ed17023c943946ed0837
@@ -0,0 +1,173 @@
+##############################################################################
+#
+# File Name - ChangeLog.pm
+#
+# Description - The change log module for the mtn-browse application. This
+# module contains all the routines for implementing the
+# change log window.
+#
+# Author - A.E.Cooper.
+#
+# Legal Stuff - Copyright (c) 2007 Anthony Edward Cooper
+# .
+#
+# This program is free software; you can redistribute it
+# and/or modify it under the terms of the GNU General Public
+# License as published by the Free Software Foundation;
+# either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be
+# useful, but WITHOUT ANY WARRANTY; without even the implied
+# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public
+# License along with this software; if not, write to the Free
+# Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307 USA.
+#
+##############################################################################
+#
+##############################################################################
+#
+# GLOBAL DATA FOR THIS MODULE
+#
+##############################################################################
+
+
+
+# ***** DIRECTIVES *****
+
+require 5.008;
+
+use lib "/home/aecoope/perl";
+use strict;
+
+# ***** FUNCTIONAL PROTOTYPES FOR THIS FILE *****
+
+# Public routines.
+
+sub get_change_log_window();
+#
+##############################################################################
+#
+# Routine - get_change_log_window
+#
+# Description - Creates or prepares an existing a change log window for
+# use.
+#
+# Data - Return Value : A reference to the newly created or unused
+# change log instance record.
+#
+##############################################################################
+
+
+
+sub get_change_log_window()
+{
+
+ my ($font,
+ $height,
+ $instance,
+ $width);
+
+ foreach my $window (@windows)
+ {
+ if ($window->{type} eq "change_log_window"
+ && ! $window->{window}->mapped())
+ {
+ $instance = $window;
+ last;
+ }
+ }
+
+ # Create a new change log window if an unused one wasn't found, otherwise
+ # reuse an existing unused one.
+
+ if (! defined($instance))
+ {
+ $instance = {};
+ $instance->{type} = "change_log_window";
+ $instance->{glade} =
+ Gtk2::GladeXML->new("../mtn-browse.glade", "changelog_window");
+
+ # Flag to stop recursive calling of callbacks.
+
+ $instance->{in_cb} = 0;
+
+ # Connect Glade registered signal handlers.
+
+ $instance->{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); },
+ $instance);
+
+ # Get the widgets that we are interested in.
+
+ $instance->{window} =
+ $instance->{glade}->get_widget("changelog_window");
+ $instance->{window}->set_icon($app_icon);
+ $instance->{changelog_textview} =
+ $instance->{glade}->get_widget("changelog_textview");
+ $instance->{changelog_scrolledwindow} =
+ $instance->{glade}->get_widget("changelog_scrolledwindow");
+
+ # Setup the changelog window deletion handler.
+
+ $instance->{window}->signal_connect
+ ("delete_event",
+ sub {
+ my($widget, $event, $instance) = @_;
+ return TRUE if ($instance->{in_cb});
+ local $instance->{in_cb} = 1;
+ $widget->hide();
+ $instance->{changelog_buffer}->set_text("");
+ return TRUE;
+ },
+ $instance);
+
+ # Setup the revision changelog viewer.
+
+ $instance->{changelog_buffer} =
+ $instance->{changelog_textview}->get_buffer();
+ create_format_tags($instance->{changelog_buffer});
+ $font = Gtk2::Pango::FontDescription->from_string("monospace 10");
+ $instance->{changelog_textview}->modify_font($font)
+ if (defined($font));
+
+ $instance->{grab_widget} = $instance->{window};
+
+ # Setup the list of windows that can be made busy for this application
+ # window.
+
+ $instance->{busy_windows} = [];
+ push(@{$instance->{busy_windows}}, $instance->{window}->window());
+ push(@{$instance->{busy_windows}},
+ $instance->{changelog_textview}->get_window("text"));
+
+ push(@windows, $instance);
+ }
+ else
+ {
+ $instance->{in_cb} = 0;
+ ($width, $height) = $instance->{window}->get_default_size();
+ $instance->{window}->resize($width, $height);
+ }
+
+ # Empty out the contents.
+
+ $instance->{changelog_buffer}->set_text("");
+
+ return $instance;
+
+}
+
+1;
============================================================
--- ComboAutoCompletion.pm a45bbf62e7f2efa0377247431714280e5cfd48cd
+++ ComboAutoCompletion.pm a45bbf62e7f2efa0377247431714280e5cfd48cd
@@ -0,0 +1,263 @@
+##############################################################################
+#
+# File Name - ComboAutoCompletion.pm
+#
+# Description - The combo box auto-completion utilities module for the
+# mtn-browse application. This module contains assorted
+# routines that implement auto-completion for alll branch and
+# revision combo boxes.
+#
+# Author - A.E.Cooper.
+#
+# Legal Stuff - Copyright (c) 2007 Anthony Edward Cooper
+# .
+#
+# This program is free software; you can redistribute it
+# and/or modify it under the terms of the GNU General Public
+# License as published by the Free Software Foundation;
+# either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be
+# useful, but WITHOUT ANY WARRANTY; without even the implied
+# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public
+# License along with this software; if not, write to the Free
+# Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307 USA.
+#
+##############################################################################
+#
+##############################################################################
+#
+# GLOBAL DATA FOR THIS MODULE
+#
+##############################################################################
+
+
+
+# ***** DIRECTIVES *****
+
+require 5.008;
+
+use strict;
+
+# ***** FUNCTIONAL PROTOTYPES FOR THIS FILE *****
+
+# Public routines.
+
+sub combo_changed_cb($$);
+sub combo_key_release_event_cb($$$);
+#
+##############################################################################
+#
+# Routine - combo_changed_cb
+#
+# Description - Callback routine called when the user changes the value of
+# a branch or revision comboboxentry by selecting an entry
+# from its pulldown list.
+#
+# Data - $widget : The widget object that received the signal.
+# $instance : The window instance that is associated with
+# this widget.
+#
+##############################################################################
+
+
+
+sub combo_changed_cb($$)
+{
+
+ my($widget, $instance) = @_;
+
+ return if ($instance->{in_cb});
+ local $instance->{in_cb} = 1;
+
+ my ($change_state,
+ $combo_details,
+ $item,
+ $value);
+
+ if ($widget == $instance->{branch_combo})
+ {
+ $change_state = BRANCH_CHANGED;
+ $combo_details = $instance->{branch_combo_details};
+ }
+ elsif ($widget == $instance->{revision_combo})
+ {
+ $change_state = REVISION_CHANGED;
+ $combo_details = $instance->{revision_combo_details};
+ }
+ elsif ($widget == $instance->{directory_combo})
+ {
+ $change_state = DIRECTORY_CHANGED;
+ $combo_details = $instance->{directory_combo_details};
+ }
+ else
+ {
+ return;
+ }
+
+ # For some reason best known to itself, Gtk+ calls this callback when the
+ # user presses a key for the first time (but not subsequently) after a
+ # value is selected via the pulldown menu. So we have to guard against
+ # this. Under these circumstances the key release callback is also called.
+ # So, put simply, only do something inside this callback if the value is a
+ # direct match to one in our list.
+
+ $value = $widget->child()->get_text();
+ foreach $item (@{$combo_details->{list}})
+ {
+ if ($value eq $item)
+ {
+ $combo_details->{value} = $value;
+ $combo_details->{completed} = 1;
+ $instance->{appbar}->clear_stack();
+ &{$instance->{update_handler}}($instance, $change_state);
+ last;
+ }
+ }
+
+}
+#
+##############################################################################
+#
+# Routine - combo_key_release_event_cb
+#
+# Description - Callback routine called when the user changes the value of
+# a branch or revision comboboxentry by entering a character
+# (key release event).
+#
+# Data - $widget : The widget object that received the signal.
+# $event : A Gtk2::Gdk::Event object describing the
+# event that has occurred.
+# $instance : The window instance that is associated with
+# this widget.
+# Return Value : TRUE if the event has been handled and needs
+# no further handling, otherwise false if the
+# event should carry on through the remaining
+# event handling.
+#
+##############################################################################
+
+
+
+sub combo_key_release_event_cb($$$)
+{
+
+ my($widget, $event, $instance) = @_;
+
+ return FALSE if ($instance->{in_cb});
+ local $instance->{in_cb} = 1;
+
+ my ($change_state,
+ $combo,
+ $combo_details,
+ $completed,
+ $completion,
+ $item,
+ $len,
+ $name,
+ $old_completed,
+ $old_value,
+ $value);
+
+ if ($widget == $instance->{branch_combo}->child())
+ {
+ $combo = $instance->{branch_combo};
+ $change_state = BRANCH_CHANGED;
+ $combo_details = $instance->{branch_combo_details};
+ $name = "branch";
+ }
+ elsif ($widget == $instance->{revision_combo}->child())
+ {
+ $combo = $instance->{revision_combo};
+ $change_state = REVISION_CHANGED;
+ $combo_details = $instance->{revision_combo_details};
+ $name = "revision";
+ }
+ elsif ($widget == $instance->{directory_combo}->child())
+ {
+ $combo = $instance->{directory_combo};
+ $change_state = DIRECTORY_CHANGED;
+ $combo_details = $instance->{directory_combo_details};
+ $name = "directory";
+ }
+ else
+ {
+ return FALSE;
+ }
+
+ # The user has typed something in then validate it and auto-complete it if
+ # necessary.
+
+ $completed = 0;
+ $old_completed = $combo_details->{completed};
+ $old_value = $combo_details->{value};
+ $value = $widget->get_text();
+ if ($value ne $old_value)
+ {
+
+ # Don't auto-complete if the user is simply deleting from the extreme
+ # right.
+
+ $len = length($value);
+ if ($value ne substr($old_value, 0, $len))
+ {
+
+ # So that the spacebar triggers auto-complete.
+
+ $value =~ s/\s+$//o;
+ $len = length($value);
+
+ $combo_details->{completion} =
+ Completion->new($combo_details->{list})
+ if (! defined($combo_details->{completion}));
+
+ if ($combo_details->{completion}->get_completion($value,
+ \$completion,
+ \$completed))
+ {
+ $instance->{appbar}->clear_stack();
+ }
+ else
+ {
+ $instance->{appbar}->
+ push("Invalid " . $name . " name`" . $value . "'");
+ }
+ $value = $completion;
+ $len = length($value);
+ $widget->set_text($value);
+ $widget->set_position(-1);
+
+ }
+ $combo_details->{value} = $value;
+ $combo_details->{last_typed_len} = length($value);
+ $combo_details->{completed} = $completed;
+
+ # Update the pulldown choices.
+
+ $combo->get_model()->clear();
+ foreach $item (@{$combo_details->{list}})
+ {
+ $combo->append_text($item) if ($value eq substr($item, 0, $len));
+ $combo_details->{completed} = 1
+ if (! $completed && $value eq $item);
+ }
+
+ # Update the window state on a significant change.
+
+ &{$instance->{update_handler}}($instance, $change_state)
+ if ($combo_details->{completed} != $old_completed
+ || $combo_details->{value} ne $old_value);
+
+ }
+
+ return FALSE;
+
+}
+
+1;
============================================================
--- Completion.pm 1054100fadcf594ae9902d61f490abd7e7d718fd
+++ Completion.pm 1054100fadcf594ae9902d61f490abd7e7d718fd
@@ -0,0 +1,202 @@
+##############################################################################
+#
+# File Name - Completion.pm
+#
+# Description - Class module that provides a basic auto-completion
+# mechanism.
+#
+# Author - A.E.Cooper.
+#
+# Legal Stuff - Copyright (c) 2007 Anthony Edward Cooper
+# .
+#
+# This library is free software; you can redistribute it
+# and/or modify it under the terms of the GNU Lesser General
+# Public License as published by the Free Software
+# Foundation; either version 3 of the License, or (at your
+# option) any later version.
+#
+# This library is distributed in the hope that it will be
+# useful, but WITHOUT ANY WARRANTY; without even the implied
+# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE. See the GNU Lesser General Public License for
+# more details.
+#
+# You should have received a copy of the GNU Lesser General
+# Public License along with this library; if not, write to
+# the Free Software Foundation, Inc., 59 Temple Place - Suite
+# 330, Boston, MA 02111-1307 USA.
+#
+##############################################################################
+#
+##############################################################################
+#
+# Package - Completion
+#
+# Description - A class for using Monotone's automate stdio interface.
+#
+##############################################################################
+
+
+
+# ***** PACKAGE DECLARATION *****
+
+package Completion;
+
+# ***** DIRECTIVES *****
+
+require 5.008;
+
+use strict;
+use integer;
+
+# ***** PACKAGE INFORMATION *****
+
+# We are just a base class.
+
+use base qw(Exporter);
+
+our @EXPORT = qw();
+our @EXPORT_OK = qw();
+our $VERSION = 0.1;
+
+# ***** FUNCTIONAL PROTOTYPES FOR THIS FILE *****
+
+# Public methods.
+
+sub get_completion($$$$);
+sub new($@);
+#
+##############################################################################
+#
+# Routine - new
+#
+# Description - Class constructor.
+#
+# Data - $class : Either the name of the class that is to be
+# created or an object of that class.
+# $list : A reference to a list of all possible
+# completions.
+# Return Value : A reference to the newly created object.
+#
+##############################################################################
+
+
+
+sub new($@)
+{
+
+ my ($class, $list) = @_;
+ $class = ref($class) if ref($class);
+
+ my($char,
+ $item,
+ $level,
+ $this);
+
+ $this = {};
+ $this->{tree} = {};
+
+ # Build up a hash tree for the list of possible items.
+
+ foreach $item (@$list)
+ {
+
+ # Build up nodes for an item.
+
+ $level = $this->{tree};
+ foreach $char (split(//o, $item))
+ {
+ if (! exists($level->{$char}))
+ {
+ $level->{$char} = {};
+ }
+ $level = $level->{$char};
+ }
+
+ # By adding this dummy node here it stops the auto-complete moving too
+ # far should another item extend beyond this point. I.e. auto
+ # completion stops at `net.venge.monotone.contrib' and not
+ # `net.venge.monotone.contrib.'. You could simply think of this node as
+ # an `end of string' token if you prefer.
+
+ $level->{""} = "";
+
+ }
+
+ return bless($this, $class);
+
+}
+#
+##############################################################################
+#
+# Routine - get_completion
+#
+# Description - Given a value, work out the largest unique match.
+#
+# Data - $this : The object.
+# $value : The value to be completed.
+# $result : A reference to a buffer that is to contain
+# the result.
+# $complete : A reference to a boolean that is to contain
+# a `result is complete' indicator.
+# Return Value : True if $value was expanded, otherwise false
+# if $value had to be truncated due to no
+# match (the maximum valid completion is still
+# returned in $result).
+#
+##############################################################################
+
+
+
+sub get_completion($$$$)
+{
+
+ my($this, $value, $result, $complete) = @_;
+
+ my($char,
+ $level);
+
+ # Lookup value, stopping when it becomes ambiguous or we get to the end of
+ # $value.
+
+ $level = $this->{tree};
+ $$result = "";
+ foreach $char (split(//o, $value))
+ {
+ last unless (exists($level->{$char}));
+ $level = $level->{$char};
+ $$result .= $char;
+ }
+
+ # Detect truncations.
+
+ return if (length($value) > length($$result));
+
+ # Now try and expand it further.
+
+ while (defined(%$level) && keys(%$level) == 1)
+ {
+ ($char) = keys(%$level);
+ $$result .= $char;
+ $level = $level->{$char};
+ }
+
+ # Detect complete completions (doesn't mean to say that it can't be
+ # extended, just that as it stands at the moment $$result does contain a
+ # valid unique value).
+
+ if (! defined(%$level) || exists($level->{""}))
+ {
+ $$complete = 1;
+ }
+ else
+ {
+ $$complete = 0;
+ }
+
+ return 1;
+
+}
+
+1;
============================================================
--- Globals.pm 6b2a79a7d7abec36a2b78963676c17f65661b130
+++ Globals.pm 6b2a79a7d7abec36a2b78963676c17f65661b130
@@ -0,0 +1,182 @@
+##############################################################################
+#
+# File Name - Globals.pm
+#
+# Description - The global data module for the mtn-browse application. This
+# module contains all the global constants and variables used
+# throughout the application.
+#
+# Author - A.E.Cooper.
+#
+# Legal Stuff - Copyright (c) 2007 Anthony Edward Cooper
+# .
+#
+# This program is free software; you can redistribute it
+# and/or modify it under the terms of the GNU General Public
+# License as published by the Free Software Foundation;
+# either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be
+# useful, but WITHOUT ANY WARRANTY; without even the implied
+# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public
+# License along with this software; if not, write to the Free
+# Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307 USA.
+#
+##############################################################################
+#
+##############################################################################
+#
+# Package - Globals
+#
+# Description - See above.
+#
+##############################################################################
+
+
+
+# ***** PACKAGE DECLARATION *****
+
+package Globals;
+
+# ***** DIRECTIVES *****
+
+require 5.008;
+
+use strict;
+
+# ***** GLOBAL DATA DECLARATIONS *****
+
+# Constants used to represent the different groups of widgets.
+
+use constant BRANCH => 0x01;
+use constant DIRECTORY => 0x02;
+use constant DIRECTORY_VIEW => 0x04;
+use constant DISPLAY_OF_FILE => 0x08;
+use constant FILE => 0x10;
+use constant REVISION => 0x20;
+use constant REVISION_LIST => 0x02;
+use constant REVISION_DETAILS => 0x04;
+
+# Constants used to represent the different state changes. Read this as
+# `what has just been changed' => `what needs to be updated'.
+
+use constant BRANCH_CHANGED => (REVISION | DIRECTORY
+ | DIRECTORY_VIEW | FILE
+ | DISPLAY_OF_FILE);
+use constant DATABASE_CHANGED => 0xff;
+use constant DIRECTORY_CHANGED => (DIRECTORY_VIEW | FILE
+ | DISPLAY_OF_FILE);
+use constant DISPLAY_OF_FILE_CHANGED => 0x00;
+use constant FILE_CHANGED => (DISPLAY_OF_FILE);
+use constant NEW_FIND => 0xff;
+use constant REVISION_CHANGED => (DIRECTORY | REVISION_LIST
+ | DIRECTORY_VIEW | FILE
+ | DISPLAY_OF_FILE);
+use constant SELECTED_REVISION_CHANGED => (REVISION_DETAILS);
+
+# Text viewable application mime types.
+
+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);
+
+# Supported text mime types (used for syntax highlighting.
+
+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"
+ }
+ );
+
+# List of window instances.
+
+our @windows;
+
+# Assorted pixmaps.
+
+our($app_icon,
+ $line_image);
+
+# The busy cursor to use for the mouse.
+
+our $busy_cursor;
+
+# The tooltips widget.
+
+our $tooltips;
+
+# ***** PACKAGE INFORMATION *****
+
+use base qw(Exporter);
+
+our %EXPORT_TAGS = (
+ constants => [qw(BRANCH
+ BRANCH_CHANGED
+ DATABASE_CHANGED
+ DIRECTORY
+ DIRECTORY_CHANGED
+ DIRECTORY_VIEW
+ DISPLAY_OF_FILE
+ DISPLAY_OF_FILE_CHANGED
+ FILE
+ FILE_CHANGED
+ NEW_FIND
+ REVISION
+ REVISION_CHANGED
+ REVISION_DETAILS
+ REVISION_LIST
+ SELECTED_REVISION_CHANGED)],
+ variables => [qw($app_icon
+ $busy_cursor
+ $line_image
+ $tooltips
+ @text_mime_types
+ @text_viewable_app_mime_types
+ @windows)]);
+our @EXPORT = qw();
+Exporter::export_ok_tags(qw(constants variables));
+our $VERSION = 0.1;
+
+1;
============================================================
--- History.pm b73d263a8bef77075f30a740a1da06bf21207277
+++ History.pm b73d263a8bef77075f30a740a1da06bf21207277
@@ -0,0 +1,1366 @@
+##############################################################################
+#
+# File Name - mtn-browse
+#
+# Description - The history module for the mtn-browse application. This
+# module contains all the routines for implementing the
+# revision and file history windows.
+#
+# Author - A.E.Cooper.
+#
+# Legal Stuff - Copyright (c) 2007 Anthony Edward Cooper
+# .
+#
+# This program is free software; you can redistribute it
+# and/or modify it under the terms of the GNU General Public
+# License as published by the Free Software Foundation;
+# either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be
+# useful, but WITHOUT ANY WARRANTY; without even the implied
+# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public
+# License along with this software; if not, write to the Free
+# Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307 USA.
+#
+##############################################################################
+#
+##############################################################################
+#
+# GLOBAL DATA FOR THIS MODULE
+#
+##############################################################################
+
+
+
+# ***** DIRECTIVES *****
+
+require 5.008;
+
+use strict;
+
+# ***** GLOBAL DATA DECLARATIONS *****
+
+# Constants for the columns within the comparison files ListStore widget.
+
+use constant CLS_NAME_COLUMN => 0;
+use constant CLS_LINE_NR_COLUMN => 1;
+
+# ***** FUNCTIONAL PROTOTYPES FOR THIS FILE *****
+
+# Public routines.
+
+sub display_file_change_history($);
+sub display_revision_change_history($);
+
+# Private routines.
+
+sub coloured_revision_change_log_button_clicked_cb($$);
+sub compare_button_clicked_cb($$);
+sub compare_revisions($$$;$);
+sub file_comparison_combobox_changed_cb($$);
+sub get_file_history_helper($$$);
+sub get_history_window();
+sub get_revision_comparison_window();
+sub get_revision_history_helper($$$);
+sub history_list_button_clicked_cb($$);
+sub mtn_diff($$$$;$);
+#
+##############################################################################
+#
+# Routine - display_revision_change_history
+#
+# Description - Display a revision's change history, complete with
+# selection and comparison buttons.
+#
+# Data - $browser : The browser instance that is associated with
+# this widget.
+#
+##############################################################################
+
+
+
+sub display_revision_change_history($)
+{
+
+ my $browser = $_[0];
+
+ my ($button,
+ @certs_list,
+ $counter,
+ $file_name,
+ %history_hash,
+ $instance,
+ @revision_ids);
+
+ $instance = get_history_window();
+ local $instance->{in_cb} = 1;
+
+ $instance->{mtn} = $browser->{mtn};
+ $instance->{file_name} = undef;
+ get_revision_ids($browser, address@hidden);
+ $instance->{first_revision_id} = "";
+ $instance->{second_revision_id} = "";
+ $instance->{window}->set_title("Revision History For " . $revision_ids[0]);
+ $instance->{history_label}->set_markup("Revision History");
+ $instance->{window}->show_all();
+
+ make_busy($instance, 1);
+ $instance->{appbar}->push("");
+ gtk2_update();
+
+ $instance->{stop_button}->set_sensitive(TRUE);
+
+ # Get the list of file change revisions. Remember to include the current
+ # revision in the history.
+
+ $instance->{appbar}->set_progress_percentage(0);
+ $instance->{appbar}->set_status("Fetching revision list");
+ gtk2_update();
+ $history_hash{$revision_ids[0]} = 1;
+ get_revision_history_helper($instance, \%history_hash, $revision_ids[0]);
+
+ # Sort the list.
+
+ $instance->{appbar}->set_status("Sorting revision list");
+ gtk2_update();
+ $instance->{history} = [];
+ $instance->{mtn}->toposort($instance->{history}, keys(%history_hash));
+ %history_hash = ();
+ @{$instance->{history}} = reverse(@{$instance->{history}});
+
+ # Display the file's history.
+
+ $instance->{appbar}->set_progress_percentage(0);
+ $instance->{appbar}->set_status("Displaying file history");
+ gtk2_update();
+ $counter = 1;
+ $instance->{stop} = 0;
+ $instance->{history_buffer}->set_text("");
+ for my $revision_id (@{$instance->{history}})
+ {
+
+ # Print out the revision summary.
+
+ $instance->{mtn}->certs(address@hidden, $revision_id);
+ generate_revision_report($instance->{history_buffer},
+ $revision_id,
+ address@hidden,
+ "");
+ $instance->{history_buffer}->
+ insert($instance->{history_buffer}->get_end_iter(), "\n\n ");
+
+ # Add the buttons.
+
+ $button = Gtk2::Button->new("Select As Revision 1");
+ $button->signal_connect("clicked",
+ \&history_list_button_clicked_cb,
+ {instance => $instance,
+ revision_id => $revision_id,
+ button_type => "1"});
+ $tooltips->set_tip($button,
+ "Select this revision for comparison\n"
+ . "as the first revision");
+ $instance->{history_textview}->add_child_at_anchor
+ ($button,
+ $instance->{history_buffer}->
+ create_child_anchor($instance->{history_buffer}->
+ get_end_iter()));
+ $button->show_all();
+ $instance->{history_buffer}->
+ insert($instance->{history_buffer}->get_end_iter(), " ");
+
+ $button = Gtk2::Button->new("Select As Revision 2");
+ $button->signal_connect("clicked",
+ \&history_list_button_clicked_cb,
+ {instance => $instance,
+ revision_id => $revision_id,
+ button_type => "2"});
+ $tooltips->set_tip($button,
+ "Select this revision for comparison\n"
+ . "as the second revision");
+ $instance->{history_textview}->add_child_at_anchor
+ ($button,
+ $instance->{history_buffer}->
+ create_child_anchor($instance->{history_buffer}->
+ get_end_iter()));
+ $button->show_all();
+ $instance->{history_buffer}->
+ insert($instance->{history_buffer}->get_end_iter(), " ");
+
+ $button = Gtk2::Button->new("View Full Revision Change Log");
+ $button->signal_connect("clicked",
+ \&history_list_button_clicked_cb,
+ {instance => $instance,
+ revision_id => $revision_id,
+ button_type => "revision-changelog"});
+ $tooltips->set_tip($button, "View the revision's full change log");
+ $instance->{history_textview}->add_child_at_anchor
+ ($button,
+ $instance->{history_buffer}->
+ create_child_anchor($instance->{history_buffer}->
+ get_end_iter()));
+ $button->show_all();
+
+ # Stop if the user wants to.
+
+ last if ($instance->{stop});
+
+ # If we aren't at the end, print out the revision separator.
+
+ if ($counter < scalar(@{$instance->{history}}))
+ {
+ $instance->{history_buffer}->
+ insert($instance->{history_buffer}->get_end_iter(), "\n");
+ $instance->{history_buffer}->
+ insert_pixbuf($instance->{history_buffer}->get_end_iter(),
+ $line_image);
+ $instance->{history_buffer}->
+ insert($instance->{history_buffer}->get_end_iter(), "\n");
+ }
+
+ if ($counter % 100 == 0)
+ {
+ $instance->{appbar}->set_progress_percentage
+ ($counter / scalar(@{$instance->{history}}));
+ gtk2_update();
+ }
+ ++ $counter;
+
+ }
+
+ $instance->{stop_button}->set_sensitive(FALSE);
+ set_label_value($instance->{numbers_label}, $counter)
+ if ($instance->{stop});
+
+ # Make sure we are at the top.
+
+ $instance->{history_scrolledwindow}->get_vadjustment()->set_value(0);
+ $instance->{history_scrolledwindow}->get_hadjustment()->set_value(0);
+ $instance->{appbar}->set_progress_percentage(0);
+ $instance->{appbar}->set_status("");
+ gtk2_update();
+
+ $instance->{appbar}->pop();
+ make_busy($instance, 0);
+
+}
+#
+##############################################################################
+#
+# Routine - display_file_change_history
+#
+# Description - Display a file's change history, complete with selection
+# and comparison buttons.
+#
+# Data - $browser : The browser instance that is associated with
+# this widget.
+#
+##############################################################################
+
+
+
+sub display_file_change_history($)
+{
+
+ my $browser = $_[0];
+
+ my ($button,
+ @certs_list,
+ $counter,
+ $file_name,
+ %history_hash,
+ $instance,
+ @revision_ids);
+
+ $instance = get_history_window();
+ local $instance->{in_cb} = 1;
+
+ $instance->{mtn} = $browser->{mtn};
+ $instance->{file_name} =
+ $browser->{file_being_viewed}->{manifest_entry}->{name};
+ $instance->{first_revision_id} = "";
+ $instance->{second_revision_id} = "";
+ $instance->{window}->set_title
+ ("File History For " . $instance->{file_name});
+ $instance->{history_label}->set_markup("File History");
+ $instance->{window}->show_all();
+
+ make_busy($instance, 1);
+ $instance->{appbar}->push("");
+ gtk2_update();
+
+ # Get the list of file change revisions. Remember that a warning is
+ # generated when one goes back beyond a file's addition revision, so
+ # temporarily disable the warning handler.
+
+ $instance->{appbar}->set_progress_percentage(0);
+ $instance->{appbar}->set_status("Fetching revision list");
+ $instance->{stop_button}->set_sensitive(TRUE);
+ gtk2_update();
+ Monotone::AutomateStdio->register_error_handler("warning");
+ get_revision_ids($browser, address@hidden);
+ get_file_history_helper($instance, \%history_hash, $revision_ids[0]);
+ Monotone::AutomateStdio->register_error_handler("both",
+ \&mtn_error_handler);
+ $instance->{stop_button}->set_sensitive(FALSE);
+
+ # Sort the list.
+
+ $instance->{appbar}->set_status("Sorting revision list");
+ gtk2_update();
+ $instance->{history} = [];
+ $instance->{mtn}->toposort($instance->{history}, keys(%history_hash));
+ %history_hash = ();
+ @{$instance->{history}} = reverse(@{$instance->{history}});
+
+ # Display the file's history.
+
+ $instance->{appbar}->set_progress_percentage(0);
+ $instance->{appbar}->set_status("Displaying file history");
+ gtk2_update();
+ $counter = 1;
+ $instance->{history_buffer}->set_text("");
+ for my $revision_id (@{$instance->{history}})
+ {
+
+ # Print out the revision summary.
+
+ $instance->{mtn}->certs(address@hidden, $revision_id);
+ generate_revision_report($instance->{history_buffer},
+ $revision_id,
+ address@hidden,
+ "");
+ $instance->{history_buffer}->
+ insert($instance->{history_buffer}->get_end_iter(), "\n\n ");
+
+ # Add the buttons.
+
+ $button = Gtk2::Button->new("Select As File 1");
+ $button->signal_connect("clicked",
+ \&history_list_button_clicked_cb,
+ {instance => $instance,
+ revision_id => $revision_id,
+ button_type => "1"});
+ $tooltips->set_tip($button,
+ "Select this file revision for\n"
+ . "comparison as the first file");
+ $instance->{history_textview}->add_child_at_anchor
+ ($button,
+ $instance->{history_buffer}->
+ create_child_anchor($instance->{history_buffer}->
+ get_end_iter()));
+ $button->show_all();
+ $instance->{history_buffer}->
+ insert($instance->{history_buffer}->get_end_iter(), " ");
+
+ $button = Gtk2::Button->new("Select As File 2");
+ $button->signal_connect("clicked",
+ \&history_list_button_clicked_cb,
+ {instance => $instance,
+ revision_id => $revision_id,
+ button_type => "2"});
+ $tooltips->set_tip($button,
+ "Select this file revision for\n"
+ . "comparison as the second file");
+ $instance->{history_textview}->add_child_at_anchor
+ ($button,
+ $instance->{history_buffer}->
+ create_child_anchor($instance->{history_buffer}->
+ get_end_iter()));
+ $button->show_all();
+ $instance->{history_buffer}->
+ insert($instance->{history_buffer}->get_end_iter(), " ");
+
+ $button = Gtk2::Button->new("View Full Revision Change Log");
+ $button->signal_connect("clicked",
+ \&history_list_button_clicked_cb,
+ {instance => $instance,
+ revision_id => $revision_id,
+ button_type => "revision-changelog"});
+ $tooltips->set_tip($button, "View the revision's full change log");
+ $instance->{history_textview}->add_child_at_anchor
+ ($button,
+ $instance->{history_buffer}->
+ create_child_anchor($instance->{history_buffer}->
+ get_end_iter()));
+ $button->show_all();
+
+ # If we aren't at the end, print out the revision separator.
+
+ if ($counter < scalar(@{$instance->{history}}))
+ {
+ $instance->{history_buffer}->
+ insert($instance->{history_buffer}->get_end_iter(), "\n");
+ $instance->{history_buffer}->
+ insert_pixbuf($instance->{history_buffer}->get_end_iter(),
+ $line_image);
+ $instance->{history_buffer}->
+ insert($instance->{history_buffer}->get_end_iter(), "\n");
+ }
+
+ $instance->{appbar}->set_progress_percentage
+ ($counter ++ / scalar(@{$instance->{history}}));
+ gtk2_update();
+
+ }
+
+ # Make sure we are at the top.
+
+ $instance->{history_scrolledwindow}->get_vadjustment()->set_value(0);
+ $instance->{history_scrolledwindow}->get_hadjustment()->set_value(0);
+ $instance->{appbar}->set_progress_percentage(0);
+ $instance->{appbar}->set_status("");
+ gtk2_update();
+
+ $instance->{appbar}->pop();
+ make_busy($instance, 0);
+
+}
+#
+##############################################################################
+#
+# Routine - history_list_button_clicked_cb
+#
+# Description - Callback routine called when the user clicks on any of the
+# buttons displayed in the history list in a history window.
+#
+# Data - $widget : The widget object that received the signal.
+# $details : A reference to an anonymous hash containing the
+# window instance, revision and action that is
+# associated with this widget.
+#
+##############################################################################
+
+
+
+sub history_list_button_clicked_cb($$)
+{
+
+ my($widget, $details) = @_;
+
+ my($cl_instance,
+ $instance,
+ $revision_id);
+
+ $instance = $details->{instance};
+ $revision_id = $details->{revision_id};
+
+ return if ($instance->{in_cb});
+ local $instance->{in_cb} = 1;
+
+ if ($details->{button_type} eq "1" || $details->{button_type} eq "2")
+ {
+ if ($details->{button_type} eq "1")
+ {
+ $instance->{first_revision_id} = $revision_id;
+ set_label_value($instance->{revision_id_1_label}, $revision_id);
+ if ($instance->{first_revision_id}
+ eq $instance->{second_revision_id})
+ {
+ $instance->{second_revision_id} = "";
+ set_label_value($instance->{revision_id_2_label}, "");
+ }
+ }
+ else
+ {
+ $instance->{second_revision_id} = $revision_id;
+ set_label_value($instance->{revision_id_2_label}, $revision_id);
+ if ($instance->{second_revision_id}
+ eq $instance->{first_revision_id})
+ {
+ $instance->{first_revision_id} = "";
+ set_label_value($instance->{revision_id_1_label}, "");
+ }
+ }
+ if ($instance->{first_revision_id} ne ""
+ && $instance->{second_revision_id} ne "")
+ {
+ $instance->{compare_button}->set_sensitive(TRUE);
+ }
+ else
+ {
+ $instance->{compare_button}->set_sensitive(FALSE);
+ }
+ }
+ else
+ {
+
+ my(@certs_list,
+ @revision_details);
+
+ # Display the full revision change log.
+
+ $cl_instance = get_change_log_window();
+ $cl_instance->{changelog_buffer}->set_text("");
+ $cl_instance->{window}->set_title("Revision " . $revision_id);
+ $instance->{mtn}->certs(address@hidden, $revision_id);
+ $instance->{mtn}->get_revision(address@hidden, $revision_id);
+ generate_revision_report($cl_instance->{changelog_buffer},
+ $revision_id,
+ address@hidden,
+ "",
+ address@hidden);
+ if ($cl_instance->{changelog_scrolledwindow}->realized())
+ {
+ $cl_instance->{changelog_scrolledwindow}->get_vadjustment()->
+ set_value(0);
+ $cl_instance->{changelog_scrolledwindow}->get_hadjustment()->
+ set_value(0);
+ }
+ $cl_instance->{window}->show_all();
+
+ }
+
+}
+#
+##############################################################################
+#
+# Routine - compare_button_clicked_cb
+#
+# Description - Callback routine called when the user clicks on the
+# revision comparison button in a history window.
+#
+# Data - $widget : The widget object that received the signal.
+# $instance : The window instance that is associated with
+# this widget.
+#
+##############################################################################
+
+
+
+sub compare_button_clicked_cb($$)
+{
+
+ my($widget, $instance) = @_;
+
+ return if ($instance->{in_cb});
+ local $instance->{in_cb} = 1;
+
+ my @revision_ids;
+
+ # Sort the revisions by date, oldest first.
+
+ @revision_ids = ($instance->{first_revision_id},
+ $instance->{second_revision_id});
+ $instance->{mtn}->toposort(address@hidden, @revision_ids);
+
+ # Now compare them.
+
+ compare_revisions($instance->{mtn},
+ $revision_ids[0],
+ $revision_ids[1],
+ $instance->{file_name});
+
+}
+#
+##############################################################################
+#
+# Routine - file_comparison_combobox_changed_cb
+#
+# Description - Callback routine called when the user changes the value of
+# the file combobox by selecting an entry from its pulldown
+# list in a revision comparison window.
+#
+# Data - $widget : The widget object that received the signal.
+# $instance : The window instance that is associated with
+# this widget.
+#
+##############################################################################
+
+
+
+sub file_comparison_combobox_changed_cb($$)
+{
+
+ my($widget, $instance) = @_;
+
+ return if ($instance->{in_cb});
+ local $instance->{in_cb} = 1;
+
+ my($iter,
+ $line_nr);
+
+ # Simply get the currently selected term and then enable/disable the text
+ # entry and date entry widgets accordingly.
+
+ $line_nr = $instance->{file_combo}->get_model()->get
+ ($instance->{file_combo}->get_active_iter(), CLS_LINE_NR_COLUMN);
+ $iter = $instance->{comparison_buffer}->get_iter_at_line($line_nr);
+ $instance->{comparison_textview}->scroll_to_iter($iter, 0, TRUE, 0, 0);
+
+}
+#
+##############################################################################
+#
+# Routine - coloured_revision_change_log_button_clicked_cb
+#
+# Description - Callback routine called when the user clicks on either of
+# the two coloured revision change log buttons in a revision
+# comparison window.
+#
+# Data - $widget : The widget object that received the signal.
+# $details : A reference to an anonymous hash containing the
+# window instance, revision and action that is
+# associated with this widget.
+#
+##############################################################################
+
+
+
+sub coloured_revision_change_log_button_clicked_cb($$)
+{
+
+ my($widget, $instance) = @_;
+
+ return if ($instance->{in_cb});
+ local $instance->{in_cb} = 1;
+
+ my(@certs_list,
+ $cl_instance,
+ $colour,
+ @revision_details,
+ $revision_id);
+
+ # Work out what revision id to use.
+
+ if ($widget == $instance->{red_revision_button})
+ {
+ $revision_id = $instance->{red_revision_id};
+ $colour = "red";
+ }
+ else
+ {
+ $revision_id = $instance->{green_revision_id};
+ $colour = "green";
+ }
+
+ # Display the full revision change log.
+
+ $cl_instance = get_change_log_window();
+ $cl_instance->{changelog_buffer}->set_text("");
+ $cl_instance->{window}->set_title("Revision " . $revision_id);
+ $instance->{mtn}->certs(address@hidden, $revision_id);
+ $instance->{mtn}->get_revision(address@hidden, $revision_id);
+ generate_revision_report($cl_instance->{changelog_buffer},
+ $revision_id,
+ address@hidden,
+ $colour,
+ address@hidden);
+ if ($cl_instance->{changelog_scrolledwindow}->realized())
+ {
+ $cl_instance->{changelog_scrolledwindow}->get_vadjustment()->
+ set_value(0);
+ $cl_instance->{changelog_scrolledwindow}->get_hadjustment()->
+ set_value(0);
+ }
+ $cl_instance->{window}->show_all();
+
+}
+#
+##############################################################################
+#
+# Routine - get_history_window
+#
+# Description - Creates or prepares an existing history window for use.
+#
+# Data - Return Value : A reference to the newly created or unused
+# history instance record.
+#
+##############################################################################
+
+
+
+sub get_history_window()
+{
+
+ my ($font,
+ $height,
+ $instance,
+ $width);
+
+ # Look for an unused window first.
+
+ foreach my $window (@windows)
+ {
+ if ($window->{type} eq "history_window"
+ && ! $window->{window}->mapped())
+ {
+ $instance = $window;
+ last;
+ }
+ }
+
+ # Create a new file history window if an unused one wasn't found, otherwise
+ # reuse an existing unused one.
+
+ if (! defined($instance))
+ {
+ $instance = {};
+ $instance->{type} = "history_window";
+ $instance->{glade} =
+ Gtk2::GladeXML->new("../mtn-browse.glade", "history_window");
+
+ # Flag to stop recursive calling of callbacks.
+
+ $instance->{in_cb} = 0;
+
+ # Connect Glade registered signal handlers.
+
+ $instance->{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); },
+ $instance);
+
+ # Get the widgets that we are interested in.
+
+ $instance->{window} = $instance->{glade}->get_widget("history_window");
+ $instance->{window}->set_icon($app_icon);
+ $instance->{appbar} = $instance->{glade}->get_widget("appbar");
+ $instance->{history_label} =
+ $instance->{glade}->get_widget("history_label");
+ $instance->{history_textview} =
+ $instance->{glade}->get_widget("history_textview");
+ $instance->{history_scrolledwindow} =
+ $instance->{glade}->get_widget("history_scrolledwindow");
+ $instance->{stop_button} =
+ $instance->{glade}->get_widget("stop_button");
+ $instance->{compare_button} =
+ $instance->{glade}->get_widget("compare_button");
+ $instance->{numbers_label} =
+ $instance->{glade}->get_widget("numbers_value_label");
+ $instance->{revision_id_1_label} =
+ $instance->{glade}->get_widget("revision_id_1_value_label");
+ $instance->{revision_id_2_label} =
+ $instance->{glade}->get_widget("revision_id_2_value_label");
+
+ # Setup the file history callbacks.
+
+ $instance->{window}->signal_connect
+ ("delete_event",
+ sub {
+ my($widget, $event, $instance) = @_;
+ return TRUE if ($instance->{in_cb});
+ local $instance->{in_cb} = 1;
+ $widget->hide();
+ $instance->{history_buffer}->set_text("");
+ return TRUE;
+ },
+ $instance);
+ $instance->{stop_button}->signal_connect
+ ("clicked", sub { $_[1]->{stop} = 1; }, $instance);
+
+ # Setup the file history viewer.
+
+ $instance->{history_buffer} =
+ $instance->{history_textview}->get_buffer();
+ create_format_tags($instance->{history_buffer});
+ $font = Gtk2::Pango::FontDescription->from_string("monospace 10");
+ $instance->{history_textview}->modify_font($font) if (defined($font));
+
+ # Make the stop button the grab widget when busy, this is so the user
+ # can interrupt the history gathering process.
+
+ $instance->{grab_widget} = $instance->{stop_button};
+
+ # Setup the list of windows that can be made busy for this application
+ # window.
+
+ $instance->{busy_windows} = [];
+ push(@{$instance->{busy_windows}}, $instance->{window}->window());
+ push(@{$instance->{busy_windows}},
+ $instance->{history_textview}->get_window("text"));
+
+ push(@windows, $instance);
+ }
+ else
+ {
+ $instance->{in_cb} = 0;
+ ($width, $height) = $instance->{window}->get_default_size();
+ $instance->{window}->resize($width, $height);
+ $instance->{stop_button}->set_sensitive(FALSE);
+ $instance->{compare_button}->set_sensitive(FALSE);
+ set_label_value($instance->{numbers_label}, "");
+ set_label_value($instance->{revision_id_1_label}, "");
+ set_label_value($instance->{revision_id_2_label}, "");
+ }
+
+ $instance->{stop} = 0;
+
+ # Empty out the contents.
+
+ $instance->{history_buffer}->set_text("");
+
+ return $instance;
+
+}
+#
+##############################################################################
+#
+# Routine - get_file_history_helper
+#
+# Description - Recursive routine for getting the revisions in a file's
+# change history.
+#
+# Data - $instance : The file history window instance.
+# $hash : A reference to a hash that is to contain the
+# list of revision ids.
+# $revision_id : The revision id from where the search is to
+# commence.
+#
+##############################################################################
+
+
+
+sub get_file_history_helper($$$)
+{
+
+ my($instance, $hash, $revision_id) = @_;
+
+ return if ($instance->{stop});
+
+ my(@change_parents,
+ @parents);
+
+ $instance->{mtn}->get_content_changed(address@hidden,
+ $revision_id,
+ $instance->{file_name});
+ foreach my $revision (@change_parents)
+ {
+ if (! exists($hash->{$revision}))
+ {
+ $hash->{$revision} = 1;
+ set_label_value($instance->{numbers_label}, scalar(keys(%$hash)));
+ gtk2_update();
+ @parents = ();
+ $instance->{mtn}->parents(address@hidden, $revision);
+ foreach my $parent (@parents)
+ {
+ get_file_history_helper($instance, $hash, $parent);
+ }
+ }
+ }
+
+}
+#
+##############################################################################
+#
+# Routine - get_revision_history_helper
+#
+# Description - Recursive routine for getting the revisions in a revision's
+# change history.
+#
+# Data - $instance : The revision history window instance.
+# $hash : A reference to a hash that is to contain the
+# list of revision ids.
+# $revision_id : The revision id from where the search is to
+# commence.
+#
+##############################################################################
+
+
+
+sub get_revision_history_helper($$$)
+{
+
+ my($instance, $hash, $revision_id) = @_;
+
+ return if ($instance->{stop});
+
+ my @parents;
+
+ $instance->{mtn}->parents(address@hidden, $revision_id);
+ foreach my $parent (@parents)
+ {
+ if (! exists($hash->{$parent}))
+ {
+ $hash->{$parent} = 1;
+ set_label_value($instance->{numbers_label}, scalar(keys(%$hash)));
+ gtk2_update();
+ get_revision_history_helper($instance, $hash, $parent);
+ }
+ }
+
+}
+#
+##############################################################################
+#
+# Routine - get_revision_comparison_window
+#
+# Description - Creates or prepares an existing revision comparison window
+# for use.
+#
+# Data - Return Value : A reference to the newly created or unused
+# change log instance record.
+#
+##############################################################################
+
+
+
+sub get_revision_comparison_window()
+{
+
+ my ($font,
+ $height,
+ $instance,
+ $renderer,
+ $width);
+
+ # Look for an unused window first.
+
+ foreach my $window (@windows)
+ {
+ if ($window->{type} eq "revision_comparison_window"
+ && ! $window->{window}->mapped())
+ {
+ $instance = $window;
+ last;
+ }
+ }
+
+ # Create a new revision comparison window if an unused one wasn't found,
+ # otherwise reuse an existing unused one.
+
+ if (! defined($instance))
+ {
+ $instance = {};
+ $instance->{type} = "revision_comparison_window";
+ $instance->{glade} = Gtk2::GladeXML->new("../mtn-browse.glade",
+ "revision_comparison_window");
+
+ # Flag to stop recursive calling of callbacks.
+
+ $instance->{in_cb} = 0;
+
+ # Connect Glade registered signal handlers.
+
+ $instance->{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); },
+ $instance);
+
+ # Get the widgets that we are interested in.
+
+ $instance->{window} =
+ $instance->{glade}->get_widget("revision_comparison_window");
+ $instance->{window}->set_icon($app_icon);
+ $instance->{appbar} = $instance->{glade}->get_widget("appbar");
+ $instance->{comparison_label} =
+ $instance->{glade}->get_widget("comparison_label");
+ $instance->{file_combo} =
+ $instance->{glade}->get_widget("file_comparison_combobox");
+ $instance->{comparison_textview} =
+ $instance->{glade}->get_widget("comparison_textview");
+ $instance->{comparison_scrolledwindow} =
+ $instance->{glade}->get_widget("comparison_scrolledwindow");
+ $instance->{red_revision_button} =
+ $instance->{glade}->get_widget("red_revision_change_log_button");
+ $instance->{green_revision_button} =
+ $instance->{glade}->get_widget("green_revision_change_log_button");
+
+ # Setup the file history callbacks.
+
+ $instance->{window}->signal_connect
+ ("delete_event",
+ sub {
+ my($widget, $event, $instance) = @_;
+ return TRUE if ($instance->{in_cb});
+ local $instance->{in_cb} = 1;
+ $widget->hide();
+ $instance->{file_combo}->get_model()->clear();
+ $instance->{comparison_buffer}->set_text("");
+ return TRUE;
+ },
+ $instance);
+
+ # Setup the file combobox.
+
+ $instance->{file_combo}->
+ set_model(Gtk2::ListStore->new("Glib::String", "Glib::Int"));
+ $renderer = Gtk2::CellRendererText->new();
+ $instance->{file_combo}->pack_start($renderer, TRUE);
+ $instance->{file_combo}->add_attribute($renderer, "text" => 0);
+ $instance->{file_combo}->get_model()->set
+ ($instance->{file_combo}->get_model()->append(),
+ CLS_NAME_COLUMN, " ",
+ CLS_LINE_NR_COLUMN, 0);
+
+ # Setup the revision comparison viewer.
+
+ $instance->{comparison_buffer} =
+ $instance->{comparison_textview}->get_buffer();
+ create_format_tags($instance->{comparison_buffer});
+ $font = Gtk2::Pango::FontDescription->from_string("monospace 10");
+ $instance->{comparison_textview}->modify_font($font)
+ if (defined($font));
+
+ # Setup the list of windows that can be made busy for this application
+ # window.
+
+ $instance->{busy_windows} = [];
+ push(@{$instance->{busy_windows}}, $instance->{window}->window());
+ push(@{$instance->{busy_windows}},
+ $instance->{comparison_textview}->get_window("text"));
+
+ push(@windows, $instance);
+ }
+ else
+ {
+ $instance->{in_cb} = 0;
+ local $instance->{in_cb} = 1;
+ ($width, $height) = $instance->{window}->get_default_size();
+ $instance->{window}->resize($width, $height);
+ $instance->{file_combo}->get_model()->clear();
+ $instance->{appbar}->set_progress_percentage(0);
+ $instance->{appbar}->clear_stack();
+ }
+
+ # Empty out the contents.
+
+ $instance->{comparison_buffer}->set_text("");
+
+ return $instance;
+
+}
+#
+##############################################################################
+#
+# Routine - compare_revisions
+#
+# Description - Compares and then displays the differeneces between the two
+# specified revisions, optionally restricting it to the
+# specified file.
+#
+# Data - $mtn : The Monotone instance handle that is to be
+# used to do the comparison.
+# $revision_id_1 : The first revision id that is to be
+# compared.
+# $revision_id_2 : The second revision id that is to be
+# compared.
+# $file_name : Either the name of the file that the
+# revision comparison should be restricted
+# to or undef for a full revision
+# comparison.
+#
+##############################################################################
+
+
+
+sub compare_revisions($$$;$)
+{
+
+ my($mtn, $revision_id_1, $revision_id_2, $file_name) = @_;
+
+ my ($char,
+ @files,
+ $i,
+ $instance,
+ $is_binary,
+ $iter,
+ $len,
+ $line,
+ @lines,
+ $max_len,
+ $name,
+ $padding,
+ $rest);
+
+ $instance = get_revision_comparison_window();
+ local $instance->{in_cb} = 1;
+
+ $instance->{window}->set_title("Differences Between Revisions "
+ . $revision_id_1
+ . " And "
+ . $revision_id_2);
+ if (defined($file_name))
+ {
+ $instance->{comparison_label}->set_markup("File Comparison");
+ }
+ else
+ {
+ $instance->{comparison_label}->
+ set_markup("Revision Comparison");
+ }
+ $instance->{window}->show_all();
+ gtk2_update();
+
+ make_busy($instance, 1);
+ $instance->{appbar}->push("");
+ gtk2_update();
+
+ $instance->{mtn} = $mtn;
+ $instance->{red_revision_id} = $revision_id_1;
+ $instance->{green_revision_id} = $revision_id_2;
+
+ # Get Monotone to do the comparison.
+
+ $instance->{appbar}->set_status("Calculating differences");
+ gtk2_update();
+ mtn_diff(address@hidden,
+ $mtn->get_db_name(),
+ $revision_id_1,
+ $revision_id_2,
+ $file_name);
+
+ # Find the longest line for future padding.
+
+ $max_len = 0;
+ foreach my $line (@lines)
+ {
+ ($char, $rest) = unpack("a1a*", $line);
+ $rest =~ s/\s+$//o;
+ $rest = expand($rest);
+ $max_len = $len if (($len = length($rest)) > $max_len);
+ $line = $char . $rest;
+ }
+
+ # Display the result, highlighting according to the diff output. Remember
+ # the first two lines are just empty comment lines.
+
+ $instance->{appbar}->set_status("Formatting and displaying differences");
+ gtk2_update();
+ $padding = " " x $max_len;
+ $line = substr(" Summary" . $padding, 0, $max_len);
+ $instance->{comparison_buffer}->insert_with_tags_by_name
+ ($instance->{comparison_buffer}->get_end_iter(),
+ $line . "\n",
+ "compare-info");
+ for ($i = 1; $i <= $#lines; ++ $i)
+ {
+
+ # Deal with the initial comment lines that summarise the entire set of
+ # differences between the revisions.
+
+ if ($lines[$i] =~ m/^\#/o)
+ {
+ $line = substr($lines[$i], 1);
+ $instance->{comparison_buffer}->insert
+ ($instance->{comparison_buffer}->get_end_iter(),
+ $line . "\n");
+ }
+
+ # Deal with lines that introduces a new file comparison.
+
+ elsif ($lines[$i] =~ m/^==/o)
+ {
+
+ # Print separator.
+
+ $instance->{comparison_buffer}->
+ insert_pixbuf($instance->{comparison_buffer}->get_end_iter(),
+ $line_image);
+ $instance->{comparison_buffer}->
+ insert($instance->{comparison_buffer}->get_end_iter(),
+ "\n");
+
+ # Extract the file name, if this doesn't work then it is probably a
+ # comment stating that the file is binary.
+
+ ++ $i;
+ ($name) = ($lines[$i] =~ m/^--- (.+)\s+[0-9a-f]{40}$/o);
+ if (defined($name))
+ {
+ $is_binary = 0;
+ }
+ else
+ {
+ ($name) = ($lines[$i] =~ m/^\# (.+) is binary$/o);
+ $is_binary = 1;
+ }
+
+ # Print out the details for the first file.
+
+ $line = substr(substr($lines[$i], $is_binary ? 1 : 3) . $padding,
+ 0,
+ $max_len);
+ $instance->{comparison_buffer}->insert_with_tags_by_name
+ ($instance->{comparison_buffer}->get_end_iter(),
+ $line . "\n",
+ "compare-first-file-info");
+
+ # Store the file name and the starting line number so that the user
+ # can later jump straight to it using the file combobox.
+
+ $iter = $instance->{comparison_buffer}->get_end_iter();
+ $iter->backward_line();
+ $instance->{comparison_buffer}->create_mark($name, $iter, FALSE);
+ push(@files, {file_name => $name, line_nr => $iter->get_line()});
+
+ # Print out the details for the second file if there is one.
+
+ if (! $is_binary)
+ {
+ ++ $i;
+ $line = substr(substr($lines[$i], 3) . $padding, 0, $max_len);
+ $instance->{comparison_buffer}->insert_with_tags_by_name
+ ($instance->{comparison_buffer}->get_end_iter(),
+ $line . "\n",
+ "compare-second-file-info");
+ }
+
+ }
+
+ # Deal with difference context lines.
+
+ elsif ($lines[$i] =~ m/^@@/o)
+ {
+ $line = substr(substr($lines[$i], 2) . $padding, 0, $max_len);
+ $instance->{comparison_buffer}->insert_with_tags_by_name
+ ($instance->{comparison_buffer}->get_end_iter(),
+ $line . "\n",
+ "compare-info");
+ }
+
+ # Deal with - change lines.
+
+ elsif ($lines[$i] =~ m/^-/o)
+ {
+ $line = substr(substr($lines[$i], 1) . $padding, 0, $max_len);
+ $instance->{comparison_buffer}->insert_with_tags_by_name
+ ($instance->{comparison_buffer}->get_end_iter(),
+ $line . "\n",
+ "compare-first-file");
+ }
+
+ # Deal with + change lines.
+
+ elsif ($lines[$i] =~ m/^\+/o)
+ {
+ $line = substr(substr($lines[$i], 1) . $padding, 0, $max_len);
+ $instance->{comparison_buffer}->insert_with_tags_by_name
+ ($instance->{comparison_buffer}->get_end_iter(),
+ $line . "\n",
+ "compare-second-file");
+ }
+
+ # Print out the rest.
+
+ else
+ {
+ $line = substr($lines[$i], 1);
+ $instance->{comparison_buffer}->insert
+ ($instance->{comparison_buffer}->get_end_iter(),
+ $line . "\n");
+ }
+
+ if (($i % 100) == 0)
+ {
+ $instance->{appbar}->set_progress_percentage
+ (($i + 1) / scalar(@lines));
+ gtk2_update();
+ }
+
+ }
+
+ # Delete the trailing newline.
+
+ $iter = $instance->{comparison_buffer}->get_end_iter();
+ $instance->{comparison_buffer}->delete
+ ($iter, $instance->{comparison_buffer}->get_end_iter())
+ if ($iter->backward_char());
+
+ # Populate the file combobox.
+
+ $instance->{appbar}->set_progress_percentage(0);
+ $instance->{appbar}->set_status("Populating file list");
+ gtk2_update();
+ @files = sort({ $a->{file_name} cmp $b->{file_name} } @files);
+ $i = 1;
+ $instance->{file_combo}->get_model()->clear();
+ foreach my $file (@files)
+ {
+ $instance->{file_combo}->get_model()->set
+ ($instance->{file_combo}->get_model()->append(),
+ CLS_NAME_COLUMN, $file->{file_name},
+ CLS_LINE_NR_COLUMN, $file->{line_nr});
+ $instance->{appbar}->set_progress_percentage($i ++ / scalar(@files));
+ gtk2_update();
+ }
+ $instance->{appbar}->set_progress_percentage(0);
+ $instance->{appbar}->set_status("");
+ gtk2_update();
+
+ # Make sure we are at the top.
+
+ $instance->{comparison_scrolledwindow}->get_vadjustment()->set_value(0);
+ $instance->{comparison_scrolledwindow}->get_hadjustment()->set_value(0);
+
+ $instance->{appbar}->pop();
+ make_busy($instance, 0);
+
+}
+#
+##############################################################################
+#
+# Routine - mtn_diff
+#
+# Description - Compare two the specified two revisions, optionally
+# restricting the comparison to the specified file.
+#
+# Data - $list : A reference to the list that is to contain
+# the output from the diff command.
+# $mtn : The Monotone database that is to be used
+# or undef if the database associated with
+# the current workspace is to be used.
+# $revision_id_1 : The first revision id that is to be
+# compared.
+# $revision_id_2 : The second revision id that is to be
+# compared.
+# $file_name : Either the name of the file that the
+# revision comparison should be restricted
+# to or undef for a full revision
+# comparison.
+# Return Value : True if the comparison worked, otherwise
+# false if something went wrong.
+#
+##############################################################################
+
+
+
+sub mtn_diff($$$$;$)
+{
+
+ my($list, $mtn_db, $revision_id_1, $revision_id_2, $file_name) = @_;
+
+ my($buffer,
+ @cmd);
+
+ # Run mtn diff.
+
+ @$list = ();
+ push(@cmd, "mtn");
+ push(@cmd, "--db=" . $mtn_db) if (defined($mtn_db));
+ push(@cmd, "diff");
+ push(@cmd, "-r");
+ push(@cmd, "i:" . $revision_id_1);
+ push(@cmd, "-r");
+ push(@cmd, "i:" . $revision_id_2);
+ push(@cmd, $file_name) if (defined($file_name));
+ run_command(\$buffer, @cmd) or return;
+
+ # Break up the input into a list of lines.
+
+ @$list = split(/\n/o, $buffer);
+
+ return 1;
+
+}
+
+1;
============================================================
--- Utilities.pm 13b6314e75888c8e6431a0bdf11d12478c31a3b5
+++ Utilities.pm 13b6314e75888c8e6431a0bdf11d12478c31a3b5
@@ -0,0 +1,694 @@
+##############################################################################
+#
+# File Name - Utilities.pm
+#
+# Description - The utilities module for the mtn-browse application. This
+# module contains assorted general purpose routines used
+# throughout the application.
+#
+# Author - A.E.Cooper.
+#
+# Legal Stuff - Copyright (c) 2007 Anthony Edward Cooper
+# .
+#
+# This program is free software; you can redistribute it
+# and/or modify it under the terms of the GNU General Public
+# License as published by the Free Software Foundation;
+# either version 3 of the License, or (at your option) any
+# later version.
+#
+# This program is distributed in the hope that it will be
+# useful, but WITHOUT ANY WARRANTY; without even the implied
+# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+# PURPOSE. See the GNU General Public License for more
+# details.
+#
+# You should have received a copy of the GNU General Public
+# License along with this software; if not, write to the Free
+# Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307 USA.
+#
+##############################################################################
+#
+##############################################################################
+#
+# GLOBAL DATA FOR THIS MODULE
+#
+##############################################################################
+
+
+
+# ***** DIRECTIVES *****
+
+require 5.008;
+
+use strict;
+
+# ***** FUNCTIONAL PROTOTYPES FOR THIS FILE *****
+
+# Public routines.
+
+sub create_format_tags($);
+sub generate_revision_report($$$$;$);
+sub get_dir_contents($$$);
+sub get_revision_ids($$);
+sub gtk2_update();
+sub make_busy($$);
+sub run_command($@);
+sub set_label_value($$);
+#
+##############################################################################
+#
+# Routine - generate_revision_report
+#
+# Description - Populate the specified Gtk2::TextBuffer with a pretty
+# printed report on the specified revision.
+#
+# Data - $text_buffer : The Gtk2::TextBuffer that is to be
+# populated.
+# $revision_id : The id of the revision being reported
+# on.
+# $certs_list : A reference to a certs list as returned
+# by $mtn->certs().
+# $colour : One of "red, "green" or "" depending
+# upon the desired colour of the text.
+# $revision_details : Either a reference to a revision
+# details list as returned by
+# $mtn->get_revision() if a detailed
+# report is to be generated or undef if
+# the report is to just be a summary.
+#
+##############################################################################
+
+
+
+sub generate_revision_report($$$$;$)
+{
+
+ my($text_buffer, $revision_id, $certs_list, $colour, $revision_details)
+ = @_;
+
+ my($bold,
+ $change_log,
+ $italics,
+ $manifest_id,
+ $normal,
+ @parent_revision_ids,
+ %revision_data,
+ %seen,
+ @unique);
+ my @types =
+ ("Added", "Removed", "Changed", "Renamed", "Attributes");
+
+ # Sort out colour attributes.
+
+ if ($colour ne "")
+ {
+ $normal = $colour;
+ $bold = "bold-" . $colour;
+ $italics = "italics-" . $colour;
+ }
+ else
+ {
+ $normal = "normal";
+ $bold = "bold";
+ $italics = "italics";
+ }
+
+ # Revision id.
+
+ $text_buffer->insert_with_tags_by_name($text_buffer->get_end_iter(),
+ "Revision id: ",
+ $bold);
+ $text_buffer->insert_with_tags_by_name($text_buffer->get_end_iter(),
+ $revision_id . "\n\n",
+ $normal);
+
+ # Certs.
+
+ foreach my $cert (@$certs_list)
+ {
+ if ($cert->{name} eq "changelog")
+ {
+ $change_log = $cert->{value};
+ $change_log =~ s/\s+$//os;
+ }
+ else
+ {
+ $cert->{value} =~ s/T/ /o if ($cert->{name} eq "date");
+ $text_buffer->insert_with_tags_by_name
+ ($text_buffer->get_end_iter(),
+ sprintf("%s:\t", ucfirst($cert->{name})),
+ $bold);
+ $text_buffer->insert_with_tags_by_name
+ ($text_buffer->get_end_iter(),
+ sprintf("%s\n", $cert->{value}),
+ $normal);
+ }
+ }
+
+ # Change log.
+
+ $text_buffer->insert_with_tags_by_name($text_buffer->get_end_iter(),
+ "\nChange Log:\n",
+ $bold);
+ $text_buffer->insert_with_tags_by_name($text_buffer->get_end_iter(),
+ sprintf("%s", $change_log),
+ $normal);
+
+ # The rest is only provided if it is a detailed report.
+
+ if (defined($revision_details))
+ {
+
+ # Revision details.
+
+ $text_buffer->insert_with_tags_by_name($text_buffer->get_end_iter(),
+ "\n\nChanges Made:\n",
+ $bold);
+ foreach my $type (@types)
+ {
+ $revision_data{$type} = [];
+ }
+ foreach my $change (@$revision_details)
+ {
+ if ($change->{type} eq "add_dir")
+ {
+ push(@{$revision_data{"Added"}}, $change->{name} . "/");
+ }
+ elsif ($change->{type} eq "add_file")
+ {
+ push(@{$revision_data{"Added"}}, $change->{name});
+ }
+ elsif ($change->{type} eq "delete")
+ {
+ push(@{$revision_data{"Removed"}}, $change->{name});
+ }
+ elsif ($change->{type} eq "patch")
+ {
+ push(@{$revision_data{"Changed"}}, $change->{name});
+ }
+ elsif ($change->{type} eq "rename")
+ {
+ push(@{$revision_data{"Renamed"}},
+ $change->{from_name} . " -> " . $change->{to_name});
+ }
+ elsif ($change->{type} eq "clear")
+ {
+ push(@{$revision_data{"Attributes"}},
+ sprintf("%s: %s was cleared",
+ $change->{name},
+ $change->{attribute}));
+ }
+ elsif ($change->{type} eq "clear" || $change->{type} eq "set")
+ {
+ push(@{$revision_data{"Attributes"}},
+ sprintf("%s: %s = %s",
+ $change->{name},
+ $change->{attribute},
+ $change->{value}));
+ }
+ elsif ($change->{type} eq "old_revision")
+ {
+ push(@parent_revision_ids, $change->{revision_id});
+ }
+ elsif ($change->{type} eq "new_manifest")
+ {
+ $manifest_id = $change->{manifest_id};
+ }
+ }
+ foreach my $type (@types)
+ {
+ if (scalar(@{$revision_data{$type}}) > 0)
+ {
+ $text_buffer->insert_with_tags_by_name
+ ($text_buffer->get_end_iter(),
+ " " . $type . ":\n",
+ $italics);
+ %seen = ();
+ @unique = sort(grep { ! $seen{$_} ++ }
+ @{$revision_data{$type}});
+ foreach my $line (@unique)
+ {
+ $text_buffer->insert_with_tags_by_name
+ ($text_buffer->get_end_iter(),
+ "\t" . $line . "\n",
+ $normal);
+ }
+ }
+ }
+
+ # Parent revision and manifest ids.
+
+ $text_buffer->insert_with_tags_by_name($text_buffer->get_end_iter(),
+ "\nParent revision id(s):\t",
+ $bold);
+ $text_buffer->insert_with_tags_by_name
+ ($text_buffer->get_end_iter(),
+ join(" ", @parent_revision_ids) . "\n",
+ $normal);
+ $text_buffer->insert_with_tags_by_name($text_buffer->get_end_iter(),
+ "Manifest id:\t\t",
+ $bold);
+ $text_buffer->insert_with_tags_by_name($text_buffer->get_end_iter(),
+ $manifest_id,
+ $normal);
+
+ }
+
+}
+#
+##############################################################################
+#
+# Routine - run_command
+#
+# Description - Run the specified command and return its output.
+#
+# Data - $buffer : A reference to the buffer that is to contain
+# the output from the command.
+# $args : A list containing the command to run and its
+# arguments.
+# Return Value : True if the command worked, otherwise false
+# if something went wrong.
+#
+##############################################################################
+
+
+
+sub run_command($@)
+{
+
+ my($buffer, @args) = @_;
+
+ my(@err,
+ $fd_err,
+ $fd_in,
+ $fd_out,
+ $pid,
+ $ret_val,
+ $status,
+ $stop,
+ $total_bytes,
+ $watcher);
+
+ # Run the command.
+
+ $fd_err = gensym();
+ eval
+ {
+ $pid = open3($fd_in, $fd_out, $fd_err, @args);
+ };
+ if ($@ ne "")
+ {
+ my $dialog = Gtk2::MessageDialog->new
+ (undef,
+ ["modal"],
+ "warning",
+ "close",
+ sprintf("The %s subprocess could not start,\n"
+ . "the system gave:\n%s",
+ Glib::Markup::escape_text($args[0]),
+ Glib::Markup::escape_text($@)));
+ $dialog->run();
+ $dialog->destroy();
+ return;
+ }
+
+ # Setup a watch handler to get read our data and handle GTK2 events whilst
+ # the command is running.
+
+ $stop = $total_bytes = 0;
+ $$buffer = "";
+ $watcher = Gtk2::Helper->add_watch
+ (fileno($fd_out), "in",
+ sub {
+ my $bytes_read;
+ if (($bytes_read = sysread($fd_out,
+ $$buffer,
+ 32768,
+ $total_bytes))
+ == 0)
+ {
+ $stop = 1;
+ }
+ else
+ {
+ $total_bytes += $bytes_read;
+ }
+ return TRUE;
+ });
+ while (! $stop)
+ {
+ Gtk2->main_iteration();
+ }
+ Gtk2::Helper->remove_watch($watcher);
+
+ # Get any error output.
+
+ @err = readline($fd_err);
+
+ close($fd_in);
+ close($fd_out);
+ close($fd_err);
+
+ # Reap the process and deal with any errors.
+
+ if (($ret_val = waitpid($pid, 0)) == -1)
+ {
+ if ($! != ECHILD)
+ {
+ my $dialog = Gtk2::MessageDialog->new_with_markup
+ (undef,
+ ["modal"],
+ "warning",
+ "close",
+ sprintf("waitpid failed with:\n%s",
+ Glib::Markup::escape_text($!)));
+ $dialog->run();
+ $dialog->destroy();
+ return;
+ }
+ }
+ $status = $?;
+ if (WIFEXITED($status) && WEXITSTATUS($status) != 0)
+ {
+ my $dialog = Gtk2::MessageDialog->new_with_markup
+ (undef,
+ ["modal"],
+ "warning",
+ "close",
+ sprintf("The %s subprocess failed with an exit status\n"
+ . "of %d and printed the following on stderr:\n"
+ . "%s",
+ Glib::Markup::escape_text($args[0]),
+ WEXITSTATUS($status),
+ Glib::Markup::escape_text(join("", @err))));
+ $dialog->run();
+ $dialog->destroy();
+ return;
+ }
+ elsif (WIFSIGNALED($status))
+ {
+ my $dialog = Gtk2::MessageDialog->new
+ (undef,
+ ["modal"],
+ "warning",
+ "close",
+ sprintf("The %s subprocess was terminated by signal %d",
+ Glib::Markup::escape_text($args[0]),
+ WTERMSIG($status)));
+ $dialog->run();
+ $dialog->destroy();
+ return;
+ }
+
+ return 1;
+
+}
+#
+##############################################################################
+#
+# Routine - get_dir_contents
+#
+# Description - Given a path and a Monotone manifest, return a subset of
+# the manifest that represents the contents of just that
+# directory along with the directory entry names.
+#
+# Data - $path : The path to the directory from the top level of
+# the manifest.
+# $manifest : A reference to a Monotone manifest.
+# $result : A reference to a list that is to contain the
+# result (a list of records containing the short
+# directory entry name and a reference to the
+# related manifest entry).
+#
+##############################################################################
+
+
+
+sub get_dir_contents($$$)
+{
+
+ my($path, $manifest, $result) = @_;
+
+ my($entry,
+ $extract_re,
+ $i,
+ $match_re,
+ $name);
+
+ $i = 0;
+ if ($path eq "")
+ {
+ $match_re = qr/^[^\/]+$/;
+ $extract_re = qr/^([^\/]+)$/;
+ }
+ else
+ {
+ $match_re = qr/^${path}\/[^\/]+$/;
+ $extract_re = qr/^${path}\/([^\/]+)$/;
+ }
+ @$result = ();
+ foreach $entry (@$manifest)
+ {
+ if ($entry->{name} =~ m/$match_re/)
+ {
+ ($name) = ($entry->{name} =~ m/$extract_re/);
+ $$result[$i ++] = {manifest_entry => $entry,
+ name => $name};
+ }
+ }
+
+}
+#
+##############################################################################
+#
+# Routine - get_revision_ids
+#
+# Description - Return the currently selected revision id, whether this is
+# specified via a tag or as a revision id.
+#
+# 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 get_revision_ids($$)
+{
+
+ my($instance, $revision_ids) = @_;
+
+ @$revision_ids=();
+ return unless ($instance->{revision_combo_details}->{completed});
+ if ($instance->{tagged_tick}->get_active())
+ {
+ $instance->{mtn}->
+ select($revision_ids,
+ "t:" . $instance->{revision_combo_details}->{value});
+ }
+ else
+ {
+ push(@$revision_ids, $instance->{revision_combo_details}->{value});
+ }
+
+}
+#
+##############################################################################
+#
+# Routine - make_busy
+#
+# Description - This routine simply makes the main window busy or active.
+#
+# Data - $instance : The window instance.
+# $busy : True if the window is to be made busy,
+# otherwise false if the window is to be made
+# active.
+#
+##############################################################################
+
+
+
+sub make_busy($$)
+{
+
+ my($instance, $busy) = @_;
+
+ # Create and store the cursors if we haven't done so already.
+
+ $busy_cursor = Gtk2::Gdk::Cursor->new("watch")
+ unless (defined($busy_cursor));
+
+ # Do it. Make the application bar grab the input when the window is busy,
+ # that way we gobble up keyboard and mouse events that could muck up the
+ # application state.
+
+ if ($busy)
+ {
+ if (exists($instance->{grab_widget}))
+ {
+ Gtk2->grab_add($instance->{grab_widget});
+ }
+ else
+ {
+ Gtk2->grab_add($instance->{appbar});
+ }
+ foreach my $instance (@windows)
+ {
+ foreach my $window (@{$instance->{busy_windows}})
+ {
+ $window->set_cursor($busy_cursor);
+ }
+ }
+ }
+ else
+ {
+ if (exists($instance->{grab_widget}))
+ {
+ Gtk2->grab_remove($instance->{grab_widget});
+ }
+ else
+ {
+ Gtk2->grab_remove($instance->{appbar});
+ }
+ foreach my $instance (@windows)
+ {
+ foreach my $window (@{$instance->{busy_windows}})
+ {
+ $window->set_cursor(undef);
+ }
+ }
+ }
+
+}
+#
+##############################################################################
+#
+# 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();
+ }
+
+}
+#
+##############################################################################
+#
+# Routine - create_format_tags
+#
+# Description - Creates the Gtk2::TextBuffer tags that are used to pretty
+# print stuff.
+#
+# Data - $text_view : The GTK2::TextBuffer widget that is to have
+# its tags created.
+#
+##############################################################################
+
+
+
+sub create_format_tags($)
+{
+
+ my ($text_buffer) = @_;
+
+ $text_buffer->create_tag("normal", "weight" => PANGO_WEIGHT_NORMAL);
+
+ $text_buffer->create_tag("bold", "weight" => PANGO_WEIGHT_BOLD);
+ $text_buffer->create_tag("italics", "style" => "italic");
+ $text_buffer->create_tag("bold-italics",
+ "weight" => PANGO_WEIGHT_BOLD,
+ "style" => "italic");
+
+ $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");
+
+ $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");
+
+ $text_buffer->create_tag("compare-info",
+ "foreground" => "Yellow",
+ "background" => "LightSlateGrey");
+
+ $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");
+
+ $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");
+
+}
+#
+##############################################################################
+#
+# Routine - set_label_value
+#
+# Description - Set the text for the given label and the tooltip for the
+# parent widget, assumed to be an event box, to the specified
+# text.
+#
+# Data - $widget : The label widget that has an event box as its
+# parent.
+# $value : The text that the label and tooltip are to be set
+# to.
+#
+##############################################################################
+
+
+
+sub set_label_value($$)
+{
+
+ my($widget, $value) = @_;
+
+ $widget->set_text($value);
+ $tooltips->set_tip($widget->parent(), $value);
+
+}
+
+1;
============================================================
# line.png is binary
============================================================
--- mtn-browse 2f9cc86df7157a7eb26bb354b04a351da03c4054
+++ mtn-browse 900690ff1257eaa6bddc465bbc66c96cd85ed282
@@ -39,15 +39,17 @@
-# ***** REQUIRED VERSION OF PERL *****
+# ***** DIRECTIVES *****
require 5.008;
+use lib "/home/aecoope/perl";
+use strict;
+
# ***** REQUIRED PACKAGES *****
-use lib "/home/aecoope/perl";
+# Standard Perl and CPAN modules.
-use strict;
use Glib qw(FALSE TRUE);
use Gnome2;
use Gnome2::VFS -init;
@@ -69,36 +71,18 @@ use Data::Dumper;
use Data::Dumper;
-# ***** GLOBAL DATA DECLARATIONS *****
+# Modules specific to this application.
-# Constants used to represent the different groups of widgets.
+use Globals qw(:constants :variables);
+use AdvancedFind;
+use ChangeLog;
+use ComboAutoCompletion;
+use Completion;
+use History;
+use Utilities;
-use constant BRANCH => 0x01;
-use constant DIRECTORY => 0x02;
-use constant DIRECTORY_VIEW => 0x04;
-use constant DISPLAY_OF_FILE => 0x08;
-use constant FILE => 0x10;
-use constant REVISION => 0x20;
-use constant REVISION_LIST => 0x02;
-use constant REVISION_DETAILS => 0x04;
+# ***** GLOBAL DATA DECLARATIONS *****
-# Constants used to represent the different state changes. Read this as
-# `what has just been changed' => `what needs to be updated'.
-
-use constant BRANCH_CHANGED => (REVISION | DIRECTORY
- | DIRECTORY_VIEW | FILE
- | DISPLAY_OF_FILE);
-use constant DATABASE_CHANGED => 0xff;
-use constant DIRECTORY_CHANGED => (DIRECTORY_VIEW | FILE
- | DISPLAY_OF_FILE);
-use constant DISPLAY_OF_FILE_CHANGED => 0x00;
-use constant FILE_CHANGED => (DISPLAY_OF_FILE);
-use constant NEW_FIND => 0xff;
-use constant REVISION_CHANGED => (DIRECTORY | REVISION_LIST
- | DIRECTORY_VIEW | FILE
- | DISPLAY_OF_FILE);
-use constant SELECTED_REVISION_CHANGED => (REVISION_DETAILS);
-
# Constants for the columns within the manifest ListStore widget.
use constant MLS_ICON_COLUMN => 0;
@@ -107,138 +91,23 @@ use constant MLS_MANIFEST_ENTRY_COLUMN =
use constant MLS_AUTHOR_COLUMN => 3;
use constant MLS_MANIFEST_ENTRY_COLUMN => 4;
-# Constants for the columns within the details ListStore widget.
-
-use constant DLS_ICON_COLUMN => 0;
-use constant DLS_NAME_COLUMN => 1;
-use constant DLS_VALUE_COLUMN => 2;
-
-# Constants for the columns within the comparison files ListStore widget.
-
-use constant CLS_NAME_COLUMN => 0;
-use constant CLS_LINE_NR_COLUMN => 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);
-
-# Supported text mime types (used for syntax highlighting.
-
-my @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"
- }
- );
-
-# A boolean to signal whether this process has received a SIGCHLD signal or
-# not.
-
-# List of window instances.
-
-my @windows;
-
-# The advanced find window management record.
-
-my $advanced_find;
-
-# Assorted pixmaps.
-
-my($app_icon,
- $line_image);
-
-# The busy cursor to use for the mouse.
-
-my $busy_cursor;
-
-# The tooltips widget.
-
-my $tooltips;
-
# ***** FUNCTIONAL PROTOTYPES FOR THIS FILE *****
# Private routines.
-sub advanced_find($$$);
sub advanced_find_button_clicked_cb($$);
-sub coloured_revision_change_log_button_clicked_cb($$);
-sub combo_changed_cb($$);
-sub combo_key_release_event_cb($$$);
-sub compare_button_clicked_cb($$);
-sub compare_revisions($$$;$);
-sub create_advanced_find_window();
-sub create_format_tags($);
sub delete_event_cb($$$);
sub destroy_event_cb($$;$);
sub directory_up_button_clicked_cb($$);
sub file_change_history_button_clicked_cb($$);
-sub file_comparison_combobox_changed_cb($$);
-sub generate_revision_report($$$$;$);
-sub get_change_log_window();
-sub get_completion($$$$;$);
-sub get_dir_contents($$$);
-sub get_file_history_helper($$$);
-sub get_history_window();
-sub get_revision_comparison_window();
-sub get_revision_history_helper($$$);
-sub get_revision_ids($$);
-sub gtk2_update();
-sub history_list_button_clicked_cb($$);
-sub make_busy($$);
sub manifest_browser_treeview_cursor_changed_cb($$);
sub manifest_browser_treeview_row_activated_cb($$$$);
-sub mtn_diff($$$$;$);
sub mtn_error_handler($$);
sub new_browser_instance();
-sub populate_button_clicked_cb($$);
sub revision_change_history_button_clicked_cb($$);
sub revision_change_log_button_clicked_cb($$);
-sub revisions_treeview_cursor_changed_cb($$);
-sub revisions_treeview_row_activated_cb($$$$);
-sub run_command($@);
-sub set_label_value($$);
sub setup_sigchld_handler($);
sub sigchld_handler();
-sub simple_query_radiobutton_toggled_cb($$);
-sub term_combobox_changed_cb($$);
-sub update_advanced_find_state($$);
sub update_browser_state($$);
#
##############################################################################
@@ -273,18 +142,13 @@ sub update_browser_state($$);
# Cleanup.
- if (defined($advanced_find))
- {
- $advanced_find->{window}->destroy();
- $advanced_find = undef;
- }
foreach my $window (@windows)
{
$window->{window}->destroy();
}
+ @windows = ();
Gnome2::VFS->shutdown();
$SIG{CHLD} = "IGNORE";
- @windows = ();
exit(0);
@@ -292,212 +156,6 @@ sub update_browser_state($$);
#
##############################################################################
#
-# Routine - combo_changed_cb
-#
-# Description - Callback routine called when the user changes the value of
-# a branch or revision comboboxentry by selecting an entry
-# from its pulldown list.
-#
-# Data - $widget : The widget object that received the signal.
-# $instance : The window instance that is associated with
-# this widget.
-#
-##############################################################################
-
-
-
-sub combo_changed_cb($$)
-{
-
- my($widget, $instance) = @_;
-
- return if ($instance->{in_cb});
- local $instance->{in_cb} = 1;
-
- my ($change_state,
- $combo_details,
- $item,
- $value);
-
- if ($widget == $instance->{branch_combo})
- {
- $change_state = BRANCH_CHANGED;
- $combo_details = $instance->{branch_combo_details};
- }
- elsif ($widget == $instance->{revision_combo})
- {
- $change_state = REVISION_CHANGED;
- $combo_details = $instance->{revision_combo_details};
- }
- elsif ($widget == $instance->{directory_combo})
- {
- $change_state = DIRECTORY_CHANGED;
- $combo_details = $instance->{directory_combo_details};
- }
- else
- {
- return;
- }
-
- # For some reason best known to itself, Gtk+ calls this callback when the
- # user presses a key for the first time (but not subsequently) after a
- # value is selected via the pulldown menu. So we have to guard against
- # this. Under these circumstances the key release callback is also called.
- # So, put simply, only do something inside this callback if the value is a
- # direct match to one in our list.
-
- $value = $widget->child()->get_text();
- foreach $item (@{$combo_details->{list}})
- {
- if ($value eq $item)
- {
- $combo_details->{value} = $value;
- $combo_details->{completed} = 1;
- $instance->{appbar}->clear_stack();
- &{$instance->{update_handler}}($instance, $change_state);
- last;
- }
- }
-
-}
-#
-##############################################################################
-#
-# Routine - combo_key_release_event_cb
-#
-# Description - Callback routine called when the user changes the value of
-# a branch or revision comboboxentry by entering a character
-# (key release event).
-#
-# Data - $widget : The widget object that received the signal.
-# $event : A Gtk2::Gdk::Event object describing the
-# event that has occurred.
-# $instance : The window instance that is associated with
-# this widget.
-# Return Value : TRUE if the event has been handled and needs
-# no further handling, otherwise false if the
-# event should carry on through the remaining
-# event handling.
-#
-##############################################################################
-
-
-
-sub combo_key_release_event_cb($$$)
-{
-
- my($widget, $event, $instance) = @_;
-
- return FALSE if ($instance->{in_cb});
- local $instance->{in_cb} = 1;
-
- my ($change_state,
- $combo,
- $combo_details,
- $completed,
- $completion,
- $item,
- $len,
- $name,
- $old_completed,
- $old_value,
- $value);
-
- if ($widget == $instance->{branch_combo}->child())
- {
- $combo = $instance->{branch_combo};
- $change_state = BRANCH_CHANGED;
- $combo_details = $instance->{branch_combo_details};
- $name = "branch";
- }
- elsif ($widget == $instance->{revision_combo}->child())
- {
- $combo = $instance->{revision_combo};
- $change_state = REVISION_CHANGED;
- $combo_details = $instance->{revision_combo_details};
- $name = "revision";
- }
- elsif ($widget == $instance->{directory_combo}->child())
- {
- $combo = $instance->{directory_combo};
- $change_state = DIRECTORY_CHANGED;
- $combo_details = $instance->{directory_combo_details};
- $name = "directory";
- }
- else
- {
- return FALSE;
- }
-
- # The user has typed something in then validate it and auto-complete it if
- # necessary.
-
- $completed = 0;
- $old_completed = $combo_details->{completed};
- $old_value = $combo_details->{value};
- $value = $widget->get_text();
- if ($value ne $old_value)
- {
-
- # Don't auto-complete if the user is simply deleting from the extreme
- # right.
-
- $len = length($value);
- if ($value ne substr($old_value, 0, $len))
- {
-
- # So that the spacebar triggers auto-complete.
-
- $value =~ s/\s+$//o;
- $len = length($value);
-
- if (get_completion($value,
- $combo_details->{list},
- \$completion,
- \$completed,
- $combo_details->{completion_cache}))
- {
- $instance->{appbar}->clear_stack();
- }
- else
- {
- $instance->{appbar}->
- push("Invalid " . $name . " name`" . $value . "'");
- }
- $value = $completion;
- $len = length($value);
- $widget->set_text($value);
- $widget->set_position(-1);
-
- }
- $combo_details->{value} = $value;
- $combo_details->{last_typed_len} = length($value);
- $combo_details->{completed} = $completed;
-
- # Update the pulldown choices.
-
- $combo->get_model()->clear();
- foreach $item (@{$combo_details->{list}})
- {
- $combo->append_text($item) if ($value eq substr($item, 0, $len));
- $combo_details->{completed} = 1
- if (! $completed && $value eq $item);
- }
-
- # Update the window state on a significant change.
-
- &{$instance->{update_handler}}($instance, $change_state)
- if ($combo_details->{completed} != $old_completed
- || $combo_details->{value} ne $old_value);
-
- }
-
- return FALSE;
-
-}
-#
-##############################################################################
-#
# Routine - tagged_checkbutton_toggled_cb
#
# Description - Callback routine called when the user changes the value of
@@ -619,165 +277,8 @@ sub revision_change_history_button_click
return if ($browser->{in_cb});
local $browser->{in_cb} = 1;
- my ($button,
- @certs_list,
- $counter,
- $file_name,
- %history_hash,
- $instance,
- @revision_ids);
+ display_revision_change_history($browser);
- $instance = get_history_window();
- local $instance->{in_cb} = 1;
-
- $instance->{mtn} = $browser->{mtn};
- $instance->{file_name} = undef;
- get_revision_ids($browser, address@hidden);
- $instance->{first_revision_id} = "";
- $instance->{second_revision_id} = "";
- $instance->{window}->set_title("Revision History For " . $revision_ids[0]);
- $instance->{history_label}->set_markup("Revision History");
- $instance->{window}->show_all();
-
- make_busy($instance, 1);
- $instance->{appbar}->push("");
- gtk2_update();
-
- $instance->{stop_button}->set_sensitive(TRUE);
-
- # Get the list of file change revisions. Remember to include the current
- # revision in the history.
-
- $instance->{appbar}->set_progress_percentage(0);
- $instance->{appbar}->set_status("Fetching revision list");
- gtk2_update();
- $history_hash{$revision_ids[0]} = 1;
- get_revision_history_helper($instance, \%history_hash, $revision_ids[0]);
-
- # Sort the list.
-
- $instance->{appbar}->set_status("Sorting revision list");
- gtk2_update();
- $instance->{history} = [];
- $instance->{mtn}->toposort($instance->{history}, keys(%history_hash));
- %history_hash = ();
- @{$instance->{history}} = reverse(@{$instance->{history}});
-
- # Display the file's history.
-
- $instance->{appbar}->set_progress_percentage(0);
- $instance->{appbar}->set_status("Displaying file history");
- gtk2_update();
- $counter = 1;
- $instance->{stop} = 0;
- $instance->{history_buffer}->set_text("");
- for my $revision_id (@{$instance->{history}})
- {
-
- # Print out the revision summary.
-
- $instance->{mtn}->certs(address@hidden, $revision_id);
- generate_revision_report($instance->{history_buffer},
- $revision_id,
- address@hidden,
- "");
- $instance->{history_buffer}->
- insert($instance->{history_buffer}->get_end_iter(), "\n\n ");
-
- # Add the buttons.
-
- $button = Gtk2::Button->new("Select As Revision 1");
- $button->signal_connect("clicked",
- \&history_list_button_clicked_cb,
- {instance => $instance,
- revision_id => $revision_id,
- button_type => "1"});
- $tooltips->set_tip($button,
- "Select this revision for comparison\n"
- . "as the first revision");
- $instance->{history_textview}->add_child_at_anchor
- ($button,
- $instance->{history_buffer}->
- create_child_anchor($instance->{history_buffer}->
- get_end_iter()));
- $button->show_all();
- $instance->{history_buffer}->
- insert($instance->{history_buffer}->get_end_iter(), " ");
-
- $button = Gtk2::Button->new("Select As Revision 2");
- $button->signal_connect("clicked",
- \&history_list_button_clicked_cb,
- {instance => $instance,
- revision_id => $revision_id,
- button_type => "2"});
- $tooltips->set_tip($button,
- "Select this revision for comparison\n"
- . "as the second revision");
- $instance->{history_textview}->add_child_at_anchor
- ($button,
- $instance->{history_buffer}->
- create_child_anchor($instance->{history_buffer}->
- get_end_iter()));
- $button->show_all();
- $instance->{history_buffer}->
- insert($instance->{history_buffer}->get_end_iter(), " ");
-
- $button = Gtk2::Button->new("View Full Revision Change Log");
- $button->signal_connect("clicked",
- \&history_list_button_clicked_cb,
- {instance => $instance,
- revision_id => $revision_id,
- button_type => "revision-changelog"});
- $tooltips->set_tip($button, "View the revision's full change log");
- $instance->{history_textview}->add_child_at_anchor
- ($button,
- $instance->{history_buffer}->
- create_child_anchor($instance->{history_buffer}->
- get_end_iter()));
- $button->show_all();
-
- # Stop if the user wants to.
-
- last if ($instance->{stop});
-
- # If we aren't at the end, print out the revision separator.
-
- if ($counter < scalar(@{$instance->{history}}))
- {
- $instance->{history_buffer}->
- insert($instance->{history_buffer}->get_end_iter(), "\n");
- $instance->{history_buffer}->
- insert_pixbuf($instance->{history_buffer}->get_end_iter(),
- $line_image);
- $instance->{history_buffer}->
- insert($instance->{history_buffer}->get_end_iter(), "\n");
- }
-
- if ($counter % 100 == 0)
- {
- $instance->{appbar}->set_progress_percentage
- ($counter / scalar(@{$instance->{history}}));
- gtk2_update();
- }
- ++ $counter;
-
- }
-
- $instance->{stop_button}->set_sensitive(FALSE);
- set_label_value($instance->{numbers_label}, $counter)
- if ($instance->{stop});
-
- # Make sure we are at the top.
-
- $instance->{history_scrolledwindow}->get_vadjustment()->set_value(0);
- $instance->{history_scrolledwindow}->get_hadjustment()->set_value(0);
- $instance->{appbar}->set_progress_percentage(0);
- $instance->{appbar}->set_status("");
- gtk2_update();
-
- $instance->{appbar}->pop();
- make_busy($instance, 0);
-
}
#
##############################################################################
@@ -925,157 +426,8 @@ sub file_change_history_button_clicked_c
return if ($browser->{in_cb});
local $browser->{in_cb} = 1;
- my ($button,
- @certs_list,
- $counter,
- $file_name,
- %history_hash,
- $instance,
- @revision_ids);
+ display_file_change_history($browser);
- $instance = get_history_window();
- local $instance->{in_cb} = 1;
-
- $instance->{mtn} = $browser->{mtn};
- $instance->{file_name} =
- $browser->{file_being_viewed}->{manifest_entry}->{name};
- $instance->{first_revision_id} = "";
- $instance->{second_revision_id} = "";
- $instance->{window}->set_title
- ("File History For " . $instance->{file_name});
- $instance->{history_label}->set_markup("File History");
- $instance->{window}->show_all();
-
- make_busy($instance, 1);
- $instance->{appbar}->push("");
- gtk2_update();
-
- # Get the list of file change revisions. Remember that a warning is
- # generated when one goes back beyond a file's addition revision, so
- # temporarily disable the warning handler.
-
- $instance->{appbar}->set_progress_percentage(0);
- $instance->{appbar}->set_status("Fetching revision list");
- $instance->{stop_button}->set_sensitive(TRUE);
- gtk2_update();
- Monotone::AutomateStdio->register_error_handler("warning");
- get_revision_ids($browser, address@hidden);
- get_file_history_helper($instance, \%history_hash, $revision_ids[0]);
- Monotone::AutomateStdio->register_error_handler("both",
- \&mtn_error_handler);
- $instance->{stop_button}->set_sensitive(FALSE);
-
- # Sort the list.
-
- $instance->{appbar}->set_status("Sorting revision list");
- gtk2_update();
- $instance->{history} = [];
- $instance->{mtn}->toposort($instance->{history}, keys(%history_hash));
- %history_hash = ();
- @{$instance->{history}} = reverse(@{$instance->{history}});
-
- # Display the file's history.
-
- $instance->{appbar}->set_progress_percentage(0);
- $instance->{appbar}->set_status("Displaying file history");
- gtk2_update();
- $counter = 1;
- $instance->{history_buffer}->set_text("");
- for my $revision_id (@{$instance->{history}})
- {
-
- # Print out the revision summary.
-
- $instance->{mtn}->certs(address@hidden, $revision_id);
- generate_revision_report($instance->{history_buffer},
- $revision_id,
- address@hidden,
- "");
- $instance->{history_buffer}->
- insert($instance->{history_buffer}->get_end_iter(), "\n\n ");
-
- # Add the buttons.
-
- $button = Gtk2::Button->new("Select As File 1");
- $button->signal_connect("clicked",
- \&history_list_button_clicked_cb,
- {instance => $instance,
- revision_id => $revision_id,
- button_type => "1"});
- $tooltips->set_tip($button,
- "Select this file revision for\n"
- . "comparison as the first file");
- $instance->{history_textview}->add_child_at_anchor
- ($button,
- $instance->{history_buffer}->
- create_child_anchor($instance->{history_buffer}->
- get_end_iter()));
- $button->show_all();
- $instance->{history_buffer}->
- insert($instance->{history_buffer}->get_end_iter(), " ");
-
- $button = Gtk2::Button->new("Select As File 2");
- $button->signal_connect("clicked",
- \&history_list_button_clicked_cb,
- {instance => $instance,
- revision_id => $revision_id,
- button_type => "2"});
- $tooltips->set_tip($button,
- "Select this file revision for\n"
- . "comparison as the second file");
- $instance->{history_textview}->add_child_at_anchor
- ($button,
- $instance->{history_buffer}->
- create_child_anchor($instance->{history_buffer}->
- get_end_iter()));
- $button->show_all();
- $instance->{history_buffer}->
- insert($instance->{history_buffer}->get_end_iter(), " ");
-
- $button = Gtk2::Button->new("View Full Revision Change Log");
- $button->signal_connect("clicked",
- \&history_list_button_clicked_cb,
- {instance => $instance,
- revision_id => $revision_id,
- button_type => "revision-changelog"});
- $tooltips->set_tip($button, "View the revision's full change log");
- $instance->{history_textview}->add_child_at_anchor
- ($button,
- $instance->{history_buffer}->
- create_child_anchor($instance->{history_buffer}->
- get_end_iter()));
- $button->show_all();
-
- # If we aren't at the end, print out the revision separator.
-
- if ($counter < scalar(@{$instance->{history}}))
- {
- $instance->{history_buffer}->
- insert($instance->{history_buffer}->get_end_iter(), "\n");
- $instance->{history_buffer}->
- insert_pixbuf($instance->{history_buffer}->get_end_iter(),
- $line_image);
- $instance->{history_buffer}->
- insert($instance->{history_buffer}->get_end_iter(), "\n");
- }
-
- $instance->{appbar}->set_progress_percentage
- ($counter ++ / scalar(@{$instance->{history}}));
- gtk2_update();
-
- }
-
- # Make sure we are at the top.
-
- $instance->{history_scrolledwindow}->get_vadjustment()->set_value(0);
- $instance->{history_scrolledwindow}->get_hadjustment()->set_value(0);
- $instance->{appbar}->set_progress_percentage(0);
- $instance->{appbar}->set_status("");
- gtk2_update();
-
- $instance->{appbar}->pop();
- make_busy($instance, 0);
-
}
#
##############################################################################
@@ -1183,560 +535,6 @@ sub manifest_browser_treeview_row_activa
#
##############################################################################
#
-# Routine - simple_query_radiobutton_toggled_cb
-#
-# Description - Callback routine called when the user changes the advanced
-# find mode radio button.
-#
-# Data - $widget : The widget object that received the
-# signal.
-# $advanced_find : The advanced find dialog window instance
-# that is associated with this widget.
-#
-##############################################################################
-
-
-
-sub simple_query_radiobutton_toggled_cb($$)
-{
-
- my($widget, $advanced_find) = @_;
-
- return if ($advanced_find->{in_cb});
- local $advanced_find->{in_cb} = 1;
-
- my($len,
- $value);
-
- # Simply enable the relevant find widgets depending upon whether simple or
- # advanced mode is selected.
-
- if ($advanced_find->{simple_query_radiobutton}->get_active())
- {
- $advanced_find->{simple_frame}->set_sensitive(TRUE);
- $advanced_find->{advanced_frame}->set_sensitive(FALSE);
- }
- else
- {
- $advanced_find->{simple_frame}->set_sensitive(FALSE);
- $advanced_find->{advanced_frame}->set_sensitive(TRUE);
- }
-
-}
-#
-##############################################################################
-#
-# Routine - execute_button_clicked_cb
-#
-# Description - Callback routine called when the user clicks on the execute
-# query button in the advanced find window.
-#
-# Data - $widget : The widget object that received the
-# signal.
-# $advanced_find : The advanced find dialog window instance
-# that is associated with this widget.
-#
-##############################################################################
-
-
-
-sub execute_button_clicked_cb($$)
-{
-
- my($widget, $advanced_find) = @_;
-
- return if ($advanced_find->{in_cb});
- local $advanced_find->{in_cb} = 1;
-
- # Simply let the update handler deal with it.
-
- &{$advanced_find->{update_handler}}($advanced_find, REVISION_CHANGED);
-
-}
-#
-##############################################################################
-#
-# Routine - populate_button_clicked_cb
-#
-# Description - Callback routine called when the user clicks on the
-# populate selector button in the advanved find window.
-#
-# Data - $widget : The widget object that received the
-# signal.
-# $advanced_find : The advanced find dialog window instance
-# that is associated with this widget.
-#
-##############################################################################
-
-
-
-sub populate_button_clicked_cb($$)
-{
-
- my($widget, $advanced_find) = @_;
-
- return if ($advanced_find->{in_cb});
- local $advanced_find->{in_cb} = 1;
-
- my($arg,
- $pos,
- $selector,
- $time_val,
- $to_insert);
-
- # Simply get the currently selected selector and then insert it into the
- # user's query string.
-
- $selector = $advanced_find->{term_combo}->get_model()->get
- ($advanced_find->{term_combo}->get_active_iter(), 0);
- $arg = $advanced_find->{argument_entry}->get_text();
- $time_val = strftime("%Y-%m-%dT%H:%M:%S",
- localtime($advanced_find->{date_dateedit}->
- get_time()));
- $to_insert = "";
- if ($selector eq "Author")
- {
- $to_insert = "a:" . (($arg eq "") ? "" : $arg);
- }
- elsif ($selector eq "Branch")
- {
- $to_insert = "b:" . (($arg eq "") ? "" : $arg);
- }
- elsif ($selector eq "Cert")
- {
- $to_insert = "c:" . (($arg eq "") ? "" : $arg);
- }
- elsif ($selector eq "Date (=)")
- {
- $to_insert = "d:" . $time_val;
- }
- elsif ($selector eq "Date (<=)")
- {
- $to_insert = "e:" . $time_val;
- }
- elsif ($selector eq "Date (>)")
- {
- $to_insert = "l:" . $time_val;
- }
- elsif ($selector eq "Head Revision")
- {
- $to_insert = "h:";
- }
- elsif ($selector eq "Identifier")
- {
- $to_insert = "i:" . (($arg eq "") ? "" : $arg);
- }
- elsif ($selector eq "Parent")
- {
- $to_insert = "p:" . (($arg eq "") ? "" : $arg);
- }
- elsif ($selector eq "Separator")
- {
- $to_insert = "/";
- }
- elsif ($selector eq "Tag")
- {
- $to_insert = "t:" . (($arg eq "") ? "" : $arg);
- }
-
- $pos = $advanced_find->{search_term_combo}->child()->get_position();
- $advanced_find->{search_term_combo}->child()->insert_text
- ($to_insert, $pos);
- $advanced_find->{search_term_combo}->child()->set_position
- ($pos + length($to_insert));
-
-}
-#
-##############################################################################
-#
-# Routine - term_combobox_changed_cb
-#
-# Description - Callback routine called when the user changes the value of
-# the term combobox by selecting an entry from its pulldown
-# list in the advanced find window.
-#
-# Data - $widget : The widget object that received the signal.
-# $instance : The window instance that is associated with
-# this widget.
-#
-##############################################################################
-
-
-
-sub term_combobox_changed_cb($$)
-{
-
- my($widget, $advanced_find) = @_;
-
- return if ($advanced_find->{in_cb});
- local $advanced_find->{in_cb} = 1;
-
- my($arg,
- $pos,
- $selector,
- $time_val,
- $to_insert);
-
- # Simply get the currently selected term and then enable/disable the text
- # entry and date entry widgets accordingly.
-
- $selector = $advanced_find->{term_combo}->get_model()->get
- ($advanced_find->{term_combo}->get_active_iter(), 0);
-
- if ($selector =~ m/^Date .*$/o)
- {
- $advanced_find->{argument_entry}->set_sensitive(FALSE);
- $advanced_find->{date_dateedit}->set_sensitive(TRUE);
- }
- elsif ($selector eq "Head" || $selector eq "Separator")
- {
- $advanced_find->{argument_entry}->set_sensitive(FALSE);
- $advanced_find->{date_dateedit}->set_sensitive(FALSE);
- }
- else
- {
- $advanced_find->{argument_entry}->set_sensitive(TRUE);
- $advanced_find->{date_dateedit}->set_sensitive(FALSE);
- }
-
-}
-#
-##############################################################################
-#
-# Routine - revisions_treeview_cursor_changed_cb
-#
-# Description - Callback routine called when the user selects an entry in
-# the revisions treeview in the advanced find window.
-#
-# Data - $widget : The widget object that received the
-# signal.
-# $advanced_find : The advanced find dialog window instance
-# that is associated with this widget.
-#
-##############################################################################
-
-
-
-sub revisions_treeview_cursor_changed_cb($$)
-{
-
- my($widget, $advanced_find) = @_;
-
- return if ($advanced_find->{in_cb});
- local $advanced_find->{in_cb} = 1;
-
- my $revision_id;
-
- # Get the selected revision id.
-
- $widget->get_selection()->selected_foreach
- (sub {
- my($model, $path, $iter) = @_;
- $revision_id = $model->get($iter, 0); });
-
- if (defined($revision_id)
- && $revision_id
- ne $advanced_find->{revisions_treeview_details}->{value})
- {
- $advanced_find->{revisions_treeview_details}->{value} = $revision_id;
- $advanced_find->{appbar}->clear_stack();
- &{$advanced_find->{update_handler}}($advanced_find,
- SELECTED_REVISION_CHANGED);
- }
-
-}
-#
-##############################################################################
-#
-# Routine - revisions_treeview_row_activated_cb
-#
-# Description - Callback routine called when the user double clicks on an
-# entry in the revisions treeview in the advanced find
-# window.
-#
-# Data - $widget : The widget object that received the
-# signal.
-# $tree_path : A Gtk2::TreePath object for the
-# selected item.
-# $tree_view_column : A Gtk2::TreeViewColumn object for the
-# selected item.
-# $advanced_find : The advanced find dialog window
-# instance that is associated with this
-# widget.
-#
-##############################################################################
-
-
-
-sub revisions_treeview_row_activated_cb($$$$)
-{
-
- my($widget, $tree_path, $tree_view_column, $advanced_find) = @_;
-
- return if ($advanced_find->{in_cb});
- local $advanced_find->{in_cb} = 1;
-
- my $revision_id;
-
- # Get the selected revision id.
-
- $widget->get_selection()->selected_foreach
- (sub {
- my($model, $path, $iter) = @_;
- $revision_id = $model->get($iter, 0); });
-
- if (defined($revision_id))
- {
- $advanced_find->{revisions_treeview_details}->{value} = $revision_id;
- $advanced_find->{appbar}->clear_stack();
- $advanced_find->{selected} = 1;
- $advanced_find->{done} = 1;
- }
-
-}
-#
-##############################################################################
-#
-# Routine - history_list_button_clicked_cb
-#
-# Description - Callback routine called when the user clicks on any of the
-# buttons displayed in the history list in a history window.
-#
-# Data - $widget : The widget object that received the signal.
-# $details : A reference to an anonymous hash containing the
-# window instance, revision and action that is
-# associated with this widget.
-#
-##############################################################################
-
-
-
-sub history_list_button_clicked_cb($$)
-{
-
- my($widget, $details) = @_;
-
- my($cl_instance,
- $instance,
- $revision_id);
-
- $instance = $details->{instance};
- $revision_id = $details->{revision_id};
-
- return if ($instance->{in_cb});
- local $instance->{in_cb} = 1;
-
- if ($details->{button_type} eq "1" || $details->{button_type} eq "2")
- {
- if ($details->{button_type} eq "1")
- {
- $instance->{first_revision_id} = $revision_id;
- set_label_value($instance->{revision_id_1_label}, $revision_id);
- if ($instance->{first_revision_id}
- eq $instance->{second_revision_id})
- {
- $instance->{second_revision_id} = "";
- set_label_value($instance->{revision_id_2_label}, "");
- }
- }
- else
- {
- $instance->{second_revision_id} = $revision_id;
- set_label_value($instance->{revision_id_2_label}, $revision_id);
- if ($instance->{second_revision_id}
- eq $instance->{first_revision_id})
- {
- $instance->{first_revision_id} = "";
- set_label_value($instance->{revision_id_1_label}, "");
- }
- }
- if ($instance->{first_revision_id} ne ""
- && $instance->{second_revision_id} ne "")
- {
- $instance->{compare_button}->set_sensitive(TRUE);
- }
- else
- {
- $instance->{compare_button}->set_sensitive(FALSE);
- }
- }
- else
- {
-
- my(@certs_list,
- @revision_details);
-
- # Display the full revision change log.
-
- $cl_instance = get_change_log_window();
- $cl_instance->{changelog_buffer}->set_text("");
- $cl_instance->{window}->set_title("Revision " . $revision_id);
- $instance->{mtn}->certs(address@hidden, $revision_id);
- $instance->{mtn}->get_revision(address@hidden, $revision_id);
- generate_revision_report($cl_instance->{changelog_buffer},
- $revision_id,
- address@hidden,
- "",
- address@hidden);
- if ($cl_instance->{changelog_scrolledwindow}->realized())
- {
- $cl_instance->{changelog_scrolledwindow}->get_vadjustment()->
- set_value(0);
- $cl_instance->{changelog_scrolledwindow}->get_hadjustment()->
- set_value(0);
- }
- $cl_instance->{window}->show_all();
-
- }
-
-}
-#
-##############################################################################
-#
-# Routine - compare_button_clicked_cb
-#
-# Description - Callback routine called when the user clicks on the
-# revision comparison button in a history window.
-#
-# Data - $widget : The widget object that received the signal.
-# $instance : The window instance that is associated with
-# this widget.
-#
-##############################################################################
-
-
-
-sub compare_button_clicked_cb($$)
-{
-
- my($widget, $instance) = @_;
-
- return if ($instance->{in_cb});
- local $instance->{in_cb} = 1;
-
- my @revision_ids;
-
- # Sort the revisions by date, oldest first.
-
- @revision_ids = ($instance->{first_revision_id},
- $instance->{second_revision_id});
- $instance->{mtn}->toposort(address@hidden, @revision_ids);
-
- # Now compare them.
-
- compare_revisions($instance->{mtn},
- $revision_ids[0],
- $revision_ids[1],
- $instance->{file_name});
-
-}
-#
-##############################################################################
-#
-# Routine - file_comparison_combobox_changed_cb
-#
-# Description - Callback routine called when the user changes the value of
-# the file combobox by selecting an entry from its pulldown
-# list in a revision comparison window.
-#
-# Data - $widget : The widget object that received the signal.
-# $instance : The window instance that is associated with
-# this widget.
-#
-##############################################################################
-
-
-
-sub file_comparison_combobox_changed_cb($$)
-{
-
- my($widget, $instance) = @_;
-
- return if ($instance->{in_cb});
- local $instance->{in_cb} = 1;
-
- my($iter,
- $line_nr);
-
- # Simply get the currently selected term and then enable/disable the text
- # entry and date entry widgets accordingly.
-
- $line_nr = $instance->{file_combo}->get_model()->get
- ($instance->{file_combo}->get_active_iter(), CLS_LINE_NR_COLUMN);
- $iter = $instance->{comparison_buffer}->get_iter_at_line($line_nr);
- $instance->{comparison_textview}->scroll_to_iter($iter, 0, TRUE, 0, 0);
-
-}
-#
-##############################################################################
-#
-# Routine - coloured_revision_change_log_button_clicked_cb
-#
-# Description - Callback routine called when the user clicks on either of
-# the two coloured revision change log buttons in a revision
-# comparison window.
-#
-# Data - $widget : The widget object that received the signal.
-# $details : A reference to an anonymous hash containing the
-# window instance, revision and action that is
-# associated with this widget.
-#
-##############################################################################
-
-
-
-sub coloured_revision_change_log_button_clicked_cb($$)
-{
-
- my($widget, $instance) = @_;
-
- return if ($instance->{in_cb});
- local $instance->{in_cb} = 1;
-
- my(@certs_list,
- $cl_instance,
- $colour,
- @revision_details,
- $revision_id);
-
- # Work out what revision id to use.
-
- if ($widget == $instance->{red_revision_button})
- {
- $revision_id = $instance->{red_revision_id};
- $colour = "red";
- }
- else
- {
- $revision_id = $instance->{green_revision_id};
- $colour = "green";
- }
-
- # Display the full revision change log.
-
- $cl_instance = get_change_log_window();
- $cl_instance->{changelog_buffer}->set_text("");
- $cl_instance->{window}->set_title("Revision " . $revision_id);
- $instance->{mtn}->certs(address@hidden, $revision_id);
- $instance->{mtn}->get_revision(address@hidden, $revision_id);
- generate_revision_report($cl_instance->{changelog_buffer},
- $revision_id,
- address@hidden,
- $colour,
- address@hidden);
- if ($cl_instance->{changelog_scrolledwindow}->realized())
- {
- $cl_instance->{changelog_scrolledwindow}->get_vadjustment()->
- set_value(0);
- $cl_instance->{changelog_scrolledwindow}->get_hadjustment()->
- set_value(0);
- }
- $cl_instance->{window}->show_all();
-
-}
-#
-##############################################################################
-#
# Routine - delete_event_cb
#
# Description - Callback routine called when the used has attempted to
@@ -2042,1249 +840,6 @@ sub new_browser_instance()
#
##############################################################################
#
-# Routine - advanced_find
-#
-# Description - Displays the advanced find dialog window and then gets the
-# user to select the revision they want.
-#
-# Data - $browser : The browser instance that started the
-# advanced find.
-# $revision_id : A reference to a variable that is to contain
-# the selected revision id.
-# $branches : A reference to a list that is to contain the
-# list of branches that the selected revision
-# is on.
-# Return Value : True if a revision has been selected,
-# otherwise false.
-#
-##############################################################################
-
-
-
-sub advanced_find($$$)
-{
-
- my($browser, $revision_id, $branches) = @_;
-
- my($height,
- $width);
-
- $advanced_find = create_advanced_find_window()
- unless (defined($advanced_find));
-
- $advanced_find->{mtn} = $browser->{mtn};
-
- # Update the window's internal state.
-
- {
- local $advanced_find->{in_cb} = 1;
- $advanced_find->{selected} = 0;
-
- # Reset the window contents, then show it.
-
- $advanced_find->{window}->set_transient_for($browser->{window});
- $advanced_find->{branch_combo_details}->{preset} = 0;
- $advanced_find->{revision_combo_details}->{preset} = 0;
- $advanced_find->{appbar}->set_progress_percentage(0);
- $advanced_find->{appbar}->clear_stack();
- &{$advanced_find->{update_handler}}($advanced_find, NEW_FIND);
- ($width, $height) = $advanced_find->{window}->get_default_size();
- $advanced_find->{window}->resize($width, $height);
- $advanced_find->{window}->show_all();
-
- # Now actually update it with any preset values.
-
- $advanced_find->{branch_combo_details}->{preset} = 1;
- $advanced_find->{branch_combo_details}->{completed} =
- $browser->{branch_combo_details}->{completed};
- $advanced_find->{branch_combo_details}->{value} =
- $browser->{branch_combo_details}->{value};
-
- $advanced_find->{revision_combo_details}->{preset} = 1;
- $advanced_find->{revision_combo_details}->{completed} =
- $browser->{revision_combo_details}->{completed};
- $advanced_find->{revision_combo_details}->{value} =
- $browser->{revision_combo_details}->{value};
-
- $advanced_find->{tagged_tick}->
- set_active($browser->{tagged_tick}->get_active());
-
- &{$advanced_find->{update_handler}}($advanced_find, NEW_FIND);
- }
-
- # Handle all events until the dialog is dismissed.
-
- $advanced_find->{done} = 0;
- while (! $advanced_find->{done})
- {
- Gtk2->main_iteration();
- }
- $advanced_find->{window}->hide();
-
- # Deal with the result.
-
- @$branches = ();
- $$revision_id = "";
- if ($advanced_find->{selected})
- {
- my($branch_list,
- @certs_list,
- $found);
-
- $$revision_id = $advanced_find->{revisions_treeview_details}->{value};
-
- # Build up a list of branches that the selected revision is on, putting
- # the branch named in the branch combo box at the head if it is still
- # applicable.
-
- $advanced_find->{mtn}->certs(address@hidden, $$revision_id);
- $found = 0;
- foreach my $cert (@certs_list)
- {
- if ($cert->{name} eq "branch")
- {
- if ($cert->{value}
- ne $advanced_find->{branch_combo_details}->{value})
- {
- push(@$branches, $cert->{value});
- }
- else
- {
- $found = 1;
- }
- }
- }
- unshift(@$branches, $advanced_find->{branch_combo_details}->{value})
- if ($found);
- push(@$branches, "") if (scalar(@$branches) == 0);
-
- return 1;
- }
- else
- {
- return;
- }
-
-}
-#
-##############################################################################
-#
-# Routine - create_advanced_find_window
-#
-# Description - Creates the advanced find dialog window.
-#
-# Data - Return Value : A reference to the newly created advanced
-# find instance record.
-#
-##############################################################################
-
-
-
-sub create_advanced_find_window()
-{
-
- my(@branch_list,
- $font,
- $instance,
- $renderer,
- $tv_column);
-
- $instance = {};
- $instance->{glade} =
- Gtk2::GladeXML->new("../mtn-browse.glade", "advanced_find_window");
-
- # Flag to stop recursive calling of callbacks.
-
- $instance->{in_cb} = 0;
-
- # Connect Glade registered signal handlers.
-
- $instance->{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); },
- $instance);
-
- # Link in the update handler for the advanced find window.
-
- $instance->{update_handler} = \&update_advanced_find_state;
-
- # Get the widgets that we are interested in.
-
- $instance->{window} =
- $instance->{glade}->get_widget("advanced_find_window");
- $instance->{window}->set_icon($app_icon);
- $instance->{appbar} = $instance->{glade}->get_widget("appbar");
- $instance->{simple_query_radiobutton} =
- $instance->{glade}->get_widget("simple_query_radiobutton");
- $instance->{simple_frame} = $instance->{glade}->get_widget("simple_frame");
- $instance->{advanced_frame} =
- $instance->{glade}->get_widget("advanced_frame");
- $instance->{branch_combo} =
- $instance->{glade}->get_widget("branch_comboboxentry");
- $instance->{revision_combo} =
- $instance->{glade}->get_widget("revision_comboboxentry");
- $instance->{tagged_tick} =
- $instance->{glade}->get_widget("tagged_checkbutton");
- $instance->{search_term_combo} =
- $instance->{glade}->get_widget("search_term_comboboxentry");
- $instance->{term_combo} = $instance->{glade}->get_widget("term_combobox");
- $instance->{argument_entry} =
- $instance->{glade}->get_widget("argument_entry");
- $instance->{date_dateedit} =
- $instance->{glade}->get_widget("date_dateedit");
- $instance->{revisions_treeview} =
- $instance->{glade}->get_widget("revisions_treeview");
- $instance->{details_textview} =
- $instance->{glade}->get_widget("details_textview");
- $instance->{details_scrolledwindow} =
- $instance->{glade}->get_widget("details_scrolledwindow");
- $instance->{selected_branch_label} =
- $instance->{glade}->get_widget("selected_branch_value_label");
- $instance->{selected_revision_label} =
- $instance->{glade}->get_widget("selected_revision_value_label");
- $instance->{ok_button} = $instance->{glade}->get_widget("ok_button");
-
- # Setup the advanced find window deletion handlers.
-
- $instance->{window}->signal_connect
- ("delete_event",
- sub { $_[2]->{done} = 1 unless ($_[2]->{in_cb}); return TRUE; },
- $instance);
- $instance->{glade}->get_widget("cancel_button")->signal_connect
- ("clicked", sub { $_[1]->{done} = 1; }, $instance);
- $instance->{glade}->get_widget("ok_button")->signal_connect
- ("clicked", sub { $_[1]->{done} = $_[1]->{selected} = 1; }, $instance);
-
- # Setup the comboboxentry key release signal handlers.
-
- $instance->{branch_combo}->child()->
- signal_connect("key_release_event",
- \&combo_key_release_event_cb,
- $instance);
- $instance->{revision_combo}->child()->
- signal_connect("key_release_event",
- \&combo_key_release_event_cb,
- $instance);
-
- # Setup the comboboxes.
-
- $instance->{branch_combo}->set_model(Gtk2::ListStore->new("Glib::String"));
- $instance->{branch_combo}->set_text_column(0);
- $instance->{branch_combo}->set_wrap_width(2);
- $instance->{revision_combo}->
- set_model(Gtk2::ListStore->new("Glib::String"));
- $instance->{revision_combo}->set_text_column(0);
- $instance->{revision_combo}->set_wrap_width(2);
- $instance->{search_term_combo}->
- set_model(Gtk2::ListStore->new("Glib::String"));
- $instance->{search_term_combo}->set_text_column(0);
- $instance->{query_history} = [];
- $instance->{term_combo}->set_active(0);
-
- # Setup the revisions list browser.
-
- $instance->{revisions_liststore} = Gtk2::ListStore->new("Glib::String");
- $instance->{revisions_treeview}->
- set_model($instance->{revisions_liststore});
- $tv_column = Gtk2::TreeViewColumn->new();
- $tv_column->set_title("Matching Revision Ids");
- $tv_column->set_sort_column_id(0);
- $renderer = Gtk2::CellRendererText->new();
- $tv_column->pack_start($renderer, FALSE);
- $tv_column->set_attributes($renderer, "text" => 0);
- $instance->{revisions_treeview}->append_column($tv_column);
- $instance->{revisions_treeview}->set_search_column(0);
-
- # Setup the revision details viewer.
-
- $instance->{details_buffer} = $instance->{details_textview}->get_buffer();
- create_format_tags($instance->{details_textview}->get_buffer());
- $font = Gtk2::Pango::FontDescription->from_string("monospace 10");
- $instance->{details_textview}->modify_font($font) if (defined($font));
-
- # Setup the list of windows that can be made busy for this application
- # window.
-
- $instance->{busy_windows} = [];
- push(@{$instance->{busy_windows}}, $instance->{window}->window());
- push(@{$instance->{busy_windows}},
- $instance->{details_textview}->get_window("text"));
-
- return $instance;
-
-}
-#
-##############################################################################
-#
-# Routine - get_change_log_window
-#
-# Description - Creates or prepares an existing a change log window for
-# use.
-#
-# Data - Return Value : A reference to the newly created or unused
-# change log instance record.
-#
-##############################################################################
-
-
-
-sub get_change_log_window()
-{
-
- my ($font,
- $height,
- $instance,
- $width);
-
- foreach my $window (@windows)
- {
- if ($window->{type} eq "change_log_window"
- && ! $window->{window}->mapped())
- {
- $instance = $window;
- last;
- }
- }
-
- # Create a new change log window if an unused one wasn't found, otherwise
- # reuse an existing unused one.
-
- if (! defined($instance))
- {
- $instance = {};
- $instance->{type} = "change_log_window";
- $instance->{glade} =
- Gtk2::GladeXML->new("../mtn-browse.glade", "changelog_window");
-
- # Flag to stop recursive calling of callbacks.
-
- $instance->{in_cb} = 0;
-
- # Connect Glade registered signal handlers.
-
- $instance->{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); },
- $instance);
-
- # Get the widgets that we are interested in.
-
- $instance->{window} =
- $instance->{glade}->get_widget("changelog_window");
- $instance->{window}->set_icon($app_icon);
- $instance->{changelog_textview} =
- $instance->{glade}->get_widget("changelog_textview");
- $instance->{changelog_scrolledwindow} =
- $instance->{glade}->get_widget("changelog_scrolledwindow");
-
- # Setup the changelog window deletion handler.
-
- $instance->{window}->signal_connect
- ("delete_event",
- sub {
- my($widget, $event, $instance) = @_;
- return TRUE if ($instance->{in_cb});
- local $instance->{in_cb} = 1;
- $widget->hide();
- $instance->{changelog_buffer}->set_text("");
- return TRUE;
- },
- $instance);
-
- # Setup the revision changelog viewer.
-
- $instance->{changelog_buffer} =
- $instance->{changelog_textview}->get_buffer();
- create_format_tags($instance->{changelog_buffer});
- $font = Gtk2::Pango::FontDescription->from_string("monospace 10");
- $instance->{changelog_textview}->modify_font($font)
- if (defined($font));
-
- $instance->{grab_widget} = $instance->{window};
-
- # Setup the list of windows that can be made busy for this application
- # window.
-
- $instance->{busy_windows} = [];
- push(@{$instance->{busy_windows}}, $instance->{window}->window());
- push(@{$instance->{busy_windows}},
- $instance->{changelog_textview}->get_window("text"));
-
- push(@windows, $instance);
- }
- else
- {
- $instance->{in_cb} = 0;
- ($width, $height) = $instance->{window}->get_default_size();
- $instance->{window}->resize($width, $height);
- }
-
- # Empty out the contents.
-
- $instance->{changelog_buffer}->set_text("");
-
- return $instance;
-
-}
-#
-##############################################################################
-#
-# Routine - get_history_window
-#
-# Description - Creates or prepares an existing change log window for use.
-#
-# Data - Return Value : A reference to the newly created or unused
-# change log instance record.
-#
-##############################################################################
-
-
-
-sub get_history_window()
-{
-
- my ($font,
- $height,
- $instance,
- $width);
-
- # Look for an unused window first.
-
- foreach my $window (@windows)
- {
- if ($window->{type} eq "history_window"
- && ! $window->{window}->mapped())
- {
- $instance = $window;
- last;
- }
- }
-
- # Create a new file history window if an unused one wasn't found, otherwise
- # reuse an existing unused one.
-
- if (! defined($instance))
- {
- $instance = {};
- $instance->{type} = "history_window";
- $instance->{glade} =
- Gtk2::GladeXML->new("../mtn-browse.glade", "history_window");
-
- # Flag to stop recursive calling of callbacks.
-
- $instance->{in_cb} = 0;
-
- # Connect Glade registered signal handlers.
-
- $instance->{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); },
- $instance);
-
- # Get the widgets that we are interested in.
-
- $instance->{window} = $instance->{glade}->get_widget("history_window");
- $instance->{window}->set_icon($app_icon);
- $instance->{appbar} = $instance->{glade}->get_widget("appbar");
- $instance->{history_label} =
- $instance->{glade}->get_widget("history_label");
- $instance->{history_textview} =
- $instance->{glade}->get_widget("history_textview");
- $instance->{history_scrolledwindow} =
- $instance->{glade}->get_widget("history_scrolledwindow");
- $instance->{stop_button} =
- $instance->{glade}->get_widget("stop_button");
- $instance->{compare_button} =
- $instance->{glade}->get_widget("compare_button");
- $instance->{numbers_label} =
- $instance->{glade}->get_widget("numbers_value_label");
- $instance->{revision_id_1_label} =
- $instance->{glade}->get_widget("revision_id_1_value_label");
- $instance->{revision_id_2_label} =
- $instance->{glade}->get_widget("revision_id_2_value_label");
-
- # Setup the file history callbacks.
-
- $instance->{window}->signal_connect
- ("delete_event",
- sub {
- my($widget, $event, $instance) = @_;
- return TRUE if ($instance->{in_cb});
- local $instance->{in_cb} = 1;
- $widget->hide();
- $instance->{history_buffer}->set_text("");
- return TRUE;
- },
- $instance);
- $instance->{stop_button}->signal_connect
- ("clicked", sub { $_[1]->{stop} = 1; }, $instance);
-
- # Setup the file history viewer.
-
- $instance->{history_buffer} =
- $instance->{history_textview}->get_buffer();
- create_format_tags($instance->{history_buffer});
- $font = Gtk2::Pango::FontDescription->from_string("monospace 10");
- $instance->{history_textview}->modify_font($font) if (defined($font));
-
- # Make the stop button the grab widget when busy, this is so the user
- # can interrupt the history gathering process.
-
- $instance->{grab_widget} = $instance->{stop_button};
-
- # Setup the list of windows that can be made busy for this application
- # window.
-
- $instance->{busy_windows} = [];
- push(@{$instance->{busy_windows}}, $instance->{window}->window());
- push(@{$instance->{busy_windows}},
- $instance->{history_textview}->get_window("text"));
-
- push(@windows, $instance);
- }
- else
- {
- $instance->{in_cb} = 0;
- ($width, $height) = $instance->{window}->get_default_size();
- $instance->{window}->resize($width, $height);
- $instance->{stop_button}->set_sensitive(FALSE);
- $instance->{compare_button}->set_sensitive(FALSE);
- set_label_value($instance->{numbers_label}, "");
- set_label_value($instance->{revision_id_1_label}, "");
- set_label_value($instance->{revision_id_2_label}, "");
- }
-
- $instance->{stop} = 0;
-
- # Empty out the contents.
-
- $instance->{history_buffer}->set_text("");
-
- return $instance;
-
-}
-#
-##############################################################################
-#
-# Routine - get_file_history_helper
-#
-# Description - Recursive routine for getting the revisions in a file's
-# change history.
-#
-# Data - $instance : The file history window instance.
-# $hash : A reference to a hash that is to contain the
-# list of revision ids.
-# $revision_id : The revision id from where the search is to
-# commence.
-#
-##############################################################################
-
-
-
-sub get_file_history_helper($$$)
-{
-
- my($instance, $hash, $revision_id) = @_;
-
- return if ($instance->{stop});
-
- my(@change_parents,
- @parents);
-
- $instance->{mtn}->get_content_changed(address@hidden,
- $revision_id,
- $instance->{file_name});
- foreach my $revision (@change_parents)
- {
- if (! exists($hash->{$revision}))
- {
- $hash->{$revision} = 1;
- set_label_value($instance->{numbers_label}, scalar(keys(%$hash)));
- gtk2_update();
- @parents = ();
- $instance->{mtn}->parents(address@hidden, $revision);
- foreach my $parent (@parents)
- {
- get_file_history_helper($instance, $hash, $parent);
- }
- }
- }
-
-}
-#
-##############################################################################
-#
-# Routine - get_revision_history_helper
-#
-# Description - Recursive routine for getting the revisions in a revision's
-# change history.
-#
-# Data - $instance : The revision history window instance.
-# $hash : A reference to a hash that is to contain the
-# list of revision ids.
-# $revision_id : The revision id from where the search is to
-# commence.
-#
-##############################################################################
-
-
-
-sub get_revision_history_helper($$$)
-{
-
- my($instance, $hash, $revision_id) = @_;
-
- return if ($instance->{stop});
-
- my @parents;
-
- $instance->{mtn}->parents(address@hidden, $revision_id);
- foreach my $parent (@parents)
- {
- if (! exists($hash->{$parent}))
- {
- $hash->{$parent} = 1;
- set_label_value($instance->{numbers_label}, scalar(keys(%$hash)));
- gtk2_update();
- get_revision_history_helper($instance, $hash, $parent);
- }
- }
-
-}
-#
-##############################################################################
-#
-# Routine - get_revision_comparison_window
-#
-# Description - Creates or prepares an existing revision comparison window
-# for use.
-#
-# Data - Return Value : A reference to the newly created or unused
-# change log instance record.
-#
-##############################################################################
-
-
-
-sub get_revision_comparison_window()
-{
-
- my ($font,
- $height,
- $instance,
- $renderer,
- $width);
-
- # Look for an unused window first.
-
- foreach my $window (@windows)
- {
- if ($window->{type} eq "revision_comparison_window"
- && ! $window->{window}->mapped())
- {
- $instance = $window;
- last;
- }
- }
-
- # Create a new revision comparison window if an unused one wasn't found,
- # otherwise reuse an existing unused one.
-
- if (! defined($instance))
- {
- $instance = {};
- $instance->{type} = "revision_comparison_window";
- $instance->{glade} = Gtk2::GladeXML->new("../mtn-browse.glade",
- "revision_comparison_window");
-
- # Flag to stop recursive calling of callbacks.
-
- $instance->{in_cb} = 0;
-
- # Connect Glade registered signal handlers.
-
- $instance->{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); },
- $instance);
-
- # Get the widgets that we are interested in.
-
- $instance->{window} =
- $instance->{glade}->get_widget("revision_comparison_window");
- $instance->{window}->set_icon($app_icon);
- $instance->{appbar} = $instance->{glade}->get_widget("appbar");
- $instance->{comparison_label} =
- $instance->{glade}->get_widget("comparison_label");
- $instance->{file_combo} =
- $instance->{glade}->get_widget("file_comparison_combobox");
- $instance->{comparison_textview} =
- $instance->{glade}->get_widget("comparison_textview");
- $instance->{comparison_scrolledwindow} =
- $instance->{glade}->get_widget("comparison_scrolledwindow");
- $instance->{red_revision_button} =
- $instance->{glade}->get_widget("red_revision_change_log_button");
- $instance->{green_revision_button} =
- $instance->{glade}->get_widget("green_revision_change_log_button");
-
- # Setup the file history callbacks.
-
- $instance->{window}->signal_connect
- ("delete_event",
- sub {
- my($widget, $event, $instance) = @_;
- return TRUE if ($instance->{in_cb});
- local $instance->{in_cb} = 1;
- $widget->hide();
- $instance->{file_combo}->get_model()->clear();
- $instance->{comparison_buffer}->set_text("");
- return TRUE;
- },
- $instance);
-
- # Setup the file combobox.
-
- $instance->{file_combo}->
- set_model(Gtk2::ListStore->new("Glib::String", "Glib::Int"));
- $renderer = Gtk2::CellRendererText->new();
- $instance->{file_combo}->pack_start($renderer, TRUE);
- $instance->{file_combo}->add_attribute($renderer, "text" => 0);
- $instance->{file_combo}->get_model()->set
- ($instance->{file_combo}->get_model()->append(),
- CLS_NAME_COLUMN, " ",
- CLS_LINE_NR_COLUMN, 0);
-
- # Setup the revision comparison viewer.
-
- $instance->{comparison_buffer} =
- $instance->{comparison_textview}->get_buffer();
- create_format_tags($instance->{comparison_buffer});
- $font = Gtk2::Pango::FontDescription->from_string("monospace 10");
- $instance->{comparison_textview}->modify_font($font)
- if (defined($font));
-
- # Setup the list of windows that can be made busy for this application
- # window.
-
- $instance->{busy_windows} = [];
- push(@{$instance->{busy_windows}}, $instance->{window}->window());
- push(@{$instance->{busy_windows}},
- $instance->{comparison_textview}->get_window("text"));
-
- push(@windows, $instance);
- }
- else
- {
- $instance->{in_cb} = 0;
- local $instance->{in_cb} = 1;
- ($width, $height) = $instance->{window}->get_default_size();
- $instance->{window}->resize($width, $height);
- $instance->{file_combo}->get_model()->clear();
- $instance->{appbar}->set_progress_percentage(0);
- $instance->{appbar}->clear_stack();
- }
-
- # Empty out the contents.
-
- $instance->{comparison_buffer}->set_text("");
-
- return $instance;
-
-}
-#
-##############################################################################
-#
-# Routine - compare_revisions
-#
-# Description - Compares and then displays the differenece between the two
-# specified revisions, optionally restricting it to the
-# specified file.
-#
-# Data - $mtn : The Monotone instance handle that is to be
-# used to do the comparison.
-# $revision_id_1 : The first revision id that is to be
-# compared.
-# $revision_id_2 : The second revision id that is to be
-# compared.
-# $file_name : Either the name of the file that the
-# revision comparison should be restricted
-# to or undef for a full revision
-# comparison.
-#
-##############################################################################
-
-
-
-sub compare_revisions($$$;$)
-{
-
- my($mtn, $revision_id_1, $revision_id_2, $file_name) = @_;
-
- my ($char,
- @files,
- $i,
- $instance,
- $is_binary,
- $iter,
- $len,
- $line,
- @lines,
- $max_len,
- $name,
- $padding,
- $rest);
-
- $instance = get_revision_comparison_window();
- local $instance->{in_cb} = 1;
-
- $instance->{window}->set_title("Differences Between Revisions "
- . $revision_id_1
- . " And "
- . $revision_id_2);
- if (defined($file_name))
- {
- $instance->{comparison_label}->set_markup("File Comparison");
- }
- else
- {
- $instance->{comparison_label}->
- set_markup("Revision Comparison");
- }
- $instance->{window}->show_all();
- gtk2_update();
-
- make_busy($instance, 1);
- $instance->{appbar}->push("");
- gtk2_update();
-
- $instance->{mtn} = $mtn;
- $instance->{red_revision_id} = $revision_id_1;
- $instance->{green_revision_id} = $revision_id_2;
-
- # Get Monotone to do the comparison.
-
- $instance->{appbar}->set_status("Calculating differences");
- gtk2_update();
- mtn_diff(address@hidden,
- $mtn->get_db_name(),
- $revision_id_1,
- $revision_id_2,
- $file_name);
-
- # Find the longest line for future padding.
-
- $max_len = 0;
- foreach my $line (@lines)
- {
- ($char, $rest) = unpack("a1a*", $line);
- $rest =~ s/\s+$//o;
- $rest = expand($rest);
- $max_len = $len if (($len = length($rest)) > $max_len);
- $line = $char . $rest;
- }
-
- # Display the result, highlighting according to the diff output. Remember
- # the first two lines are just empty comment lines.
-
- $instance->{appbar}->set_status("Formatting and displaying differences");
- gtk2_update();
- $padding = " " x $max_len;
- $line = substr(" Summary" . $padding, 0, $max_len);
- $instance->{comparison_buffer}->insert_with_tags_by_name
- ($instance->{comparison_buffer}->get_end_iter(),
- $line . "\n",
- "compare-info");
- for ($i = 1; $i <= $#lines; ++ $i)
- {
-
- # Deal with the initial comment lines that summarise the entire set of
- # differences between the revisions.
-
- if ($lines[$i] =~ m/^\#/o)
- {
- $line = substr($lines[$i], 1);
- $instance->{comparison_buffer}->insert
- ($instance->{comparison_buffer}->get_end_iter(),
- $line . "\n");
- }
-
- # Deal with lines that introduces a new file comparison.
-
- elsif ($lines[$i] =~ m/^==/o)
- {
-
- # Print separator.
-
- $instance->{comparison_buffer}->
- insert_pixbuf($instance->{comparison_buffer}->get_end_iter(),
- $line_image);
- $instance->{comparison_buffer}->
- insert($instance->{comparison_buffer}->get_end_iter(),
- "\n");
-
- # Extract the file name, if this doesn't work then it is probably a
- # comment stating that the file is binary.
-
- ++ $i;
- ($name) = ($lines[$i] =~ m/^--- (.+)\s+[0-9a-f]{40}$/o);
- if (defined($name))
- {
- $is_binary = 0;
- }
- else
- {
- ($name) = ($lines[$i] =~ m/^\# (.+) is binary$/o);
- $is_binary = 1;
- }
-
- # Print out the details for the first file.
-
- $line = substr(substr($lines[$i], $is_binary ? 1 : 3) . $padding,
- 0,
- $max_len);
- $instance->{comparison_buffer}->insert_with_tags_by_name
- ($instance->{comparison_buffer}->get_end_iter(),
- $line . "\n",
- "compare-first-file-info");
-
- # Store the file name and the starting line number so that the user
- # can later jump straight to it using the file combobox.
-
- $iter = $instance->{comparison_buffer}->get_end_iter();
- $iter->backward_line();
- $instance->{comparison_buffer}->create_mark($name, $iter, FALSE);
- push(@files, {file_name => $name, line_nr => $iter->get_line()});
-
- # Print out the details for the second file if there is one.
-
- if (! $is_binary)
- {
- ++ $i;
- $line = substr(substr($lines[$i], 3) . $padding, 0, $max_len);
- $instance->{comparison_buffer}->insert_with_tags_by_name
- ($instance->{comparison_buffer}->get_end_iter(),
- $line . "\n",
- "compare-second-file-info");
- }
-
- }
-
- # Deal with difference context lines.
-
- elsif ($lines[$i] =~ m/^@@/o)
- {
- $line = substr(substr($lines[$i], 2) . $padding, 0, $max_len);
- $instance->{comparison_buffer}->insert_with_tags_by_name
- ($instance->{comparison_buffer}->get_end_iter(),
- $line . "\n",
- "compare-info");
- }
-
- # Deal with - change lines.
-
- elsif ($lines[$i] =~ m/^-/o)
- {
- $line = substr(substr($lines[$i], 1) . $padding, 0, $max_len);
- $instance->{comparison_buffer}->insert_with_tags_by_name
- ($instance->{comparison_buffer}->get_end_iter(),
- $line . "\n",
- "compare-first-file");
- }
-
- # Deal with + change lines.
-
- elsif ($lines[$i] =~ m/^\+/o)
- {
- $line = substr(substr($lines[$i], 1) . $padding, 0, $max_len);
- $instance->{comparison_buffer}->insert_with_tags_by_name
- ($instance->{comparison_buffer}->get_end_iter(),
- $line . "\n",
- "compare-second-file");
- }
-
- # Print out the rest.
-
- else
- {
- $line = substr($lines[$i], 1);
- $instance->{comparison_buffer}->insert
- ($instance->{comparison_buffer}->get_end_iter(),
- $line . "\n");
- }
-
- if (($i % 100) == 0)
- {
- $instance->{appbar}->set_progress_percentage
- (($i + 1) / scalar(@lines));
- gtk2_update();
- }
-
- }
-
- # Delete the trailing newline.
-
- $iter = $instance->{comparison_buffer}->get_end_iter();
- $instance->{comparison_buffer}->delete
- ($iter, $instance->{comparison_buffer}->get_end_iter())
- if ($iter->backward_char());
-
- # Populate the file combobox.
-
- $instance->{appbar}->set_progress_percentage(0);
- $instance->{appbar}->set_status("Populating file list");
- gtk2_update();
- @files = sort({ $a->{file_name} cmp $b->{file_name} } @files);
- $i = 1;
- $instance->{file_combo}->get_model()->clear();
- foreach my $file (@files)
- {
- $instance->{file_combo}->get_model()->set
- ($instance->{file_combo}->get_model()->append(),
- CLS_NAME_COLUMN, $file->{file_name},
- CLS_LINE_NR_COLUMN, $file->{line_nr});
- $instance->{appbar}->set_progress_percentage($i ++ / scalar(@files));
- gtk2_update();
- }
- $instance->{appbar}->set_progress_percentage(0);
- $instance->{appbar}->set_status("");
- gtk2_update();
-
- # Make sure we are at the top.
-
- $instance->{comparison_scrolledwindow}->get_vadjustment()->set_value(0);
- $instance->{comparison_scrolledwindow}->get_hadjustment()->set_value(0);
-
- $instance->{appbar}->pop();
- make_busy($instance, 0);
-
-}
-#
-##############################################################################
-#
-# Routine - generate_revision_report
-#
-# Description - Populate the specified Gtk2::TextBuffer with a pretty
-# printed report on the specified revision.
-#
-# Data - $text_buffer : The Gtk2::TextBuffer that is to be
-# populated.
-# $revision_id : The id of the revision being reported
-# on.
-# $certs_list : A reference to a certs list as returned
-# by $mtn->certs().
-# $colour : One of "red, "green" or "" depending
-# upon the desired colour of the text.
-# $revision_details : Either a reference to a revision
-# details list as returned by
-# $mtn->get_revision() if a detailed
-# report is to be generated or undef if
-# the report is to just be a summary.
-#
-##############################################################################
-
-
-
-sub generate_revision_report($$$$;$)
-{
-
- my($text_buffer, $revision_id, $certs_list, $colour, $revision_details)
- = @_;
-
- my($bold,
- $change_log,
- $italics,
- $manifest_id,
- $normal,
- @parent_revision_ids,
- %revision_data,
- %seen,
- @unique);
- my @types =
- ("Added", "Removed", "Changed", "Renamed", "Attributes");
-
- # Sort out colour attributes.
-
- if ($colour ne "")
- {
- $normal = $colour;
- $bold = "bold-" . $colour;
- $italics = "italics-" . $colour;
- }
- else
- {
- $normal = "normal";
- $bold = "bold";
- $italics = "italics";
- }
-
- # Revision id.
-
- $text_buffer->insert_with_tags_by_name($text_buffer->get_end_iter(),
- "Revision id: ",
- $bold);
- $text_buffer->insert_with_tags_by_name($text_buffer->get_end_iter(),
- $revision_id . "\n\n",
- $normal);
-
- # Certs.
-
- foreach my $cert (@$certs_list)
- {
- if ($cert->{name} eq "changelog")
- {
- $change_log = $cert->{value};
- $change_log =~ s/\s+$//os;
- }
- else
- {
- $cert->{value} =~ s/T/ /o if ($cert->{name} eq "date");
- $text_buffer->insert_with_tags_by_name
- ($text_buffer->get_end_iter(),
- sprintf("%s:\t", ucfirst($cert->{name})),
- $bold);
- $text_buffer->insert_with_tags_by_name
- ($text_buffer->get_end_iter(),
- sprintf("%s\n", $cert->{value}),
- $normal);
- }
- }
-
- # Change log.
-
- $text_buffer->insert_with_tags_by_name($text_buffer->get_end_iter(),
- "\nChange Log:\n",
- $bold);
- $text_buffer->insert_with_tags_by_name($text_buffer->get_end_iter(),
- sprintf("%s", $change_log),
- $normal);
-
- # The rest is only provided if it is a detailed report.
-
- if (defined($revision_details))
- {
-
- # Revision details.
-
- $text_buffer->insert_with_tags_by_name($text_buffer->get_end_iter(),
- "\n\nChanges Made:\n",
- $bold);
- foreach my $type (@types)
- {
- $revision_data{$type} = [];
- }
- foreach my $change (@$revision_details)
- {
- if ($change->{type} eq "add_dir")
- {
- push(@{$revision_data{"Added"}}, $change->{name} . "/");
- }
- elsif ($change->{type} eq "add_file")
- {
- push(@{$revision_data{"Added"}}, $change->{name});
- }
- elsif ($change->{type} eq "delete")
- {
- push(@{$revision_data{"Removed"}}, $change->{name});
- }
- elsif ($change->{type} eq "patch")
- {
- push(@{$revision_data{"Changed"}}, $change->{name});
- }
- elsif ($change->{type} eq "rename")
- {
- push(@{$revision_data{"Renamed"}},
- $change->{from_name} . " -> " . $change->{to_name});
- }
- elsif ($change->{type} eq "clear")
- {
- push(@{$revision_data{"Attributes"}},
- sprintf("%s: %s was cleared",
- $change->{name},
- $change->{attribute}));
- }
- elsif ($change->{type} eq "clear" || $change->{type} eq "set")
- {
- push(@{$revision_data{"Attributes"}},
- sprintf("%s: %s = %s",
- $change->{name},
- $change->{attribute},
- $change->{value}));
- }
- elsif ($change->{type} eq "old_revision")
- {
- push(@parent_revision_ids, $change->{revision_id});
- }
- elsif ($change->{type} eq "new_manifest")
- {
- $manifest_id = $change->{manifest_id};
- }
- }
- foreach my $type (@types)
- {
- if (scalar(@{$revision_data{$type}}) > 0)
- {
- $text_buffer->insert_with_tags_by_name
- ($text_buffer->get_end_iter(),
- " " . $type . ":\n",
- $italics);
- %seen = ();
- @unique = sort(grep { ! $seen{$_} ++ }
- @{$revision_data{$type}});
- foreach my $line (@unique)
- {
- $text_buffer->insert_with_tags_by_name
- ($text_buffer->get_end_iter(),
- "\t" . $line . "\n",
- $normal);
- }
- }
- }
-
- # Parent revision and manifest ids.
-
- $text_buffer->insert_with_tags_by_name($text_buffer->get_end_iter(),
- "\nParent revision id(s):\t",
- $bold);
- $text_buffer->insert_with_tags_by_name
- ($text_buffer->get_end_iter(),
- join(" ", @parent_revision_ids) . "\n",
- $normal);
- $text_buffer->insert_with_tags_by_name($text_buffer->get_end_iter(),
- "Manifest id:\t\t",
- $bold);
- $text_buffer->insert_with_tags_by_name($text_buffer->get_end_iter(),
- $manifest_id,
- $normal);
-
- }
-
-}
-#
-##############################################################################
-#
# Routine - update_browser_state
#
# Description - Update the display of the specified browser instance
@@ -3316,7 +871,7 @@ sub update_browser_state($$)
# Reset the branch selection.
- $browser->{branch_combo_details}->{completion_cache} = {};
+ $browser->{branch_combo_details}->{completion} = undef;
if (! $browser->{branch_combo_details}->{preset})
{
$browser->{branch_combo_details}->{completed} = 0;
@@ -3362,7 +917,7 @@ sub update_browser_state($$)
# Reset the revision selection.
- $browser->{revision_combo_details}->{completion_cache} = {};
+ $browser->{revision_combo_details}->{completion} = undef;
if (! $browser->{revision_combo_details}->{preset})
{
$browser->{revision_combo_details}->{completed} = 0;
@@ -3447,7 +1002,7 @@ sub update_browser_state($$)
# Reset the directory combo.
- $browser->{directory_combo_details}->{completion_cache} = {};
+ $browser->{directory_combo_details}->{completion} = undef;
$browser->{directory_combo_details}->{completed} = 0;
$browser->{directory_combo_details}->{last_typed_len} = 0;
$browser->{directory_combo_details}->{value} = "";
@@ -3881,744 +1436,6 @@ sub update_browser_state($$)
#
##############################################################################
#
-# Routine - update_advanced_find_state
-#
-# Description - Update the display of the specified advanced find dialog
-# window instance according to the specified state.
-#
-# Data - $advanced_find : The advanced find dialog window instance
-# that is to have its state updated.
-# $changed : What the user has changed.
-#
-##############################################################################
-
-
-
-sub update_advanced_find_state($$)
-{
-
- my($advanced_find, $changed) = @_;
-
- my $made_busy = 0;
-
- if ($advanced_find->{window}->realized())
- {
- $made_busy = 1;
- make_busy($advanced_find, 1);
- }
- $advanced_find->{appbar}->push("");
- gtk2_update();
-
- # The list of available branches has changed.
-
- if ($changed & BRANCH)
- {
-
- my @branch_list;
-
- # Reset the query mode.
-
- $advanced_find->{simple_query_radiobutton}->set_active(TRUE);
- $advanced_find->{simple_frame}->set_sensitive(TRUE);
- $advanced_find->{advanced_frame}->set_sensitive(FALSE);
-
- # Reset the branch selection.
-
- $advanced_find->{branch_combo_details}->{completion_cache} = {};
- if (! $advanced_find->{branch_combo_details}->{preset})
- {
- $advanced_find->{branch_combo_details}->{completed} = 0;
- $advanced_find->{branch_combo_details}->{value} = "";
- }
- $advanced_find->{branch_combo_details}->{preset} = 0;
-
- # Get the new list of branches.
-
- $advanced_find->{appbar}->set_status("Fetching branch list");
- gtk2_update();
- $advanced_find->{mtn}->branches(address@hidden)
- if (defined($advanced_find->{mtn}));
- $advanced_find->{branch_combo_details}->{list} = address@hidden;
-
- # Update the branch list combobox.
-
- $advanced_find->{appbar}->set_status("Populating branch list");
- gtk2_update();
- my $counter = 1;
- $advanced_find->{branch_combo}->get_model()->clear();
- foreach my $branch (@branch_list)
- {
- $advanced_find->{branch_combo}->append_text($branch);
- $advanced_find->{appbar}->set_progress_percentage
- ($counter ++ / scalar(@branch_list));
- gtk2_update();
- }
- $advanced_find->{branch_combo}->child()->
- set_text($advanced_find->{branch_combo_details}->{value});
- $advanced_find->{appbar}->set_progress_percentage(0);
- $advanced_find->{appbar}->set_status("");
- gtk2_update();
-
- }
-
- # The list of available revisions has changed.
-
- if ($changed & REVISION)
- {
-
- my @revision_list;
-
- # Reset the revision selection.
-
- $advanced_find->{revision_combo_details}->{completion_cache} = {};
- if (! $advanced_find->{revision_combo_details}->{preset})
- {
- $advanced_find->{revision_combo_details}->{completed} = 0;
- $advanced_find->{revision_combo_details}->{value} = "";
- }
- $advanced_find->{revision_combo_details}->{preset} = 0;
-
- # Get the new list of revisions.
-
- if ($advanced_find->{branch_combo_details}->{completed})
- {
- $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_tick}->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();
- }
- $advanced_find->{revision_combo_details}->{list} = address@hidden;
-
- # Update the revision list combobox.
-
- $advanced_find->{appbar}->set_progress_percentage(0);
- $advanced_find->{appbar}->set_status("Populating revision list");
- gtk2_update();
- my $counter = 1;
- $advanced_find->{revision_combo}->get_model()->clear();
- foreach my $revision (@revision_list)
- {
- $advanced_find->{revision_combo}->append_text($revision);
- $advanced_find->{appbar}->set_progress_percentage
- ($counter ++ / scalar(@revision_list));
- gtk2_update();
- }
- $advanced_find->{revision_combo}->child()->
- set_text($advanced_find->{revision_combo_details}->{value});
- $advanced_find->{appbar}->set_progress_percentage(0);
- $advanced_find->{appbar}->set_status("");
- gtk2_update();
-
- }
-
- # The list of displayed revisions has changed.
-
- if ($changed & REVISION_LIST)
- {
-
- my($counter,
- @revision_ids);
-
- # Reset the revisions tree view.
-
- $advanced_find->{revisions_liststore}->clear();
- $advanced_find->{revisions_treeview_details}->{value} = "";
-
- # Get the list of matching revisions.
-
- $advanced_find->{appbar}->set_status("Finding revisions");
- gtk2_update();
- if ($advanced_find->{simple_query_radiobutton}->get_active())
- {
- if ($advanced_find->{revision_combo_details}->{completed})
- {
- get_revision_ids($advanced_find, address@hidden);
- }
- }
- else
- {
- my $query;
- $query = $advanced_find->{search_term_combo}->child()->get_text();
-
- # Remember the user can type in any old rubbish with advanced
- # queries! So protect ourselves.
-
- Monotone::AutomateStdio->register_error_handler
- ("both",
- sub {
- my($severity, $message) = @_;
- my $dialog;
- $dialog = Gtk2::MessageDialog->new_with_markup
- ($advanced_find->{window},
- ["modal"],
- "warning",
- "close",
- sprintf("Problem with your query, Monotone "
- . "gave:\n%s",
- Glib::Markup::escape_text($message)));
- $dialog->run();
- $dialog->destroy();
- die("Bad query"); });
- eval
- {
- $advanced_find->{mtn}->select(address@hidden, $query);
- };
-
- # If the query was valid the store it in the history.
-
- if (! $@)
- {
- my $found;
- if (scalar(@revision_ids) == 0)
- {
- my $dialog;
- $dialog = Gtk2::MessageDialog->new
- ($advanced_find->{window},
- ["modal"],
- "info",
- "close",
- "No revisions matched your query");
- $dialog->run();
- $dialog->destroy();
- }
- $found = 0;
- foreach my $entry (@{$advanced_find->{query_history}})
- {
- if ($entry eq $query)
- {
- $found = 1;
- last;
- }
- }
- if (! $found)
- {
- if (unshift(@{$advanced_find->{query_history}}, $query)
- > 20)
- {
- pop(@{$advanced_find->{query_history}});
- }
- $advanced_find->{search_term_combo}->get_model()->clear();
- foreach my $entry (@{$advanced_find->{query_history}})
- {
- $advanced_find->{search_term_combo}->
- append_text($entry);
- }
- }
- }
- Monotone::AutomateStdio->register_error_handler
- ("both", \&mtn_error_handler);
-
- }
- $advanced_find->{mtn}->toposort(address@hidden, @revision_ids);
- @revision_ids = reverse(@revision_ids);
-
- # Update the revisions tree view.
-
- $advanced_find->{appbar}->set_status("Populating revision details");
- $counter = 1;
- foreach my $item (@revision_ids)
- {
- $advanced_find->{revisions_liststore}->
- set($advanced_find->{revisions_liststore}->append(),
- 0, $item);
- $advanced_find->{appbar}->set_progress_percentage
- ($counter ++ / scalar(@revision_ids));
- gtk2_update();
- }
- $advanced_find->{revisions_treeview}->scroll_to_point(0, 0)
- if ($advanced_find->{revisions_treeview}->realized());
-
- $advanced_find->{appbar}->set_progress_percentage(0);
- $advanced_find->{appbar}->set_status("");
- gtk2_update();
-
- }
-
- # The selected revision has changed.
-
- if ($changed & REVISION_DETAILS)
- {
-
- if ($advanced_find->{revisions_treeview_details}->{value} ne "")
- {
- if ($advanced_find->{selected_revision_label}->get_text()
- ne $advanced_find->{revisions_treeview_details}->{value})
- {
- my ($branch,
- @certs_list,
- @revision_details);
-
- $advanced_find->{details_buffer}->set_text("");
- $advanced_find->{mtn}->certs
- (address@hidden,
- $advanced_find->{revisions_treeview_details}->{value});
- $advanced_find->{mtn}->get_revision
- (address@hidden,
- $advanced_find->{revisions_treeview_details}->{value});
- generate_revision_report
- ($advanced_find->{details_buffer},
- $advanced_find->{revisions_treeview_details}->{value},
- address@hidden,
- "",
- address@hidden);
-
- # Scroll back up to the top left.
-
- if ($advanced_find->{details_scrolledwindow}->realized())
- {
- $advanced_find->{details_scrolledwindow}->
- get_vadjustment()->set_value(0);
- $advanced_find->{details_scrolledwindow}->
- get_hadjustment()->set_value(0);
- }
-
- # Update the selected branch and revision labels.
-
- $branch = "";
- foreach my $cert (@certs_list)
- {
- if ($cert->{name} eq "branch")
- {
- $branch = $cert->{value};
- last;
- }
- }
- set_label_value($advanced_find->{selected_branch_label},
- $branch);
- set_label_value($advanced_find->{selected_revision_label},
- $advanced_find->{revisions_treeview_details}->
- {value});
-
- $advanced_find->{ok_button}->set_sensitive(TRUE);
- }
- }
- else
- {
- $advanced_find->{ok_button}->set_sensitive(FALSE);
- $advanced_find->{details_buffer}->set_text("");
- set_label_value($advanced_find->{selected_branch_label}, "");
- set_label_value($advanced_find->{selected_revision_label}, "");
- }
-
- }
-
- $advanced_find->{appbar}->pop();
- make_busy($advanced_find, 0) if ($made_busy);
-
-}
-#
-##############################################################################
-#
-# Routine - get_completion
-#
-# Description - Given a value and a list, work out the largest unique
-# match. Used for auto completion.
-#
-# Data - $value : The value to be completed.
-# $list : A reference to a list containing all
-# possible completions.
-# $result : A reference to a buffer that is to contain
-# the result.
-# $complete : A reference to a boolean that is to contain
-# a `result is complete' indicator.
-# $cache : An optional reference to a hash that will be
-# used to cache the hash tree (saved
-# recomputation).
-# Return Value : True if $value was expanded, otherwise false
-# if $value had to be truncated due to no
-# match (the maximum valid completion is still
-# returned in $result).
-#
-##############################################################################
-
-
-
-sub get_completion($$$$;$)
-{
-
- my($value, $list, $result, $complete, $cache) = @_;
-
- my($char,
- $item,
- $level,
- %local_cache,
- $tree);
-
- # Work out what cache we are to use.
-
- $tree = (defined($cache)) ? $cache : \%local_cache;
-
- # Unless we are given an already built hash tree, build one up for the list
- # of possible items.
-
- if (scalar(keys(%$tree)) == 0)
- {
- foreach $item (@$list)
- {
-
- # Build up nodes for an item.
-
- $level = $tree;
- foreach $char (split(//o, $item))
- {
- if (! exists($level->{$char}))
- {
- $level->{$char} = {};
- }
- $level = $level->{$char};
- }
-
- # By adding this dummy node here it stops the auto-complete moving
- # too far should another item extend beyond this point. I.e. auto
- # completion stops at `net.venge.monotone.contrib' and not
- # `net.venge.monotone.contrib.'. You could simply think of this
- # node as an `end of string' token if you prefer.
-
- $level->{""} = "";
-
- }
- }
-
- # Lookup value, stopping when it becomes ambiguous or we get to the end of
- # $value.
-
- $level = $tree;
- $$result = "";
- foreach $char (split(//o, $value))
- {
- last unless (exists($level->{$char}));
- $level = $level->{$char};
- $$result .= $char;
- }
-
- # Detect truncations.
-
- return if (length($value) > length($$result));
-
- # Now try and expand it further.
-
- while (defined(%$level) && keys(%$level) == 1)
- {
- ($char) = keys(%$level);
- $$result .= $char;
- $level = $level->{$char};
- }
-
- # Detect complete completions (doesn't mean to say that it can't be
- # extended, just that as it stands at the moment $$result does contain a
- # valid unique value).
-
- if (! defined(%$level) || exists($level->{""}))
- {
- $$complete = 1;
- }
- else
- {
- $$complete = 0;
- }
-
- return 1;
-
-}
-#
-##############################################################################
-#
-# Routine - mtn_diff
-#
-# Description - Compare two the specified two revisions, optionally
-# restricting the comparison to the specified file.
-#
-# Data - $list : A reference to the list that is to contain
-# the output from the diff command.
-# $mtn : The Monotone database that is to be used
-# or undef if the database associated with
-# the current workspace is to be used.
-# $revision_id_1 : The first revision id that is to be
-# compared.
-# $revision_id_2 : The second revision id that is to be
-# compared.
-# $file_name : Either the name of the file that the
-# revision comparison should be restricted
-# to or undef for a full revision
-# comparison.
-# Return Value : True if the comparison worked, otherwise
-# false if something went wrong.
-#
-##############################################################################
-
-
-
-sub mtn_diff($$$$;$)
-{
-
- my($list, $mtn_db, $revision_id_1, $revision_id_2, $file_name) = @_;
-
- my($buffer,
- @cmd);
-
- # Run mtn diff.
-
- @$list = ();
- push(@cmd, "mtn");
- push(@cmd, "--db=" . $mtn_db) if (defined($mtn_db));
- push(@cmd, "diff");
- push(@cmd, "-r");
- push(@cmd, "i:" . $revision_id_1);
- push(@cmd, "-r");
- push(@cmd, "i:" . $revision_id_2);
- push(@cmd, $file_name) if (defined($file_name));
- run_command(\$buffer, @cmd) or return;
-
- # Break up the input into a list of lines.
-
- @$list = split(/\n/o, $buffer);
-
- return 1;
-
-}
-#
-##############################################################################
-#
-# Routine - run_command
-#
-# Description - Run the specified command and return its output.
-#
-# Data - $buffer : A reference to the buffer that is to contain
-# the output from the command.
-# $args : A list containing the command to run and its
-# arguments.
-# Return Value : True if the command worked, otherwise false
-# if something went wrong.
-#
-##############################################################################
-
-
-
-sub run_command($@)
-{
-
- my($buffer, @args) = @_;
-
- my(@err,
- $fd_err,
- $fd_in,
- $fd_out,
- $pid,
- $ret_val,
- $status,
- $stop,
- $total_bytes,
- $watcher);
-
- # Run the command.
-
- $fd_err = gensym();
- eval
- {
- $pid = open3($fd_in, $fd_out, $fd_err, @args);
- };
- if ($@ ne "")
- {
- my $dialog = Gtk2::MessageDialog->new
- (undef,
- ["modal"],
- "warning",
- "close",
- sprintf("The %s subprocess could not start,\n"
- . "the system gave:\n%s",
- Glib::Markup::escape_text($args[0]),
- Glib::Markup::escape_text($@)));
- $dialog->run();
- $dialog->destroy();
- return;
- }
-
- # Setup a watch handler to get read our data and handle GTK2 events whilst
- # the command is running.
-
- $stop = $total_bytes = 0;
- $$buffer = "";
- $watcher = Gtk2::Helper->add_watch
- (fileno($fd_out), "in",
- sub {
- my $bytes_read;
- if (($bytes_read = sysread($fd_out,
- $$buffer,
- 32768,
- $total_bytes))
- == 0)
- {
- $stop = 1;
- }
- else
- {
- $total_bytes += $bytes_read;
- }
- return TRUE;
- });
- while (! $stop)
- {
- Gtk2->main_iteration();
- }
- Gtk2::Helper->remove_watch($watcher);
-
- # Get any error output.
-
- @err = readline($fd_err);
-
- close($fd_in);
- close($fd_out);
- close($fd_err);
-
- # Reap the process and deal with any errors.
-
- if (($ret_val = waitpid($pid, 0)) == -1)
- {
- if ($! != ECHILD)
- {
- my $dialog = Gtk2::MessageDialog->new_with_markup
- (undef,
- ["modal"],
- "warning",
- "close",
- sprintf("waitpid failed with:\n%s",
- Glib::Markup::escape_text($!)));
- $dialog->run();
- $dialog->destroy();
- return;
- }
- }
- $status = $?;
- if (WIFEXITED($status) && WEXITSTATUS($status) != 0)
- {
- my $dialog = Gtk2::MessageDialog->new_with_markup
- (undef,
- ["modal"],
- "warning",
- "close",
- sprintf("The %s subprocess failed with an exit status\n"
- . "of %d and printed the following on stderr:\n"
- . "%s",
- Glib::Markup::escape_text($args[0]),
- WEXITSTATUS($status),
- Glib::Markup::escape_text(join("", @err))));
- $dialog->run();
- $dialog->destroy();
- return;
- }
- elsif (WIFSIGNALED($status))
- {
- my $dialog = Gtk2::MessageDialog->new
- (undef,
- ["modal"],
- "warning",
- "close",
- sprintf("The %s subprocess was terminated by signal %d",
- Glib::Markup::escape_text($args[0]),
- WTERMSIG($status)));
- $dialog->run();
- $dialog->destroy();
- return;
- }
-
- return 1;
-
-}
-#
-##############################################################################
-#
-# Routine - get_dir_contents
-#
-# Description - Given a path and a Monotone manifest, return a subset of
-# the manifest that represents the contents of just that
-# directory along with the directory entry names.
-#
-# Data - $path : The path to the directory from the top level of
-# the manifest.
-# $manifest : A reference to a Monotone manifest.
-# $result : A reference to a list that is to contain the
-# result (a list of records containing the short
-# directory entry name and a reference to the
-# related manifest entry).
-#
-##############################################################################
-
-
-
-sub get_dir_contents($$$)
-{
-
- my($path, $manifest, $result) = @_;
-
- my($entry,
- $extract_re,
- $i,
- $match_re,
- $name);
-
- $i = 0;
- if ($path eq "")
- {
- $match_re = qr/^[^\/]+$/;
- $extract_re = qr/^([^\/]+)$/;
- }
- else
- {
- $match_re = qr/^${path}\/[^\/]+$/;
- $extract_re = qr/^${path}\/([^\/]+)$/;
- }
- @$result = ();
- foreach $entry (@$manifest)
- {
- if ($entry->{name} =~ m/$match_re/)
- {
- ($name) = ($entry->{name} =~ m/$extract_re/);
- $$result[$i ++] = {manifest_entry => $entry,
- name => $name};
- }
- }
-
-}
-#
-##############################################################################
-#
# Routine - mtn_error_handler
#
# Description - This routine is called when ever there is a problem with
@@ -4768,233 +1585,3 @@ sub setup_sigchld_handler($)
return 1; });
}
-#
-##############################################################################
-#
-# Routine - get_revision_ids
-#
-# Description - Return the currently selected revision id, whether this is
-# specified via a tag or as a revision id.
-#
-# 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 get_revision_ids($$)
-{
-
- my($instance, $revision_ids) = @_;
-
- @$revision_ids=();
- return unless ($instance->{revision_combo_details}->{completed});
- if ($instance->{tagged_tick}->get_active())
- {
- $instance->{mtn}->
- select($revision_ids,
- "t:" . $instance->{revision_combo_details}->{value});
- }
- else
- {
- push(@$revision_ids, $instance->{revision_combo_details}->{value});
- }
-
-}
-#
-##############################################################################
-#
-# Routine - make_busy
-#
-# Description - This routine simply makes the main window busy or active.
-#
-# Data - $instance : The window instance.
-# $busy : True if the window is to be made busy,
-# otherwise false if the window is to be made
-# active.
-#
-##############################################################################
-
-
-
-sub make_busy($$)
-{
-
- my($instance, $busy) = @_;
-
- # Create and store the cursors if we haven't done so already.
-
- $busy_cursor = Gtk2::Gdk::Cursor->new("watch")
- unless (defined($busy_cursor));
-
- # Do it. Make the application bar grab the input when the window is busy,
- # that way we gobble up keyboard and mouse events that could muck up the
- # application state.
-
- if ($busy)
- {
- if (exists($instance->{grab_widget}))
- {
- Gtk2->grab_add($instance->{grab_widget});
- }
- else
- {
- Gtk2->grab_add($instance->{appbar});
- }
- foreach my $instance (@windows)
- {
- foreach my $window (@{$instance->{busy_windows}})
- {
- $window->set_cursor($busy_cursor);
- }
- }
- }
- else
- {
- if (exists($instance->{grab_widget}))
- {
- Gtk2->grab_remove($instance->{grab_widget});
- }
- else
- {
- Gtk2->grab_remove($instance->{appbar});
- }
- foreach my $instance (@windows)
- {
- foreach my $window (@{$instance->{busy_windows}})
- {
- $window->set_cursor(undef);
- }
- }
- }
-
-}
-#
-##############################################################################
-#
-# 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();
- }
-
-}
-#
-##############################################################################
-#
-# Routine - create_format_tags
-#
-# Description - Creates the Gtk2::TextBuffer tags that are used to pretty
-# print stuff.
-#
-# Data - $text_view : The GTK2::TextBuffer widget that is to have
-# its tags created.
-#
-##############################################################################
-
-
-
-sub create_format_tags($)
-{
-
- my ($text_buffer) = @_;
-
- $text_buffer->create_tag("normal", "weight" => PANGO_WEIGHT_NORMAL);
-
- $text_buffer->create_tag("bold", "weight" => PANGO_WEIGHT_BOLD);
- $text_buffer->create_tag("italics", "style" => "italic");
- $text_buffer->create_tag("bold-italics",
- "weight" => PANGO_WEIGHT_BOLD,
- "style" => "italic");
-
- $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");
-
- $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");
-
- $text_buffer->create_tag("compare-info",
- "foreground" => "Yellow",
- "background" => "LightSlateGrey");
-
- $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");
-
- $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");
-
-}
-#
-##############################################################################
-#
-# Routine - set_label_value
-#
-# Description - Set the text for the given label and the tooltip for the
-# parent widget, assumed to be an event box, to the specified
-# text.
-#
-# Data - $widget : The label widget that has an event box as its
-# parent.
-# $value : The text that the label and tooltip are to be set
-# to.
-#
-##############################################################################
-
-
-
-sub set_label_value($$)
-{
-
- my($widget, $value) = @_;
-
- $widget->set_text($value);
- $tooltips->set_tip($widget->parent(), $value);
-
-}