####################################################################################### # # Class: Tk::Wizard # # Description: The Tk::Wizard class is a Tk component that allows users to create a # Microsoft style application wizard without having to write the code to # manage the wizard framework components, like event dispatching, etc. # ####################################################################################### package Tk::Wizard; use SelfLoader; use Tk; use Tk::JPEG; use Tk::Text; use strict; no strict 'refs'; ###################################################################################################### # # Method: new # # Description: Creates a new object of type Tk::Wizard and sets the default state # # Parameters: None # # Exceptions: N/A in this release # ###################################################################################################### sub new($) { my $invocant = shift; my $class = ref( $invocant) || $invocant; my $self = { # required for GUI operation hWin => $_[0], # configuration parameters title => "Generic Wizard", filename => "image.jpg", # event handling references preNextButtonAction => undef, postNextButtonAction => undef, prePrevButtonAction => undef, postPrevButtonAction => undef, preHelpButtonAction => undef, helpButtonAction => undef, postHelpButtonAction => undef, preFinishButtonAction => undef, finishButtonAction => undef, postFinishButtonAction => undef, preCancelButtonAction => undef, preCloseWindowAction => undef, # wizard page control list and ptr wizardPageList => [], # ref to empty array wizardPagePtr => 0, # internally used to track the wizard page being shown wizardFrame => "" }; return bless( $self, $class); } # end of sub new ###################################################################################################### # # Method: setActionEventHandlers # # Description: Allows the documented set of actions to be overwritten to hook the Tk::Wizard # framework into the application. While no handler must be overwritten, it is # very useful to overwrite the helpButtionAction and the finishButtonAction # event handlers. # # Parameters: None # # Exceptions: N/A in this release # ###################################################################################################### sub setActionEventHandlers { my $self = shift; my %newHandlers = ( @_ ); foreach( keys %newHandlers) { $self->{"$_"} = $newHandlers{$_}; } # end of foreach } # end of setActionEventHandlers ###################################################################################################### # # Method: setParamaters # # Description: Provide useful definitions for the two main parameters - title and filename. title # refers to the scalar string that will be printed on the wizard's title bar. filename # is the location and name of a JPEG file that should be displayed on the left side of # the wizard. # # Parameters: None # # Exceptions: N/A in this release # ###################################################################################################### sub setParameters { my $self = shift; my %newParams = ( @_ ); foreach( keys %newParams) { $self->{"$_"} = $newParams{"$_"}; } # end of foreach } # end of setParameters ###################################################################################################### # # Method: addWizardPage # # Description: Puts the passed in wizard page at the end of the wizard page list. # # Parameters: $page = The wizard page that should be added to the end of the wizard list. # # Exceptions: N/A in this release # ###################################################################################################### sub addWizardPage { my ($self, $page) = @_; push @{$self->{wizardPageList}}, $page; } # end of sub addWizardPage ###################################################################################################### # # Method: currentPage # # Description: Returns the page number that is currently being displayed. Page counting starts with 1. # # Parameters: None # # Exceptions: N/A in this release # ###################################################################################################### sub currentPage { my($self) = @_; return ($self->{wizardPagePtr} + 1); } # end of sub currentPage 1; #################################################################################### # Functions below this level are only loaded on demand. This gives the appearance of # faster load times. See module SelfLoader for details. #################################################################################### __DATA__ sub parent { my ($self) = @_; return $self->{hWin}; } ###################################################################################################### # # Method: wpFrame # # Description: This returns a Tk::Frame object that is a child of the Wizard control. It should be # used when creating wizard pages since some padding parameters are applied to it by # the wizard control. # # Parameters: N/A # # Exceptions: N/A in this release # ###################################################################################################### sub wpFrame { my ($self) = @_; my $frame = $self->{hWin}->Frame( -width => 400, -height => 400); $frame->packPropagate( 0); return $frame; } # end of wpFrame ###################################################################################################### # # Method: Show # # Description: The Show method actually creates all of the necessary components on the window that is # set in the new method. It is important that Wizard applications that use MainWindow for # drawing the Wizard also dispatch MainLoop after the Show method. # # Parameters: None # # Exceptions: N/A in this release # ###################################################################################################### sub Show { my ($self) = @_; # # builds the buttons on the bottom of thw wizard # my $buttonPanel = $self->{hWin}->Frame(); $buttonPanel->Button( -text => "Cancel", -command => [ \&CancelButtonEventCycle, $self, $self->{hWin}], -width => 10 ) ->pack( -side => "right", -expand => 0); $self->{nextButtonRef} = $buttonPanel->Button( -text => "Next >", -command => [ \&NextButtonEventCycle, $self ], -width => 10 )->pack( -side => "right", -expand => 0); $self->{prevButtonRef} = $buttonPanel->Button( -text => "< Previous", -command => [ \&PrevButtonEventCycle, $self ], -width => 10, -state => "disabled" )->pack( -side => "right", -expand => 0); $buttonPanel->Button( -text => "Help", -command => [ \&HelpButtonEventCycle, $self ], -width => 10 )->pack( -side => 'left', -anchor => 'w'); $buttonPanel->pack( -side => "bottom", -fill => 'x', -pady => 4, -padx => 4); # # builds the image on the left side of the wizard # $self->{hWin}->Photo( "sidebanner", -format => "jpeg", -file => $self->{filename}); $self->{hWin}->Label( -image => "sidebanner")->pack( -side => "left", -anchor => "w"); # # This populates the wizard page panel on the side of the screen. # $self->{wizardFrame} = $self->{wizardPageList}->[($self->{wizardPagePtr})]->()->pack( -side => "top", -expand => 0); # # setup the containing window to match the criteria for a wizard widget # $self->{hWin}->configure( -title => $self->{title}); $self->{hWin}->resizable( 0, 0); # forbid resize $self->{hWin}->withdraw; # position in screen center $self->{hWin}->Popup; $self->{hWin}->transient; # forbid minimize $self->{hWin}->protocol( WM_DELETE_WINDOW => [ \&CloseWindowEventCycle, $self, $self->{hWin}]); } # end of sub Show ###################################################################################################### # # Method: dispatch # # Description: Thin wrapper to dispatch event cycles as needed # # Parameters: The dispatch function is an internal function used to determine if the dispatch back reference # is undefined or if it should be dispatched. Undefined methods are used to denote dispatchback # methods to bypass. This reduces the number of method dispatchs made for each handler and also # increased the usability of the set methods above when trying to unregister event handlers. # # Exceptions: N/A in this release # ###################################################################################################### sub dispatch { my ($handler) = @_; if( defined $handler) { return !($handler->());} return 0; } # end of sub dispatch ###################################################################################################### # # Method: NextButtonEventCycle # # Description: Runs the complete view of the action handler cycle for the "Next>" button on the # wizard button bar. This includes dispatching the preNextButtonAction and # postNextButtonAction handler at the apporprate times. # # Parameters: None # # Exceptions: N/A in this release # ###################################################################################################### sub NextButtonEventCycle { my ($self) = @_; if( dispatch( $self->{preNextButtonAction})) { return;} # advance the wizard page pointer and then adjust the navigation buttons. # readraw the frame when finished to get changes to take effect. $self->{wizardPagePtr}++; $self->{wizardPagePtr} = $#{$self->{wizardPageList}} if( $self->{wizardPagePtr} >= $#{ $self->{wizardPageList}}); if( $self->{nextButtonRef}->cget( -text) eq "Finish") { if( dispatch( $self->{finishButtonAction})) { return; } $self->CloseWindowEventCycle(); } $self->{prevButtonRef}->configure( -state => "normal"); $self->{nextButtonRef}->configure( -text => "Finish") if( $self->{wizardPagePtr} == $#{ $self->{wizardPageList}}); $self->redrawWizardPage; if( dispatch( $self->{postNextButtonAction})) { return; } } # end of sub NextButtonEventCycle ###################################################################################################### # # Method: PrevButtonEventCycle # # Description: Runs the complete view of the action handler cycle for the "{prePrevButtonAction})) { return; } # move the wizard pointer back one position and then adjust the navigation buttons # to reflect any state changes. Don't fall off end of page pointer $self->{wizardPagePtr}--; $self->{wizardPagePtr} = 0 if( $self->{wizardPagePtr} < 0); $self->{nextButtonRef}->configure( -text => "Next >"); $self->{prevButtonRef}->configure( -state => "disabled") if( $self->{wizardPagePtr} == 0); $self->redrawWizardPage; if( dispatch( $self->{postPrevButtonAction})) { return; } } # end of sub PrevButtonEventCycle ###################################################################################################### # # Method: HelpButtonEventCycle # # Description: This generates all of the events required when the Help button is clicked. This runs # through the pre event handler, the event handler and then the post event handler. If # no event handlers are defined, the method does nothing. # # Parameters: None # # Exceptions: N/A in this release # ###################################################################################################### sub HelpButtonEventCycle { my ($self) = @_; if( dispatch( $self->{preHelpButtonAction})) { return; } if( dispatch( $self->{helpButtonAction})) { return; } if( dispatch( $self->{postHelpButtonAction})) { return; } } # end of sub HelpButtonEventCycle ###################################################################################################### # # Method: CancelButtonEventCycle # # Description: This generates all of the necessary events reqruied for a good Wizard control when # the cancel button is clicked. This involves dispatching the preCancelButtonAction handler # and then activating the CloseWindowEventCycle to run through the process of closing # the window. # # Parameters: None # # Exceptions: N/A in this release # ###################################################################################################### sub CancelButtonEventCycle { my ($self, $hGUI) = @_; if( dispatch( $self->{preCancelButtonAction})) { return;} $self->CloseWindowEventCycle( $hGUI); } # end of sub CancelButtonEventCycle ###################################################################################################### # # Method: CloseWindowEventCycle # # Description: This generates all of the necessary events required for a good Wizard control when # the Window is about to be closed. This involves dispatching the preCloseWindowAction handler # and then destroying the reference to the Window control. # # Parameters: None # # Exceptions: N/A in this release # ###################################################################################################### sub CloseWindowEventCycle { my ($self, $hGUI) = @_; if( dispatch( $self->{preCloseWindowAction})) { return;} $hGUI->destroy; } # end of sub CloseWindowEventCycle ###################################################################################################### # # Method: redrawWizardPage # # Description: Update the wizard page panel by unpacking the existing controls and then repacking. # This allows updates to the page pointer to become visible. # # Parameters: None # # Exceptions: N/A in this release # ###################################################################################################### sub redrawWizardPage { my ( $self) = @_; $self->{wizardFrame}->packForget; $self->{wizardFrame} = $self->{wizardPageList}->[$self->{wizardPagePtr}]->()->pack( -side => "top"); } # end of sub redrawWizardPagePanel ############################# # End of Package Tk::Wizard # ############################# 1; __END__ =head1 NAME Tk::Wizard - GUI Wizard Framework =head1 SYNOPSIS use Tk::Wizard; my $wizard = new Wizard( new MainWindow); $wizard->addWizardPage( \&createPage1); $wizard->addWizardPage( \&createPage2); $wizard->addWizardPage( \&createPage3); $wizard->Show(); MainLoop; =head1 DESCRIPTION The Tk::Wizard class automates a large part of creating a program wizard to collect information and then perform some complex task based upon the answers and information gathered from the user. The wizard feel is largly based upon the Microsoft wizard style that appeared in products like Office 95 and Office 97. =head1 METHODS =head2 Tk::Wizard-Enew( @args) Creates a new instance of the Tk::Wizard object. The @args list is a hash list of the different options that can be specified for the class. These include: Parameters: -title => This is the title that will be displayed in the Windows title bar -filename => This is the path and name of a JPEG file that will be displayed on the right side of the screen. Action Event Handlers: -preNextButtonAction => This is a reference to a function that will be dispatched before the Next button is processed. -postNextButtonAction => This is a reference to a function that will be dispatched after the Next button is processed. -prePrevButtonAction => This is a reference to a function that will be dispatched before the Previous button is processed. -postPrevButtonAction => This is a reference to a function that will be dispatched after the Previous button is processed. -preHelpButtonAction => This is a reference to a function that will be dispatched before the Help button is processed. -helpButtonAction => This is a reference to a function that will be dispatched to handle the Help button action. -postHelpButtonAction => This is a reference to a function that will be dispatched after the Help button is processed. -preFinishButtonAction => This is a reference to a function that will be dispatched before the Finish button is processed. -finishButtonAction => This is a reference to a funciton that will be dispatched to handle the Finish button action. -postFinishButtonAction => This is a reference to a function that will be dispatched after the Finish button is processed. -preCancelButtonAction => This is a reference to a function that will be dispatched before the Cancel button is processed. -preCloseWindowAction => This is a reference to a funciton that will be dispatched before the window is issued a close command. See the section Action Event Handlers for more details. =head2 Tk::Wizard-EsetActionEventHandlers( @args) This method can be used to set or change the action event handler functions of a Tk::Wizard instance. The @args list accepts any Action Event Handler value pairs that could be passed into the new method. See the section Action Event Handlers for more details. =head2 Tk::Wizard-EsetParameters( @args) This method can be used to set or change the parameters of a Tk::Wizard instance. The @args list accepts any Parameter value pairs that could be passed into the new method. =head2 Tk::Wizard-EShow() This method must be dispatched before the Wizard will be displayed. =head2 Tk::Wizard-EaddWizardPage( $page) This method is used to add a Wizard page to the wizard. The $page parameter must be a Tk::Frame object. The pages are stored and will be displayed in the order that they were added to the Wizard control. =head2 Tk::Wizard-EcurrentPage() This returns the index of the page that is currently shown. Pages are indexed starting at 0 with the first page that is associated with the wizard through the addWizardPage method. =head2 Tk::Wizard-EwpFrame() This returns a wizard page frame. This method should be called when the user of the wizard wants to build the page specific controls. =head2 Tk::Wizard-Eparent This returns the parent Tk widget that was used to create the wizard and all of the controls. This is defined as part of the new method. =head1 Action Event Handlers The action event handler functions should not accept and parameters and return a TRUE or FALSE variable. If the remainder of the action should continue a TRUE value is returned. If a FALSE value is returned, execution of the event handler stops. This can be used to prompt the user if they want to quit and take action based upon the input. =head1 SUPPORT A list of the current development efforts can be found at http://www.uwm.edu/~dthable/perl =head1 COPYRIGHT Copyright (c) 2002 Daniel T. Hable Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. =cut