Subversion-Projekte lars-tiefland.prado

Revision

Blame | Letzte Änderung | Log anzeigen | RSS feed

<?php
/**
 * TWizard and the relevant class definitions.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @link http://www.pradosoft.com/
 * @copyright Copyright &copy; 2005-2008 PradoSoft
 * @license http://www.pradosoft.com/license/
 * @version $Id: TWizard.php 2541 2008-10-21 15:05:13Z qiang.xue $
 * @package System.Web.UI.WebControls
 */

Prado::using('System.Web.UI.WebControls.TMultiView');
Prado::using('System.Web.UI.WebControls.TPanel');
Prado::using('System.Web.UI.WebControls.TButton');
Prado::using('System.Web.UI.WebControls.TLinkButton');
Prado::using('System.Web.UI.WebControls.TImageButton');
Prado::using('System.Web.UI.WebControls.TDataList');
Prado::using('System.Web.UI.WebControls.TWizardNavigationButtonStyle');

/**
 * Class TWizard.
 *
 * TWizard splits a large form and presents the user with a series of smaller
 * forms to complete. TWizard is analogous to the installation wizard commonly
 * used to install software in Windows.
 *
 * The smaller forms are called wizard steps ({@link TWizardStep}, which can be accessed via
 * {@link getWizardSteps WizardSteps}. In template, wizard steps can be added
 * into a wizard using the following syntax,
 * <code>
 *   <com:TWizard>
 *      <com:TWizardStep Title="step 1">
 *          content in step 1, may contain other controls
 *      </com:TWizardStep>
 *      <com:TWizardStep Title="step 2">
 *          content in step 2, may contain other controls
 *      </com:TWizardStep>
 *   </com:TWizard>
 * </code>
 *
 * Each wizard step can be one of the following types:
 * - Start : the first step in the wizard.
 * - Step : the internal steps in the wizard.
 * - Finish : the last step that allows user interaction.
 * - Complete : the step that shows a summary to user (no interaction is allowed).
 * - Auto : the step type is determined by wizard automatically.
 * At any time, only one step is visible to end-users, which can be obtained
 * by {@link getActiveStep ActiveStep}. Its index in the step collection is given by
 * {@link getActiveStepIndex ActiveStepIndex}.
 *
 * Wizard content can be customized in many ways.
 *
 * The layout of a wizard consists of four parts: header, step content, navigation
 * and side bar. Their content are affected by the following properties, respectively,
 * - header: {@link setHeaderText HeaderText} and {@link setHeaderTemplate HeaderTemplate}.
 *   If both are present, the latter takes precedence.
 * - step: {@link getWizardSteps WizardSteps}.
 * - navigation: {@link setStartNavigationTemplate StartNavigationTemplate},
 *   {@link setStepNavigationTemplate StepNavigationTemplate},
 *   {@link setFinishNavigationTemplate FinishNavigationTemplate}.
 *   Default templates will be used if above templates are not set.
 * - side bar: {@link setSideBarTemplate SideBarTemplate}.
 *   A default template will be used if this template is not set.
 *   Its visibility is toggled by {@link setShowSideBar ShowSideBar}.
 *
 * The style of these wizard layout components can be customized via the following style properties,
 * - header: {@link getHeaderStyle HeaderStyle}.
 * - step: {@link getStepStyle StepStyle}.
 * - navigation: {@link getNavigationStyle NavigationStyle},
 *   {@link getStartNextButtonStyle StartNextButtonStyle},
 *   {@link getStepNextButtonStyle StepNextButtonStyle},
 *   {@link getStepPreviousButtonStyle StepPreviousButtonStyle},
 *   {@link getFinishPreviousButtonStyle FinishPreviousButtonStyle},
 *   {@link getFinishCompleteButtonStyle FinishCompleteButtonStyle},
 *   {@link getCancelButtonStyle CancelButtonStyle}.
 * - side bar: {@link getSideBarStyle SideBarStyle} and {@link getSideBarButtonStyle SideBarButtonStyle}.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TWizard.php 2541 2008-10-21 15:05:13Z qiang.xue $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TWizard extends TWebControl implements INamingContainer
{
        /**
         * Wizard step types.
         * @deprecated deprecated since version 3.0.4 (use TWizardStepType constants instead)
         */
        const ST_AUTO='Auto';
        const ST_START='Start';
        const ST_STEP='Step';
        const ST_FINISH='Finish';
        const ST_COMPLETE='Complete';
        /**
         * Navigation commands.
         */
        const CMD_PREVIOUS='PreviousStep';
        const CMD_NEXT='NextStep';
        const CMD_CANCEL='Cancel';
        const CMD_COMPLETE='Complete';
        const CMD_MOVETO='MoveTo';
        /**
         * Side bar button ID
         */
        const ID_SIDEBAR_BUTTON='SideBarButton';
        /**
         * Side bar data list
         */
        const ID_SIDEBAR_LIST='SideBarList';

        /**
         * @var TMultiView multiview that contains the wizard steps
         */
        private $_multiView=null;
        /**
         * @var mixed navigation template for the start step.
         */
        private $_startNavigationTemplate=null;
        /**
         * @var mixed navigation template for internal steps.
         */
        private $_stepNavigationTemplate=null;
        /**
         * @var mixed navigation template for the finish step.
         */
        private $_finishNavigationTemplate=null;
        /**
         * @var mixed template for wizard header.
         */
        private $_headerTemplate=null;
        /**
         * @var mixed template for the side bar.
         */
        private $_sideBarTemplate=null;
        /**
         * @var TWizardStepCollection
         */
        private $_wizardSteps=null;
        /**
         * @var TPanel container of the wizard header
         */
        private $_header;
        /**
         * @var TPanel container of the wizard step content
         */
        private $_stepContent;
        /**
         * @var TPanel container of the wizard side bar
         */
        private $_sideBar;
        /**
         * @var TPanel navigation panel
         */
        private $_navigation;
        /**
         * @var TWizardNavigationContainer container of the start navigation
         */
        private $_startNavigation;
        /**
         * @var TWizardNavigationContainer container of the step navigation
         */
        private $_stepNavigation;
        /**
         * @var TWizardNavigationContainer container of the finish navigation
         */
        private $_finishNavigation;
        /**
         * @var boolean whether ActiveStepIndex was already set
         */
        private $_activeStepIndexSet=false;
        /**
         * @var TDataList side bar data list.
         */
        private $_sideBarDataList;
        /**
         * @var boolean whether navigation should be cancelled (a status set in OnSideBarButtonClick)
         */
        private $_cancelNavigation=false;

        /**
         * @return string tag name for the wizard
         */
        protected function getTagName()
        {
                return 'div';
        }

        /**
         * Adds {@link TWizardStep} objects into step collection.
         * This method overrides the parent implementation and is
         * invoked when template is being instantiated.
         * @param mixed object instantiated in template
         */
        public function addParsedObject($object)
        {
                if($object instanceof TWizardStep)
                        $this->getWizardSteps()->add($object);
        }

        /**
         * @return TWizardStep the currently active wizard step
         */
        public function getActiveStep()
        {
                return $this->getMultiView()->getActiveView();
        }

        /**
         * @param TWizardStep step to be activated
         * @throws TInvalidOperationException if the step is not in the wizard step collection
         */
        public function setActiveStep($step)
        {
                if(($index=$this->getWizardSteps()->indexOf($step))<0)
                        throw new TInvalidOperationException('wizard_step_invalid');
                $this->setActiveStepIndex($index);
        }

        /**
         * @return integer the zero-based index of the active wizard step
         */
        public function getActiveStepIndex()
        {
                return $this->getMultiView()->getActiveViewIndex();
        }

        /**
         * @param integer the zero-based index of the wizard step to be activated
         */
        public function setActiveStepIndex($value)
        {
                $value=TPropertyValue::ensureInteger($value);
                $multiView=$this->getMultiView();
                if($multiView->getActiveViewIndex()!==$value)
                {
                        $multiView->setActiveViewIndex($value);
                        $this->_activeStepIndexSet=true;
                        if($this->_sideBarDataList!==null && $this->getSideBarTemplate()!==null)
                        {
                                $this->_sideBarDataList->setSelectedItemIndex($this->getActiveStepIndex());
                                $this->_sideBarDataList->dataBind();
                        }
                }
        }

        /**
         * @return TWizardStepCollection collection of wizard steps
         */
        public function getWizardSteps()
        {
                if($this->_wizardSteps===null)
                        $this->_wizardSteps=new TWizardStepCollection($this);
                return $this->_wizardSteps;
        }

        /**
         * @return boolean whether to display a cancel button in each wizard step. Defaults to false.
         */
        public function getShowCancelButton()
        {
                return $this->getViewState('ShowCancelButton',false);
        }

        /**
         * @param boolean whether to display a cancel button in each wizard step.
         */
        public function setShowCancelButton($value)
        {
                $this->setViewState('ShowCancelButton',TPropertyValue::ensureBoolean($value),false);
        }

        /**
         * @return boolean whether to display a side bar that contains links to wizard steps. Defaults to true.
         */
        public function getShowSideBar()
        {
                return $this->getViewState('ShowSideBar',true);
        }

        /**
         * @param boolean whether to display a side bar that contains links to wizard steps.
         */
        public function setShowSideBar($value)
        {
                $this->setViewState('ShowSideBar',TPropertyValue::ensureBoolean($value),true);
                $this->requiresControlsRecreation();
        }

        /**
         * @return ITemplate navigation template for the start step. Defaults to null.
         */
        public function getStartNavigationTemplate()
        {
                return $this->_startNavigationTemplate;
        }

        /**
         * @param ITemplate navigation template for the start step.
         */
        public function setStartNavigationTemplate($value)
        {
                $this->_startNavigationTemplate=$value;
                $this->requiresControlsRecreation();
        }

        /**
         * @return ITemplate navigation template for internal steps. Defaults to null.
         */
        public function getStepNavigationTemplate()
        {
                return $this->_stepNavigationTemplate;
        }

        /**
         * @param ITemplate navigation template for internal steps.
         */
        public function setStepNavigationTemplate($value)
        {
                $this->_stepNavigationTemplate=$value;
                $this->requiresControlsRecreation();
        }

        /**
         * @return ITemplate navigation template for the finish step. Defaults to null.
         */
        public function getFinishNavigationTemplate()
        {
                return $this->_finishNavigationTemplate;
        }

        /**
         * @param ITemplate navigation template for the finish step.
         */
        public function setFinishNavigationTemplate($value)
        {
                $this->_finishNavigationTemplate=$value;
                $this->requiresControlsRecreation();
        }

        /**
         * @return ITemplate template for wizard header. Defaults to null.
         */
        public function getHeaderTemplate()
        {
                return $this->_headerTemplate;
        }

        /**
         * @param ITemplate template for wizard header.
         */
        public function setHeaderTemplate($value)
        {
                $this->_headerTemplate=$value;
                $this->requiresControlsRecreation();
        }

        /**
         * @return ITemplate template for the side bar. Defaults to null.
         */
        public function getSideBarTemplate()
        {
                return $this->_sideBarTemplate;
        }

        /**
         * @param ITemplate template for the side bar.
         */
        public function setSideBarTemplate($value)
        {
                $this->_sideBarTemplate=$value;
                $this->requiresControlsRecreation();
        }

        /**
         * @return string header text. Defaults to ''.
         */
        public function getHeaderText()
        {
                return $this->getViewState('HeaderText','');
        }

        /**
         * @param string header text.
         */
        public function setHeaderText($value)
        {
                $this->setViewState('HeaderText',TPropertyValue::ensureString($value),'');
        }

        /**
         * @return string the URL that the browser will be redirected to if the cancel button in the
         * wizard is clicked. Defaults to ''.
         */
        public function getCancelDestinationUrl()
        {
                return $this->getViewState('CancelDestinationUrl','');
        }

        /**
         * @param string the URL that the browser will be redirected to if the cancel button in the
         * wizard is clicked.
         */
        public function setCancelDestinationUrl($value)
        {
                $this->setViewState('CancelDestinationUrl',TPropertyValue::ensureString($value),'');
        }

        /**
         * @return string the URL that the browser will be redirected to if the wizard finishes.
         * Defaults to ''.
         */
        public function getFinishDestinationUrl()
        {
                return $this->getViewState('FinishDestinationUrl','');
        }

        /**
         * @param string the URL that the browser will be redirected to if the wizard finishes.
         */
        public function setFinishDestinationUrl($value)
        {
                $this->setViewState('FinishDestinationUrl',TPropertyValue::ensureString($value),'');
        }

        /**
         * @return TStyle the style for the buttons displayed in the side bar.
         */
        public function getSideBarButtonStyle()
        {
                if(($style=$this->getViewState('SideBarButtonStyle',null))===null)
                {
                        $style=new TStyle;
                        $this->setViewState('SideBarButtonStyle',$style,null);
                }
                return $style;
        }

        /**
         * @return TStyle the style common for all navigation buttons.
         */
        public function getNavigationButtonStyle()
        {
                if(($style=$this->getViewState('NavigationButtonStyle',null))===null)
                {
                        $style=new TStyle;
                        $this->setViewState('NavigationButtonStyle',$style,null);
                }
                return $style;
        }

        /**
         * @return TWizardNavigationButtonStyle the style for the next button in the start wizard step.
         */
        public function getStartNextButtonStyle()
        {
                if(($style=$this->getViewState('StartNextButtonStyle',null))===null)
                {
                        $style=new TWizardNavigationButtonStyle;
                        $style->setButtonText('Next');
                        $this->setViewState('StartNextButtonStyle',$style,null);
                }
                return $style;
        }

        /**
         * @return TWizardNavigationButtonStyle the style for the next button in each internal wizard step.
         */
        public function getStepNextButtonStyle()
        {
                if(($style=$this->getViewState('StepNextButtonStyle',null))===null)
                {
                        $style=new TWizardNavigationButtonStyle;
                        $style->setButtonText('Next');
                        $this->setViewState('StepNextButtonStyle',$style,null);
                }
                return $style;
        }

        /**
         * @return TWizardNavigationButtonStyle the style for the previous button in the start wizard step.
         */
        public function getStepPreviousButtonStyle()
        {
                if(($style=$this->getViewState('StepPreviousButtonStyle',null))===null)
                {
                        $style=new TWizardNavigationButtonStyle;
                        $style->setButtonText('Previous');
                        $this->setViewState('StepPreviousButtonStyle',$style,null);
                }
                return $style;
        }

        /**
         * @return TWizardNavigationButtonStyle the style for the complete button in the finish wizard step.
         */
        public function getFinishCompleteButtonStyle()
        {
                if(($style=$this->getViewState('FinishCompleteButtonStyle',null))===null)
                {
                        $style=new TWizardNavigationButtonStyle;
                        $style->setButtonText('Complete');
                        $this->setViewState('FinishCompleteButtonStyle',$style,null);
                }
                return $style;
        }

        /**
         * @return TWizardNavigationButtonStyle the style for the previous button in the start wizard step.
         */
        public function getFinishPreviousButtonStyle()
        {
                if(($style=$this->getViewState('FinishPreviousButtonStyle',null))===null)
                {
                        $style=new TWizardNavigationButtonStyle;
                        $style->setButtonText('Previous');
                        $this->setViewState('FinishPreviousButtonStyle',$style,null);
                }
                return $style;
        }

        /**
         * @return TWizardNavigationButtonStyle the style for the cancel button
         */
        public function getCancelButtonStyle()
        {
                if(($style=$this->getViewState('CancelButtonStyle',null))===null)
                {
                        $style=new TWizardNavigationButtonStyle;
                        $style->setButtonText('Cancel');
                        $this->setViewState('CancelButtonStyle',$style,null);
                }
                return $style;
        }

        /**
         * @return TPanelStyle the style for the side bar.
         */
        public function getSideBarStyle()
        {
                if(($style=$this->getViewState('SideBarStyle',null))===null)
                {
                        $style=new TPanelStyle;
                        $this->setViewState('SideBarStyle',$style,null);
                }
                return $style;
        }

        /**
         * @return TPanelStyle the style for the header.
         */
        public function getHeaderStyle()
        {
                if(($style=$this->getViewState('HeaderStyle',null))===null)
                {
                        $style=new TPanelStyle;
                        $this->setViewState('HeaderStyle',$style,null);
                }
                return $style;
        }

        /**
         * @return TPanelStyle the style for each internal wizard step.
         */
        public function getStepStyle()
        {
                if(($style=$this->getViewState('StepStyle',null))===null)
                {
                        $style=new TPanelStyle;
                        $this->setViewState('StepStyle',$style,null);
                }
                return $style;
        }

        /**
         * @return TPanelStyle the style for the navigation panel.
         */
        public function getNavigationStyle()
        {
                if(($style=$this->getViewState('NavigationStyle',null))===null)
                {
                        $style=new TPanelStyle;
                        $this->setViewState('NavigationStyle',$style,null);
                }
                return $style;
        }

        /**
         * @return boolean whether to use default layout to arrange side bar and the rest wizard components. Defaults to true.
         */
        public function getUseDefaultLayout()
        {
                return $this->getViewState('UseDefaultLayout',true);
        }

        /**
         * @param boolean whether to use default layout to arrange side bar and the rest wizard components.
         * If true, an HTML table will be used which places the side bar in the left cell
         * while the rest components in the right cell.
         */
        public function setUseDefaultLayout($value)
        {
                $this->setViewState('UseDefaultLayout',TPropertyValue::ensureBoolean($value),true);
        }

        /**
         * @return TPanel container of the wizard header
         */
        public function getHeader()
        {
                return $this->_header;
        }

        /**
         * @return TPanel container of the wizard step content
         */
        public function getStepContent()
        {
                return $this->_stepContent;
        }

        /**
         * @return TPanel container of the wizard side bar
         */
        public function getSideBar()
        {
                return $this->_sideBar;
        }

        /**
         * @return TWizardNavigationContainer container of the start navigation
         */
        public function getStartNavigation()
        {
                return $this->_startNavigation;
        }

        /**
         * @return TWizardNavigationContainer container of the step navigation
         */
        public function getStepNavigation()
        {
                return $this->_stepNavigation;
        }

        /**
         * @return TWizardNavigationContainer container of the finish navigation
         */
        public function getFinishNavigation()
        {
                return $this->_finishNavigation;
        }

        /**
         * Raises <b>OnActiveStepChanged</b> event.
         * This event is raised when the current visible step is changed in the
         * wizard.
         * @param TEventParameter event parameter
         */
        public function onActiveStepChanged($param)
        {
                $this->raiseEvent('OnActiveStepChanged',$this,$param);
        }

        /**
         * Raises <b>OnCancelButtonClick</b> event.
         * This event is raised when a cancel navigation button is clicked in the
         * current active step.
         * @param TEventParameter event parameter
         */
        public function onCancelButtonClick($param)
        {
                $this->raiseEvent('OnCancelButtonClick',$this,$param);
                if(($url=$this->getCancelDestinationUrl())!=='')
                        $this->getResponse()->redirect($url);
        }

        /**
         * Raises <b>OnCompleteButtonClick</b> event.
         * This event is raised when a finish navigation button is clicked in the
         * current active step.
         * @param TWizardNavigationEventParameter event parameter
         */
        public function onCompleteButtonClick($param)
        {
                $this->raiseEvent('OnCompleteButtonClick',$this,$param);
                if(($url=$this->getFinishDestinationUrl())!=='')
                        $this->getResponse()->redirect($url);
        }

        /**
         * Raises <b>OnNextButtonClick</b> event.
         * This event is raised when a next navigation button is clicked in the
         * current active step.
         * @param TWizardNavigationEventParameter event parameter
         */
        public function onNextButtonClick($param)
        {
                $this->raiseEvent('OnNextButtonClick',$this,$param);
        }

        /**
         * Raises <b>OnPreviousButtonClick</b> event.
         * This event is raised when a previous navigation button is clicked in the
         * current active step.
         * @param TWizardNavigationEventParameter event parameter
         */
        public function onPreviousButtonClick($param)
        {
                $this->raiseEvent('OnPreviousButtonClick',$this,$param);
        }

        /**
         * Raises <b>OnSideBarButtonClick</b> event.
         * This event is raised when a link button in the side bar is clicked.
         * @param TWizardNavigationEventParameter event parameter
         */
        public function onSideBarButtonClick($param)
        {
                $this->raiseEvent('OnSideBarButtonClick',$this,$param);
        }

        /**
         * Returns the multiview that holds the wizard steps.
         * This method should only be used by control developers.
         * @return TMultiView the multiview holding wizard steps
         */
        public function getMultiView()
        {
                if($this->_multiView===null)
                {
                        $this->_multiView=new TMultiView;
                        $this->_multiView->setID('WizardMultiView');
                        $this->_multiView->attachEventHandler('OnActiveViewChanged',array($this,'onActiveStepChanged'));
                        $this->_multiView->ignoreBubbleEvents();
                }
                return $this->_multiView;
        }

        /**
         * Adds a wizard step to the multiview.
         * This method should only be used by control developers.
         * It is invoked when a step is added into the step collection of the wizard.
         * @param TWizardStep wizard step to be added into multiview.
         */
        public function addedWizardStep($step)
        {
                if(($wizard=$step->getWizard())!==null)
                        $wizard->getWizardSteps()->remove($step);
                $step->setWizard($this);
                $this->wizardStepsChanged();
        }

        /**
         * Removes a wizard step from the multiview.
         * This method should only be used by control developers.
         * It is invoked when a step is removed from the step collection of the wizard.
         * @param TWizardStep wizard step to be removed from multiview.
         */
        public function removedWizardStep($step)
        {
                $step->setWizard(null);
                $this->wizardStepsChanged();
        }

        /**
         * Creates the child controls of the wizard.
         * This method overrides the parent implementation.
         * @param TEventParameter event parameter
         */
        public function onInit($param)
        {
                parent::onInit($param);
                $this->ensureChildControls();
                if($this->getActiveStepIndex()<0 && $this->getWizardSteps()->getCount()>0)
                        $this->setActiveStepIndex(0);
        }

        /**
         * Saves the current active step index into history.
         * This method is invoked by the framework when the control state is being saved.
         */
        public function saveState()
        {
                $index=$this->getActiveStepIndex();
                $history=$this->getHistory();
                if(!$history->getCount() || $history->peek()!==$index)
                        $history->push($index);
        }

        /**
         * Indicates the wizard needs to recreate all child controls.
         */
        protected function requiresControlsRecreation()
        {
                if($this->getChildControlsCreated())
                        $this->setChildControlsCreated(false);
        }

        /**
         * Renders the wizard.
         * @param THtmlWriter
         */
        public function render($writer)
        {
                $this->ensureChildControls();
                if($this->getHasControls())
                {
                        if($this->getUseDefaultLayout())
                        {
                                $this->applyControlProperties();
                                $this->renderBeginTag($writer);
                                $writer->write("\n<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" height=\"100%\" width=\"100%\">\n<tr><td width=\"1\" valign=\"top\">\n");
                                $this->_sideBar->renderControl($writer);
                                $writer->write("\n</td><td valign=\"top\">\n");
                                $this->_header->renderControl($writer);
                                $this->_stepContent->renderControl($writer);
                                $this->_navigation->renderControl($writer);
                                $writer->write("\n</td></tr></table>\n");
                                $this->renderEndTag($writer);
                        }
                        else
                        {
                                $this->applyControlProperties();
                                $this->renderBeginTag($writer);
                                $this->_sideBar->renderControl($writer);
                                $this->_header->renderControl($writer);
                                $this->_stepContent->renderControl($writer);
                                $this->_navigation->renderControl($writer);
                                $this->renderEndTag($writer);
                        }
                }
        }

        /**
         * Applies various properties to the components of wizard
         */
        protected function applyControlProperties()
        {
                $this->applyHeaderProperties();
                $this->applySideBarProperties();
                $this->applyStepContentProperties();
                $this->applyNavigationProperties();
        }

        /**
         * Applies properties to the wizard header
         */
        protected function applyHeaderProperties()
        {
                if(($style=$this->getViewState('HeaderStyle',null))!==null)
                        $this->_header->getStyle()->mergeWith($style);
                if($this->getHeaderTemplate()===null)
                {
                        $this->_header->getControls()->clear();
                        $this->_header->getControls()->add($this->getHeaderText());
                }
        }

        /**
         * Applies properties to the wizard sidebar
         */
        protected function applySideBarProperties()
        {
                $this->_sideBar->setVisible($this->getShowSideBar());
                if($this->_sideBarDataList!==null && $this->getShowSideBar())
                {
                        $this->_sideBarDataList->setDataSource($this->getWizardSteps());
                        $this->_sideBarDataList->setSelectedItemIndex($this->getActiveStepIndex());
                        $this->_sideBarDataList->dataBind();
                        if(($style=$this->getViewState('SideBarButtonStyle',null))!==null)
                        {
                                foreach($this->_sideBarDataList->getItems() as $item)
                                {
                                        if(($button=$item->findControl('SideBarButton'))!==null)
                                                $button->getStyle()->mergeWith($style);
                                }
                        }
                }
                if(($style=$this->getViewState('SideBarStyle',null))!==null)
                        $this->_sideBar->getStyle()->mergeWith($style);
        }

        /**
         * Applies properties to the wizard step content
         */
        protected function applyStepContentProperties()
        {
                if(($style=$this->getViewState('StepStyle',null))!==null)
                        $this->_stepContent->getStyle()->mergeWith($style);
        }

        /**
         * Apply properties to various navigation panels.
         */
        protected function applyNavigationProperties()
        {
                $wizardSteps=$this->getWizardSteps();
                $activeStep=$this->getActiveStep();
                $activeStepIndex=$this->getActiveStepIndex();

                if(!$this->_navigation)
                        return;
                else if($activeStepIndex<0 || $activeStepIndex>=$wizardSteps->getCount())
                {
                        $this->_navigation->setVisible(false);
                        return;
                }

                // set visibility of different types of navigation panel
                $showStandard=true;
                foreach($wizardSteps as $step)
                {
                        if(($step instanceof TTemplatedWizardStep) && ($container=$step->getNavigationContainer())!==null)
                        {
                                if($activeStep===$step)
                                {
                                        $container->setVisible(true);
                                        $showStandard=false;
                                }
                                else
                                        $container->setVisible(false);
                        }
                }
                $activeStepType=$this->getStepType($activeStep);
                if($activeStepType===TWizardStepType::Complete)
                {
                        $this->_sideBar->setVisible(false);
                        $this->_header->setVisible(false);
                }
                $this->_startNavigation->setVisible($showStandard && $activeStepType===self::ST_START);
                $this->_stepNavigation->setVisible($showStandard && $activeStepType===self::ST_STEP);
                $this->_finishNavigation->setVisible($showStandard && $activeStepType===self::ST_FINISH);

                if(($navigationStyle=$this->getViewState('NavigationStyle',null))!==null)
                        $this->_navigation->getStyle()->mergeWith($navigationStyle);

                $displayCancelButton=$this->getShowCancelButton();
                $cancelButtonStyle=$this->getCancelButtonStyle();
                $buttonStyle=$this->getViewState('NavigationButtonStyle',null);
                if($buttonStyle!==null)
                        $cancelButtonStyle->mergeWith($buttonStyle);

                // apply styles to start navigation buttons
                if(($cancelButton=$this->_startNavigation->getCancelButton())!==null)
                {
                        $cancelButton->setVisible($displayCancelButton);
                        $cancelButtonStyle->apply($cancelButton);
                }
                if(($button=$this->_startNavigation->getNextButton())!==null)
                {
                        $button->setVisible(true);
                        $style=$this->getStartNextButtonStyle();
                        if($buttonStyle!==null)
                                $style->mergeWith($buttonStyle);
                        $style->apply($button);
                }

                // apply styles to finish navigation buttons
                if(($cancelButton=$this->_finishNavigation->getCancelButton())!==null)
                {
                        $cancelButton->setVisible($displayCancelButton);
                        $cancelButtonStyle->apply($cancelButton);
                }
                if(($button=$this->_finishNavigation->getPreviousButton())!==null)
                {
                        $button->setVisible($this->allowNavigationToPreviousStep());
                        $style=$this->getFinishPreviousButtonStyle();
                        if($buttonStyle!==null)
                                $style->mergeWith($buttonStyle);
                        $style->apply($button);
                }
                if(($button=$this->_finishNavigation->getCompleteButton())!==null)
                {
                        $button->setVisible(true);
                        $style=$this->getFinishCompleteButtonStyle();
                        if($buttonStyle!==null)
                                $style->mergeWith($buttonStyle);
                        $style->apply($button);
                }

                // apply styles to step navigation buttons
                if(($cancelButton=$this->_stepNavigation->getCancelButton())!==null)
                {
                        $cancelButton->setVisible($displayCancelButton);
                        $cancelButtonStyle->apply($cancelButton);
                }
                if(($button=$this->_stepNavigation->getPreviousButton())!==null)
                {
                        $button->setVisible($this->allowNavigationToPreviousStep());
                        $style=$this->getStepPreviousButtonStyle();
                        if($buttonStyle!==null)
                                $style->mergeWith($buttonStyle);
                        $style->apply($button);
                }
                if(($button=$this->_stepNavigation->getNextButton())!==null)
                {
                        $button->setVisible(true);
                        $style=$this->getStepNextButtonStyle();
                        if($buttonStyle!==null)
                                $style->mergeWith($buttonStyle);
                        $style->apply($button);
                }
        }

        /**
         * @return TStack history containing step indexes that were navigated before
         */
        protected function getHistory()
        {
                if(($history=$this->getControlState('History',null))===null)
                {
                        $history=new TStack;
                        $this->setControlState('History',$history);
                }
                return $history;
        }

        /**
         * Determines the type of the specified wizard step.
         * @param TWizardStep
         * @return TWizardStepType type of the step
         */
        protected function getStepType($wizardStep)
        {
                if(($type=$wizardStep->getStepType())===TWizardStepType::Auto)
                {
                        $steps=$this->getWizardSteps();
                        if(($index=$steps->indexOf($wizardStep))>=0)
                        {
                                $stepCount=$steps->getCount();
                                if($stepCount===1 || ($index<$stepCount-1 && $steps->itemAt($index+1)->getStepType()===TWizardStepType::Complete))
                                        return TWizardStepType::Finish;
                                else if($index===0)
                                        return TWizardStepType::Start;
                                else if($index===$stepCount-1)
                                        return TWizardStepType::Finish;
                                else
                                        return TWizardStepType::Step;
                        }
                        else
                                return $type;
                }
                else
                        return $type;
        }

        /**
         * Clears up everything within the wizard.
         */
        protected function reset()
        {
                $this->getControls()->clear();
                $this->_header=null;
                $this->_stepContent=null;
                $this->_sideBar=null;
                $this->_sideBarDataList=null;
                $this->_navigation=null;
                $this->_startNavigation=null;
                $this->_stepNavigation=null;
                $this->_finishNavigation=null;
        }

        /**
         * Creates child controls within the wizard
         */
        public function createChildControls()
        {
                $this->reset();
                $this->createSideBar();
                $this->createHeader();
                $this->createStepContent();
                $this->createNavigation();
//              $this->clearChildState();
        }

        /**
         * Creates the wizard header.
         */
        protected function createHeader()
        {
                $this->_header=new TPanel;
                if(($template=$this->getHeaderTemplate())!==null)
                        $template->instantiateIn($this->_header);
                else
                        $this->_header->getControls()->add($this->getHeaderText());
                $this->getControls()->add($this->_header);
        }

        /**
         * Creates the wizard side bar
         */
        protected function createSideBar()
        {
                if($this->getShowSideBar())
                {
                        if(($template=$this->getSideBarTemplate())===null)
                                $template=new TWizardSideBarTemplate;
                        $this->_sideBar=new TPanel;
                        $template->instantiateIn($this->_sideBar);
                        $this->getControls()->add($this->_sideBar);

                        if(($this->_sideBarDataList=$this->_sideBar->findControl(self::ID_SIDEBAR_LIST))!==null)
                        {
                                $this->_sideBarDataList->attachEventHandler('OnItemCommand',array($this,'dataListItemCommand'));
                                $this->_sideBarDataList->attachEventHandler('OnItemDataBound',array($this,'dataListItemDataBound'));
                                $this->_sideBarDataList->setDataSource($this->getWizardSteps());
                                $this->_sideBarDataList->setSelectedItemIndex($this->getActiveStepIndex());
                                $this->_sideBarDataList->dataBind();
                        }
                }
                else
                {
                        $this->_sideBar=new TPanel;
                        $this->getControls()->add($this->_sideBar);
                }
        }

        /**
         * Event handler for sidebar datalist's OnItemCommand event.
         * This method is used internally by wizard. It mainly
         * sets the active step index according to the button clicked in the sidebar.
         * @param mixed sender of the event
         * @param TDataListCommandEventParameter
         */
        public function dataListItemCommand($sender,$param)
        {
                $item=$param->getItem();
                if($param->getCommandName()===self::CMD_MOVETO)
                {
                        $stepIndex=$this->getActiveStepIndex();
                        $newStepIndex=TPropertyValue::ensureInteger($param->getCommandParameter());
                        $navParam=new TWizardNavigationEventParameter($stepIndex);
                        $navParam->setNextStepIndex($newStepIndex);

                        // if the button clicked causes validation which fails,
                        // by default we will cancel navigation to the new step
                        $button=$param->getCommandSource();
                        if(($button instanceof IButtonControl) && $button->getCausesValidation() && ($page=$this->getPage())!==null && !$page->getIsValid())
                                $navParam->setCancelNavigation(true);

                        $this->_activeStepIndexSet=false;
                        $this->onSideBarButtonClick($navParam);
                        $this->_cancelNavigation=$navParam->getCancelNavigation();
                        if(!$this->_cancelNavigation)
                        {
                                if(!$this->_activeStepIndexSet && $this->allowNavigationToStep($newStepIndex))
                                        $this->setActiveStepIndex($newStepIndex);
                        }
                        else
                                $this->setActiveStepIndex($stepIndex);
                }
        }

        /**
         * Event handler for sidebar datalist's OnItemDataBound event.
         * This method is used internally by wizard. It mainly configures
         * the buttons in the sidebar datalist.
         * @param mixed sender of the event
         * @param TDataListItemEventParameter
         */
        public function dataListItemDataBound($sender,$param)
        {
                $item=$param->getItem();
                $itemType=$item->getItemType();
                if($itemType==='Item' || $itemType==='AlternatingItem' || $itemType==='SelectedItem' || $itemType==='EditItem')
                {
                        if(($button=$item->findControl(self::ID_SIDEBAR_BUTTON))!==null)
                        {
                                $step=$item->getData();
                                if(($this->getStepType($step)===TWizardStepType::Complete))
                                        $button->setEnabled(false);
                                if(($title=$step->getTitle())!=='')
                                        $button->setText($title);
                                else
                                        $button->setText($step->getID(false));
                                $index=$this->getWizardSteps()->indexOf($step);
                                $button->setCommandName(self::CMD_MOVETO);
                                $button->setCommandParameter("$index");
                        }
                }
        }

        /**
         * Creates wizard step content.
         */
        protected function createStepContent()
        {
                foreach($this->getWizardSteps() as $step)
                {
                        if($step instanceof TTemplatedWizardStep)
                                $step->ensureChildControls();
                }
                $multiView=$this->getMultiView();
                $this->_stepContent=new TPanel;
                $this->_stepContent->getControls()->add($multiView);
                $this->getControls()->add($this->_stepContent);
                if($multiView->getViews()->getCount())
                        $multiView->setActiveViewIndex(0);
        }

        /**
         * Creates navigation panel.
         */
        protected function createNavigation()
        {
                $this->_navigation=new TPanel;
                $this->getControls()->add($this->_navigation);
                $controls=$this->_navigation->getControls();
                foreach($this->getWizardSteps() as $step)
                {
                        if($step instanceof TTemplatedWizardStep)
                        {
                                $step->instantiateNavigationTemplate();
                                if(($panel=$step->getNavigationContainer())!==null)
                                        $controls->add($panel);
                        }
                }
                $this->_startNavigation=$this->createStartNavigation();
                $controls->add($this->_startNavigation);
                $this->_stepNavigation=$this->createStepNavigation();
                $controls->add($this->_stepNavigation);
                $this->_finishNavigation=$this->createFinishNavigation();
                $controls->add($this->_finishNavigation);
        }

        /**
         * Creates start navigation panel.
         */
        protected function createStartNavigation()
        {
                if(($template=$this->getStartNavigationTemplate())===null)
                        $template=new TWizardStartNavigationTemplate($this);
                $navigation=new TWizardNavigationContainer;
                $template->instantiateIn($navigation);
                return $navigation;
        }

        /**
         * Creates step navigation panel.
         */
        protected function createStepNavigation()
        {
                if(($template=$this->getStepNavigationTemplate())===null)
                        $template=new TWizardStepNavigationTemplate($this);
                $navigation=new TWizardNavigationContainer;
                $template->instantiateIn($navigation);
                return $navigation;
        }

        /**
         * Creates finish navigation panel.
         */
        protected function createFinishNavigation()
        {
                if(($template=$this->getFinishNavigationTemplate())===null)
                        $template=new TWizardFinishNavigationTemplate($this);
                $navigation=new TWizardNavigationContainer;
                $template->instantiateIn($navigation);
                return $navigation;
        }

        /**
         * Updates the sidebar datalist if any.
         * This method is invoked when any wizard step is changed.
         */
        public function wizardStepsChanged()
        {
                if($this->_sideBarDataList!==null)
                {
                        $this->_sideBarDataList->setDataSource($this->getWizardSteps());
                        $this->_sideBarDataList->setSelectedItemIndex($this->getActiveStepIndex());
                        $this->_sideBarDataList->dataBind();
                }
        }

        /**
         * Determines the index of the previous step based on history.
         * @param boolean whether the first item in the history stack should be popped
         * up after calling this method.
         */
        protected function getPreviousStepIndex($popStack)
        {
                $history=$this->getHistory();
                if($history->getCount()>=0)
                {
                        $activeStepIndex=$this->getActiveStepIndex();
                        $previousStepIndex=-1;
                        if($popStack)
                        {
                                $previousStepIndex=$history->pop();
                                if($activeStepIndex===$previousStepIndex && $history->getCount()>0)
                                        $previousStepIndex=$history->pop();
                        }
                        else
                        {
                                $previousStepIndex=$history->peek();
                                if($activeStepIndex===$previousStepIndex && $history->getCount()>1)
                                {
                                        $saveIndex=$history->pop();
                                        $previousStepIndex=$history->peek();
                                        $history->push($saveIndex);
                                }
                        }
                        return $activeStepIndex===$previousStepIndex ? -1 : $previousStepIndex;
                }
                else
                        return -1;
        }

        /**
         * @return boolean whether navigation to the previous step is allowed
         */
        protected function allowNavigationToPreviousStep()
        {
                if(($index=$this->getPreviousStepIndex(false))!==-1)
                        return $this->getWizardSteps()->itemAt($index)->getAllowReturn();
                else
                        return false;
        }

        /**
         * @param integer index of the step
         * @return boolean whether navigation to the specified step is allowed
         */
        protected function allowNavigationToStep($index)
        {
                if($this->getHistory()->contains($index))
                        return $this->getWizardSteps()->itemAt($index)->getAllowReturn();
                else
                        return true;
        }

        /**
         * Handles bubbled events.
         * This method mainly translate certain command events into
         * wizard-specific events.
         * @param mixed sender of the original command event
         * @param TEventParameter event parameter
         * @throws TInvalidDataValueException if a navigation command is associated with an invalid parameter
         */
        public function bubbleEvent($sender,$param)
        {
                if($param instanceof TCommandEventParameter)
                {
                        $command=$param->getCommandName();
                        if(strcasecmp($command,self::CMD_CANCEL)===0)
                        {
                                $this->onCancelButtonClick($param);
                                return true;
                        }

                        $type=$this->getStepType($this->getActiveStep());
                        $index=$this->getActiveStepIndex();
                        $navParam=new TWizardNavigationEventParameter($index);
                        if(($sender instanceof IButtonControl) && $sender->getCausesValidation() && ($page=$this->getPage())!==null && !$page->getIsValid())
                                $navParam->setCancelNavigation(true);

                        $handled=false;
                        $movePrev=false;
                        $this->_activeStepIndexSet=false;

                        if(strcasecmp($command,self::CMD_NEXT)===0)
                        {
                                if($type!==self::ST_START && $type!==self::ST_STEP)
                                        throw new TInvalidDataValueException('wizard_command_invalid',self::CMD_NEXT);
                                if($index<$this->getWizardSteps()->getCount()-1)
                                        $navParam->setNextStepIndex($index+1);
                                $this->onNextButtonClick($navParam);
                                $handled=true;
                        }
                        else if(strcasecmp($command,self::CMD_PREVIOUS)===0)
                        {
                                if($type!==self::ST_FINISH && $type!==self::ST_STEP)
                                        throw new TInvalidDataValueException('wizard_command_invalid',self::CMD_PREVIOUS);
                                $movePrev=true;
                                if(($prevIndex=$this->getPreviousStepIndex(false))>=0)
                                        $navParam->setNextStepIndex($prevIndex);
                                $this->onPreviousButtonClick($navParam);
                                $handled=true;
                        }
                        else if(strcasecmp($command,self::CMD_COMPLETE)===0)
                        {
                                if($type!==self::ST_FINISH)
                                        throw new TInvalidDataValueException('wizard_command_invalid',self::CMD_COMPLETE);
                                if($index<$this->getWizardSteps()->getCount()-1)
                                        $navParam->setNextStepIndex($index+1);
                                $this->onCompleteButtonClick($navParam);
                                $handled=true;
                        }
                        else if(strcasecmp($command,self::CMD_MOVETO)===0)
                        {
                                if($this->_cancelNavigation)  // may be set in onSideBarButtonClick
                                        $navParam->setCancelNavigation(true);
                                $requestedStep=$param->getCommandParameter();
                                if (!is_numeric($requestedStep))
                                {
                                        $requestedIndex=-1;
                                        foreach ($this->getWizardSteps() as $index=>$step)
                                                if ($step->getId()===$requestedStep)
                                                {
                                                        $requestedIndex=$index;
                                                        break;
                                                }
                                        if ($requestedIndex<0)
                                                throw new TConfigurationException('wizard_step_invalid');
                                }
                                else
                                        $requestedIndex=TPropertyValue::ensureInteger($requestedStep);
                                $navParam->setNextStepIndex($requestedIndex);
                                $handled=true;
                        }

                        if($handled)
                        {
                                if(!$navParam->getCancelNavigation())
                                {
                                        $nextStepIndex=$navParam->getNextStepIndex();
                                        if(!$this->_activeStepIndexSet && $this->allowNavigationToStep($nextStepIndex))
                                        {
                                                if($movePrev)
                                                        $this->getPreviousStepIndex(true);  // pop out the previous move from history
                                                $this->setActiveStepIndex($nextStepIndex);
                                        }
                                }
                                else
                                        $this->setActiveStepIndex($index);
                                return true;
                        }
                }
                return false;
        }
}


