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}