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 }