/**
 * TWizardStep class.
 *
 * TWizardStep represents a wizard step. The wizard owning the step
 * can be obtained by {@link getWizard Wizard}.
 * To specify the type of the step, set {@link setStepType StepType};
 * For step title, set {@link setTitle Title}. If a step can be re-visited,
 * set {@link setAllowReturn AllowReturn} to true.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TWizard.php 2541 2008-10-21 15:05:13Z qiang.xue $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TWizardStep extends TView
{
        private $_wizard;

        /**
         * @return TWizard the wizard owning this step
         */
        public function getWizard()
        {
                return $this->_wizard;
        }

        /**
         * Sets the wizard owning this step.
         * This method is used internally by {@link TWizard}.
         * @param TWizard the wizard owning this step
         */
        public function setWizard($wizard)
        {
                $this->_wizard=$wizard;
        }

        /**
         * @return string the title for this step.
         */
        public function getTitle()
        {
                return $this->getViewState('Title','');
        }

        /**
         * @param string the title for this step.
         */
        public function setTitle($value)
        {
                $this->setViewState('Title',$value,'');
                if($this->_wizard)
                        $this->_wizard->wizardStepsChanged();
        }

        /**
         * @return boolean whether this step can be re-visited. Default to true.
         */
        public function getAllowReturn()
        {
                return $this->getViewState('AllowReturn',true);
        }

        /**
         * @param boolean whether this step can be re-visited.
         */
        public function setAllowReturn($value)
        {
                $this->setViewState('AllowReturn',TPropertyValue::ensureBoolean($value),true);
        }

        /**
         * @return TWizardStepType the wizard step type. Defaults to TWizardStepType::Auto.
         */
        public function getStepType()
        {
                return $this->getViewState('StepType',TWizardStepType::Auto);
        }

        /**
         * @param TWizardStepType the wizard step type.
         */
        public function setStepType($type)
        {
                $type=TPropertyValue::ensureEnum($type,'TWizardStepType');
                if($type!==$this->getStepType())
                {
                        $this->setViewState('StepType',$type,TWizardStepType::Auto);
                        if($this->_wizard)
                                $this->_wizard->wizardStepsChanged();
                }
        }
}


