001 /*
002 * Copyright 2002-2004 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005 * use this file except in compliance with the License. You may obtain a copy of
006 * the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013 * License for the specific language governing permissions and limitations under
014 * the License.
015 */
016 package org.springframework.richclient.dialog;
017
018 import java.awt.Dimension;
019 import java.beans.PropertyChangeEvent;
020 import java.beans.PropertyChangeListener;
021 import java.util.ArrayList;
022 import java.util.Iterator;
023 import java.util.List;
024
025 import javax.swing.JComponent;
026
027 import org.springframework.richclient.core.UIConstants;
028 import org.springframework.richclient.form.Form;
029 import org.springframework.richclient.util.GuiStandardUtils;
030 import org.springframework.richclient.util.LabelUtils;
031 import org.springframework.util.Assert;
032
033 /**
034 * An implementation of the <code>DialogPage</code> interface that collects a
035 * group of dialog pages together so that they can be used in place of a single
036 * <code>DialogPage</code>
037 * <p>
038 * It is expected that subclasses will provide various ways of displaying the
039 * active page and navigating to inactive child pages.
040 * <p>
041 * Services of a <code>CompositeDialogPage</code> include:
042 * <ul>
043 * <li>creating the page controls of the child pages and determining the
044 * largest sized of these controls.
045 * <li>keeping track of an active page that will be used to provide
046 * messages/titles for the composite.
047 * <li>pageComplete property of composite is true if all child pages are
048 * complete
049 * </ul>
050 *
051 * @author oliverh
052 * @see org.springframework.richclient.dialog.TabbedDialogPage
053 */
054 public abstract class CompositeDialogPage extends AbstractDialogPage {
055
056 private final ChildChangeHandler childChangeHandler = new ChildChangeHandler();
057
058 private List pages = new ArrayList();
059
060 private int largestPageWidth;
061
062 private int largestPageHeight;
063
064 private boolean autoConfigureChildPages = true;
065
066 private DialogPage activePage;
067
068 public CompositeDialogPage(String pageId) {
069 super(pageId, true);
070 }
071
072 public CompositeDialogPage(String pageId, boolean autoConfigure) {
073 super(pageId, autoConfigure);
074 }
075
076 public void setAutoConfigureChildPages(boolean autoConfigure) {
077 this.autoConfigureChildPages = autoConfigure;
078 }
079
080 /**
081 * Adds a DialogPage to the list of pages managed by this
082 * CompositeDialogPage.
083 *
084 * @param page the page to add
085 */
086 public void addPage(DialogPage page) {
087 pages.add(page);
088 if (autoConfigureChildPages) {
089 String id = getId() + "." + page.getId();
090 getObjectConfigurer().configure(page, id);
091 }
092 }
093
094 /**
095 * Adds a new page to the list of pages managed by this CompositeDialogPage.
096 * The page is created by wrapping the form page in a FormBackedDialogPage.
097 *
098 * @param formPage the form page to be insterted
099 * @return the DialogPage that wraps formPage
100 */
101 public DialogPage addForm(Form form) {
102 DialogPage page = createDialogPage(form);
103 addPage(page);
104 return page;
105 }
106
107 /**
108 * Adds an array DialogPage to the list of pages managed by this
109 * CompositeDialogPage.
110 *
111 * @param pages the pages to add
112 */
113 public void addPages(DialogPage[] pages) {
114 for (int i = 0; i < pages.length; i++) {
115 addPage(pages[i]);
116 }
117 }
118
119 /**
120 * Subclasses should extend if extra pages need to be added before the
121 * composite creates its control. New pages should be added by calling
122 * <code>addPage</code>.
123 */
124 protected void addPages() {
125 }
126
127 protected List getPages() {
128 return pages;
129 }
130
131 /**
132 * Sets the active page of this CompositeDialogPage.
133 *
134 * @param activePage the page to be made active. Must be one of the child
135 * pages.
136 */
137 public void setActivePage(DialogPage activePage) {
138 DialogPage oldPage = this.activePage;
139
140 Assert.isTrue(activePage == null || pages.contains(activePage));
141 if (oldPage == activePage) {
142 return;
143 }
144 this.activePage = activePage;
145
146 updateMessage();
147 if (oldPage != null) {
148 updatePageLabels(oldPage);
149 }
150 if (activePage != null) {
151 updatePageLabels(activePage);
152 }
153 }
154
155 /**
156 * Gets the active page of this CompositeDialogPage.
157 *
158 * @return the active page; or null if no page is active.
159 */
160 public DialogPage getActivePage() {
161 return activePage;
162 }
163
164 protected DialogPage createDialogPage(Form form) {
165 return new FormBackedDialogPage(form, !autoConfigureChildPages);
166 }
167
168 protected void createPageControls() {
169 addPages();
170 Assert.notEmpty(getPages(), "Pages must have been added first");
171 for (Iterator i = pages.iterator(); i.hasNext();) {
172 DialogPage page = (DialogPage) i.next();
173 prepareDialogPage(page);
174 }
175 }
176
177 /**
178 * Prepare a dialog page - Add our property listeners and configure the
179 * control's look.
180 * @param page to process
181 */
182 protected void prepareDialogPage(DialogPage page) {
183 page.addPropertyChangeListener(childChangeHandler);
184 JComponent c = page.getControl();
185 GuiStandardUtils.attachDialogBorder(c);
186 Dimension size = c.getPreferredSize();
187 if (size.width > largestPageWidth) {
188 largestPageWidth = size.width;
189 }
190 if (size.height > largestPageHeight) {
191 largestPageHeight = size.height;
192 }
193 }
194
195 /**
196 * Get the size of the largest page added so far.
197 * @return Dimension of largest page
198 */
199 public Dimension getLargestPageSize() {
200 return new Dimension(largestPageWidth + UIConstants.ONE_SPACE, largestPageHeight + UIConstants.ONE_SPACE);
201 }
202
203 protected void updatePageComplete(DialogPage page) {
204 boolean pageComplete = true;
205 for (Iterator i = pages.iterator(); i.hasNext();) {
206 if (!((DialogPage) i.next()).isPageComplete()) {
207 pageComplete = false;
208 break;
209 }
210 }
211 setPageComplete(pageComplete);
212 }
213
214 protected void updatePageEnabled(DialogPage page) {
215
216 }
217
218 protected void updatePageLabels(DialogPage page) {
219
220 }
221
222 protected void updateMessage() {
223 if (activePage != null) {
224 setDescription(activePage.getDescription());
225 setMessage(activePage.getMessage());
226 }
227 else {
228 setDescription(null);
229 setMessage(null);
230 }
231 }
232
233 protected class ChildChangeHandler implements PropertyChangeListener {
234 public void propertyChange(PropertyChangeEvent e) {
235 if (DialogPage.PAGE_COMPLETE_PROPERTY.equals(e.getPropertyName())) {
236 CompositeDialogPage.this.updatePageComplete((DialogPage) e.getSource());
237 }
238 else if (Messagable.MESSAGE_PROPERTY.equals(e.getPropertyName())) {
239 if (getActivePage() == e.getSource()) {
240 updateMessage();
241 }
242 }
243 else {
244 CompositeDialogPage.this.updatePageLabels((DialogPage) e.getSource());
245 }
246
247 if ("visible".equals(e.getPropertyName())) {
248 DialogPage page = (DialogPage) e.getSource();
249 updatePageVisibility(page);
250 }
251
252
253 if ("enabled".equals(e.getPropertyName())) {
254 DialogPage page = (DialogPage) e.getSource();
255 updatePageEnabled(page);
256 }
257 }
258 }
259
260 protected void onPageSelected(DialogPage page, boolean selected) {
261
262 }
263
264 protected void updatePageVisibility(DialogPage page) {
265
266 }
267
268 protected final String getDecoratedPageTitle(DialogPage page) {
269 return decoratePageTitle(page, page.getTitle());
270 }
271
272 /**
273 * Decorates the page title of the given <code>DialogPage</code>.
274 * <p>
275 * Can be overridden to provide additional decorations.
276 * <p>
277 * The default implementation returns a html with an indication whether the
278 * page is complete or incomplete
279 *
280 * @param page the page
281 * @param title the title
282 * @return the decorated page title
283 */
284 protected String decoratePageTitle(DialogPage page, String title) {
285 return LabelUtils.htmlBlock("<center>" + title + "<sup><font size=-3 color=red>"
286 + (page.isPageComplete() ? "" : "*"));
287 }
288 }