# # # add_file "WindowManager.pm" # content [36ef984a1f656a2eb160c4d9316ddb4d8e9a20bd] # # patch "AdvancedFind.pm" # from [e9ff33befd44ace6ba5b2ae8f19ba741aec3842c] # to [8bf4995aaee6880d46d7352170a299e43aa23053] # # patch "Annotate.pm" # from [c6d0364b1eb14d721a0ea41ddbd5bdad8be6ff92] # to [7cee86fa814a8ec2d79ee9284ed150010ec560d2] # # patch "ChangeLog.pm" # from [ee5aff7fcef19a382436ed70b03e97622c3e6203] # to [1e2e21a37698986ddd54ecb0ad513bcb4b9abb3a] # # patch "FindText.pm" # from [a39b09074fbe7e628f6e4d00dff7bc185b76518d] # to [97a85e65c2990934ed5a983f50bf3851da1cb0ce] # # patch "Globals.pm" # from [66eff7abeb8443d29387430e0272aa8422d870ae] # to [a24f4acd16f3849fa802bbfdc496d61483b22ac3] # # patch "History.pm" # from [8b6634e67b518d62381d23706d63705c8275c5ff] # to [7923dcc1bfcb958b514b5af9fec270960b99d2f3] # # patch "Utilities.pm" # from [bd2f0818e5f09a128c1ca297bb6612181d50ea1e] # to [ddd6c902f8cf6ef5010e29a94cebb3ec0c6f1490] # # patch "mtn-browse" # from [bcf437debb1b500536ecc393c8aaa5521bd62266] # to [d76ff1eab8e1e12e1c97e898143c6c226a2325b1] # ============================================================ --- WindowManager.pm 36ef984a1f656a2eb160c4d9316ddb4d8e9a20bd +++ WindowManager.pm 36ef984a1f656a2eb160c4d9316ddb4d8e9a20bd @@ -0,0 +1,380 @@ +############################################################################## +# +# File Name - WindowManager.pm +# +# Description - Class module that provides a class for managing application +# window, i.e. their recycling, busy cursor management etc. +# +# 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 - WindowManager +# +# Description - See above. +# +############################################################################## + + + +# ***** PACKAGE DECLARATION ***** + +package WindowManager; + +# ***** DIRECTIVES ***** + +require 5.008; + +use strict; +use integer; + +# ***** REQUIRED PACKAGES ***** + +# Standard Perl and CPAN modules. + +use Carp; + +# ***** GLOBAL DATA DECLARATIONS ***** + +# The singleton object. + +my $singleton; + +# ***** 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 add_busy_widgets($$@); +sub cleanup($); +sub cond_find($$$); +sub find_unused($$); +sub instance($); +sub make_busy($$$); +sub manage($$$;$); +# +############################################################################## +# +# Routine - instance +# +# Description - Class constructor and accessor for a singleton class. +# +# Data - $class : Either the name of the class that is to be +# created or an object of that class. +# Return Value : A reference to the WindowManager object, +# which may have been created. +# +############################################################################## + + + +sub instance($) +{ + + my $class = ref($_[0]) ? ref($_[0]) : $_[0]; + + if (! defined($singleton)) + { + $singleton = {}; + $singleton->{windows} = []; + $singleton->{busy_cursor} = undef; + $singleton->{grab_widget_stack} = []; + return bless($singleton, $class); + + } + else + { + return $singleton; + } + +} +# +############################################################################## +# +# Routine - cleanup +# +# Description - Cleans up all resources managed by this object. +# +# Data - $this : The object. +# +############################################################################## + + + +sub cleanup($) +{ + + my $this = $_[0]; + + # Destroy all the window widgets and thereby all their children. + + foreach my $window (@{$this->{windows}}) + { + $window->{window}->destroy(); + } + + # Free up everything used by this object and associated window instance + # records. + + $this->{windows} = []; + $this->{busy_cursor} = undef; + $this->{grab_widget_stack} = []; + $singleton = undef; + +} +# +############################################################################## +# +# Routine - manage +# +# Description - Take the given window instance record and start managing +# it. Please note that it is expected that the top level +# window widget will be in a field called `window'. +# +# Data - $this : The object. +# $instance : A reference to the window instance record +# that is to be managed. +# $type : The type of window that is to be managed. +# $grab_widget : The widget that should be used with an input +# grab when making the window busy. This is +# optional but if not present then the +# instance record is expected to contain an +# `appbar' widget field. +# +############################################################################## + + + +sub manage($$$;$) +{ + + my($this, $instance, $type, $grab_widget) = @_; + + # Check for instance record compliance. + + croak("No window field found") unless (exists($instance->{window})); + croak("No appbar field found") + unless (exists($instance->{appbar}) || defined($grab_widget)); + foreach my $field ("busy_widgets", "grab_widget", "type") + { + croak($field . " field found - I manage this") + if (exists($instance->{$field})); + } + croak("Cannot manage unrealised windows") + unless(defined($instance->{window}->window())); + + # Ok so store what we need in the instance record. + + $instance->{type} = $type; + $instance->{busy_widgets} = [$instance->{window}->window()]; + $instance->{grab_widget} = + defined($grab_widget) ? $grab_widget : $instance->{appbar}; + + # Store the instance record in our window list. + + push(@{$this->{windows}}, $instance); + +} +# +############################################################################## +# +# Routine - add_busy_widgets +# +# Description - Add the specified additional widgets for busy cursor +# handling. +# +# Data - $this : The object. +# $instance : A reference to the window instance record that +# is to be updated. +# @widgets : The list of additional widgets that are to be +# handled. +# +############################################################################## + + + +sub add_busy_widgets($$@) +{ + + my($this, $instance, @widgets) = @_; + + push(@{$instance->{busy_widgets}}, @widgets); + +} +# +############################################################################## +# +# Routine - find_unused +# +# Description - Try and find an unused window of the specified type in the +# list of managed window instance records. +# +# Data - $this : The object. +# $type : The type of window that is to be found. +# Return Value : A reference to the found window instance +# record on success, otherwise undef on +# failure. +# +############################################################################## + + + +sub find_unused($$) +{ + + my($this, $type) = @_; + + foreach my $window (@{$this->{windows}}) + { + return $window + if ($window->{type} eq $type && ! $window->{window}->mapped()); + } + + return; + +} +# +############################################################################## +# +# Routine - cond_find +# +# Description - Try and find a window of the specified type that also +# satisfies the conditions laid down in the specified +# predicate routine. +# +# Data - $this : The object. +# $type : The type of window that is to be found. If +# this is undef then the predicate routine is +# called for all window instance records. +# $predicate : A reference to the predicate routine that is +# to be called against every window of the +# specified type. The one argument to this +# routine is a window instance record. +# Return Value : A reference to the found window instance +# record on success, otherwise undef on +# failure. +# +############################################################################## + + + +sub cond_find($$$) +{ + + my($this, $type, $predicate) = @_; + + foreach my $window (@{$this->{windows}}) + { + return $window if ((! defined ($type) || $window->{type} eq $type) + && &$predicate($window)); + } + + return; + +} +# +############################################################################## +# +# Routine - make_busy +# +# Description - Make all managed windows either busy or active. +# +# Data - $this : The object. +# $instance : The window instance that is to have the input +# grab (this is invariably the window that is +# busy processing something). +# $busy : True if the window is to be made busy, +# otherwise false if the window is to be made +# active. +# +############################################################################## + + + +sub make_busy($$$) +{ + + my($this, $instance, $busy) = @_; + + croak("Called with an unmanaged instance record") + unless (exists($instance->{grab_widget})); + + # Create and store the cursors if we haven't done so already. + + $this->{busy_cursor} = Gtk2::Gdk::Cursor->new("watch") + unless (defined($this->{busy_cursor})); + + # Do it. Make the grab widget, usually the window's 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) + { + Gtk2->grab_add($instance->{grab_widget}); + foreach my $win_instance (@{$this->{windows}}) + { + foreach my $window (@{$win_instance->{busy_widgets}}) + { + $window->set_cursor($this->{busy_cursor}); + } + } + push(@{$this->{grab_widget_stack}}, $instance->{grab_widget}); + } + else + { + my $grab_widget; + if (defined($grab_widget = pop(@{$this->{grab_widget_stack}}))) + { + Gtk2->grab_remove($grab_widget); + if ($#{$this->{grab_widget_stack}} < 0) + { + foreach my $win_instance (@{$this->{windows}}) + { + foreach my $window (@{$win_instance->{busy_widgets}}) + { + $window->set_cursor(undef); + } + } + } + else + { + Gtk2->grab_add($this->{grab_widget_stack}-> + [$#{$this->{grab_widget_stack}}]); + } + } + } + +} + +1; ============================================================ --- AdvancedFind.pm e9ff33befd44ace6ba5b2ae8f19ba741aec3842c +++ AdvancedFind.pm 8bf4995aaee6880d46d7352170a299e43aa23053 @@ -96,22 +96,12 @@ sub advanced_find($$$) my($advanced_find, $new, $ret_val); + my $wm = WindowManager->instance(); - # Look for an unused window first. - - foreach my $window (@windows) - { - if ($window->{type} eq $window_type && ! $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)) + if (defined($advanced_find = $wm->find_unused($window_type))) { $new = 0; } @@ -146,18 +136,16 @@ sub advanced_find($$$) $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 necessary, register the window for management and setup the list + # of additional 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); + $wm->manage($advanced_find, $window_type); + $wm->add_busy_widgets($advanced_find, + $advanced_find->{details_textview}-> + get_window("text")); } # Now actually update it with any preset values. @@ -253,13 +241,11 @@ sub create_advanced_find_window() { my(@branch_list, - $font, $instance, $renderer, $tv_column); $instance = {}; - $instance->{type} = $window_type; $instance->{glade} = Gtk2::GladeXML->new($glade_file, $window_type); # Flag to stop recursive calling of callbacks. @@ -363,8 +349,7 @@ sub create_advanced_find_window() $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)); + $instance->{details_textview}->modify_font($mono_font); return $instance; @@ -705,11 +690,12 @@ sub update_advanced_find_state($$) my($advanced_find, $changed) = @_; my $made_busy = 0; + my $wm = WindowManager->instance(); if ($advanced_find->{window}->realized()) { $made_busy = 1; - make_busy($advanced_find, 1); + $wm->make_busy($advanced_find, 1); } $advanced_find->{appbar}->push(""); gtk2_update(); @@ -1056,7 +1042,7 @@ sub update_advanced_find_state($$) } $advanced_find->{appbar}->pop(); - make_busy($advanced_find, 0) if ($made_busy); + $wm->make_busy($advanced_find, 0) if ($made_busy); } ============================================================ --- Annotate.pm c6d0364b1eb14d721a0ea41ddbd5bdad8be6ff92 +++ Annotate.pm 7cee86fa814a8ec2d79ee9284ed150010ec560d2 @@ -88,6 +88,7 @@ sub display_annotation($$$) $prefix_tag, $template, $text_tag); + my $wm = WindowManager->instance(); $instance = get_annotation_window(); local $instance->{in_cb} = 1; @@ -95,7 +96,7 @@ sub display_annotation($$$) $instance->{window}->set_title("Annotated Listing Of " . $file_name); $instance->{window}->show_all(); - make_busy($instance, 1); + $wm->make_busy($instance, 1); $instance->{appbar}->push(""); gtk2_update(); @@ -185,7 +186,7 @@ sub display_annotation($$$) gtk2_update(); $instance->{appbar}->pop(); - make_busy($instance, 0); + $wm->make_busy($instance, 0); } # @@ -205,30 +206,18 @@ sub get_annotation_window() sub get_annotation_window() { - my($font, - $height, + my($height, $instance, - $width, - $window_type); + $width); + my $window_type = "annotation_window"; + my $wm = WindowManager->instance(); - $window_type = "annotation_window"; - - foreach my $window (@windows) - { - if ($window->{type} eq $window_type && ! $window->{window}->mapped()) - { - $instance = $window; - last; - } - } - # Create a new annotation window if an unused one wasn't found, otherwise # reuse an existing unused one. - if (! defined($instance)) + if (! defined($instance = $wm->find_unused($window_type))) { $instance = {}; - $instance->{type} = $window_type; $instance->{glade} = Gtk2::GladeXML->new($glade_file, $window_type); # Flag to stop recursive calling of callbacks. @@ -267,21 +256,14 @@ sub get_annotation_window() $instance->{annotation_buffer} = $instance->{annotation_textview}->get_buffer(); create_format_tags($instance->{annotation_buffer}); - $font = Gtk2::Pango::FontDescription->from_string("monospace 10"); - $instance->{annotation_textview}->modify_font($font) - if (defined($font)); + $instance->{annotation_textview}->modify_font($mono_font); - $instance->{grab_widget} = $instance->{window}; + # Register the window for management. - # 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->{annotation_textview}->get_window("text")); - - push(@windows, $instance); + $wm->manage($instance, $window_type, $instance->{window}); + $wm->add_busy_widgets($instance, + $instance->{annotation_textview}-> + get_window("text")); } else { ============================================================ --- ChangeLog.pm ee5aff7fcef19a382436ed70b03e97622c3e6203 +++ ChangeLog.pm 1e2e21a37698986ddd54ecb0ad513bcb4b9abb3a @@ -121,30 +121,18 @@ sub get_change_log_window() sub get_change_log_window() { - my($font, - $height, + my($height, $instance, - $width, - $window_type); + $width); + my $window_type = "changelog_window"; + my $wm = WindowManager->instance(); - $window_type = "changelog_window"; - - foreach my $window (@windows) - { - if ($window->{type} eq $window_type && ! $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)) + if (! defined($instance = $wm->find_unused($window_type))) { $instance = {}; - $instance->{type} = $window_type; $instance->{glade} = Gtk2::GladeXML->new($glade_file, $window_type); # Flag to stop recursive calling of callbacks. @@ -182,21 +170,14 @@ sub get_change_log_window() $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->{changelog_textview}->modify_font($mono_font); - $instance->{grab_widget} = $instance->{window}; + # Register the window for management. - # 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); + $wm->manage($instance, $window_type, $instance->{window}); + $wm->add_busy_widgets($instance, + $instance->{changelog_textview}-> + get_window("text")); } else { ============================================================ --- FindText.pm a39b09074fbe7e628f6e4d00dff7bc185b76518d +++ FindText.pm 97a85e65c2990934ed5a983f50bf3851da1cb0ce @@ -61,6 +61,7 @@ sub find_combo_changed_cb($$); # Private routines. sub find_combo_changed_cb($$); +sub find_current_window($); sub get_find_text_window($$); # ############################################################################## @@ -68,12 +69,12 @@ sub get_find_text_window($$); # Routine - find_text # # Description - Display the find text window associated with the specified -# text view widget, creating one if necessary, and allow the +# textview widget, creating one if necessary, and allow the # user to search through the related text buffer. # # Data - $parent : The parent window widget for the find text # window. -# $text_view : The text view widget that is to be searched. +# $text_view : The textview widget that is to be searched. # ############################################################################## @@ -84,27 +85,12 @@ sub find_text($$) my($parent, $text_view) = @_; - my $instance; + # Only go looking for a spare find text window, creating one if necessary, + # if there isn't one already mapped for the specified textview widget. - foreach my $window (@windows) - { - if ($window->{type} eq $window_type - && $window->{window}->mapped() - && $window->{text_view} == $text_view) - { - $instance = $window; - last; - } - } + get_find_text_window($parent, $text_view) + if (! defined(find_current_window($text_view))); - # A suitable mapped find text window cannot be found so reuse an unsued one - # or create a new one. - - if (! defined($instance)) - { - $instance = get_find_text_window($parent, $text_view); - } - } # ############################################################################## @@ -112,9 +98,9 @@ sub find_text($$) # Routine - reset_find_text # # Description - Resets the search context for the find text window -# associated with the specified text view widget. +# associated with the specified textview widget. # -# Data - $text_view : The text view widget to which the find text +# Data - $text_view : The textview widget to which the find text # window is associated. # ############################################################################## @@ -128,21 +114,10 @@ sub reset_find_text($) my $instance; - foreach my $window (@windows) - { - if ($window->{type} eq $window_type - && $window->{window}->mapped() - && $window->{text_view} == $text_view) - { - $instance = $window; - last; - } - } - # Simply reset the search context for the found find text window. $instance->{match_offset_start} = $instance->{match_offset_end} = -1 - if (defined($instance)); + if (defined(find_current_window($text_view))); } # @@ -151,9 +126,9 @@ sub reset_find_text($) # Routine - disable_find_text # # Description - Disables or enables the find text window associated with -# the specified text view widget. +# the specified textview widget. # -# Data - $text_view : The text view widget to which the find text +# Data - $text_view : The textview widget to which the find text # window is associated. # $disable : True if the window is to be disabled, # otherwise false if it is to be enabled. @@ -169,20 +144,9 @@ sub disable_find_text($$) my $instance; - foreach my $window (@windows) - { - if ($window->{type} eq $window_type - && $window->{window}->mapped() - && $window->{text_view} == $text_view) - { - $instance = $window; - last; - } - } - # Simply disable/enable the found find text window. - if (defined($instance)) + if (defined(find_current_window($text_view))) { if ($disable) { @@ -202,149 +166,6 @@ sub disable_find_text($$) # ############################################################################## # -# Routine - get_find_text_window -# -# Description - Creates or prepares an existing find text window for use. -# -# Data - $parent : The parent window widget for the find text -# window. -# $text_view : The text view widget that is to be searched. -# -############################################################################## - - - -sub get_find_text_window($$) -{ - - my($parent, $text_view) = @_; - - my($instance, - $new); - - foreach my $window (@windows) - { - if ($window->{type} eq $window_type && ! $window->{window}->mapped()) - { - $instance = $window; - last; - } - } - - # Create a new find text window if an unused one wasn't found, otherwise - # reuse an existing unused one. - - if (! defined($instance)) - { - $new = 1; - $instance = {}; - $instance->{type} = $window_type; - $instance->{glade} = Gtk2::GladeXML->new($glade_file, $window_type); - - # Flag to stop recursive calling of callbacks. - - $instance->{in_cb} = 0; - - # Connect Glade registered signal handlers. - - glade_signal_autoconnect($instance->{glade}, $instance); - - # Get the widgets that we are interested in. - - $instance->{window} = $instance->{glade}->get_widget($window_type); - $instance->{main_vbox} = $instance->{glade}->get_widget("main_vbox"); - $instance->{find_combo} = - $instance->{glade}->get_widget("find_comboboxentry"); - $instance->{case_sensitive_tick} = - $instance->{glade}->get_widget("case_sensitive_checkbutton"); - $instance->{search_backwards_tick} = - $instance->{glade}->get_widget("search_backwards_checkbutton"); - $instance->{find_button} = - $instance->{glade}->get_widget("find_button"); - - # Setup the find text window deletion handlers. - - $instance->{window}->signal_connect - ("delete_event", - sub { - my($widget, $event, $instance) = @_; - return TRUE if ($instance->{in_cb}); - local $instance->{in_cb} = 1; - $instance->{window}->hide(); - return TRUE; - }, - $instance); - $instance->{glade}->get_widget("close_button")->signal_connect - ("clicked", - sub { - my($widget, $instance) = @_; - return TRUE if ($instance->{in_cb}); - local $instance->{in_cb} = 1; - $instance->{window}->hide(); - }, - $instance); - - # Setup the comboboxentry changed signal handler. - - $instance->{find_combo}->child()-> - signal_connect("changed", \&find_combo_changed_cb, $instance); - - # Setup the combobox. - - $instance->{find_combo}-> - set_model(Gtk2::ListStore->new("Glib::String")); - $instance->{find_combo}->set_text_column(0); - $instance->{search_history} = []; - - $instance->{grab_widget} = $instance->{window}; - } - else - { - $new = 0; - $instance->{in_cb} = 0; - $instance->{main_vbox}->set_sensitive(TRUE); - } - - # Reset the search context. - - $instance->{match_offset_start} = $instance->{match_offset_end} = -1; - $instance->{old_y} = 0; - $instance->{old_search_term} = ""; - - # Make sure the find button is only enabled when there is something entered - # into the comboboxentry widget. - - $instance->{find_button}->set_sensitive - ((length($instance->{find_combo}->child()->get_text()) > 0) ? - TRUE : FALSE); - - # Store the associated text view and text buffer. - - $instance->{text_view} = $text_view; - $instance->{text_buffer} = $instance->{text_view}->get_buffer(); - - # Reparent window and display it. - - $instance->{window}->set_transient_for($parent); - $instance->{window}->show_all(); - - # If necessary, setup the list of windows that can be made busy for this - # application window. - - if ($new) - { - $instance->{busy_windows} = []; - push(@{$instance->{busy_windows}}, $instance->{window}->window()); - - push(@windows, $instance); - } - - return $instance; - -} -# -############################################################################## -# # Routine - find_button_clicked_cb # # Description - Callback routine called when the user clicks on the find @@ -604,5 +425,160 @@ sub find_combo_changed_cb($$) TRUE : FALSE); } +# +############################################################################## +# +# Routine - find_current_window +# +# Description - Look for a find text window that is mapped and belongs to +# the specified textview widget. +# +# Data - $text_view : The textview widget that is to be searched. +# +############################################################################## + + +sub find_current_window($) +{ + + my $text_view = $_[0]; + + return WindowManager->instance()->cond_find + ($window_type, + sub { + my $instance = $_[0]; + return 1 + if ($instance->{window}->mapped() + && $instance->{text_view} == $text_view); + return; + }); + +} +# +############################################################################## +# +# Routine - get_find_text_window +# +# Description - Creates or prepares an existing find text window for use. +# +# Data - $parent : The parent window widget for the find text +# window. +# $text_view : The textview widget that is to be searched. +# +############################################################################## + + + +sub get_find_text_window($$) +{ + + my($parent, $text_view) = @_; + + my($instance, + $new); + my $wm = WindowManager->instance(); + + # Create a new find text window if an unused one wasn't found, otherwise + # reuse an existing unused one. + + if (! defined($instance = $wm->find_unused($window_type))) + { + $new = 1; + $instance = {}; + $instance->{glade} = Gtk2::GladeXML->new($glade_file, $window_type); + + # Flag to stop recursive calling of callbacks. + + $instance->{in_cb} = 0; + + # Connect Glade registered signal handlers. + + glade_signal_autoconnect($instance->{glade}, $instance); + + # Get the widgets that we are interested in. + + $instance->{window} = $instance->{glade}->get_widget($window_type); + $instance->{main_vbox} = $instance->{glade}->get_widget("main_vbox"); + $instance->{find_combo} = + $instance->{glade}->get_widget("find_comboboxentry"); + $instance->{case_sensitive_tick} = + $instance->{glade}->get_widget("case_sensitive_checkbutton"); + $instance->{search_backwards_tick} = + $instance->{glade}->get_widget("search_backwards_checkbutton"); + $instance->{find_button} = + $instance->{glade}->get_widget("find_button"); + + # Setup the find text window deletion handlers. + + $instance->{window}->signal_connect + ("delete_event", + sub { + my($widget, $event, $instance) = @_; + return TRUE if ($instance->{in_cb}); + local $instance->{in_cb} = 1; + $instance->{window}->hide(); + return TRUE; + }, + $instance); + $instance->{glade}->get_widget("close_button")->signal_connect + ("clicked", + sub { + my($widget, $instance) = @_; + return TRUE if ($instance->{in_cb}); + local $instance->{in_cb} = 1; + $instance->{window}->hide(); + }, + $instance); + + # Setup the comboboxentry changed signal handler. + + $instance->{find_combo}->child()-> + signal_connect("changed", \&find_combo_changed_cb, $instance); + + # Setup the combobox. + + $instance->{find_combo}-> + set_model(Gtk2::ListStore->new("Glib::String")); + $instance->{find_combo}->set_text_column(0); + $instance->{search_history} = []; + } + else + { + $new = 0; + $instance->{in_cb} = 0; + $instance->{main_vbox}->set_sensitive(TRUE); + } + + # Reset the search context. + + $instance->{match_offset_start} = $instance->{match_offset_end} = -1; + $instance->{old_y} = 0; + $instance->{old_search_term} = ""; + + # Make sure the find button is only enabled when there is something entered + # into the comboboxentry widget. + + $instance->{find_button}->set_sensitive + ((length($instance->{find_combo}->child()->get_text()) > 0) ? + TRUE : FALSE); + + # Store the associated textview and text buffer. + + $instance->{text_view} = $text_view; + $instance->{text_buffer} = $instance->{text_view}->get_buffer(); + + # Reparent window and display it. + + $instance->{window}->set_transient_for($parent); + $instance->{window}->show_all(); + + # If necessary, register the window for management. + + $wm->manage($instance, $window_type, $instance->{window}) if ($new); + + return $instance; + +} + 1; ============================================================ --- Globals.pm 66eff7abeb8443d29387430e0272aa8422d870ae +++ Globals.pm a24f4acd16f3849fa802bbfdc496d61483b22ac3 @@ -58,8 +58,7 @@ use constant DISPLAY_OF_FILE => 0x08; 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 => 0x10; use constant REVISION_LIST => 0x02; use constant REVISION_DETAILS => 0x04; @@ -68,43 +67,35 @@ use constant BRANCH_CHANGED = use constant ALL_CHANGED => 0xff; use constant BRANCH_CHANGED => (REVISION | DIRECTORY - | DIRECTORY_VIEW | FILE - | DISPLAY_OF_FILE); + | DIRECTORY_VIEW | 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 DIRECTORY_CHANGED => (DIRECTORY_VIEW | DISPLAY_OF_FILE); 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); + | DIRECTORY_VIEW | 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. +# The tooltips widget. -our $tmp_dir; +our $tooltips; -# List of window instances. +# The mono-spaced font used for displaying text files. -our @windows; +our $mono_font; # Assorted pixmaps. our $line_image; -# The busy cursor to use for the mouse. +# Location of the temporary working directory. -our $busy_cursor; +our $tmp_dir; -# The tooltips widget. - -our $tooltips; - # Text viewable application mime types. our @text_viewable_app_mime_types = @@ -167,8 +158,6 @@ our %EXPORT_TAGS = (constants => [qw(ALL DIRECTORY_CHANGED DIRECTORY_VIEW DISPLAY_OF_FILE - DISPLAY_OF_FILE_CHANGED - FILE FILE_CHANGED NEW_FIND REVISION @@ -176,14 +165,13 @@ our %EXPORT_TAGS = (constants => [qw(ALL REVISION_DETAILS REVISION_LIST SELECTED_REVISION_CHANGED)], - variables => [qw($busy_cursor - $glade_file + variables => [qw($glade_file $line_image + $mono_font @text_mime_types @text_viewable_app_mime_types $tmp_dir - $tooltips - @windows)]); + $tooltips)]); our @EXPORT = qw(); Exporter::export_ok_tags(qw(constants variables)); our $VERSION = 0.1; ============================================================ --- History.pm 8b6634e67b518d62381d23706d63705c8275c5ff +++ History.pm 7923dcc1bfcb958b514b5af9fec270960b99d2f3 @@ -1,6 +1,6 @@ ############################################################################## # -# File Name - mtn-browse +# File Name - History.pm # # Description - The history module for the mtn-browse application. This # module contains all the routines for implementing the @@ -97,6 +97,7 @@ sub display_revision_change_history($$) $counter, %history_hash, $instance); + my $wm = WindowManager->instance(); $instance = get_history_window(); local $instance->{in_cb} = 1; @@ -109,7 +110,7 @@ sub display_revision_change_history($$) $instance->{history_label}->set_markup("Revision History"); $instance->{window}->show_all(); - make_busy($instance, 1); + $wm->make_busy($instance, 1); $instance->{appbar}->push(""); gtk2_update(); @@ -265,7 +266,7 @@ sub display_revision_change_history($$) gtk2_update(); $instance->{appbar}->pop(); - make_busy($instance, 0); + $wm->make_busy($instance, 0); } # @@ -297,6 +298,7 @@ sub display_file_change_history($$$) $counter, %history_hash, $instance); + my $wm = WindowManager->instance(); $instance = get_history_window(); local $instance->{in_cb} = 1; @@ -310,7 +312,7 @@ sub display_file_change_history($$$) $instance->{history_label}->set_markup("File History"); $instance->{window}->show_all(); - make_busy($instance, 1); + $wm->make_busy($instance, 1); $instance->{appbar}->push(""); gtk2_update(); @@ -456,7 +458,7 @@ sub display_file_change_history($$$) gtk2_update(); $instance->{appbar}->pop(); - make_busy($instance, 0); + $wm->make_busy($instance, 0); } # @@ -676,6 +678,7 @@ sub compare_revisions($$$;$) $name, $padding, $rest); + my $wm = WindowManager->instance(); $instance = get_revision_comparison_window(); local $instance->{in_cb} = 1; @@ -695,7 +698,7 @@ sub compare_revisions($$$;$) } $instance->{window}->show_all(); - make_busy($instance, 1); + $wm->make_busy($instance, 1); $instance->{appbar}->push(""); gtk2_update(); @@ -899,7 +902,7 @@ sub compare_revisions($$$;$) $instance->{comparison_scrolledwindow}->get_hadjustment()->set_value(0); $instance->{appbar}->pop(); - make_busy($instance, 0); + $wm->make_busy($instance, 0); } # @@ -1002,32 +1005,18 @@ sub get_history_window() sub get_history_window() { - my($font, - $height, + my($height, $instance, - $width, - $window_type); + $width); + my $window_type = "history_window"; + my $wm = WindowManager->instance(); - $window_type = "history_window"; - - # Look for an unused window first. - - foreach my $window (@windows) - { - if ($window->{type} eq $window_type && ! $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)) + if (! defined($instance = $wm->find_unused($window_type))) { $instance = {}; - $instance->{type} = $window_type; $instance->{glade} = Gtk2::GladeXML->new($glade_file, $window_type); # Flag to stop recursive calling of callbacks. @@ -1081,23 +1070,14 @@ sub get_history_window() $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)); + $instance->{history_textview}->modify_font($mono_font); - # Make the stop button the grab widget when busy, this is so the user - # can interrupt the history gathering process. + # Register the window for management. - $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); + $wm->manage($instance, $window_type, $instance->{stop_button}); + $wm->add_busy_widgets($instance, + $instance->{history_textview}-> + get_window("text")); } else { @@ -1226,33 +1206,19 @@ sub get_revision_comparison_window() sub get_revision_comparison_window() { - my($font, - $height, + my($height, $instance, $renderer, - $width, - $window_type); + $width); + my $window_type = "revision_comparison_window"; + my $wm = WindowManager->instance(); - $window_type = "revision_comparison_window"; - - # Look for an unused window first. - - foreach my $window (@windows) - { - if ($window->{type} eq $window_type && ! $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)) + if (! defined($instance = $wm->find_unused($window_type))) { $instance = {}; - $instance->{type} = $window_type; $instance->{glade} = Gtk2::GladeXML->new($glade_file, $window_type); # Flag to stop recursive calling of callbacks. @@ -1322,19 +1288,14 @@ sub get_revision_comparison_window() $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)); + $instance->{comparison_textview}->modify_font($mono_font); - # Setup the list of windows that can be made busy for this application - # window. + # Register the window for management. - $instance->{busy_windows} = []; - push(@{$instance->{busy_windows}}, $instance->{window}->window()); - push(@{$instance->{busy_windows}}, - $instance->{comparison_textview}->get_window("text")); - - push(@windows, $instance); + $wm->manage($instance, $window_type, $instance->{stop_button}); + $wm->add_busy_widgets($instance, + $instance->{comparison_textview}-> + get_window("text")); } else { ============================================================ --- Utilities.pm bd2f0818e5f09a128c1ca297bb6612181d50ea1e +++ Utilities.pm ddd6c902f8cf6ef5010e29a94cebb3ec0c6f1490 @@ -55,7 +55,6 @@ sub gtk2_update(); sub get_revision_ids($$); sub glade_signal_autoconnect($$); sub gtk2_update(); -sub make_busy($$); sub run_command($@); sub set_label_value($$); # @@ -583,74 +582,6 @@ sub glade_signal_autoconnect($$) # ############################################################################## # -# 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 ============================================================ --- mtn-browse bcf437debb1b500536ecc393c8aaa5521bd62266 +++ mtn-browse d76ff1eab8e1e12e1c97e898143c6c226a2325b1 @@ -89,6 +89,7 @@ use Utilities; use FindText; use History; use Utilities; +use WindowManager; # ***** GLOBAL DATA DECLARATIONS ***** @@ -160,6 +161,7 @@ sub view_button_clicked_cb($$); setup_sigchld_handler(\&sigchld_handler); $glade_file = LIB_PATH . "/UI/mtn-browse.glade"; $tooltips = Gtk2::Tooltips->new(); + $mono_font = Gtk2::Pango::FontDescription->from_string("monospace 10"); $line_image = Gtk2::Gdk::Pixbuf->new_from_file(LIB_PATH . "/UI/line.png"); # Create the temporary working directory. @@ -210,6 +212,9 @@ sub view_button_clicked_cb($$); (sub { my $browser = $_[0]; + return if ($browser->{in_cb}); + local $browser->{in_cb} = 1; + $browser->{branch_combo_details}->{preset} = 1; $browser->{branch_combo_details}->{value} = $branch; $browser->{revision_combo_details}->{preset} = 1; @@ -220,6 +225,7 @@ sub view_button_clicked_cb($$); return FALSE; }, $browser); + $mtn = undef; } # Hand control over to Gtk2. @@ -228,11 +234,7 @@ sub view_button_clicked_cb($$); # Cleanup. - foreach my $window (@windows) - { - $window->{window}->destroy(); - } - @windows = (); + WindowManager->instance()->cleanup(); Gnome2::VFS->shutdown(); $SIG{CHLD} = "IGNORE"; @@ -937,8 +939,9 @@ sub view_button_clicked_cb($$) $fh, $file_name); - if (! ($file_name = - generate_tmp_path($browser->{file_being_viewed}->{short_name}))) + if (! defined($file_name = + generate_tmp_path($browser->{file_being_viewed}-> + {short_name}))) { my $dialog = Gtk2::MessageDialog->new ($browser->{window}, @@ -1191,16 +1194,17 @@ sub main_window_delete_event_cb($$$) # Only exit if this is the only browser window currently being shown. $others_mapped = 0; - foreach my $window (@windows) - { - if ($window->{type} eq $window_type - && $window != $browser - && $window->{window}->mapped()) - { - $others_mapped = 1; - last; - } - } + WindowManager->instance()->cond_find + ($window_type, + sub { + my $instance = $_[0]; + if ($instance != $browser && $instance->{window}->mapped()) + { + $others_mapped = 1; + return 1; + } + return; + }); $browser->{window}->hide(); $browser->{mtn} = undef; @@ -1252,30 +1256,19 @@ sub get_browser_window(;$$$$$) my($mtn, $branch, $revision_id, $directory, $file) = @_; my $browser; + my $wm = WindowManager->instance(); - # Look for an unused window first. - - foreach my $window (@windows) - { - if ($window->{type} eq $window_type && ! $window->{window}->mapped()) - { - $browser = $window; - last; - } - } - # Create a new browser window if an unused one wasn't found, otherwise # reuse an existing unused one. - if (! defined($browser)) + if (! defined($browser = $wm->find_unused($window_type))) { - my($font, - $image, + + my($image, $renderer, $tv_column); $browser = {}; - $browser->{type} = $window_type; $browser->{mtn} = $mtn; $browser->{glade} = Gtk2::GladeXML->new($glade_file, $window_type); @@ -1447,8 +1440,7 @@ sub get_browser_window(;$$$$$) Gtk2::SourceView::LanguagesManager->new(); $browser->{file_view_sv} = Gtk2::SourceView::View-> new_with_buffer($browser->{file_view_svbuffer}); - $font = Gtk2::Pango::FontDescription->from_string("monospace 10"); - $browser->{file_view_sv}->modify_font($font) if (defined($font)); + $browser->{file_view_sv}->modify_font($mono_font); $browser->{file_view_sv}->set_cursor_visible(FALSE); $browser->{file_view_sv}->set_editable(FALSE); $browser->{glade}->get_widget("file_view_scrolledwindow")-> @@ -1457,6 +1449,12 @@ sub get_browser_window(;$$$$$) $browser->{window}->show_all(); + # Register the window for management. + + $wm->manage($browser, $window_type); + $wm->add_busy_widgets($browser, + $browser->{file_view_sv}->get_window("text")); + # Update the browser's internal state. $browser->{branch_combo_details}->{preset} = 0; @@ -1465,18 +1463,10 @@ sub get_browser_window(;$$$$$) $browser->{file_being_viewed_preset_value} = ""; &{$browser->{update_handler}}($browser, DATABASE_CHANGED); - # Setup the list of windows that can be made busy for this application - # window. - - $browser->{busy_windows} = []; - push(@{$browser->{busy_windows}}, $browser->{window}->window()); - push(@{$browser->{busy_windows}}, - $browser->{file_view_sv}->get_window("text")); - - push(@windows, $browser); } else { + my($height, $width); @@ -1498,6 +1488,7 @@ sub get_browser_window(;$$$$$) $browser->{mtn} = $mtn; &{$browser->{update_handler}}($browser, DATABASE_CHANGED); } + } # Now deal with any presetting that is required. @@ -1554,24 +1545,59 @@ sub update_browser_state($$) my($browser, $changed) = @_; - make_busy($browser, 1); + my $wm = WindowManager->instance(); + + $wm->make_busy($browser, 1); $browser->{appbar}->push(""); gtk2_update(); - # Disable the browser if no database is associated with it. + # The database has changed. - if (! defined($browser->{mtn})) + if ($changed & BRANCH) { - $browser->{close_toolbutton}->set_sensitive(FALSE); - $browser->{properties_toolbutton}->set_sensitive(FALSE); - $browser->{main_vbox}->set_sensitive(FALSE); + + my $db_name; + + if (! defined($browser->{mtn})) + { + + # Disable the browser as no database is associated with it. + + $browser->{close_toolbutton}->set_sensitive(FALSE); + $browser->{properties_toolbutton}->set_sensitive(FALSE); + $browser->{main_vbox}->set_sensitive(FALSE); + set_label_value($browser->{database_name_label}, ""); + + } + else + { + + # Enable the browser as there is a database associated with it. + + $browser->{close_toolbutton}->set_sensitive(TRUE); + $browser->{properties_toolbutton}->set_sensitive(TRUE); + $browser->{main_vbox}->set_sensitive(TRUE); + if (! defined($browser->{mtn}->get_db_name())) + { + $browser->{mtn}->get_option(\$db_name, "database"); + set_label_value($browser->{database_name_label}, + sprintf(" (%s)", $db_name)); + } + else + { + set_label_value($browser->{database_name_label}, + $browser->{mtn}->get_db_name()); + } + + # 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); + + } + } - else - { - $browser->{close_toolbutton}->set_sensitive(TRUE); - $browser->{properties_toolbutton}->set_sensitive(TRUE); - $browser->{main_vbox}->set_sensitive(TRUE); - } # The list of available branches has changed. @@ -1593,19 +1619,6 @@ sub update_browser_state($$) $browser->{branch_combo_details}->{complete} = 0; $browser->{branch_combo_details}->{value} = ""; } - if (! defined($browser->{mtn})) - { - set_label_value($browser->{database_name_label}, ""); - } - elsif (! defined($browser->{mtn}->get_db_name())) - { - set_label_value($browser->{database_name_label}, ""); - } - else - { - set_label_value($browser->{database_name_label}, - $browser->{mtn}->get_db_name()); - } # Get the new list of branches. @@ -2221,12 +2234,8 @@ sub update_browser_state($$) } - if ($changed & FILE) - { - } - $browser->{appbar}->pop(); - make_busy($browser, 0); + $wm->make_busy($browser, 0); } # @@ -2311,35 +2320,36 @@ sub sigchld_handler() # If it is an mtn process then close down the relevant object so # that it will automatically restart when needed. - foreach my $window (@windows) - { - if (exists($window->{mtn}) && defined($window->{mtn}) - && $window->{mtn}->get_pid() == $pid) - { - $window->{mtn}->closedown(); - my $dialog = Gtk2::MessageDialog->new - (undef, - ["modal"], - "warning", - "close", - sprintf("The mtn subprocess just unexpectedly\n" - . "exited (%s).\n" - . "This shouldn't happen.\n" - . "It will be restarted when needed.", - WIFSIGNALED($status) ? - sprintf("terminated by signal %d", - WTERMSIG($status)) : - sprintf("exited with status %d", - WEXITSTATUS($status)))); - $dialog->run(); - $dialog->destroy(); - last; - } - } + WindowManager->instance()->cond_find + (undef, + sub { + my $instance = $_[0]; + if (exists($instance->{mtn}) && defined($instance->{mtn}) + && $instance->{mtn}->get_pid() == $pid) + { + $instance->{mtn}->closedown(); + my $dialog = Gtk2::MessageDialog->new + (undef, + ["modal"], + "warning", + "close", + sprintf("The mtn subprocess just unexpectedly\n" + . "exited (%s).\n" + . "This shouldn't happen.\n" + . "It will be restarted when " + . "needed.", + WIFSIGNALED($status) ? + sprintf("terminated by signal %d", + WTERMSIG($status)) : + sprintf("exited with status %d", + WEXITSTATUS($status)))); + $dialog->run(); + $dialog->destroy(); + return 1; + } + return; + }); - if (WIFSIGNALED($status)) - { - } } } warn("waitpid failed: $!") if ($pid < 0 && $! != ECHILD);