/**
 * TCompleteWizardStep class.
 *
 * TCompleteWizardStep represents a wizard step of type TWizardStepType::Complete.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TWizard.php 2541 2008-10-21 15:05:13Z qiang.xue $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TCompleteWizardStep extends TWizardStep
{
        /**
         * @return TWizardStepType the wizard step type. Always TWizardStepType::Complete.
         */
        public function getStepType()
        {
                return TWizardStepType::Complete;
        }

        /**
         * @param string the wizard step type.
         * @throws TInvalidOperationException whenever this method is invoked.
         */
        public function setStepType($value)
        {
                throw new TInvalidOperationException('completewizardstep_steptype_readonly');
        }
}


/**
 * TTemplatedWizardStep class.
 *
 * TTemplatedWizardStep represents a wizard step whose content and navigation
 * can be customized using templates. To customize the step content, specify
 * {@link setContentTemplate ContentTemplate}. To customize navigation specific
 * to the step, specify {@link setNavigationTemplate NavigationTemplate}. Note,
 * if the navigation template is not specified, default navigation will be used.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TWizard.php 2541 2008-10-21 15:05:13Z qiang.xue $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TTemplatedWizardStep extends TWizardStep implements INamingContainer
{
        /**
         * @var ITemplate the template for displaying the navigation UI of a wizard step.
         */
        private $_navigationTemplate=null;
        /**
         * @var ITemplate the template for displaying the content within the wizard step.
         */
        private $_contentTemplate=null;
        /**
         * @var TWizardNavigationContainer
         */
        private $_navigationContainer=null;

        /**
         * Creates child controls.
         * This method mainly instantiates the content template, if any.
         */
        public function createChildControls()
        {
                $this->getControls()->clear();
                if($this->_contentTemplate)
                        $this->_contentTemplate->instantiateIn($this);
        }

        /**
         * Ensures child controls are created.
         * @param mixed event parameter
         */
        public function onInit($param)
        {
                parent::onInit($param);
                $this->ensureChildControls();
        }

        /**
         * @return ITemplate the template for the content of the wizard step.
         */
        public function getContentTemplate()
        {
                return $this->_contentTemplate;
        }

        /**
         * @param ITemplate the template for the content of the wizard step.
         */
        public function setContentTemplate($value)
        {
                $this->_contentTemplate=$value;
        }

        /**
         * @return ITemplate the template for displaying the navigation UI of a wizard step. Defaults to null.
         */
        public function getNavigationTemplate()
        {
                return $this->_navigationTemplate;
        }

        /**
         * @param ITemplate the template for displaying the navigation UI of a wizard step.
         */
        public function setNavigationTemplate($value)
        {
                $this->_navigationTemplate=$value;
        }

        /**
         * @return TWizardNavigationContainer the control containing the navigation.
         * It could be null if no navigation template is specified.
         */
        public function getNavigationContainer()
        {
                return $this->_navigationContainer;
        }

        /**
         * Instantiates the navigation template if any
         */
        public function instantiateNavigationTemplate()
        {
                if(!$this->_navigationContainer && $this->_navigationTemplate)
                {
                        $this->_navigationContainer=new TWizardNavigationContainer;
                        $this->_navigationTemplate->instantiateIn($this->_navigationContainer);
                }
        }
}


