# # # add_file "monotone-viz.png" # content [41853635f2bb5932843f444ed771d8567293aac6] # # add_file "monotone-viz.svg" # content [973e3787f47c4a046492a95282b7569ce0268179] # # patch "AdvancedFind.pm" # from [a90d12286cf6e8c1f007258d4f1f47132ad31ad6] # to [e9ff33befd44ace6ba5b2ae8f19ba741aec3842c] # # patch "Annotate.pm" # from [3d84076dd913242ae97ac02928f0c9c06d135bb0] # to [c6d0364b1eb14d721a0ea41ddbd5bdad8be6ff92] # # patch "ChangeLog.pm" # from [29bfd342dd8c58bec9de9a76117d716d1d751ec1] # to [ee5aff7fcef19a382436ed70b03e97622c3e6203] # # patch "ComboAutoCompletion.pm" # from [e937b0a45f4630b3d66c78facb6bd46424e5ff6e] # to [2acc9ad8ef8ac0c2baba340449aaf332e0b14bfb] # # patch "FindText.pm" # from [fb45334bd2a8c0fd1882b6db0f809ea7a4d4263c] # to [a39b09074fbe7e628f6e4d00dff7bc185b76518d] # # patch "Globals.pm" # from [ecdab5e2efb3efb1365114eebe1a8c78f69fde70] # to [66eff7abeb8443d29387430e0272aa8422d870ae] # # patch "History.pm" # from [af7d6fa7f0a54b077395133175779100bfb733c3] # to [8b6634e67b518d62381d23706d63705c8275c5ff] # # patch "Utilities.pm" # from [ed70fcf9cb4926124ffacfaaf44c0496b7556ff8] # to [bd2f0818e5f09a128c1ca297bb6612181d50ea1e] # # patch "mtn-browse" # from [26de6cb7a6b55cd1d37820d87d2a2c45b65f6b06] # to [bcf437debb1b500536ecc393c8aaa5521bd62266] # # patch "mtn-browse.glade" # from [56a671155946b9d7d2878ecdc2e1fdf3e5cddb43] # to [b87a22f4310537a066635f9e4aa8554dd053f606] # # set "monotone-viz.png" # attr "mtn:manual_merge" # value "true" # ============================================================ # monotone-viz.png is binary ============================================================ --- monotone-viz.svg 973e3787f47c4a046492a95282b7569ce0268179 +++ monotone-viz.svg 973e3787f47c4a046492a95282b7569ce0268179 @@ -0,0 +1,200 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + ============================================================ --- AdvancedFind.pm a90d12286cf6e8c1f007258d4f1f47132ad31ad6 +++ AdvancedFind.pm e9ff33befd44ace6ba5b2ae8f19ba741aec3842c @@ -94,9 +94,8 @@ sub advanced_find($$$) my($browser, $revision_id, $branches) = @_; my($advanced_find, - $height, $new, - $width); + $ret_val); # Look for an unused window first. @@ -127,6 +126,10 @@ sub advanced_find($$$) # Update the window's internal state. { + + my($height, + $width); + local $advanced_find->{in_cb} = 1; $advanced_find->{selected} = 0; @@ -160,14 +163,14 @@ sub 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}->{complete} = + $browser->{branch_combo_details}->{complete}; $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}->{complete} = + $browser->{revision_combo_details}->{complete}; $advanced_find->{revision_combo_details}->{value} = $browser->{revision_combo_details}->{value}; @@ -175,6 +178,7 @@ sub advanced_find($$$) set_active($browser->{tagged_tick}->get_active()); &{$advanced_find->{update_handler}}($advanced_find, NEW_FIND); + } # Handle all events until the dialog is dismissed. @@ -223,13 +227,13 @@ sub advanced_find($$$) if ($found); push(@$branches, "") if (scalar(@$branches) == 0); - return 1; + $ret_val = 1; } - else - { - return; - } + $advanced_find->{mtn} = undef; + + return $ret_val; + } # ############################################################################## @@ -256,8 +260,7 @@ sub create_advanced_find_window() $instance = {}; $instance->{type} = $window_type; - $instance->{glade} = - Gtk2::GladeXML->new("../mtn-browse.glade", $window_type); + $instance->{glade} = Gtk2::GladeXML->new($glade_file, $window_type); # Flag to stop recursive calling of callbacks. @@ -274,7 +277,6 @@ sub create_advanced_find_window() # Get the widgets that we are interested in. $instance->{window} = $instance->{glade}->get_widget($window_type); - $instance->{window}->set_icon($app_icon); $instance->{appbar} = $instance->{glade}->get_widget("appbar"); $instance->{simple_query_radiobutton} = $instance->{glade}->get_widget("simple_query_radiobutton"); @@ -734,7 +736,7 @@ sub update_advanced_find_state($$) } else { - $advanced_find->{branch_combo_details}->{completed} = 0; + $advanced_find->{branch_combo_details}->{complete} = 0; $advanced_find->{branch_combo_details}->{value} = ""; } @@ -783,13 +785,13 @@ sub update_advanced_find_state($$) } else { - $advanced_find->{revision_combo_details}->{completed} = 0; + $advanced_find->{revision_combo_details}->{complete} = 0; $advanced_find->{revision_combo_details}->{value} = ""; } # Get the new list of revisions. - if ($advanced_find->{branch_combo_details}->{completed}) + if ($advanced_find->{branch_combo_details}->{complete}) { $advanced_find->{appbar}->set_status("Fetching revision list"); gtk2_update(); @@ -876,14 +878,15 @@ sub update_advanced_find_state($$) gtk2_update(); if ($advanced_find->{simple_query_radiobutton}->get_active()) { - if ($advanced_find->{revision_combo_details}->{completed}) + if ($advanced_find->{revision_combo_details}->{complete}) { get_revision_ids($advanced_find, address@hidden); } } else { - my $query; + my($err, + $query); $query = $advanced_find->{search_term_combo}->child()->get_text(); # Remember the user can type in any old rubbish with advanced @@ -909,10 +912,13 @@ sub update_advanced_find_state($$) { $advanced_find->{mtn}->select(address@hidden, $query); }; + $err = $@; + Monotone::AutomateStdio->register_error_handler + ("both", \&mtn_error_handler); # If the query was valid the store it in the history. - if (! $@) + if ($err eq "") { my $found; if (scalar(@revision_ids) == 0) @@ -951,8 +957,6 @@ sub update_advanced_find_state($$) } } } - Monotone::AutomateStdio->register_error_handler - ("both", \&mtn_error_handler); } $advanced_find->{mtn}->toposort(address@hidden, @revision_ids); @@ -1010,6 +1014,9 @@ sub update_advanced_find_state($$) # Scroll back up to the top left. + $advanced_find->{details_buffer}-> + place_cursor($advanced_find->{details_buffer}-> + get_start_iter()); if ($advanced_find->{details_scrolledwindow}->realized()) { $advanced_find->{details_scrolledwindow}-> ============================================================ --- Annotate.pm 3d84076dd913242ae97ac02928f0c9c06d135bb0 +++ Annotate.pm c6d0364b1eb14d721a0ea41ddbd5bdad8be6ff92 @@ -176,6 +176,8 @@ sub display_annotation($$$) # Make sure we are at the top. + $instance->{annotation_buffer}-> + place_cursor($instance->{annotation_buffer}->get_start_iter()); $instance->{annotation_scrolledwindow}->get_vadjustment()->set_value(0); $instance->{annotation_scrolledwindow}->get_hadjustment()->set_value(0); $instance->{appbar}->set_progress_percentage(0); @@ -227,8 +229,7 @@ sub get_annotation_window() { $instance = {}; $instance->{type} = $window_type; - $instance->{glade} = - Gtk2::GladeXML->new("../mtn-browse.glade", $window_type); + $instance->{glade} = Gtk2::GladeXML->new($glade_file, $window_type); # Flag to stop recursive calling of callbacks. @@ -241,7 +242,6 @@ sub get_annotation_window() # Get the widgets that we are interested in. $instance->{window} = $instance->{glade}->get_widget($window_type); - $instance->{window}->set_icon($app_icon); $instance->{appbar} = $instance->{glade}->get_widget("appbar"); $instance->{annotation_textview} = $instance->{glade}->get_widget("annotation_textview"); ============================================================ --- ChangeLog.pm 29bfd342dd8c58bec9de9a76117d716d1d751ec1 +++ ChangeLog.pm ee5aff7fcef19a382436ed70b03e97622c3e6203 @@ -94,6 +94,8 @@ sub display_change_log($$;$$) address@hidden, $text_colour ? $text_colour : "", address@hidden); + $instance->{changelog_buffer}-> + place_cursor($instance->{changelog_buffer}->get_start_iter()); if ($instance->{changelog_scrolledwindow}->realized()) { $instance->{changelog_scrolledwindow}->get_vadjustment()->set_value(0); @@ -143,8 +145,7 @@ sub get_change_log_window() { $instance = {}; $instance->{type} = $window_type; - $instance->{glade} = - Gtk2::GladeXML->new("../mtn-browse.glade", $window_type); + $instance->{glade} = Gtk2::GladeXML->new($glade_file, $window_type); # Flag to stop recursive calling of callbacks. @@ -157,7 +158,6 @@ sub get_change_log_window() # Get the widgets that we are interested in. $instance->{window} = $instance->{glade}->get_widget($window_type); - $instance->{window}->set_icon($app_icon); $instance->{changelog_textview} = $instance->{glade}->get_widget("changelog_textview"); $instance->{changelog_scrolledwindow} = ============================================================ --- ComboAutoCompletion.pm e937b0a45f4630b3d66c78facb6bd46424e5ff6e +++ ComboAutoCompletion.pm 2acc9ad8ef8ac0c2baba340449aaf332e0b14bfb @@ -114,7 +114,7 @@ sub combo_changed_cb($$) if ($value eq $item) { $combo_details->{value} = $value; - $combo_details->{completed} = 1; + $combo_details->{complete} = 1; $instance->{appbar}->clear_stack(); &{$instance->{update_handler}}($instance, $change_state); last; @@ -156,12 +156,12 @@ sub combo_key_release_event_cb($$$) my($change_state, $combo, $combo_details, - $completed, + $complete, $completion, $item, $len, $name, - $old_completed, + $old_complete, $old_value, $value); @@ -194,8 +194,8 @@ sub combo_key_release_event_cb($$$) # The user has typed something in then validate it and auto-complete it if # necessary. - $completed = 0; - $old_completed = $combo_details->{completed}; + $complete = 0; + $old_complete = $combo_details->{complete}; $old_value = $combo_details->{value}; $value = $widget->get_text(); if ($value ne $old_value) @@ -219,7 +219,7 @@ sub combo_key_release_event_cb($$$) if ($combo_details->{completion}->get_completion($value, \$completion, - \$completed)) + \$complete)) { $instance->{appbar}->clear_stack(); } @@ -235,7 +235,7 @@ sub combo_key_release_event_cb($$$) } $combo_details->{value} = $value; - $combo_details->{completed} = $completed; + $combo_details->{complete} = $complete; # Update the pulldown choices. @@ -243,14 +243,13 @@ sub combo_key_release_event_cb($$$) 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); + $combo_details->{complete} = 1 if (! $complete && $value eq $item); } # Update the window state on a significant change. &{$instance->{update_handler}}($instance, $change_state) - if ($combo_details->{completed} != $old_completed + if ($combo_details->{complete} != $old_complete || $combo_details->{value} ne $old_value); } ============================================================ --- FindText.pm fb45334bd2a8c0fd1882b6db0f809ea7a4d4263c +++ FindText.pm a39b09074fbe7e628f6e4d00dff7bc185b76518d @@ -239,8 +239,7 @@ sub get_find_text_window($$) $new = 1; $instance = {}; $instance->{type} = $window_type; - $instance->{glade} = - Gtk2::GladeXML->new("../mtn-browse.glade", $window_type); + $instance->{glade} = Gtk2::GladeXML->new($glade_file, $window_type); # Flag to stop recursive calling of callbacks. @@ -253,7 +252,6 @@ sub get_find_text_window($$) # Get the widgets that we are interested in. $instance->{window} = $instance->{glade}->get_widget($window_type); - $instance->{window}->set_icon($app_icon); $instance->{main_vbox} = $instance->{glade}->get_widget("main_vbox"); $instance->{find_combo} = $instance->{glade}->get_widget("find_comboboxentry"); ============================================================ --- Globals.pm ecdab5e2efb3efb1365114eebe1a8c78f69fde70 +++ Globals.pm 66eff7abeb8443d29387430e0272aa8422d870ae @@ -81,6 +81,30 @@ use constant SELECTED_REVISION_CHANGED = | DISPLAY_OF_FILE); use constant SELECTED_REVISION_CHANGED => (REVISION_DETAILS); +# Location of the Glade UI XML file for mtn-browse. + +our $glade_file; + +# Location of the temporary working directory. + +our $tmp_dir; + +# List of window instances. + +our @windows; + +# Assorted pixmaps. + +our $line_image; + +# The busy cursor to use for the mouse. + +our $busy_cursor; + +# The tooltips widget. + +our $tooltips; + # Text viewable application mime types. our @text_viewable_app_mime_types = @@ -101,7 +125,7 @@ our @text_viewable_app_mime_types = x-troff xhtml+xml); -# Supported text mime types (used for syntax highlighting. +# Supported text mime types (used for syntax highlighting). our @text_mime_types = ( @@ -131,23 +155,6 @@ our @text_mime_types = } ); -# 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); @@ -169,12 +176,13 @@ our %EXPORT_TAGS = (constants => [qw(ALL REVISION_DETAILS REVISION_LIST SELECTED_REVISION_CHANGED)], - variables => [qw($app_icon - $busy_cursor + variables => [qw($busy_cursor + $glade_file $line_image - $tooltips @text_mime_types @text_viewable_app_mime_types + $tmp_dir + $tooltips @windows)]); our @EXPORT = qw(); Exporter::export_ok_tags(qw(constants variables)); ============================================================ --- History.pm af7d6fa7f0a54b077395133175779100bfb733c3 +++ History.pm 8b6634e67b518d62381d23706d63705c8275c5ff @@ -256,6 +256,8 @@ sub display_revision_change_history($$) # Make sure we are at the top. + $instance->{history_buffer}-> + place_cursor($instance->{history_buffer}->get_start_iter()); $instance->{history_scrolledwindow}->get_vadjustment()->set_value(0); $instance->{history_scrolledwindow}->get_hadjustment()->set_value(0); $instance->{appbar}->set_progress_percentage(0); @@ -445,6 +447,8 @@ sub display_file_change_history($$$) # Make sure we are at the top. + $instance->{history_buffer}-> + place_cursor($instance->{history_buffer}->get_start_iter()); $instance->{history_scrolledwindow}->get_vadjustment()->set_value(0); $instance->{history_scrolledwindow}->get_hadjustment()->set_value(0); $instance->{appbar}->set_progress_percentage(0); @@ -633,6 +637,274 @@ sub compare_button_clicked_cb($$) # ############################################################################## # +# 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(); + + 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_buffer}-> + place_cursor($instance->{comparison_buffer}->get_start_iter()); + $instance->{comparison_scrolledwindow}->get_vadjustment()->set_value(0); + $instance->{comparison_scrolledwindow}->get_hadjustment()->set_value(0); + + $instance->{appbar}->pop(); + make_busy($instance, 0); + +} +# +############################################################################## +# # Routine - file_comparison_combobox_changed_cb # # Description - Callback routine called when the user changes the value of @@ -756,8 +1028,7 @@ sub get_history_window() { $instance = {}; $instance->{type} = $window_type; - $instance->{glade} = - Gtk2::GladeXML->new("../mtn-browse.glade", $window_type); + $instance->{glade} = Gtk2::GladeXML->new($glade_file, $window_type); # Flag to stop recursive calling of callbacks. @@ -770,7 +1041,6 @@ sub get_history_window() # Get the widgets that we are interested in. $instance->{window} = $instance->{glade}->get_widget($window_type); - $instance->{window}->set_icon($app_icon); $instance->{appbar} = $instance->{glade}->get_widget("appbar"); $instance->{history_label} = $instance->{glade}->get_widget("history_label"); @@ -799,6 +1069,7 @@ sub get_history_window() local $instance->{in_cb} = 1; $widget->hide(); $instance->{history_buffer}->set_text(""); + $instance->{mtn} = undef; return TRUE; }, $instance); @@ -982,8 +1253,7 @@ sub get_revision_comparison_window() { $instance = {}; $instance->{type} = $window_type; - $instance->{glade} = - Gtk2::GladeXML->new("../mtn-browse.glade", $window_type); + $instance->{glade} = Gtk2::GladeXML->new($glade_file, $window_type); # Flag to stop recursive calling of callbacks. @@ -1005,7 +1275,6 @@ sub get_revision_comparison_window() # Get the widgets that we are interested in. $instance->{window} = $instance->{glade}->get_widget($window_type); - $instance->{window}->set_icon($app_icon); $instance->{appbar} = $instance->{glade}->get_widget("appbar"); $instance->{comparison_label} = $instance->{glade}->get_widget("comparison_label"); @@ -1031,6 +1300,7 @@ sub get_revision_comparison_window() $widget->hide(); $instance->{file_combo}->get_model()->clear(); $instance->{comparison_buffer}->set_text(""); + $instance->{mtn} = undef; return TRUE; }, $instance); @@ -1087,272 +1357,6 @@ sub get_revision_comparison_window() # ############################################################################## # -# 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(); - - 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 ============================================================ --- Utilities.pm ed70fcf9cb4926124ffacfaaf44c0496b7556ff8 +++ Utilities.pm bd2f0818e5f09a128c1ca297bb6612181d50ea1e @@ -50,6 +50,7 @@ sub generate_revision_report($$$$;$); sub create_format_tags($); sub generate_revision_report($$$$;$); +sub generate_tmp_path($); sub get_dir_contents($$$); sub get_revision_ids($$); sub glade_signal_autoconnect($$); @@ -261,6 +262,53 @@ sub generate_revision_report($$$$;$) # ############################################################################## # +# Routine - generate_tmp_path +# +# Description - Generate a unique and temporary path for the specified file +# name. The file name is included in the result and will be +# unchanged. +# +# Data - $file_name : The file name component that is to be used. +# Return Value : The full, unique, temporary path on success, +# otherwise undef on failure. +# +############################################################################## + + + +sub generate_tmp_path($) +{ + + my $file_name = $_[0]; + + my($path, + $i); + + # Loop through looking for a temporary subdirectory not containing the + # specified file. + + for ($i = 0; ; ++ $i) + { + if (-d ($tmp_dir . "/" . $i)) + { + if (! -e ($path = $tmp_dir . "/" . $i . "/" . $file_name)) + { + return $path; + } + } + else + { + return unless mkdir($tmp_dir . "/" . $i); + return $tmp_dir . "/" . $i . "/" . $file_name; + } + } + + return; + +} +# +############################################################################## +# # Routine - run_command # # Description - Run the specified command and return its output. @@ -485,7 +533,7 @@ sub get_revision_ids($$) my($instance, $revision_ids) = @_; @$revision_ids=(); - return unless ($instance->{revision_combo_details}->{completed}); + return unless ($instance->{revision_combo_details}->{complete}); if ($instance->{tagged_tick}->get_active()) { $instance->{mtn}-> ============================================================ --- mtn-browse 26de6cb7a6b55cd1d37820d87d2a2c45b65f6b06 +++ mtn-browse bcf437debb1b500536ecc393c8aaa5521bd62266 @@ -43,7 +43,11 @@ require 5.008; require 5.008; -use lib "/home/aecoope/perl"; +BEGIN +{ + use constant LIB_PATH => "/home/aecoope/perl"; +} +use lib LIB_PATH; use strict; # ***** REQUIRED PACKAGES ***** @@ -51,20 +55,18 @@ use File::Basename; # Standard Perl and CPAN modules. use File::Basename; +use File::Temp; use Glib qw(FALSE TRUE); use Gnome2; use Gnome2::VFS -init; use Gtk2 -init; -set_locale Gtk2; -init Gtk2; use Gtk2::GladeXML; use Gtk2::Helper; use Gtk2::Pango; use Gtk2::SourceView; +use IO::File; use IPC::Open3; -use Monotone::AutomateStdio; -use POSIX qw(:errno_h :sys_wait_h); -use POSIX qw(strftime); +use POSIX qw(:errno_h :sys_wait_h strftime); use Symbol qw(gensym); use Text::Tabs; @@ -72,6 +74,10 @@ use Data::Dumper; use Data::Dumper; +# Monotone AutomateStdio module. + +use Monotone::AutomateStdio; + # Modules specific to this application. use Globals qw(:constants :variables); @@ -104,19 +110,28 @@ sub annotate_button_clicked_cb($$); sub advanced_find_button_clicked_cb($$); sub annotate_button_clicked_cb($$); +sub close_toolbutton_clicked_cb($$); sub directory_up_button_clicked_cb($$); sub file_change_history_button_clicked_cb($$); sub get_browser_window(;$$$$$); +sub help_toolbutton_clicked_cb($$); sub main_window_delete_event_cb($$$); sub manifest_browser_treeview_cursor_changed_cb($$); sub manifest_browser_treeview_row_activated_cb($$$$); +sub monotone_viz_button_clicked_cb($$); sub mtn_error_handler($$); +sub new_toolbutton_clicked_cb($$); +sub open_toolbutton_clicked_cb($$); +sub preferences_toolbutton_clicked_cb($$); +sub properties_toolbutton_clicked_cb($$); sub revision_change_history_button_clicked_cb($$); sub revision_change_log_button_clicked_cb($$); +sub save_as_button_clicked_cb($$); sub search_text_button_clicked_cb($$); sub setup_sigchld_handler($); sub sigchld_handler(); sub update_browser_state($$); +sub view_button_clicked_cb($$); # ############################################################################## # @@ -133,19 +148,80 @@ sub update_browser_state($$); { - # Initialise the libraries and generate the interface. + my($branch, + $browser, + $mtn, + $revision_id); + # Initialise stuff. + + set_locale Gtk2; Gnome2::Program->init("mtn-browse", 0.1); + setup_sigchld_handler(\&sigchld_handler); + $glade_file = LIB_PATH . "/UI/mtn-browse.glade"; + $tooltips = Gtk2::Tooltips->new(); + $line_image = Gtk2::Gdk::Pixbuf->new_from_file(LIB_PATH . "/UI/line.png"); + + # Create the temporary working directory. + + eval + { + $tmp_dir = File::Temp::tempdir("mtn-browse_XXXXXXXXXX", + TMPDIR => 1, + CLEANUP => 1); + }; + if ($@ ne "") + { + my $dialog = Gtk2::MessageDialog->new + (undef, + ["modal"], + "error", + "close", + sprintf("%s\nThis is fatal, I am going to exit.", $@)); + $dialog->run(); + $dialog->destroy(); + exit(1); + } + + # Attempt to create an mtn handle, if it fails then assume that we are not + # inside a workspace. However if it does work then pre-load the browser + # with the relevant branch and revision. + + eval + { + $mtn = Monotone::AutomateStdio->new(); + $mtn->get_option(\$branch, "branch"); + $mtn->get_base_revision_id(\$revision_id); + }; + + # Set up the error handlers for the Monotone library. + Monotone::AutomateStdio->register_error_handler("both", \&mtn_error_handler); - setup_sigchld_handler(\&sigchld_handler); - $tooltips = Gtk2::Tooltips->new(); - $app_icon = Gtk2::Gdk::Pixbuf->new_from_file("../mtn-browse.png"); - $line_image = Gtk2::Gdk::Pixbuf->new_from_file("../line.png"); - get_browser_window(Monotone::AutomateStdio->new()); - # get_browser_window(); - # get_browser_window(); + # Create the browser window and display it. Please note that updating the + # browser to reflect the current workspace is done in an idle handler so + # that control can be handed over to Gtk2 before updating the display. + + $browser = get_browser_window($mtn); + if (defined($mtn)) + { + Glib::Idle->add + (sub { + my $browser = $_[0]; + + $browser->{branch_combo_details}->{preset} = 1; + $browser->{branch_combo_details}->{value} = $branch; + $browser->{revision_combo_details}->{preset} = 1; + $browser->{revision_combo_details}->{value} = $revision_id; + $browser->{tagged_tick}->set_active(FALSE); + &{$browser->{update_handler}}($browser, ALL_CHANGED); + + return FALSE; + }, + $browser); + } + # Hand control over to Gtk2. Gtk2->main(); @@ -166,6 +242,277 @@ sub update_browser_state($$); # ############################################################################## # +# Routine - new_toolbutton_clicked_cb +# +# Description - Callback routine called when the user clicks on the +# new toolbutton in a main browser window. +# +# Data - $widget : The widget object that received the signal. +# $browser : The browser instance that is associated with +# this widget. +# +############################################################################## + + + +sub new_toolbutton_clicked_cb($$) +{ + + my($widget, $browser) = @_; + + return if ($browser->{in_cb}); + local $browser->{in_cb} = 1; + + # Simply get a new/unused browser window and display it. + + get_browser_window(); + +} +# +############################################################################## +# +# Routine - open_toolbutton_clicked_cb +# +# Description - Callback routine called when the user clicks on the +# open toolbutton in a main browser window. +# +# Data - $widget : The widget object that received the signal. +# $browser : The browser instance that is associated with +# this widget. +# +############################################################################## + + + +sub open_toolbutton_clicked_cb($$) +{ + + my($widget, $browser) = @_; + + return if ($browser->{in_cb}); + local $browser->{in_cb} = 1; + + my($chooser_dialog, + $done); + + $chooser_dialog = Gtk2::FileChooserDialog->new("Open Database", + $browser->{window}, + "open", + "gtk-cancel" => "cancel", + "gtk-open" => "ok"); + + do + { + if ($chooser_dialog->run() eq "ok") + { + + my ($err, + $fh, + $file_name, + $mtn); + + $file_name = $chooser_dialog->get_filename(); + + # The user has selected a file. First make sure we can open it for + # reading (I know I could use the -r test but this takes care of + # any other unforeseen access problems as well). + + if (! defined($fh = IO::File->new($file_name, "r"))) + { + my $dialog = Gtk2::MessageDialog->new + ($browser->{window}, + ["modal"], + "warning", + "close", + $!); + $dialog->run(); + $dialog->destroy(); + } + else + { + + $fh->close(); + $fh = undef; + + # Ok it is a readable file, try and open it but deal with any + # errors in a nicer way than normal. + + Monotone::AutomateStdio->register_error_handler("both"); + eval + { + $mtn = Monotone::AutomateStdio->new($file_name); + }; + $err = $@; + Monotone::AutomateStdio->register_error_handler + ("both", \&mtn_error_handler); + if ($err ne "") + { + my $dialog = Gtk2::MessageDialog->new + ($browser->{window}, + ["modal"], + "warning", + "close", + "Not a valid Monotone database"); + $dialog->run(); + $dialog->destroy(); + } + else + { + + # Seems to be ok so update the browser with the new + # database. + + $browser->{mtn} = $mtn; + &{$browser->{update_handler}}($browser, DATABASE_CHANGED); + $done = 1; + + } + + } + + } + else + { + $done = 1; + } + } + while (! $done); + + $chooser_dialog->destroy(); + +} +# +############################################################################## +# +# Routine - close_toolbutton_clicked_cb +# +# Description - Callback routine called when the user clicks on the +# close toolbutton in a main browser window. +# +# Data - $widget : The widget object that received the signal. +# $browser : The browser instance that is associated with +# this widget. +# +############################################################################## + + + +sub close_toolbutton_clicked_cb($$) +{ + + my($widget, $browser) = @_; + + return if ($browser->{in_cb}); + local $browser->{in_cb} = 1; + + # Simply reset the browser's Monotone instance and update its display. + + $browser->{mtn} = undef; + &{$browser->{update_handler}}($browser, DATABASE_CHANGED); + +} +# +############################################################################## +# +# Routine - properties_toolbutton_clicked_cb +# +# Description - Callback routine called when the user clicks on the +# properties toolbutton in a main browser window. +# +# Data - $widget : The widget object that received the signal. +# $browser : The browser instance that is associated with +# this widget. +# +############################################################################## + + + +sub properties_toolbutton_clicked_cb($$) +{ + + my($widget, $browser) = @_; + + return if ($browser->{in_cb}); + local $browser->{in_cb} = 1; + + my $dialog = Gtk2::MessageDialog->new($browser->{window}, + ["modal"], + "info", + "close", + "Not implemented"); + $dialog->run(); + $dialog->destroy(); + +} +# +############################################################################## +# +# Routine - preferences_toolbutton_clicked_cb +# +# Description - Callback routine called when the user clicks on the +# preferences toolbutton in a main browser window. +# +# Data - $widget : The widget object that received the signal. +# $browser : The browser instance that is associated with +# this widget. +# +############################################################################## + + + +sub preferences_toolbutton_clicked_cb($$) +{ + + my($widget, $browser) = @_; + + return if ($browser->{in_cb}); + local $browser->{in_cb} = 1; + + my $dialog = Gtk2::MessageDialog->new($browser->{window}, + ["modal"], + "info", + "close", + "Not implemented"); + $dialog->run(); + $dialog->destroy(); + +} +# +############################################################################## +# +# Routine - help_toolbutton_clicked_cb +# +# Description - Callback routine called when the user clicks on the +# help toolbutton in a main browser window. +# +# Data - $widget : The widget object that received the signal. +# $browser : The browser instance that is associated with +# this widget. +# +############################################################################## + + + +sub help_toolbutton_clicked_cb($$) +{ + + my($widget, $browser) = @_; + + return if ($browser->{in_cb}); + local $browser->{in_cb} = 1; + + my $dialog = Gtk2::MessageDialog->new($browser->{window}, + ["modal"], + "info", + "close", + "Not implemented"); + $dialog->run(); + $dialog->destroy(); + +} +# +############################################################################## +# # Routine - tagged_checkbutton_toggled_cb # # Description - Callback routine called when the user changes the value of @@ -228,7 +575,7 @@ sub advanced_find_button_clicked_cb($$) $preset_branch = 1; $state = BRANCH_CHANGED; - if ($browser->{branch_combo_details}->{completed}) + if ($browser->{branch_combo_details}->{complete}) { foreach my $name (@branches) { @@ -329,6 +676,56 @@ sub revision_change_log_button_clicked_c # ############################################################################## # +# Routine - monotone_viz_button_clicked_cb +# +# Description - Callback routine called when the user clicks on the +# monotone-viz button in a main browser window. +# +# Data - $widget : The widget object that received the signal. +# $browser : The browser instance that is associated with +# this widget. +# +############################################################################## + + + +sub monotone_viz_button_clicked_cb($$) +{ + + my($widget, $browser) = @_; + + return if ($browser->{in_cb}); + local $browser->{in_cb} = 1; + + my($cmd, + $db_name); + + # Build up the monotone-viz command, we need the database name and then + # optionally the branch and revision ids. + + $cmd = "monotone-viz "; + if (! defined($db_name = $browser->{mtn}->get_db_name())) + { + $browser->{mtn}->get_option(\$db_name, "database"); + } + $cmd .= $db_name; + if ($browser->{branch_combo_details}->{complete}) + { + $cmd .= " " . $browser->{branch_combo_details}->{value}; + if ($browser->{revision_combo_details}->{complete}) + { + $cmd .= " " . $browser->{revision_combo_details}->{value}; + } + } + + # Launch Monotone-Viz. + + system($cmd . " &"); + +} +# +############################################################################## +# # Routine - directory_up_button_clicked_cb # # Description - Callback routine called when the user clicks on the up @@ -364,7 +761,7 @@ sub directory_up_button_clicked_cb($$) $value = dirname($value); $value = "" if ($value eq "."); $browser->{directory_combo_details}->{value} = $value; - $browser->{directory_combo_details}->{completed} = 1; + $browser->{directory_combo_details}->{complete} = 1; $len = length($value); if ($len < $old_len) { @@ -411,6 +808,189 @@ sub search_text_button_clicked_cb($$) # ############################################################################## # +# Routine - save_as_button_clicked_cb +# +# Description - Callback routine called when the user clicks on the save as +# button in a main browser window. +# +# Data - $widget : The widget object that received the signal. +# $browser : The browser instance that is associated with +# this widget. +# +############################################################################## + + + +sub save_as_button_clicked_cb($$) +{ + + my($widget, $browser) = @_; + + return if ($browser->{in_cb}); + local $browser->{in_cb} = 1; + + my($chooser_dialog, + $continue, + $done); + + $chooser_dialog = Gtk2::FileChooserDialog->new("Save As", + $browser->{window}, + "save", + "gtk-cancel" => "cancel", + "gtk-save" => "ok"); + $chooser_dialog->set_current_name + ($browser->{file_being_viewed}->{short_name}); + + do + { + if ($chooser_dialog->run() eq "ok") + { + + my ($data, + $fh, + $file_name); + + $continue = 1; + $file_name = $chooser_dialog->get_filename(); + + # See if the file exists, if so then get a confirmation from the + # user. + + if (-e $file_name) + { + my $dialog = Gtk2::MessageDialog->new + ($browser->{window}, + ["modal"], + "question", + "yes-no", + "File already exists.\nDo you want to replace it?"); + $dialog->set_title("Confirm"); + $continue = 0 if ($dialog->run() ne "yes"); + $dialog->destroy(); + } + + if ($continue) + { + + # Attempt to save the contents to the file. + + if (! defined($fh = IO::File->new($file_name, "w"))) + { + my $dialog = Gtk2::MessageDialog->new + ($browser->{window}, + ["modal"], + "warning", + "close", + $!); + $dialog->run(); + $dialog->destroy(); + } + else + { + $browser->{mtn}->get_file(\$data, + $browser->{file_being_viewed}-> + {manifest_entry}->{file_id}); + $fh->print($data); + $fh->close(); + $done = 1; + } + + } + + } + else + { + $done = 1; + } + } + while (! $done); + + $chooser_dialog->destroy(); + +} +# +############################################################################## +# +# Routine - view_button_clicked_cb +# +# Description - Callback routine called when the user clicks on the view +# button in a main browser window. +# +# Data - $widget : The widget object that received the signal. +# $browser : The browser instance that is associated with +# this widget. +# +############################################################################## + + + +sub view_button_clicked_cb($$) +{ + + my($widget, $browser) = @_; + + return if ($browser->{in_cb}); + local $browser->{in_cb} = 1; + + my($data, + $editor, + $fh, + $file_name); + + if (! ($file_name = + generate_tmp_path($browser->{file_being_viewed}->{short_name}))) + { + my $dialog = Gtk2::MessageDialog->new + ($browser->{window}, + ["modal"], + "warning", + "Cannot generate temporary file name", + $!); + $dialog->run(); + $dialog->destroy(); + return; + } + + # Attempt to save the contents to the file. + + if (! defined($fh = IO::File->new($file_name, "w"))) + { + my $dialog = Gtk2::MessageDialog->new + ($browser->{window}, + ["modal"], + "warning", + "close", + $!); + $dialog->run(); + $dialog->destroy(); + return; + } + $browser->{mtn}->get_file(\$data, + $browser->{file_being_viewed}-> + {manifest_entry}->{file_id}); + $fh->print($data); + $fh->close(); + $fh = undef; + + # Load into an editor. + + if (exists($ENV{EDITOR}) + && ($ENV{EDITOR} eq "emacs" + || $ENV{EDITOR} eq "xemacs" + || $ENV{EDITOR} eq "gvim")) + { + $editor = $ENV{EDITOR}; + } + else + { + $editor = "xterm -e vi"; + } + system($editor . " " . $file_name . " &"); + +} +# +############################################################################## +# # Routine - annotate_button_clicked_cb # # Description - Callback routine called when the user clicks on the @@ -569,7 +1149,7 @@ sub manifest_browser_treeview_row_activa if (defined($manifest_entry) && $manifest_entry->{type} eq "directory") { $browser->{directory_combo_details}->{value} = $manifest_entry->{name}; - $browser->{directory_combo_details}->{completed} = 1; + $browser->{directory_combo_details}->{complete} = 1; $browser->{directory_combo}->child()-> set_text($manifest_entry->{name}); $browser->{appbar}->clear_stack(); @@ -697,8 +1277,7 @@ sub get_browser_window(;$$$$$) $browser = {}; $browser->{type} = $window_type; $browser->{mtn} = $mtn; - $browser->{glade} = - Gtk2::GladeXML->new("../mtn-browse.glade", $window_type); + $browser->{glade} = Gtk2::GladeXML->new($glade_file, $window_type); # Flag to stop recursive calling of callbacks. @@ -715,9 +1294,12 @@ sub get_browser_window(;$$$$$) # Get the widgets that we are interested in. $browser->{window} = $browser->{glade}->get_widget($window_type); - $browser->{window}->set_icon($app_icon); $browser->{appbar} = $browser->{glade}->get_widget("appbar"); $browser->{main_vbox} = $browser->{glade}->get_widget("main_vbox"); + $browser->{close_toolbutton} = + $browser->{glade}->get_widget("close_toolbutton"); + $browser->{properties_toolbutton} = + $browser->{glade}->get_widget("properties_toolbutton"); $browser->{branch_combo} = $browser->{glade}->get_widget("branch_comboboxentry"); $browser->{revision_combo} = @@ -942,6 +1524,12 @@ sub get_browser_window(;$$$$$) &{$browser->{update_handler}}($browser, ALL_CHANGED); } + # Make sure that the branch comboboxentry has the focus and not the bonobo + # dock. + + $browser->{branch_combo}->child()->grab_focus(); + $browser->{branch_combo}->child()->set_position(-1); + return $browser; } @@ -974,10 +1562,14 @@ sub update_browser_state($$) if (! defined($browser->{mtn})) { + $browser->{close_toolbutton}->set_sensitive(FALSE); + $browser->{properties_toolbutton}->set_sensitive(FALSE); $browser->{main_vbox}->set_sensitive(FALSE); } else { + $browser->{close_toolbutton}->set_sensitive(TRUE); + $browser->{properties_toolbutton}->set_sensitive(TRUE); $browser->{main_vbox}->set_sensitive(TRUE); } @@ -993,12 +1585,12 @@ sub update_browser_state($$) $browser->{branch_combo_details}->{completion} = undef; if ($browser->{branch_combo_details}->{preset}) { - $browser->{branch_combo_details}->{completed} = 1; + $browser->{branch_combo_details}->{complete} = 1; $browser->{branch_combo_details}->{preset} = 0; } else { - $browser->{branch_combo_details}->{completed} = 0; + $browser->{branch_combo_details}->{complete} = 0; $browser->{branch_combo_details}->{value} = ""; } if (! defined($browser->{mtn})) @@ -1055,18 +1647,18 @@ sub update_browser_state($$) $browser->{revision_combo_details}->{completion} = undef; if ($browser->{revision_combo_details}->{preset}) { - $browser->{revision_combo_details}->{completed} = 1; + $browser->{revision_combo_details}->{complete} = 1; $browser->{revision_combo_details}->{preset} = 0; } else { - $browser->{revision_combo_details}->{completed} = 0; + $browser->{revision_combo_details}->{complete} = 0; $browser->{revision_combo_details}->{value} = ""; } # Get the new list of revisions. - if ($browser->{branch_combo_details}->{completed}) + if ($browser->{branch_combo_details}->{complete}) { $browser->{appbar}->set_status("Fetching revision list"); gtk2_update(); @@ -1144,12 +1736,12 @@ sub update_browser_state($$) $browser->{directory_combo_details}->{completion} = undef; if ($browser->{directory_combo_details}->{preset}) { - $browser->{directory_combo_details}->{completed} = 1; + $browser->{directory_combo_details}->{complete} = 1; $browser->{revision_combo_details}->{preset} = 0; } else { - $browser->{directory_combo_details}->{completed} = 0; + $browser->{directory_combo_details}->{complete} = 0; $browser->{directory_combo_details}->{value} = ""; } set_label_value($browser->{revision_id_label}, ""); @@ -1164,7 +1756,7 @@ sub update_browser_state($$) $browser->{appbar}->set_status("Fetching manifest"); gtk2_update(); - if ($browser->{revision_combo_details}->{completed}) + if ($browser->{revision_combo_details}->{complete}) { my @revision_ids; get_revision_ids($browser, address@hidden); @@ -1186,7 +1778,7 @@ sub update_browser_state($$) ($browser->{window}, ["modal"], "info", "close", $message); $dialog->run(); $dialog->destroy(); - $browser->{revision_combo_details}->{completed} = 0; + $browser->{revision_combo_details}->{complete} = 0; $browser->{revision_combo_details}->{value} = ""; $browser->{revision_combo}->child()->set_text(""); } @@ -1251,7 +1843,7 @@ sub update_browser_state($$) foreach my $widget (@{$browser->{revision_sensitive_group}}) { $widget-> - set_sensitive($browser->{revision_combo_details}->{completed}); + set_sensitive($browser->{revision_combo_details}->{complete}); } gtk2_update(); @@ -1277,7 +1869,7 @@ sub update_browser_state($$) # Get the contents of the new directory. - if ($browser->{directory_combo_details}->{completed} + if ($browser->{directory_combo_details}->{complete} || $browser->{directory_combo_details}->{value} eq "") { get_dir_contents($browser->{directory_combo_details}->{value}, @@ -1721,7 +2313,7 @@ sub sigchld_handler() foreach my $window (@windows) { - if (exists($window->{mtn}) + if (exists($window->{mtn}) && defined($window->{mtn}) && $window->{mtn}->get_pid() == $pid) { $window->{mtn}->closedown(); ============================================================ --- mtn-browse.glade 56a671155946b9d7d2878ecdc2e1fdf3e5cddb43 +++ mtn-browse.glade b87a22f4310537a066635f9e4aa8554dd053f606 @@ -15,6 +15,7 @@ 550 True False + mtn-browse.png True False False @@ -24,17 +25,17 @@ - + True True - + True GTK_SHADOW_NONE - + True @@ -208,17 +209,17 @@ 0 0 0 - BONOBO_DOCK_ITEM_BEH_EXCLUSIVE|BONOBO_DOCK_ITEM_BEH_NEVER_VERTICAL|BONOBO_DOCK_ITEM_BEH_LOCKED + BONOBO_DOCK_ITEM_BEH_EXCLUSIVE|BONOBO_DOCK_ITEM_BEH_NEVER_FLOATING|BONOBO_DOCK_ITEM_BEH_NEVER_VERTICAL|BONOBO_DOCK_ITEM_BEH_LOCKED - + True GTK_SHADOW_OUT - + True GTK_ORIENTATION_HORIZONTAL GTK_TOOLBAR_BOTH @@ -226,13 +227,14 @@ True - + True - New workspace + New browser gtk-new True True False + False @@ -241,13 +243,27 @@ - + True - Open database + True + True + True + + + False + False + + + + + + True + Open a database gtk-open True True False + False @@ -256,30 +272,43 @@ - + True - Reload database - _Reload - True - gtk-refresh + Close the current database + gtk-close True True False + False + True + + + + + + True + True + True + True + + + False False - + True - Database properties + Display current database properties gtk-properties True True False + False @@ -288,7 +317,7 @@ - + True True True @@ -301,13 +330,14 @@ - + True User preferences gtk-preferences True True False + False @@ -316,13 +346,14 @@ - + True Help gtk-help True True False + False @@ -547,6 +578,34 @@ criteria for selecting a revision + + True + View revision history +in Monotone-Viz + True + GTK_RELIEF_NORMAL + True + + + + + True + monotone-viz.png + 0.5 + 0.5 + 0 + 0 + + + + + 0 + False + False + + + + True View revision's change history @@ -1345,6 +1404,7 @@ last updated with respect to this revisi True GTK_RELIEF_NORMAL True + @@ -1373,11 +1433,12 @@ into a viewer True GTK_RELIEF_NORMAL True + - + True - gtk-open + gnome-stock-book-open 4 0.5 0.5 @@ -1995,6 +2056,7 @@ into a viewer 550 True False + mtn-browse.png True True False @@ -3152,6 +3214,7 @@ Tag 550 True False + mtn-browse.png True False False @@ -3244,6 +3307,7 @@ Tag 550 True False + mtn-browse.png True False False @@ -3841,6 +3905,7 @@ file version that is to be compared550 True False + mtn-browse.png True False False @@ -4098,6 +4163,7 @@ that had the green highlighted text550 True False + mtn-browse.png True False False @@ -4213,6 +4279,7 @@ that had the green highlighted textFalse False False + mtn-browse.png True True False