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    }