/**
 * TWizardStepCollection class.
 *
 * TWizardStepCollection represents the collection of wizard steps owned
 * by a {@link TWizard}.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TWizard.php 2541 2008-10-21 15:05:13Z qiang.xue $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TWizardStepCollection extends TList
{
        /**
         * @var TWizard
         */
        private $_wizard;

        /**
         * Constructor.
         * @param TWizard wizard that owns this collection
         */
        public function __construct(TWizard $wizard)
        {
                $this->_wizard=$wizard;
        }

        /**
         * Inserts an item at the specified position.
         * This method overrides the parent implementation by checking if
         * the item being added is a {@link TWizardStep}.
         * @param integer the speicified position.
         * @param mixed new item
         * @throws TInvalidDataTypeException if the item being added is not TWizardStep.
         */
        public function insertAt($index,$item)
        {
                if($item instanceof TWizardStep)
                {
                        parent::insertAt($index,$item);
                        $this->_wizard->getMultiView()->getViews()->insertAt($index,$item);
                        $this->_wizard->addedWizardStep($item);
                }
                else
                        throw new TInvalidDataTypeException('wizardstepcollection_wizardstep_required');
        }

        /**
         * Removes an item at the specified position.
         * @param integer the index of the item to be removed.
         * @return mixed the removed item.
         */
        public function removeAt($index)
        {
                $step=parent::removeAt($index);
                $this->_wizard->getMultiView()->getViews()->remove($step);
                $this->_wizard->removedWizardStep($step);
                return $step;
        }
}


