]> git.sur5r.net Git - bacula/bacula/blob - gui/baculum/framework/Web/UI/WebControls/TWizard.php
Add Baculum
[bacula/bacula] / gui / baculum / framework / Web / UI / WebControls / TWizard.php
1 <?php
2 /**
3  * TWizard and the relevant class definitions.
4  *
5  * @author Qiang Xue <qiang.xue@gmail.com>
6  * @link http://www.pradosoft.com/
7  * @copyright Copyright &copy; 2005-2013 PradoSoft
8  * @license http://www.pradosoft.com/license/
9  * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $
10  * @package System.Web.UI.WebControls
11  */
12
13 Prado::using('System.Web.UI.WebControls.TMultiView');
14 Prado::using('System.Web.UI.WebControls.TPanel');
15 Prado::using('System.Web.UI.WebControls.TButton');
16 Prado::using('System.Web.UI.WebControls.TLinkButton');
17 Prado::using('System.Web.UI.WebControls.TImageButton');
18 Prado::using('System.Web.UI.WebControls.TDataList');
19 Prado::using('System.Web.UI.WebControls.TWizardNavigationButtonStyle');
20
21 /**
22  * Class TWizard.
23  *
24  * TWizard splits a large form and presents the user with a series of smaller
25  * forms to complete. TWizard is analogous to the installation wizard commonly
26  * used to install software in Windows.
27  *
28  * The smaller forms are called wizard steps ({@link TWizardStep}, which can be accessed via
29  * {@link getWizardSteps WizardSteps}. In template, wizard steps can be added
30  * into a wizard using the following syntax,
31  * <code>
32  *   <com:TWizard>
33  *      <com:TWizardStep Title="step 1">
34  *          content in step 1, may contain other controls
35  *      </com:TWizardStep>
36  *      <com:TWizardStep Title="step 2">
37  *          content in step 2, may contain other controls
38  *      </com:TWizardStep>
39  *   </com:TWizard>
40  * </code>
41  *
42  * Each wizard step can be one of the following types:
43  * - Start : the first step in the wizard.
44  * - Step : the internal steps in the wizard.
45  * - Finish : the last step that allows user interaction.
46  * - Complete : the step that shows a summary to user (no interaction is allowed).
47  * - Auto : the step type is determined by wizard automatically.
48  * At any time, only one step is visible to end-users, which can be obtained
49  * by {@link getActiveStep ActiveStep}. Its index in the step collection is given by
50  * {@link getActiveStepIndex ActiveStepIndex}.
51  *
52  * Wizard content can be customized in many ways.
53  *
54  * The layout of a wizard consists of four parts: header, step content, navigation
55  * and side bar. Their content are affected by the following properties, respectively,
56  * - header: {@link setHeaderText HeaderText} and {@link setHeaderTemplate HeaderTemplate}.
57  *   If both are present, the latter takes precedence.
58  * - step: {@link getWizardSteps WizardSteps}.
59  * - navigation: {@link setStartNavigationTemplate StartNavigationTemplate},
60  *   {@link setStepNavigationTemplate StepNavigationTemplate},
61  *   {@link setFinishNavigationTemplate FinishNavigationTemplate}.
62  *   Default templates will be used if above templates are not set.
63  * - side bar: {@link setSideBarTemplate SideBarTemplate}.
64  *   A default template will be used if this template is not set.
65  *   Its visibility is toggled by {@link setShowSideBar ShowSideBar}.
66  *
67  * The style of these wizard layout components can be customized via the following style properties,
68  * - header: {@link getHeaderStyle HeaderStyle}.
69  * - step: {@link getStepStyle StepStyle}.
70  * - navigation: {@link getNavigationStyle NavigationStyle},
71  *   {@link getStartNextButtonStyle StartNextButtonStyle},
72  *   {@link getStepNextButtonStyle StepNextButtonStyle},
73  *   {@link getStepPreviousButtonStyle StepPreviousButtonStyle},
74  *   {@link getFinishPreviousButtonStyle FinishPreviousButtonStyle},
75  *   {@link getFinishCompleteButtonStyle FinishCompleteButtonStyle},
76  *   {@link getCancelButtonStyle CancelButtonStyle}.
77  * - side bar: {@link getSideBarStyle SideBarStyle} and {@link getSideBarButtonStyle SideBarButtonStyle}.
78  *
79  * @author Qiang Xue <qiang.xue@gmail.com>
80  * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $
81  * @package System.Web.UI.WebControls
82  * @since 3.0
83  */
84 class TWizard extends TWebControl implements INamingContainer
85 {
86         /**
87          * Wizard step types.
88          * @deprecated deprecated since version 3.0.4 (use TWizardStepType constants instead)
89          */
90         const ST_AUTO='Auto';
91         const ST_START='Start';
92         const ST_STEP='Step';
93         const ST_FINISH='Finish';
94         const ST_COMPLETE='Complete';
95         /**
96          * Navigation commands.
97          */
98         const CMD_PREVIOUS='PreviousStep';
99         const CMD_NEXT='NextStep';
100         const CMD_CANCEL='Cancel';
101         const CMD_COMPLETE='Complete';
102         const CMD_MOVETO='MoveTo';
103         /**
104          * Side bar button ID
105          */
106         const ID_SIDEBAR_BUTTON='SideBarButton';
107         /**
108          * Side bar data list
109          */
110         const ID_SIDEBAR_LIST='SideBarList';
111
112         /**
113          * @var TMultiView multiview that contains the wizard steps
114          */
115         private $_multiView=null;
116         /**
117          * @var mixed navigation template for the start step.
118          */
119         private $_startNavigationTemplate=null;
120         /**
121          * @var mixed navigation template for internal steps.
122          */
123         private $_stepNavigationTemplate=null;
124         /**
125          * @var mixed navigation template for the finish step.
126          */
127         private $_finishNavigationTemplate=null;
128         /**
129          * @var mixed template for wizard header.
130          */
131         private $_headerTemplate=null;
132         /**
133          * @var mixed template for the side bar.
134          */
135         private $_sideBarTemplate=null;
136         /**
137          * @var TWizardStepCollection
138          */
139         private $_wizardSteps=null;
140         /**
141          * @var TPanel container of the wizard header
142          */
143         private $_header;
144         /**
145          * @var TPanel container of the wizard step content
146          */
147         private $_stepContent;
148         /**
149          * @var TPanel container of the wizard side bar
150          */
151         private $_sideBar;
152         /**
153          * @var TPanel navigation panel
154          */
155         private $_navigation;
156         /**
157          * @var TWizardNavigationContainer container of the start navigation
158          */
159         private $_startNavigation;
160         /**
161          * @var TWizardNavigationContainer container of the step navigation
162          */
163         private $_stepNavigation;
164         /**
165          * @var TWizardNavigationContainer container of the finish navigation
166          */
167         private $_finishNavigation;
168         /**
169          * @var boolean whether ActiveStepIndex was already set
170          */
171         private $_activeStepIndexSet=false;
172         /**
173          * @var TDataList side bar data list.
174          */
175         private $_sideBarDataList;
176         /**
177          * @var boolean whether navigation should be cancelled (a status set in OnSideBarButtonClick)
178          */
179         private $_cancelNavigation=false;
180
181         /**
182          * @return string tag name for the wizard
183          */
184         protected function getTagName()
185         {
186                 return 'div';
187         }
188
189         /**
190          * Adds {@link TWizardStep} objects into step collection.
191          * This method overrides the parent implementation and is
192          * invoked when template is being instantiated.
193          * @param mixed object instantiated in template
194          */
195         public function addParsedObject($object)
196         {
197                 if($object instanceof TWizardStep)
198                         $this->getWizardSteps()->add($object);
199         }
200
201         /**
202          * @return TWizardStep the currently active wizard step
203          */
204         public function getActiveStep()
205         {
206                 return $this->getMultiView()->getActiveView();
207         }
208
209         /**
210          * @param TWizardStep step to be activated
211          * @throws TInvalidOperationException if the step is not in the wizard step collection
212          */
213         public function setActiveStep($step)
214         {
215                 if(($index=$this->getWizardSteps()->indexOf($step))<0)
216                         throw new TInvalidOperationException('wizard_step_invalid');
217                 $this->setActiveStepIndex($index);
218         }
219
220         /**
221          * @return integer the zero-based index of the active wizard step
222          */
223         public function getActiveStepIndex()
224         {
225                 return $this->getMultiView()->getActiveViewIndex();
226         }
227
228         /**
229          * @param integer the zero-based index of the wizard step to be activated
230          */
231         public function setActiveStepIndex($value)
232         {
233                 $value=TPropertyValue::ensureInteger($value);
234                 $multiView=$this->getMultiView();
235                 if($multiView->getActiveViewIndex()!==$value)
236                 {
237                         $multiView->setActiveViewIndex($value);
238                         $this->_activeStepIndexSet=true;
239                         if($this->_sideBarDataList!==null && $this->getSideBarTemplate()!==null)
240                         {
241                                 $this->_sideBarDataList->setSelectedItemIndex($this->getActiveStepIndex());
242                                 $this->_sideBarDataList->dataBind();
243                         }
244                 }
245         }
246
247         /**
248          * @return TWizardStepCollection collection of wizard steps
249          */
250         public function getWizardSteps()
251         {
252                 if($this->_wizardSteps===null)
253                         $this->_wizardSteps=new TWizardStepCollection($this);
254                 return $this->_wizardSteps;
255         }
256
257         /**
258          * @return boolean whether to display a cancel button in each wizard step. Defaults to false.
259          */
260         public function getShowCancelButton()
261         {
262                 return $this->getViewState('ShowCancelButton',false);
263         }
264
265         /**
266          * @param boolean whether to display a cancel button in each wizard step.
267          */
268         public function setShowCancelButton($value)
269         {
270                 $this->setViewState('ShowCancelButton',TPropertyValue::ensureBoolean($value),false);
271         }
272
273         /**
274          * @return boolean whether to display a side bar that contains links to wizard steps. Defaults to true.
275          */
276         public function getShowSideBar()
277         {
278                 return $this->getViewState('ShowSideBar',true);
279         }
280
281         /**
282          * @param boolean whether to display a side bar that contains links to wizard steps.
283          */
284         public function setShowSideBar($value)
285         {
286                 $this->setViewState('ShowSideBar',TPropertyValue::ensureBoolean($value),true);
287         }
288
289         /**
290          * @return ITemplate navigation template for the start step. Defaults to null.
291          */
292         public function getStartNavigationTemplate()
293         {
294                 return $this->_startNavigationTemplate;
295         }
296
297         /**
298          * @param ITemplate navigation template for the start step.
299          */
300         public function setStartNavigationTemplate($value)
301         {
302                 $this->_startNavigationTemplate=$value;
303                 $this->requiresControlsRecreation();
304         }
305
306         /**
307          * @return ITemplate navigation template for internal steps. Defaults to null.
308          */
309         public function getStepNavigationTemplate()
310         {
311                 return $this->_stepNavigationTemplate;
312         }
313
314         /**
315          * @param ITemplate navigation template for internal steps.
316          */
317         public function setStepNavigationTemplate($value)
318         {
319                 $this->_stepNavigationTemplate=$value;
320                 $this->requiresControlsRecreation();
321         }
322
323         /**
324          * @return ITemplate navigation template for the finish step. Defaults to null.
325          */
326         public function getFinishNavigationTemplate()
327         {
328                 return $this->_finishNavigationTemplate;
329         }
330
331         /**
332          * @param ITemplate navigation template for the finish step.
333          */
334         public function setFinishNavigationTemplate($value)
335         {
336                 $this->_finishNavigationTemplate=$value;
337                 $this->requiresControlsRecreation();
338         }
339
340         /**
341          * @return ITemplate template for wizard header. Defaults to null.
342          */
343         public function getHeaderTemplate()
344         {
345                 return $this->_headerTemplate;
346         }
347
348         /**
349          * @param ITemplate template for wizard header.
350          */
351         public function setHeaderTemplate($value)
352         {
353                 $this->_headerTemplate=$value;
354                 $this->requiresControlsRecreation();
355         }
356
357         /**
358          * @return ITemplate template for the side bar. Defaults to null.
359          */
360         public function getSideBarTemplate()
361         {
362                 return $this->_sideBarTemplate;
363         }
364
365         /**
366          * @param ITemplate template for the side bar.
367          */
368         public function setSideBarTemplate($value)
369         {
370                 $this->_sideBarTemplate=$value;
371                 $this->requiresControlsRecreation();
372         }
373
374         /**
375          * @return string header text. Defaults to ''.
376          */
377         public function getHeaderText()
378         {
379                 return $this->getViewState('HeaderText','');
380         }
381
382         /**
383          * @param string header text.
384          */
385         public function setHeaderText($value)
386         {
387                 $this->setViewState('HeaderText',TPropertyValue::ensureString($value),'');
388         }
389
390         /**
391          * @return string the URL that the browser will be redirected to if the cancel button in the
392          * wizard is clicked. Defaults to ''.
393          */
394         public function getCancelDestinationUrl()
395         {
396                 return $this->getViewState('CancelDestinationUrl','');
397         }
398
399         /**
400          * @param string the URL that the browser will be redirected to if the cancel button in the
401          * wizard is clicked.
402          */
403         public function setCancelDestinationUrl($value)
404         {
405                 $this->setViewState('CancelDestinationUrl',TPropertyValue::ensureString($value),'');
406         }
407
408         /**
409          * @return string the URL that the browser will be redirected to if the wizard finishes.
410          * Defaults to ''.
411          */
412         public function getFinishDestinationUrl()
413         {
414                 return $this->getViewState('FinishDestinationUrl','');
415         }
416
417         /**
418          * @param string the URL that the browser will be redirected to if the wizard finishes.
419          */
420         public function setFinishDestinationUrl($value)
421         {
422                 $this->setViewState('FinishDestinationUrl',TPropertyValue::ensureString($value),'');
423         }
424
425         /**
426          * @return TStyle the style for the buttons displayed in the side bar.
427          */
428         public function getSideBarButtonStyle()
429         {
430                 if(($style=$this->getViewState('SideBarButtonStyle',null))===null)
431                 {
432                         $style=new TStyle;
433                         $this->setViewState('SideBarButtonStyle',$style,null);
434                 }
435                 return $style;
436         }
437
438         /**
439          * @return TStyle the style common for all navigation buttons.
440          */
441         public function getNavigationButtonStyle()
442         {
443                 if(($style=$this->getViewState('NavigationButtonStyle',null))===null)
444                 {
445                         $style=new TStyle;
446                         $this->setViewState('NavigationButtonStyle',$style,null);
447                 }
448                 return $style;
449         }
450
451         /**
452          * @return TWizardNavigationButtonStyle the style for the next button in the start wizard step.
453          */
454         public function getStartNextButtonStyle()
455         {
456                 if(($style=$this->getViewState('StartNextButtonStyle',null))===null)
457                 {
458                         $style=new TWizardNavigationButtonStyle;
459                         $style->setButtonText('Next');
460                         $this->setViewState('StartNextButtonStyle',$style,null);
461                 }
462                 return $style;
463         }
464
465         /**
466          * @return TWizardNavigationButtonStyle the style for the next button in each internal wizard step.
467          */
468         public function getStepNextButtonStyle()
469         {
470                 if(($style=$this->getViewState('StepNextButtonStyle',null))===null)
471                 {
472                         $style=new TWizardNavigationButtonStyle;
473                         $style->setButtonText('Next');
474                         $this->setViewState('StepNextButtonStyle',$style,null);
475                 }
476                 return $style;
477         }
478
479         /**
480          * @return TWizardNavigationButtonStyle the style for the previous button in the start wizard step.
481          */
482         public function getStepPreviousButtonStyle()
483         {
484                 if(($style=$this->getViewState('StepPreviousButtonStyle',null))===null)
485                 {
486                         $style=new TWizardNavigationButtonStyle;
487                         $style->setButtonText('Previous');
488                         $this->setViewState('StepPreviousButtonStyle',$style,null);
489                 }
490                 return $style;
491         }
492
493         /**
494          * @return TWizardNavigationButtonStyle the style for the complete button in the finish wizard step.
495          */
496         public function getFinishCompleteButtonStyle()
497         {
498                 if(($style=$this->getViewState('FinishCompleteButtonStyle',null))===null)
499                 {
500                         $style=new TWizardNavigationButtonStyle;
501                         $style->setButtonText('Complete');
502                         $this->setViewState('FinishCompleteButtonStyle',$style,null);
503                 }
504                 return $style;
505         }
506
507         /**
508          * @return TWizardNavigationButtonStyle the style for the previous button in the start wizard step.
509          */
510         public function getFinishPreviousButtonStyle()
511         {
512                 if(($style=$this->getViewState('FinishPreviousButtonStyle',null))===null)
513                 {
514                         $style=new TWizardNavigationButtonStyle;
515                         $style->setButtonText('Previous');
516                         $this->setViewState('FinishPreviousButtonStyle',$style,null);
517                 }
518                 return $style;
519         }
520
521         /**
522          * @return TWizardNavigationButtonStyle the style for the cancel button
523          */
524         public function getCancelButtonStyle()
525         {
526                 if(($style=$this->getViewState('CancelButtonStyle',null))===null)
527                 {
528                         $style=new TWizardNavigationButtonStyle;
529                         $style->setButtonText('Cancel');
530                         $this->setViewState('CancelButtonStyle',$style,null);
531                 }
532                 return $style;
533         }
534
535         /**
536          * @return TPanelStyle the style for the side bar.
537          */
538         public function getSideBarStyle()
539         {
540                 if(($style=$this->getViewState('SideBarStyle',null))===null)
541                 {
542                         $style=new TPanelStyle;
543                         $this->setViewState('SideBarStyle',$style,null);
544                 }
545                 return $style;
546         }
547
548         /**
549          * @return TPanelStyle the style for the header.
550          */
551         public function getHeaderStyle()
552         {
553                 if(($style=$this->getViewState('HeaderStyle',null))===null)
554                 {
555                         $style=new TPanelStyle;
556                         $this->setViewState('HeaderStyle',$style,null);
557                 }
558                 return $style;
559         }
560
561         /**
562          * @return TPanelStyle the style for each internal wizard step.
563          */
564         public function getStepStyle()
565         {
566                 if(($style=$this->getViewState('StepStyle',null))===null)
567                 {
568                         $style=new TPanelStyle;
569                         $this->setViewState('StepStyle',$style,null);
570                 }
571                 return $style;
572         }
573
574         /**
575          * @return TPanelStyle the style for the navigation panel.
576          */
577         public function getNavigationStyle()
578         {
579                 if(($style=$this->getViewState('NavigationStyle',null))===null)
580                 {
581                         $style=new TPanelStyle;
582                         $this->setViewState('NavigationStyle',$style,null);
583                 }
584                 return $style;
585         }
586
587         /**
588          * @return boolean whether to use default layout to arrange side bar and the rest wizard components. Defaults to true.
589          */
590         public function getUseDefaultLayout()
591         {
592                 return $this->getViewState('UseDefaultLayout',true);
593         }
594
595         /**
596          * @param boolean whether to use default layout to arrange side bar and the rest wizard components.
597          * If true, an HTML table will be used which places the side bar in the left cell
598          * while the rest components in the right cell.
599          */
600         public function setUseDefaultLayout($value)
601         {
602                 $this->setViewState('UseDefaultLayout',TPropertyValue::ensureBoolean($value),true);
603         }
604
605         /**
606          * @return TPanel container of the wizard header
607          */
608         public function getHeader()
609         {
610                 return $this->_header;
611         }
612
613         /**
614          * @return TPanel container of the wizard step content
615          */
616         public function getStepContent()
617         {
618                 return $this->_stepContent;
619         }
620
621         /**
622          * @return TPanel container of the wizard side bar
623          */
624         public function getSideBar()
625         {
626                 return $this->_sideBar;
627         }
628
629         /**
630          * @return TWizardNavigationContainer container of the start navigation
631          */
632         public function getStartNavigation()
633         {
634                 return $this->_startNavigation;
635         }
636
637         /**
638          * @return TWizardNavigationContainer container of the step navigation
639          */
640         public function getStepNavigation()
641         {
642                 return $this->_stepNavigation;
643         }
644
645         /**
646          * @return TWizardNavigationContainer container of the finish navigation
647          */
648         public function getFinishNavigation()
649         {
650                 return $this->_finishNavigation;
651         }
652
653         /**
654          * Raises <b>OnActiveStepChanged</b> event.
655          * This event is raised when the current visible step is changed in the
656          * wizard.
657          * @param TEventParameter event parameter
658          */
659         public function onActiveStepChanged($param)
660         {
661                 $this->raiseEvent('OnActiveStepChanged',$this,$param);
662         }
663
664         /**
665          * Raises <b>OnCancelButtonClick</b> event.
666          * This event is raised when a cancel navigation button is clicked in the
667          * current active step.
668          * @param TEventParameter event parameter
669          */
670         public function onCancelButtonClick($param)
671         {
672                 $this->raiseEvent('OnCancelButtonClick',$this,$param);
673                 if(($url=$this->getCancelDestinationUrl())!=='')
674                         $this->getResponse()->redirect($url);
675         }
676
677         /**
678          * Raises <b>OnCompleteButtonClick</b> event.
679          * This event is raised when a finish navigation button is clicked in the
680          * current active step.
681          * @param TWizardNavigationEventParameter event parameter
682          */
683         public function onCompleteButtonClick($param)
684         {
685                 $this->raiseEvent('OnCompleteButtonClick',$this,$param);
686                 if(($url=$this->getFinishDestinationUrl())!=='')
687                         $this->getResponse()->redirect($url);
688         }
689
690         /**
691          * Raises <b>OnNextButtonClick</b> event.
692          * This event is raised when a next navigation button is clicked in the
693          * current active step.
694          * @param TWizardNavigationEventParameter event parameter
695          */
696         public function onNextButtonClick($param)
697         {
698                 $this->raiseEvent('OnNextButtonClick',$this,$param);
699         }
700
701         /**
702          * Raises <b>OnPreviousButtonClick</b> event.
703          * This event is raised when a previous navigation button is clicked in the
704          * current active step.
705          * @param TWizardNavigationEventParameter event parameter
706          */
707         public function onPreviousButtonClick($param)
708         {
709                 $this->raiseEvent('OnPreviousButtonClick',$this,$param);
710         }
711
712         /**
713          * Raises <b>OnSideBarButtonClick</b> event.
714          * This event is raised when a link button in the side bar is clicked.
715          * @param TWizardNavigationEventParameter event parameter
716          */
717         public function onSideBarButtonClick($param)
718         {
719                 $this->raiseEvent('OnSideBarButtonClick',$this,$param);
720         }
721
722         /**
723          * Returns the multiview that holds the wizard steps.
724          * This method should only be used by control developers.
725          * @return TMultiView the multiview holding wizard steps
726          */
727         public function getMultiView()
728         {
729                 if($this->_multiView===null)
730                 {
731                         $this->_multiView=new TMultiView;
732                         $this->_multiView->setID('WizardMultiView');
733                         $this->_multiView->attachEventHandler('OnActiveViewChanged',array($this,'onActiveStepChanged'));
734                         $this->_multiView->ignoreBubbleEvents();
735                 }
736                 return $this->_multiView;
737         }
738
739         /**
740          * Adds a wizard step to the multiview.
741          * This method should only be used by control developers.
742          * It is invoked when a step is added into the step collection of the wizard.
743          * @param TWizardStep wizard step to be added into multiview.
744          */
745         public function addedWizardStep($step)
746         {
747                 if(($wizard=$step->getWizard())!==null)
748                         $wizard->getWizardSteps()->remove($step);
749                 $step->setWizard($this);
750                 $this->wizardStepsChanged();
751         }
752
753         /**
754          * Removes a wizard step from the multiview.
755          * This method should only be used by control developers.
756          * It is invoked when a step is removed from the step collection of the wizard.
757          * @param TWizardStep wizard step to be removed from multiview.
758          */
759         public function removedWizardStep($step)
760         {
761                 $step->setWizard(null);
762                 $this->wizardStepsChanged();
763         }
764
765         /**
766          * Creates the child controls of the wizard.
767          * This method overrides the parent implementation.
768          * @param TEventParameter event parameter
769          */
770         public function onInit($param)
771         {
772                 parent::onInit($param);
773                 $this->ensureChildControls();
774                 $this->setEnsureId(true);
775                 if($this->getActiveStepIndex()<0 && $this->getWizardSteps()->getCount()>0)
776                         $this->setActiveStepIndex(0);
777         }
778
779         /**
780          * Saves the current active step index into history.
781          * This method is invoked by the framework when the control state is being saved.
782          */
783         public function saveState()
784         {
785                 $index=$this->getActiveStepIndex();
786                 $history=$this->getHistory();
787                 if(!$history->getCount() || $history->peek()!==$index)
788                         $history->push($index);
789         }
790
791         /**
792          * Indicates the wizard needs to recreate all child controls.
793          */
794         protected function requiresControlsRecreation()
795         {
796                 if($this->getChildControlsCreated())
797                         $this->setChildControlsCreated(false);
798         }
799
800         /**
801          * Renders the wizard.
802          * @param THtmlWriter
803          */
804         public function render($writer)
805         {
806                 $this->ensureChildControls();
807                 if($this->getHasControls())
808                 {
809                         if($this->getUseDefaultLayout())
810                         {
811                                 $this->applyControlProperties();
812                                 $this->renderBeginTag($writer);
813                                 $writer->write("\n<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" height=\"100%\" width=\"100%\">\n<tr><td width=\"1\" valign=\"top\">\n");
814                                 $this->_sideBar->renderControl($writer);
815                                 $writer->write("\n</td><td valign=\"top\">\n");
816                                 $this->_header->renderControl($writer);
817                                 $this->_stepContent->renderControl($writer);
818                                 $this->_navigation->renderControl($writer);
819                                 $writer->write("\n</td></tr></table>\n");
820                                 $this->renderEndTag($writer);
821                         }
822                         else
823                         {
824                                 $this->applyControlProperties();
825                                 $this->renderBeginTag($writer);
826                                 $this->_sideBar->renderControl($writer);
827                                 $this->_header->renderControl($writer);
828                                 $this->_stepContent->renderControl($writer);
829                                 $this->_navigation->renderControl($writer);
830                                 $this->renderEndTag($writer);
831                         }
832                 }
833         }
834
835         /**
836          * Applies various properties to the components of wizard
837          */
838         protected function applyControlProperties()
839         {
840                 $this->applyHeaderProperties();
841                 $this->applySideBarProperties();
842                 $this->applyStepContentProperties();
843                 $this->applyNavigationProperties();
844         }
845
846         /**
847          * Applies properties to the wizard header
848          */
849         protected function applyHeaderProperties()
850         {
851                 if(($style=$this->getViewState('HeaderStyle',null))!==null)
852                         $this->_header->getStyle()->mergeWith($style);
853                 if($this->getHeaderTemplate()===null)
854                 {
855                         $this->_header->getControls()->clear();
856                         $this->_header->getControls()->add($this->getHeaderText());
857                 }
858         }
859
860         /**
861          * Applies properties to the wizard sidebar
862          */
863         protected function applySideBarProperties()
864         {
865                 $this->_sideBar->setVisible($this->getShowSideBar());
866                 if($this->_sideBarDataList!==null && $this->getShowSideBar())
867                 {
868                         $this->_sideBarDataList->setDataSource($this->getWizardSteps());
869                         $this->_sideBarDataList->setSelectedItemIndex($this->getActiveStepIndex());
870                         $this->_sideBarDataList->dataBind();
871                         if(($style=$this->getViewState('SideBarButtonStyle',null))!==null)
872                         {
873                                 foreach($this->_sideBarDataList->getItems() as $item)
874                                 {
875                                         if(($button=$item->findControl('SideBarButton'))!==null)
876                                                 $button->getStyle()->mergeWith($style);
877                                 }
878                         }
879                 }
880                 if(($style=$this->getViewState('SideBarStyle',null))!==null)
881                         $this->_sideBar->getStyle()->mergeWith($style);
882         }
883
884         /**
885          * Applies properties to the wizard step content
886          */
887         protected function applyStepContentProperties()
888         {
889                 if(($style=$this->getViewState('StepStyle',null))!==null)
890                         $this->_stepContent->getStyle()->mergeWith($style);
891         }
892
893         /**
894          * Apply properties to various navigation panels.
895          */
896         protected function applyNavigationProperties()
897         {
898                 $wizardSteps=$this->getWizardSteps();
899                 $activeStep=$this->getActiveStep();
900                 $activeStepIndex=$this->getActiveStepIndex();
901
902                 if(!$this->_navigation)
903                         return;
904                 else if($activeStepIndex<0 || $activeStepIndex>=$wizardSteps->getCount())
905                 {
906                         $this->_navigation->setVisible(false);
907                         return;
908                 }
909
910                 // set visibility of different types of navigation panel
911                 $showStandard=true;
912                 foreach($wizardSteps as $step)
913                 {
914                         if(($step instanceof TTemplatedWizardStep) && ($container=$step->getNavigationContainer())!==null)
915                         {
916                                 if($activeStep===$step)
917                                 {
918                                         $container->setVisible(true);
919                                         $showStandard=false;
920                                 }
921                                 else
922                                         $container->setVisible(false);
923                         }
924                 }
925                 $activeStepType=$this->getStepType($activeStep);
926                 if($activeStepType===TWizardStepType::Complete)
927                 {
928                         $this->_sideBar->setVisible(false);
929                         $this->_header->setVisible(false);
930                 }
931                 $this->_startNavigation->setVisible($showStandard && $activeStepType===self::ST_START);
932                 $this->_stepNavigation->setVisible($showStandard && $activeStepType===self::ST_STEP);
933                 $this->_finishNavigation->setVisible($showStandard && $activeStepType===self::ST_FINISH);
934
935                 if(($navigationStyle=$this->getViewState('NavigationStyle',null))!==null)
936                         $this->_navigation->getStyle()->mergeWith($navigationStyle);
937
938                 $displayCancelButton=$this->getShowCancelButton();
939                 $cancelButtonStyle=$this->getCancelButtonStyle();
940                 $buttonStyle=$this->getViewState('NavigationButtonStyle',null);
941                 if($buttonStyle!==null)
942                         $cancelButtonStyle->mergeWith($buttonStyle);
943
944                 // apply styles to start navigation buttons
945                 if(($cancelButton=$this->_startNavigation->getCancelButton())!==null)
946                 {
947                         $cancelButton->setVisible($displayCancelButton);
948                         $cancelButtonStyle->apply($cancelButton);
949                 }
950                 if(($button=$this->_startNavigation->getNextButton())!==null)
951                 {
952                         $button->setVisible(true);
953                         $style=$this->getStartNextButtonStyle();
954                         if($buttonStyle!==null)
955                                 $style->mergeWith($buttonStyle);
956                         $style->apply($button);
957                         if($activeStepType===TWizardStepType::Start)
958                                 $this->getPage()->getClientScript()->registerDefaultButton($this, $button);
959                 }
960
961                 // apply styles to finish navigation buttons
962                 if(($cancelButton=$this->_finishNavigation->getCancelButton())!==null)
963                 {
964                         $cancelButton->setVisible($displayCancelButton);
965                         $cancelButtonStyle->apply($cancelButton);
966                 }
967                 if(($button=$this->_finishNavigation->getPreviousButton())!==null)
968                 {
969                         $button->setVisible($this->allowNavigationToPreviousStep());
970                         $style=$this->getFinishPreviousButtonStyle();
971                         if($buttonStyle!==null)
972                                 $style->mergeWith($buttonStyle);
973                         $style->apply($button);
974                 }
975                 if(($button=$this->_finishNavigation->getCompleteButton())!==null)
976                 {
977                         $button->setVisible(true);
978                         $style=$this->getFinishCompleteButtonStyle();
979                         if($buttonStyle!==null)
980                                 $style->mergeWith($buttonStyle);
981                         $style->apply($button);
982                         if($activeStepType===TWizardStepType::Finish)
983                                 $this->getPage()->getClientScript()->registerDefaultButton($this, $button);
984                 }
985
986                 // apply styles to step navigation buttons
987                 if(($cancelButton=$this->_stepNavigation->getCancelButton())!==null)
988                 {
989                         $cancelButton->setVisible($displayCancelButton);
990                         $cancelButtonStyle->apply($cancelButton);
991                 }
992                 if(($button=$this->_stepNavigation->getPreviousButton())!==null)
993                 {
994                         $button->setVisible($this->allowNavigationToPreviousStep());
995                         $style=$this->getStepPreviousButtonStyle();
996                         if($buttonStyle!==null)
997                                 $style->mergeWith($buttonStyle);
998                         $style->apply($button);
999                 }
1000                 if(($button=$this->_stepNavigation->getNextButton())!==null)
1001                 {
1002                         $button->setVisible(true);
1003                         $style=$this->getStepNextButtonStyle();
1004                         if($buttonStyle!==null)
1005                                 $style->mergeWith($buttonStyle);
1006                         $style->apply($button);
1007                         if($activeStepType===TWizardStepType::Step)
1008                                 $this->getPage()->getClientScript()->registerDefaultButton($this, $button);
1009                 }
1010         }
1011
1012         /**
1013          * @return TStack history containing step indexes that were navigated before
1014          */
1015         protected function getHistory()
1016         {
1017                 if(($history=$this->getControlState('History',null))===null)
1018                 {
1019                         $history=new TStack;
1020                         $this->setControlState('History',$history);
1021                 }
1022                 return $history;
1023         }
1024
1025         /**
1026          * Determines the type of the specified wizard step.
1027          * @param TWizardStep
1028          * @return TWizardStepType type of the step
1029          */
1030         protected function getStepType($wizardStep)
1031         {
1032                 if(($type=$wizardStep->getStepType())===TWizardStepType::Auto)
1033                 {
1034                         $steps=$this->getWizardSteps();
1035                         if(($index=$steps->indexOf($wizardStep))>=0)
1036                         {
1037                                 $stepCount=$steps->getCount();
1038                                 if($stepCount===1 || ($index<$stepCount-1 && $steps->itemAt($index+1)->getStepType()===TWizardStepType::Complete))
1039                                         return TWizardStepType::Finish;
1040                                 else if($index===0)
1041                                         return TWizardStepType::Start;
1042                                 else if($index===$stepCount-1)
1043                                         return TWizardStepType::Finish;
1044                                 else
1045                                         return TWizardStepType::Step;
1046                         }
1047                         else
1048                                 return $type;
1049                 }
1050                 else
1051                         return $type;
1052         }
1053
1054         /**
1055          * Clears up everything within the wizard.
1056          */
1057         protected function reset()
1058         {
1059                 $this->getControls()->clear();
1060                 $this->_header=null;
1061                 $this->_stepContent=null;
1062                 $this->_sideBar=null;
1063                 $this->_sideBarDataList=null;
1064                 $this->_navigation=null;
1065                 $this->_startNavigation=null;
1066                 $this->_stepNavigation=null;
1067                 $this->_finishNavigation=null;
1068         }
1069
1070         /**
1071          * Creates child controls within the wizard
1072          */
1073         public function createChildControls()
1074         {
1075                 $this->reset();
1076                 $this->createSideBar();
1077                 $this->createHeader();
1078                 $this->createStepContent();
1079                 $this->createNavigation();
1080 //              $this->clearChildState();
1081         }
1082
1083         /**
1084          * Creates the wizard header.
1085          */
1086         protected function createHeader()
1087         {
1088                 $this->_header=new TPanel;
1089                 if(($template=$this->getHeaderTemplate())!==null)
1090                         $template->instantiateIn($this->_header);
1091                 else
1092                         $this->_header->getControls()->add($this->getHeaderText());
1093                 $this->getControls()->add($this->_header);
1094         }
1095
1096         /**
1097          * Creates the wizard side bar
1098          */
1099         protected function createSideBar()
1100         {
1101                 if($this->getShowSideBar())
1102                 {
1103                         if(($template=$this->getSideBarTemplate())===null)
1104                                 $template=new TWizardSideBarTemplate;
1105                         $this->_sideBar=new TPanel;
1106                         $template->instantiateIn($this->_sideBar);
1107                         $this->getControls()->add($this->_sideBar);
1108
1109                         if(($this->_sideBarDataList=$this->_sideBar->findControl(self::ID_SIDEBAR_LIST))!==null)
1110                         {
1111                                 $this->_sideBarDataList->attachEventHandler('OnItemCommand',array($this,'dataListItemCommand'));
1112                                 $this->_sideBarDataList->attachEventHandler('OnItemDataBound',array($this,'dataListItemDataBound'));
1113                                 $this->_sideBarDataList->setDataSource($this->getWizardSteps());
1114                                 $this->_sideBarDataList->setSelectedItemIndex($this->getActiveStepIndex());
1115                                 $this->_sideBarDataList->dataBind();
1116                         }
1117                 }
1118                 else
1119                 {
1120                         $this->_sideBar=new TPanel;
1121                         $this->getControls()->add($this->_sideBar);
1122                 }
1123         }
1124
1125         /**
1126          * Event handler for sidebar datalist's OnItemCommand event.
1127          * This method is used internally by wizard. It mainly
1128          * sets the active step index according to the button clicked in the sidebar.
1129          * @param mixed sender of the event
1130          * @param TDataListCommandEventParameter
1131          */
1132         public function dataListItemCommand($sender,$param)
1133         {
1134                 $item=$param->getItem();
1135                 if($param->getCommandName()===self::CMD_MOVETO)
1136                 {
1137                         $stepIndex=$this->getActiveStepIndex();
1138                         $newStepIndex=TPropertyValue::ensureInteger($param->getCommandParameter());
1139                         $navParam=new TWizardNavigationEventParameter($stepIndex);
1140                         $navParam->setNextStepIndex($newStepIndex);
1141
1142                         // if the button clicked causes validation which fails,
1143                         // by default we will cancel navigation to the new step
1144                         $button=$param->getCommandSource();
1145                         if(($button instanceof IButtonControl) && $button->getCausesValidation() && ($page=$this->getPage())!==null && !$page->getIsValid())
1146                                 $navParam->setCancelNavigation(true);
1147
1148                         $this->_activeStepIndexSet=false;
1149                         $this->onSideBarButtonClick($navParam);
1150                         $this->_cancelNavigation=$navParam->getCancelNavigation();
1151                         if(!$this->_cancelNavigation)
1152                         {
1153                                 if(!$this->_activeStepIndexSet && $this->allowNavigationToStep($newStepIndex))
1154                                         $this->setActiveStepIndex($newStepIndex);
1155                         }
1156                         else
1157                                 $this->setActiveStepIndex($stepIndex);
1158                 }
1159         }
1160
1161         /**
1162          * Event handler for sidebar datalist's OnItemDataBound event.
1163          * This method is used internally by wizard. It mainly configures
1164          * the buttons in the sidebar datalist.
1165          * @param mixed sender of the event
1166          * @param TDataListItemEventParameter
1167          */
1168         public function dataListItemDataBound($sender,$param)
1169         {
1170                 $item=$param->getItem();
1171                 $itemType=$item->getItemType();
1172                 if($itemType==='Item' || $itemType==='AlternatingItem' || $itemType==='SelectedItem' || $itemType==='EditItem')
1173                 {
1174                         if(($button=$item->findControl(self::ID_SIDEBAR_BUTTON))!==null)
1175                         {
1176                                 $step=$item->getData();
1177                                 if(($this->getStepType($step)===TWizardStepType::Complete))
1178                                         $button->setEnabled(false);
1179                                 if(($title=$step->getTitle())!=='')
1180                                         $button->setText($title);
1181                                 else
1182                                         $button->setText($step->getID(false));
1183                                 $index=$this->getWizardSteps()->indexOf($step);
1184                                 $button->setCommandName(self::CMD_MOVETO);
1185                                 $button->setCommandParameter("$index");
1186                         }
1187                 }
1188         }
1189
1190         /**
1191          * Creates wizard step content.
1192          */
1193         protected function createStepContent()
1194         {
1195                 foreach($this->getWizardSteps() as $step)
1196                 {
1197                         if($step instanceof TTemplatedWizardStep)
1198                                 $step->ensureChildControls();
1199                 }
1200                 $multiView=$this->getMultiView();
1201                 $this->_stepContent=new TPanel;
1202                 $this->_stepContent->getControls()->add($multiView);
1203                 $this->getControls()->add($this->_stepContent);
1204                 if($multiView->getViews()->getCount())
1205                         $multiView->setActiveViewIndex(0);
1206         }
1207
1208         /**
1209          * Creates navigation panel.
1210          */
1211         protected function createNavigation()
1212         {
1213                 $this->_navigation=new TPanel;
1214                 $this->getControls()->add($this->_navigation);
1215                 $controls=$this->_navigation->getControls();
1216                 foreach($this->getWizardSteps() as $step)
1217                 {
1218                         if($step instanceof TTemplatedWizardStep)
1219                         {
1220                                 $step->instantiateNavigationTemplate();
1221                                 if(($panel=$step->getNavigationContainer())!==null)
1222                                         $controls->add($panel);
1223                         }
1224                 }
1225                 $this->_startNavigation=$this->createStartNavigation();
1226                 $controls->add($this->_startNavigation);
1227                 $this->_stepNavigation=$this->createStepNavigation();
1228                 $controls->add($this->_stepNavigation);
1229                 $this->_finishNavigation=$this->createFinishNavigation();
1230                 $controls->add($this->_finishNavigation);
1231         }
1232
1233         /**
1234          * Creates start navigation panel.
1235          */
1236         protected function createStartNavigation()
1237         {
1238                 if(($template=$this->getStartNavigationTemplate())===null)
1239                         $template=new TWizardStartNavigationTemplate($this);
1240                 $navigation=new TWizardNavigationContainer;
1241                 $template->instantiateIn($navigation);
1242                 return $navigation;
1243         }
1244
1245         /**
1246          * Creates step navigation panel.
1247          */
1248         protected function createStepNavigation()
1249         {
1250                 if(($template=$this->getStepNavigationTemplate())===null)
1251                         $template=new TWizardStepNavigationTemplate($this);
1252                 $navigation=new TWizardNavigationContainer;
1253                 $template->instantiateIn($navigation);
1254                 return $navigation;
1255         }
1256
1257         /**
1258          * Creates finish navigation panel.
1259          */
1260         protected function createFinishNavigation()
1261         {
1262                 if(($template=$this->getFinishNavigationTemplate())===null)
1263                         $template=new TWizardFinishNavigationTemplate($this);
1264                 $navigation=new TWizardNavigationContainer;
1265                 $template->instantiateIn($navigation);
1266                 return $navigation;
1267         }
1268
1269         /**
1270          * Updates the sidebar datalist if any.
1271          * This method is invoked when any wizard step is changed.
1272          */
1273         public function wizardStepsChanged()
1274         {
1275                 if($this->_sideBarDataList!==null)
1276                 {
1277                         $this->_sideBarDataList->setDataSource($this->getWizardSteps());
1278                         $this->_sideBarDataList->setSelectedItemIndex($this->getActiveStepIndex());
1279                         $this->_sideBarDataList->dataBind();
1280                 }
1281         }
1282
1283         /**
1284          * Determines the index of the previous step based on history.
1285          * @param boolean whether the first item in the history stack should be popped
1286          * up after calling this method.
1287          */
1288         protected function getPreviousStepIndex($popStack)
1289         {
1290                 $history=$this->getHistory();
1291                 if($history->getCount()>=0)
1292                 {
1293                         $activeStepIndex=$this->getActiveStepIndex();
1294                         $previousStepIndex=-1;
1295                         if($popStack)
1296                         {
1297                                 $previousStepIndex=$history->pop();
1298                                 if($activeStepIndex===$previousStepIndex && $history->getCount()>0)
1299                                         $previousStepIndex=$history->pop();
1300                         }
1301                         else
1302                         {
1303                                 $previousStepIndex=$history->peek();
1304                                 if($activeStepIndex===$previousStepIndex && $history->getCount()>1)
1305                                 {
1306                                         $saveIndex=$history->pop();
1307                                         $previousStepIndex=$history->peek();
1308                                         $history->push($saveIndex);
1309                                 }
1310                         }
1311                         return $activeStepIndex===$previousStepIndex ? -1 : $previousStepIndex;
1312                 }
1313                 else
1314                         return -1;
1315         }
1316
1317         /**
1318          * @return boolean whether navigation to the previous step is allowed
1319          */
1320         protected function allowNavigationToPreviousStep()
1321         {
1322                 if(($index=$this->getPreviousStepIndex(false))!==-1)
1323                         return $this->getWizardSteps()->itemAt($index)->getAllowReturn();
1324                 else
1325                         return false;
1326         }
1327
1328         /**
1329          * @param integer index of the step
1330          * @return boolean whether navigation to the specified step is allowed
1331          */
1332         protected function allowNavigationToStep($index)
1333         {
1334                 if($this->getHistory()->contains($index))
1335                         return $this->getWizardSteps()->itemAt($index)->getAllowReturn();
1336                 else
1337                         return true;
1338         }
1339
1340         /**
1341          * Handles bubbled events.
1342          * This method mainly translate certain command events into
1343          * wizard-specific events.
1344          * @param mixed sender of the original command event
1345          * @param TEventParameter event parameter
1346          * @throws TInvalidDataValueException if a navigation command is associated with an invalid parameter
1347          */
1348         public function bubbleEvent($sender,$param)
1349         {
1350                 if($param instanceof TCommandEventParameter)
1351                 {
1352                         $command=$param->getCommandName();
1353                         if(strcasecmp($command,self::CMD_CANCEL)===0)
1354                         {
1355                                 $this->onCancelButtonClick($param);
1356                                 return true;
1357                         }
1358
1359                         $type=$this->getStepType($this->getActiveStep());
1360                         $index=$this->getActiveStepIndex();
1361                         $navParam=new TWizardNavigationEventParameter($index);
1362                         if(($sender instanceof IButtonControl) && $sender->getCausesValidation() && ($page=$this->getPage())!==null && !$page->getIsValid())
1363                                 $navParam->setCancelNavigation(true);
1364
1365                         $handled=false;
1366                         $movePrev=false;
1367                         $this->_activeStepIndexSet=false;
1368
1369                         if(strcasecmp($command,self::CMD_NEXT)===0)
1370                         {
1371                                 if($type!==self::ST_START && $type!==self::ST_STEP)
1372                                         throw new TInvalidDataValueException('wizard_command_invalid',self::CMD_NEXT);
1373                                 if($index<$this->getWizardSteps()->getCount()-1)
1374                                         $navParam->setNextStepIndex($index+1);
1375                                 $this->onNextButtonClick($navParam);
1376                                 $handled=true;
1377                         }
1378                         else if(strcasecmp($command,self::CMD_PREVIOUS)===0)
1379                         {
1380                                 if($type!==self::ST_FINISH && $type!==self::ST_STEP)
1381                                         throw new TInvalidDataValueException('wizard_command_invalid',self::CMD_PREVIOUS);
1382                                 $movePrev=true;
1383                                 if(($prevIndex=$this->getPreviousStepIndex(false))>=0)
1384                                         $navParam->setNextStepIndex($prevIndex);
1385                                 $this->onPreviousButtonClick($navParam);
1386                                 $handled=true;
1387                         }
1388                         else if(strcasecmp($command,self::CMD_COMPLETE)===0)
1389                         {
1390                                 if($type!==self::ST_FINISH)
1391                                         throw new TInvalidDataValueException('wizard_command_invalid',self::CMD_COMPLETE);
1392                                 if($index<$this->getWizardSteps()->getCount()-1)
1393                                         $navParam->setNextStepIndex($index+1);
1394                                 $this->onCompleteButtonClick($navParam);
1395                                 $handled=true;
1396                         }
1397                         else if(strcasecmp($command,self::CMD_MOVETO)===0)
1398                         {
1399                                 if($this->_cancelNavigation)  // may be set in onSideBarButtonClick
1400                                         $navParam->setCancelNavigation(true);
1401                                 $requestedStep=$param->getCommandParameter();
1402                                 if (!is_numeric($requestedStep))
1403                                 {
1404                                         $requestedIndex=-1;
1405                                         foreach ($this->getWizardSteps() as $index=>$step)
1406                                                 if ($step->getId()===$requestedStep)
1407                                                 {
1408                                                         $requestedIndex=$index;
1409                                                         break;
1410                                                 }
1411                                         if ($requestedIndex<0)
1412                                                 throw new TConfigurationException('wizard_step_invalid');
1413                                 }
1414                                 else
1415                                         $requestedIndex=TPropertyValue::ensureInteger($requestedStep);
1416                                 $navParam->setNextStepIndex($requestedIndex);
1417                                 $handled=true;
1418                         }
1419
1420                         if($handled)
1421                         {
1422                                 if(!$navParam->getCancelNavigation())
1423                                 {
1424                                         $nextStepIndex=$navParam->getNextStepIndex();
1425                                         if(!$this->_activeStepIndexSet && $this->allowNavigationToStep($nextStepIndex))
1426                                         {
1427                                                 if($movePrev)
1428                                                         $this->getPreviousStepIndex(true);  // pop out the previous move from history
1429                                                 $this->setActiveStepIndex($nextStepIndex);
1430                                         }
1431                                 }
1432                                 else
1433                                         $this->setActiveStepIndex($index);
1434                                 return true;
1435                         }
1436                 }
1437                 return false;
1438         }
1439 }
1440
1441
1442 /**
1443  * TWizardStep class.
1444  *
1445  * TWizardStep represents a wizard step. The wizard owning the step
1446  * can be obtained by {@link getWizard Wizard}.
1447  * To specify the type of the step, set {@link setStepType StepType};
1448  * For step title, set {@link setTitle Title}. If a step can be re-visited,
1449  * set {@link setAllowReturn AllowReturn} to true.
1450  *
1451  * @author Qiang Xue <qiang.xue@gmail.com>
1452  * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $
1453  * @package System.Web.UI.WebControls
1454  * @since 3.0
1455  */
1456 class TWizardStep extends TView
1457 {
1458         private $_wizard;
1459
1460         /**
1461          * @return TWizard the wizard owning this step
1462          */
1463         public function getWizard()
1464         {
1465                 return $this->_wizard;
1466         }
1467
1468         /**
1469          * Sets the wizard owning this step.
1470          * This method is used internally by {@link TWizard}.
1471          * @param TWizard the wizard owning this step
1472          */
1473         public function setWizard($wizard)
1474         {
1475                 $this->_wizard=$wizard;
1476         }
1477
1478         /**
1479          * @return string the title for this step.
1480          */
1481         public function getTitle()
1482         {
1483                 return $this->getViewState('Title','');
1484         }
1485
1486         /**
1487          * @param string the title for this step.
1488          */
1489         public function setTitle($value)
1490         {
1491                 $this->setViewState('Title',$value,'');
1492                 if($this->_wizard)
1493                         $this->_wizard->wizardStepsChanged();
1494         }
1495
1496         /**
1497          * @return boolean whether this step can be re-visited. Default to true.
1498          */
1499         public function getAllowReturn()
1500         {
1501                 return $this->getViewState('AllowReturn',true);
1502         }
1503
1504         /**
1505          * @param boolean whether this step can be re-visited.
1506          */
1507         public function setAllowReturn($value)
1508         {
1509                 $this->setViewState('AllowReturn',TPropertyValue::ensureBoolean($value),true);
1510         }
1511
1512         /**
1513          * @return TWizardStepType the wizard step type. Defaults to TWizardStepType::Auto.
1514          */
1515         public function getStepType()
1516         {
1517                 return $this->getViewState('StepType',TWizardStepType::Auto);
1518         }
1519
1520         /**
1521          * @param TWizardStepType the wizard step type.
1522          */
1523         public function setStepType($type)
1524         {
1525                 $type=TPropertyValue::ensureEnum($type,'TWizardStepType');
1526                 if($type!==$this->getStepType())
1527                 {
1528                         $this->setViewState('StepType',$type,TWizardStepType::Auto);
1529                         if($this->_wizard)
1530                                 $this->_wizard->wizardStepsChanged();
1531                 }
1532         }
1533 }
1534
1535
1536 /**
1537  * TCompleteWizardStep class.
1538  *
1539  * TCompleteWizardStep represents a wizard step of type TWizardStepType::Complete.
1540  *
1541  * @author Qiang Xue <qiang.xue@gmail.com>
1542  * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $
1543  * @package System.Web.UI.WebControls
1544  * @since 3.0
1545  */
1546 class TCompleteWizardStep extends TWizardStep
1547 {
1548         /**
1549          * @return TWizardStepType the wizard step type. Always TWizardStepType::Complete.
1550          */
1551         public function getStepType()
1552         {
1553                 return TWizardStepType::Complete;
1554         }
1555
1556         /**
1557          * @param string the wizard step type.
1558          * @throws TInvalidOperationException whenever this method is invoked.
1559          */
1560         public function setStepType($value)
1561         {
1562                 throw new TInvalidOperationException('completewizardstep_steptype_readonly');
1563         }
1564 }
1565
1566
1567 /**
1568  * TTemplatedWizardStep class.
1569  *
1570  * TTemplatedWizardStep represents a wizard step whose content and navigation
1571  * can be customized using templates. To customize the step content, specify
1572  * {@link setContentTemplate ContentTemplate}. To customize navigation specific
1573  * to the step, specify {@link setNavigationTemplate NavigationTemplate}. Note,
1574  * if the navigation template is not specified, default navigation will be used.
1575  *
1576  * @author Qiang Xue <qiang.xue@gmail.com>
1577  * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $
1578  * @package System.Web.UI.WebControls
1579  * @since 3.0
1580  */
1581 class TTemplatedWizardStep extends TWizardStep implements INamingContainer
1582 {
1583         /**
1584          * @var ITemplate the template for displaying the navigation UI of a wizard step.
1585          */
1586         private $_navigationTemplate=null;
1587         /**
1588          * @var ITemplate the template for displaying the content within the wizard step.
1589          */
1590         private $_contentTemplate=null;
1591         /**
1592          * @var TWizardNavigationContainer
1593          */
1594         private $_navigationContainer=null;
1595
1596         /**
1597          * Creates child controls.
1598          * This method mainly instantiates the content template, if any.
1599          */
1600         public function createChildControls()
1601         {
1602                 $this->getControls()->clear();
1603                 if($this->_contentTemplate)
1604                         $this->_contentTemplate->instantiateIn($this);
1605         }
1606
1607         /**
1608          * Ensures child controls are created.
1609          * @param mixed event parameter
1610          */
1611         public function onInit($param)
1612         {
1613                 parent::onInit($param);
1614                 $this->ensureChildControls();
1615         }
1616
1617         /**
1618          * @return ITemplate the template for the content of the wizard step.
1619          */
1620         public function getContentTemplate()
1621         {
1622                 return $this->_contentTemplate;
1623         }
1624
1625         /**
1626          * @param ITemplate the template for the content of the wizard step.
1627          */
1628         public function setContentTemplate($value)
1629         {
1630                 $this->_contentTemplate=$value;
1631         }
1632
1633         /**
1634          * @return ITemplate the template for displaying the navigation UI of a wizard step. Defaults to null.
1635          */
1636         public function getNavigationTemplate()
1637         {
1638                 return $this->_navigationTemplate;
1639         }
1640
1641         /**
1642          * @param ITemplate the template for displaying the navigation UI of a wizard step.
1643          */
1644         public function setNavigationTemplate($value)
1645         {
1646                 $this->_navigationTemplate=$value;
1647         }
1648
1649         /**
1650          * @return TWizardNavigationContainer the control containing the navigation.
1651          * It could be null if no navigation template is specified.
1652          */
1653         public function getNavigationContainer()
1654         {
1655                 return $this->_navigationContainer;
1656         }
1657
1658         /**
1659          * Instantiates the navigation template if any
1660          */
1661         public function instantiateNavigationTemplate()
1662         {
1663                 if(!$this->_navigationContainer && $this->_navigationTemplate)
1664                 {
1665                         $this->_navigationContainer=new TWizardNavigationContainer;
1666                         $this->_navigationTemplate->instantiateIn($this->_navigationContainer);
1667                 }
1668         }
1669 }
1670
1671
1672 /**
1673  * TWizardStepCollection class.
1674  *
1675  * TWizardStepCollection represents the collection of wizard steps owned
1676  * by a {@link TWizard}.
1677  *
1678  * @author Qiang Xue <qiang.xue@gmail.com>
1679  * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $
1680  * @package System.Web.UI.WebControls
1681  * @since 3.0
1682  */
1683 class TWizardStepCollection extends TList
1684 {
1685         /**
1686          * @var TWizard
1687          */
1688         private $_wizard;
1689
1690         /**
1691          * Constructor.
1692          * @param TWizard wizard that owns this collection
1693          */
1694         public function __construct(TWizard $wizard)
1695         {
1696                 $this->_wizard=$wizard;
1697         }
1698
1699         /**
1700          * Inserts an item at the specified position.
1701          * This method overrides the parent implementation by checking if
1702          * the item being added is a {@link TWizardStep}.
1703          * @param integer the speicified position.
1704          * @param mixed new item
1705          * @throws TInvalidDataTypeException if the item being added is not TWizardStep.
1706          */
1707         public function insertAt($index,$item)
1708         {
1709                 if($item instanceof TWizardStep)
1710                 {
1711                         parent::insertAt($index,$item);
1712                         $this->_wizard->getMultiView()->getViews()->insertAt($index,$item);
1713                         $this->_wizard->addedWizardStep($item);
1714                 }
1715                 else
1716                         throw new TInvalidDataTypeException('wizardstepcollection_wizardstep_required');
1717         }
1718
1719         /**
1720          * Removes an item at the specified position.
1721          * @param integer the index of the item to be removed.
1722          * @return mixed the removed item.
1723          */
1724         public function removeAt($index)
1725         {
1726                 $step=parent::removeAt($index);
1727                 $this->_wizard->getMultiView()->getViews()->remove($step);
1728                 $this->_wizard->removedWizardStep($step);
1729                 return $step;
1730         }
1731 }
1732
1733
1734 /**
1735  * TWizardNavigationContainer class.
1736  *
1737  * TWizardNavigationContainer represents a control containing
1738  * a wizard navigation. The navigation may contain a few buttons, including
1739  * {@link getPreviousButton PreviousButton}, {@link getNextButton NextButton},
1740  * {@link getCancelButton CancelButton}, {@link getCompleteButton CompleteButton}.
1741  *
1742  * @author Qiang Xue <qiang.xue@gmail.com>
1743  * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $
1744  * @package System.Web.UI.WebControls
1745  * @since 3.0
1746  */
1747 class TWizardNavigationContainer extends TControl implements INamingContainer
1748 {
1749         private $_previousButton=null;
1750         private $_nextButton=null;
1751         private $_cancelButton=null;
1752         private $_completeButton=null;
1753
1754         /**
1755          * @return mixed the previous button
1756          */
1757         public function getPreviousButton()
1758         {
1759                 return $this->_previousButton;
1760         }
1761
1762         /**
1763          * @param mixed the previous button
1764          */
1765         public function setPreviousButton($value)
1766         {
1767                 $this->_previousButton=$value;
1768         }
1769
1770         /**
1771          * @return mixed the next button
1772          */
1773         public function getNextButton()
1774         {
1775                 return $this->_nextButton;
1776         }
1777
1778         /**
1779          * @param mixed the next button
1780          */
1781         public function setNextButton($value)
1782         {
1783                 $this->_nextButton=$value;
1784         }
1785
1786         /**
1787          * @return mixed the cancel button
1788          */
1789         public function getCancelButton()
1790         {
1791                 return $this->_cancelButton;
1792         }
1793
1794         /**
1795          * @param mixed the cancel button
1796          */
1797         public function setCancelButton($value)
1798         {
1799                 $this->_cancelButton=$value;
1800         }
1801
1802         /**
1803          * @return mixed the complete button
1804          */
1805         public function getCompleteButton()
1806         {
1807                 return $this->_completeButton;
1808         }
1809
1810         /**
1811          * @param mixed the complete button
1812          */
1813         public function setCompleteButton($value)
1814         {
1815                 $this->_completeButton=$value;
1816         }
1817 }
1818
1819
1820 /**
1821  * TWizardNavigationEventParameter class.
1822  *
1823  * TWizardNavigationEventParameter represents the parameter for
1824  * {@link TWizard}'s navigation events.
1825  *
1826  * The index of the currently active step can be obtained from
1827  * {@link getCurrentStepIndex CurrentStepIndex}, while the index
1828  * of the candidate new step is in {@link getNextStepIndex NextStepIndex}.
1829  * By modifying {@link setNextStepIndex NextStepIndex}, the new step
1830  * can be changed to another one. If there is anything wrong with
1831  * the navigation and it is not wanted, set {@link setCancelNavigation CancelNavigation}
1832  * to true.
1833  *
1834  * @author Qiang Xue <qiang.xue@gmail.com>
1835  * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $
1836  * @package System.Web.UI.WebControls
1837  * @since 3.0
1838  */
1839 class TWizardNavigationEventParameter extends TEventParameter
1840 {
1841         private $_cancel=false;
1842         private $_currentStep;
1843         private $_nextStep;
1844
1845         /**
1846          * Constructor.
1847          * @param integer current step index
1848          */
1849         public function __construct($currentStep)
1850         {
1851                 $this->_currentStep=$currentStep;
1852                 $this->_nextStep=$currentStep;
1853         }
1854
1855         /**
1856          * @return integer the zero-based index of the currently active step.
1857          */
1858         public function getCurrentStepIndex()
1859         {
1860                 return $this->_currentStep;
1861         }
1862
1863         /**
1864          * @return integer the zero-based index of the next step. Default to {@link getCurrentStepIndex CurrentStepIndex}.
1865          */
1866         public function getNextStepIndex()
1867         {
1868                 return $this->_nextStep;
1869         }
1870
1871         /**
1872          * @param integer the zero-based index of the next step.
1873          */
1874         public function setNextStepIndex($index)
1875         {
1876                 $this->_nextStep=TPropertyValue::ensureInteger($index);
1877         }
1878
1879         /**
1880          * @return boolean whether navigation to the next step should be canceled. Default to false.
1881          */
1882         public function getCancelNavigation()
1883         {
1884                 return $this->_cancel;
1885         }
1886
1887         /**
1888          * @param boolean whether navigation to the next step should be canceled.
1889          */
1890         public function setCancelNavigation($value)
1891         {
1892                 $this->_cancel=TPropertyValue::ensureBoolean($value);
1893         }
1894 }
1895
1896 /**
1897  * TWizardSideBarTemplate class.
1898  * TWizardSideBarTemplate is the default template for wizard sidebar.
1899  * @author Qiang Xue <qiang.xue@gmail.com>
1900  * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $
1901  * @package System.Web.UI.WebControls
1902  * @since 3.0
1903  */
1904 class TWizardSideBarTemplate extends TComponent implements ITemplate
1905 {
1906         /**
1907          * Instantiates the template.
1908          * It creates a {@link TDataList} control.
1909          * @param TControl parent to hold the content within the template
1910          */
1911         public function instantiateIn($parent)
1912         {
1913                 $dataList=new TDataList;
1914                 $dataList->setID(TWizard::ID_SIDEBAR_LIST);
1915                 $dataList->getSelectedItemStyle()->getFont()->setBold(true);
1916                 $dataList->setItemTemplate(new TWizardSideBarListItemTemplate);
1917                 $parent->getControls()->add($dataList);
1918         }
1919 }
1920
1921 /**
1922  * TWizardSideBarListItemTemplate class.
1923  * TWizardSideBarListItemTemplate is the default template for each item in the sidebar datalist.
1924  * @author Qiang Xue <qiang.xue@gmail.com>
1925  * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $
1926  * @package System.Web.UI.WebControls
1927  * @since 3.0
1928  */
1929 class TWizardSideBarListItemTemplate extends TComponent implements ITemplate
1930 {
1931         /**
1932          * Instantiates the template.
1933          * It creates a {@link TLinkButton}.
1934          * @param TControl parent to hold the content within the template
1935          */
1936         public function instantiateIn($parent)
1937         {
1938                 $button=new TLinkButton;
1939                 $button->setID(TWizard::ID_SIDEBAR_BUTTON);
1940                 $parent->getControls()->add($button);
1941         }
1942 }
1943
1944 /**
1945  * TWizardNavigationTemplate class.
1946  * TWizardNavigationTemplate is the base class for various navigation templates.
1947  * @author Qiang Xue <qiang.xue@gmail.com>
1948  * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $
1949  * @package System.Web.UI.WebControls
1950  * @since 3.0
1951  */
1952 class TWizardNavigationTemplate extends TComponent implements ITemplate
1953 {
1954         private $_wizard;
1955
1956         /**
1957          * Constructor.
1958          * @param TWizard the wizard owning this template
1959          */
1960         public function __construct($wizard)
1961         {
1962                 $this->_wizard=$wizard;
1963         }
1964
1965         /**
1966          * @return TWizard the wizard owning this template
1967          */
1968         public function getWizard()
1969         {
1970                 return $this->_wizard;
1971         }
1972
1973         /**
1974          * Instantiates the template.
1975          * Derived classes should override this method.
1976          * @param TControl parent to hold the content within the template
1977          */
1978         public function instantiateIn($parent)
1979         {
1980         }
1981
1982         /**
1983          * Creates a navigation button.
1984          * It creates a {@link TButton}, {@link TLinkButton}, or {@link TImageButton},
1985          * depending on the given parameters.
1986          * @param TWizardNavigationButtonStyle button style
1987          * @param boolean whether the button should cause validation
1988          * @param string command name for the button's OnCommand event
1989          * @throws TInvalidDataValueException if the button type is not recognized
1990          */
1991         protected function createNavigationButton($buttonStyle,$causesValidation,$commandName)
1992         {
1993                 switch($buttonStyle->getButtonType())
1994                 {
1995                         case TWizardNavigationButtonType::Button:
1996                                 $button=new TButton;
1997                                 break;
1998                         case TWizardNavigationButtonType::Link:
1999                                 $button=new TLinkButton;
2000                                 break;
2001                         case TWizardNavigationButtonType::Image:
2002                                 $button=new TImageButton;
2003                                 $button->setImageUrl($buttonStyle->getImageUrl());
2004                                 break;
2005                         default:
2006                                 throw new TInvalidDataValueException('wizard_buttontype_unknown',$buttonStyle->getButtonType());
2007                 }
2008                 $button->setText($buttonStyle->getButtonText());
2009                 $button->setCausesValidation($causesValidation);
2010                 $button->setCommandName($commandName);
2011                 return $button;
2012         }
2013 }
2014
2015 /**
2016  * TWizardStartNavigationTemplate class.
2017  * TWizardStartNavigationTemplate is the template used as default wizard start navigation panel.
2018  * It consists of two buttons, Next and Cancel.
2019  * @author Qiang Xue <qiang.xue@gmail.com>
2020  * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $
2021  * @package System.Web.UI.WebControls
2022  * @since 3.0
2023  */
2024 class TWizardStartNavigationTemplate extends TWizardNavigationTemplate
2025 {
2026         /**
2027          * Instantiates the template.
2028          * @param TControl parent to hold the content within the template
2029          */
2030         public function instantiateIn($parent)
2031         {
2032                 $nextButton=$this->createNavigationButton($this->getWizard()->getStartNextButtonStyle(),true,TWizard::CMD_NEXT);
2033                 $cancelButton=$this->createNavigationButton($this->getWizard()->getCancelButtonStyle(),false,TWizard::CMD_CANCEL);
2034
2035                 $controls=$parent->getControls();
2036                 $controls->add($nextButton);
2037                 $controls->add("\n");
2038                 $controls->add($cancelButton);
2039
2040                 $parent->setNextButton($nextButton);
2041                 $parent->setCancelButton($cancelButton);
2042         }
2043 }
2044
2045 /**
2046  * TWizardFinishNavigationTemplate class.
2047  * TWizardFinishNavigationTemplate is the template used as default wizard finish navigation panel.
2048  * It consists of three buttons, Previous, Complete and Cancel.
2049  * @author Qiang Xue <qiang.xue@gmail.com>
2050  * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $
2051  * @package System.Web.UI.WebControls
2052  * @since 3.0
2053  */
2054 class TWizardFinishNavigationTemplate extends TWizardNavigationTemplate
2055 {
2056         /**
2057          * Instantiates the template.
2058          * @param TControl parent to hold the content within the template
2059          */
2060         public function instantiateIn($parent)
2061         {
2062                 $previousButton=$this->createNavigationButton($this->getWizard()->getFinishPreviousButtonStyle(),false,TWizard::CMD_PREVIOUS);
2063                 $completeButton=$this->createNavigationButton($this->getWizard()->getFinishCompleteButtonStyle(),true,TWizard::CMD_COMPLETE);
2064                 $cancelButton=$this->createNavigationButton($this->getWizard()->getCancelButtonStyle(),false,TWizard::CMD_CANCEL);
2065
2066                 $controls=$parent->getControls();
2067                 $controls->add($previousButton);
2068                 $controls->add("\n");
2069                 $controls->add($completeButton);
2070                 $controls->add("\n");
2071                 $controls->add($cancelButton);
2072
2073                 $parent->setPreviousButton($previousButton);
2074                 $parent->setCompleteButton($completeButton);
2075                 $parent->setCancelButton($cancelButton);
2076         }
2077 }
2078
2079 /**
2080  * TWizardStepNavigationTemplate class.
2081  * TWizardStepNavigationTemplate is the template used as default wizard step navigation panel.
2082  * It consists of three buttons, Previous, Next and Cancel.
2083  * @author Qiang Xue <qiang.xue@gmail.com>
2084  * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $
2085  * @package System.Web.UI.WebControls
2086  * @since 3.0
2087  */
2088 class TWizardStepNavigationTemplate extends TWizardNavigationTemplate
2089 {
2090         /**
2091          * Instantiates the template.
2092          * @param TControl parent to hold the content within the template
2093          */
2094         public function instantiateIn($parent)
2095         {
2096                 $previousButton=$this->createNavigationButton($this->getWizard()->getStepPreviousButtonStyle(),false,TWizard::CMD_PREVIOUS);
2097                 $nextButton=$this->createNavigationButton($this->getWizard()->getStepNextButtonStyle(),true,TWizard::CMD_NEXT);
2098                 $cancelButton=$this->createNavigationButton($this->getWizard()->getCancelButtonStyle(),false,TWizard::CMD_CANCEL);
2099
2100                 $controls=$parent->getControls();
2101                 $controls->add($previousButton);
2102                 $controls->add("\n");
2103                 $controls->add($nextButton);
2104                 $controls->add("\n");
2105                 $controls->add($cancelButton);
2106
2107                 $parent->setPreviousButton($previousButton);
2108                 $parent->setNextButton($nextButton);
2109                 $parent->setCancelButton($cancelButton);
2110         }
2111 }
2112
2113
2114 /**
2115  * TWizardNavigationButtonType class.
2116  * TWizardNavigationButtonType defines the enumerable type for the possible types of buttons
2117  * that can be used in the navigation part of a {@link TWizard}.
2118  *
2119  * The following enumerable values are defined:
2120  * - Button: a regular click button
2121  * - Image: an image button
2122  * - Link: a hyperlink button
2123  *
2124  * @author Qiang Xue <qiang.xue@gmail.com>
2125  * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $
2126  * @package System.Web.UI.WebControls
2127  * @since 3.0.4
2128  */
2129 class TWizardNavigationButtonType extends TEnumerable
2130 {
2131         const Button='Button';
2132         const Image='Image';
2133         const Link='Link';
2134 }
2135
2136
2137 /**
2138  * TWizardStepType class.
2139  * TWizardStepType defines the enumerable type for the possible types of {@link TWizard wizard} steps.
2140  *
2141  * The following enumerable values are defined:
2142  * - Auto: the type is automatically determined based on the location of the wizard step in the whole step collection.
2143  * - Complete: the step is the last summary step.
2144  * - Start: the step is the first step
2145  * - Step: the step is between the begin and the end steps.
2146  * - Finish: the last step before the Complete step.
2147  *
2148  * @author Qiang Xue <qiang.xue@gmail.com>
2149  * @version $Id: TWizard.php 3274 2013-02-15 08:32:25Z ctrlaltca $
2150  * @package System.Web.UI.WebControls
2151  * @since 3.0.4
2152  */
2153 class TWizardStepType extends TEnumerable
2154 {
2155         const Auto='Auto';
2156         const Complete='Complete';
2157         const Start='Start';
2158         const Step='Step';
2159         const Finish='Finish';
2160 }
2161