001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.wicket.extensions.wizard;
018
019import org.apache.wicket.Component;
020import org.apache.wicket.feedback.ContainerFeedbackMessageFilter;
021import org.apache.wicket.markup.html.WebMarkupContainer;
022import org.apache.wicket.markup.html.form.Form;
023import org.apache.wicket.markup.html.panel.FeedbackPanel;
024import org.apache.wicket.markup.html.panel.Panel;
025
026
027/**
028 * A wizard is a dialog component that takes users through a number of steps to complete a task. It
029 * has common functionality like a next, previous, finish and cancel button, and it uses a
030 * {@link IWizardModel} to navigate through the steps.
031 * <p>
032 * Before you can use the wizard component, it needs to be initialized with a model. You do this by
033 * calling {@link #init(IWizardModel)} with the wizard model you intent to use.
034 * </p>
035 * 
036 * <p>
037 * This default implementation should be useful for basic cases, if the layout is exactly what you
038 * need. If you want to provide your own layout and/ or have more or less components (e.g. you want
039 * to additionally provide an overview component), you can override this class and add the
040 * components you want yourself using methods like {@link #newButtonBar(String)} et-cetera.
041 * </p>
042 * 
043 * @author Eelco Hillenius
044 */
045public class Wizard extends Panel implements IWizardModelListener, IWizard
046{
047        /** Component id of the buttons panel as used by the default wizard panel. */
048        public static final String BUTTONS_ID = "buttons";
049
050        /** Component id of the feedback panel as used by the default wizard panel. */
051        public static final String FEEDBACK_ID = "feedback";
052
053        /** Component id of the header panel as used by the default wizard panel. */
054        public static final String HEADER_ID = "header";
055
056        /** Component id of the overview panel as used by the default wizard panel. */
057        public static final String OVERVIEW_ID = "overview";
058
059        /** Component id of the form as used by the default wizard panel. */
060        public static final String FORM_ID = "form";
061
062        /**
063         * Component id of the view panel (where the main wizard contents go) as used by the default
064         * wizard panel.
065         */
066        public static final String VIEW_ID = "view";
067
068        private static final long serialVersionUID = 1L;
069
070        /**
071         * The form in which the view is nested, and on which the wizard buttons work.
072         */
073        private Form<?> form;
074
075        /** The wizard model. */
076        private IWizardModel wizardModel;
077
078        /**
079         * Construct. Adds the default style.
080         * <p>
081         * If you override this class, it makes sense to call this constructor (super(id)), then - in
082         * your constructor - construct a transition model and then call {@link #init(IWizardModel)} to
083         * initialize the wizard.
084         * </p>
085         * <p>
086         * This constructor is not meant for normal clients of this class
087         * </p>
088         * 
089         * @param id
090         *            The component model
091         */
092        public Wizard(final String id)
093        {
094                super(id);
095        }
096
097        /**
098         * Construct with a transition model. Adds the default style.
099         * <p>
100         * For most clients, this is typically the right constructor to use.
101         * </p>
102         * 
103         * @param id
104         *            The component id
105         * @param wizardModel
106         *            The transitions model
107         */
108        public Wizard(final String id, final IWizardModel wizardModel)
109        {
110                super(id);
111
112                init(wizardModel);
113        }
114
115        /**
116         * Convenience method to get the active step from the model.
117         * 
118         * @return The active step
119         */
120        public final IWizardStep getActiveStep()
121        {
122                return getWizardModel().getActiveStep();
123        }
124
125        /**
126         * Gets the form in which the view is nested, and on which the wizard buttons work.
127         * 
128         * @return The wizard form
129         */
130        public Form<?> getForm()
131        {
132                return form;
133        }
134
135        /**
136         * @see org.apache.wicket.extensions.wizard.IWizard#getWizardModel()
137         */
138        @Override
139        public final IWizardModel getWizardModel()
140        {
141                return wizardModel;
142        }
143
144        /**
145         * Turn versioning off for wizards. This works best when the wizard is <strong>not</strong>
146         * accessed from bookmarkable pages, so that the url doesn't change at all.
147         * 
148         * @return False
149         * @see org.apache.wicket.Component#isVersioned()
150         */
151        @Override
152        public boolean isVersioned()
153        {
154                return false;
155        }
156
157        /**
158         * @see org.apache.wicket.extensions.wizard.IWizardModelListener#onActiveStepChanged(org.apache.wicket.extensions.wizard.IWizardStep)
159         */
160        @Override
161        public void onActiveStepChanged(final IWizardStep newStep)
162        {
163                form.replace(newStep.getView(VIEW_ID, this, this));
164                form.replace(newStep.getHeader(HEADER_ID, this, this));
165        }
166
167        /**
168         * Called when the wizard is canceled.
169         */
170        @Override
171        public void onCancel()
172        {
173        }
174
175        /**
176         * Called when the wizard is finished.
177         */
178        @Override
179        public void onFinish()
180        {
181        }
182
183        /**
184         * Initialize this wizard with a transition model.
185         * <p>
186         * If you constructed this wizard using a constructor without the transitions model argument,
187         * <strong>you must</strong> call this method prior to actually using it.
188         * </p>
189         * 
190         * @param wizardModel
191         */
192        protected void init(final IWizardModel wizardModel)
193        {
194                if (wizardModel == null)
195                {
196                        throw new IllegalArgumentException("argument wizardModel must be not null");
197                }
198
199                this.wizardModel = wizardModel;
200
201                form = newForm(FORM_ID);
202                addOrReplace(form);
203                // dummy view to be replaced
204                form.addOrReplace(new WebMarkupContainer(HEADER_ID));
205                form.addOrReplace(newFeedbackPanel(FEEDBACK_ID));
206                // add dummy view; will be replaced on initialization
207                form.addOrReplace(new WebMarkupContainer(VIEW_ID));
208                form.addOrReplace(newButtonBar(BUTTONS_ID));
209                form.addOrReplace(newOverviewBar(OVERVIEW_ID));
210
211                wizardModel.addListener(this);
212
213                // reset model to prepare for action
214                wizardModel.reset();
215        }
216
217        /**
218         * Create a new button bar. Clients can override this method to provide a custom button bar.
219         * 
220         * @param id
221         *            The id to be used to construct the component
222         * 
223         * @return A new button bar
224         */
225        protected Component newButtonBar(final String id)
226        {
227                return new WizardButtonBar(id, this);
228        }
229
230        /**
231         * Create a new feedback panel. Clients can override this method to provide a custom feedback
232         * panel.
233         * 
234         * @param id
235         *            The id to be used to construct the component
236         * 
237         * @return A new feedback panel
238         */
239        protected Component newFeedbackPanel(final String id)
240        {
241                return new FeedbackPanel(id, new ContainerFeedbackMessageFilter(this));
242        }
243
244        /**
245         * Create a new form. Clients can override this method to provide a custom {@link Form}.
246         * 
247         * @param <E>
248         *            The form's model object type
249         * 
250         * @param id
251         *            The id to be used to construct the component
252         * @return a new form
253         */
254        protected <E> Form<E> newForm(final String id)
255        {
256                return new Form<>(id);
257        }
258
259        /**
260         * Create a new overview bar. Clients can override this method to provide a custom bar.
261         * 
262         * @param id
263         *            The id to be used to construct the component
264         * 
265         * @return A new overview bar
266         */
267        protected Component newOverviewBar(final String id)
268        {
269                // return a dummy component by default as we don't have an overview
270                // component
271                return new WebMarkupContainer(id).setVisible(false);
272        }
273}