/**
 * TWizardNavigationContainer class.
 *
 * TWizardNavigationContainer represents a control containing
 * a wizard navigation. The navigation may contain a few buttons, including
 * {@link getPreviousButton PreviousButton}, {@link getNextButton NextButton},
 * {@link getCancelButton CancelButton}, {@link getCompleteButton CompleteButton}.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TWizard.php 2541 2008-10-21 15:05:13Z qiang.xue $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TWizardNavigationContainer extends TControl implements INamingContainer
{
        private $_previousButton=null;
        private $_nextButton=null;
        private $_cancelButton=null;
        private $_completeButton=null;

        /**
         * @return mixed the previous button
         */
        public function getPreviousButton()
        {
                return $this->_previousButton;
        }

        /**
         * @param mixed the previous button
         */
        public function setPreviousButton($value)
        {
                $this->_previousButton=$value;
        }

        /**
         * @return mixed the next button
         */
        public function getNextButton()
        {
                return $this->_nextButton;
        }

        /**
         * @param mixed the next button
         */
        public function setNextButton($value)
        {
                $this->_nextButton=$value;
        }

        /**
         * @return mixed the cancel button
         */
        public function getCancelButton()
        {
                return $this->_cancelButton;
        }

        /**
         * @param mixed the cancel button
         */
        public function setCancelButton($value)
        {
                $this->_cancelButton=$value;
        }

        /**
         * @return mixed the complete button
         */
        public function getCompleteButton()
        {
                return $this->_completeButton;
        }

        /**
         * @param mixed the complete button
         */
        public function setCompleteButton($value)
        {
                $this->_completeButton=$value;
        }
}


/**
 * TWizardNavigationEventParameter class.
 *
 * TWizardNavigationEventParameter represents the parameter for
 * {@link TWizard}'s navigation events.
 *
 * The index of the currently active step can be obtained from
 * {@link getCurrentStepIndex CurrentStepIndex}, while the index
 * of the candidate new step is in {@link getNextStepIndex NextStepIndex}.
 * By modifying {@link setNextStepIndex NextStepIndex}, the new step
 * can be changed to another one. If there is anything wrong with
 * the navigation and it is not wanted, set {@link setCancelNavigation CancelNavigation}
 * to true.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TWizard.php 2541 2008-10-21 15:05:13Z qiang.xue $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TWizardNavigationEventParameter extends TEventParameter
{
        private $_cancel=false;
        private $_currentStep;
        private $_nextStep;

        /**
         * Constructor.
         * @param integer current step index
         */
        public function __construct($currentStep)
        {
                $this->_currentStep=$currentStep;
                $this->_nextStep=$currentStep;
        }

        /**
         * @return integer the zero-based index of the currently active step.
         */
        public function getCurrentStepIndex()
        {
                return $this->_currentStep;
        }

        /**
         * @return integer the zero-based index of the next step. Default to {@link getCurrentStepIndex CurrentStepIndex}.
         */
        public function getNextStepIndex()
        {
                return $this->_nextStep;
        }

        /**
         * @param integer the zero-based index of the next step.
         */
        public function setNextStepIndex($index)
        {
                $this->_nextStep=TPropertyValue::ensureInteger($index);
        }

        /**
         * @return boolean whether navigation to the next step should be canceled. Default to false.
         */
        public function getCancelNavigation()
        {
                return $this->_cancel;
        }

        /**
         * @param boolean whether navigation to the next step should be canceled.
         */
        public function setCancelNavigation($value)
        {
                $this->_cancel=TPropertyValue::ensureBoolean($value);
        }
}

/**
 * TWizardSideBarTemplate class.
 * TWizardSideBarTemplate is the default template for wizard sidebar.
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TWizard.php 2541 2008-10-21 15:05:13Z qiang.xue $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TWizardSideBarTemplate extends TComponent implements ITemplate
{
        /**
         * Instantiates the template.
         * It creates a {@link TDataList} control.
         * @param TControl parent to hold the content within the template
         */
        public function instantiateIn($parent)
        {
                $dataList=new TDataList;
                $dataList->setID(TWizard::ID_SIDEBAR_LIST);
                $dataList->getSelectedItemStyle()->getFont()->setBold(true);
                $dataList->setItemTemplate(new TWizardSideBarListItemTemplate);
                $parent->getControls()->add($dataList);
        }
}

/**
 * TWizardSideBarListItemTemplate class.
 * TWizardSideBarListItemTemplate is the default template for each item in the sidebar datalist.
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TWizard.php 2541 2008-10-21 15:05:13Z qiang.xue $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TWizardSideBarListItemTemplate extends TComponent implements ITemplate
{
        /**
         * Instantiates the template.
         * It creates a {@link TLinkButton}.
         * @param TControl parent to hold the content within the template
         */
        public function instantiateIn($parent)
        {
                $button=new TLinkButton;
                $button->setID(TWizard::ID_SIDEBAR_BUTTON);
                $parent->getControls()->add($button);
        }
}

/**
 * TWizardNavigationTemplate class.
 * TWizardNavigationTemplate is the base class for various navigation templates.
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TWizard.php 2541 2008-10-21 15:05:13Z qiang.xue $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TWizardNavigationTemplate extends TComponent implements ITemplate
{
        private $_wizard;

        /**
         * Constructor.
         * @param TWizard the wizard owning this template
         */
        public function __construct($wizard)
        {
                $this->_wizard=$wizard;
        }

        /**
         * @return TWizard the wizard owning this template
         */
        public function getWizard()
        {
                return $this->_wizard;
        }

        /**
         * Instantiates the template.
         * Derived classes should override this method.
         * @param TControl parent to hold the content within the template
         */
        public function instantiateIn($parent)
        {
        }

        /**
         * Creates a navigation button.
         * It creates a {@link TButton}, {@link TLinkButton}, or {@link TImageButton},
         * depending on the given parameters.
         * @param TWizardNavigationButtonStyle button style
         * @param boolean whether the button should cause validation
         * @param string command name for the button's OnCommand event
         * @throws TInvalidDataValueException if the button type is not recognized
         */
        protected function createNavigationButton($buttonStyle,$causesValidation,$commandName)
        {
                switch($buttonStyle->getButtonType())
                {
                        case TWizardNavigationButtonType::Button:
                                $button=new TButton;
                                break;
                        case TWizardNavigationButtonType::Link:
                                $button=new TLinkButton;
                                break;
                        case TWizardNavigationButtonType::Image:
                                $button=new TImageButton;
                                $button->setImageUrl($buttonStyle->getImageUrl());
                                break;
                        default:
                                throw new TInvalidDataValueException('wizard_buttontype_unknown',$buttonStyle->getButtonType());
                }
                $button->setText($buttonStyle->getButtonText());
                $button->setCausesValidation($causesValidation);
                $button->setCommandName($commandName);
                return $button;
        }
}

/**
 * TWizardStartNavigationTemplate class.
 * TWizardStartNavigationTemplate is the template used as default wizard start navigation panel.
 * It consists of two buttons, Next and Cancel.
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TWizard.php 2541 2008-10-21 15:05:13Z qiang.xue $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TWizardStartNavigationTemplate extends TWizardNavigationTemplate
{
        /**
         * Instantiates the template.
         * @param TControl parent to hold the content within the template
         */
        public function instantiateIn($parent)
        {
                $nextButton=$this->createNavigationButton($this->getWizard()->getStartNextButtonStyle(),true,TWizard::CMD_NEXT);
                $cancelButton=$this->createNavigationButton($this->getWizard()->getCancelButtonStyle(),false,TWizard::CMD_CANCEL);

                $controls=$parent->getControls();
                $controls->add($nextButton);
                $controls->add("\n");
                $controls->add($cancelButton);

                $parent->setNextButton($nextButton);
                $parent->setCancelButton($cancelButton);
        }
}

/**
 * TWizardFinishNavigationTemplate class.
 * TWizardFinishNavigationTemplate is the template used as default wizard finish navigation panel.
 * It consists of three buttons, Previous, Complete and Cancel.
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TWizard.php 2541 2008-10-21 15:05:13Z qiang.xue $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TWizardFinishNavigationTemplate extends TWizardNavigationTemplate
{
        /**
         * Instantiates the template.
         * @param TControl parent to hold the content within the template
         */
        public function instantiateIn($parent)
        {
                $previousButton=$this->createNavigationButton($this->getWizard()->getFinishPreviousButtonStyle(),false,TWizard::CMD_PREVIOUS);
                $completeButton=$this->createNavigationButton($this->getWizard()->getFinishCompleteButtonStyle(),true,TWizard::CMD_COMPLETE);
                $cancelButton=$this->createNavigationButton($this->getWizard()->getCancelButtonStyle(),false,TWizard::CMD_CANCEL);

                $controls=$parent->getControls();
                $controls->add($previousButton);
                $controls->add("\n");
                $controls->add($completeButton);
                $controls->add("\n");
                $controls->add($cancelButton);

                $parent->setPreviousButton($previousButton);
                $parent->setCompleteButton($completeButton);
                $parent->setCancelButton($cancelButton);
        }
}

/**
 * TWizardStepNavigationTemplate class.
 * TWizardStepNavigationTemplate is the template used as default wizard step navigation panel.
 * It consists of three buttons, Previous, Next and Cancel.
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TWizard.php 2541 2008-10-21 15:05:13Z qiang.xue $
 * @package System.Web.UI.WebControls
 * @since 3.0
 */
class TWizardStepNavigationTemplate extends TWizardNavigationTemplate
{
        /**
         * Instantiates the template.
         * @param TControl parent to hold the content within the template
         */
        public function instantiateIn($parent)
        {
                $previousButton=$this->createNavigationButton($this->getWizard()->getStepPreviousButtonStyle(),false,TWizard::CMD_PREVIOUS);
                $nextButton=$this->createNavigationButton($this->getWizard()->getStepNextButtonStyle(),true,TWizard::CMD_NEXT);
                $cancelButton=$this->createNavigationButton($this->getWizard()->getCancelButtonStyle(),false,TWizard::CMD_CANCEL);

                $controls=$parent->getControls();
                $controls->add($previousButton);
                $controls->add("\n");
                $controls->add($nextButton);
                $controls->add("\n");
                $controls->add($cancelButton);

                $parent->setPreviousButton($previousButton);
                $parent->setNextButton($nextButton);
                $parent->setCancelButton($cancelButton);
        }
}


/**
 * TWizardNavigationButtonType class.
 * TWizardNavigationButtonType defines the enumerable type for the possible types of buttons
 * that can be used in the navigation part of a {@link TWizard}.
 *
 * The following enumerable values are defined:
 * - Button: a regular click button
 * - Image: an image button
 * - Link: a hyperlink button
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TWizard.php 2541 2008-10-21 15:05:13Z qiang.xue $
 * @package System.Web.UI.WebControls
 * @since 3.0.4
 */
class TWizardNavigationButtonType extends TEnumerable
{
        const Button='Button';
        const Image='Image';
        const Link='Link';
}


/**
 * TWizardStepType class.
 * TWizardStepType defines the enumerable type for the possible types of {@link TWizard wizard} steps.
 *
 * The following enumerable values are defined:
 * - Auto: the type is automatically determined based on the location of the wizard step in the whole step collection.
 * - Complete: the step is the last summary step.
 * - Start: the step is the first step
 * - Step: the step is between the begin and the end steps.
 * - Finish: the last step before the Complete step.
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @version $Id: TWizard.php 2541 2008-10-21 15:05:13Z qiang.xue $
 * @package System.Web.UI.WebControls
 * @since 3.0.4
 */
class TWizardStepType extends TEnumerable
{
        const Auto='Auto';
        const Complete='Complete';
        const Start='Start';
        const Step='Step';
        const Finish='Finish';
}