001 /*
002 * Copyright 2002-2008 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.application.support;
017
018 import java.beans.PropertyChangeEvent;
019 import java.beans.PropertyChangeListener;
020 import java.util.ArrayList;
021 import java.util.Collections;
022 import java.util.HashSet;
023 import java.util.Iterator;
024 import java.util.List;
025
026 import org.springframework.context.ApplicationListener;
027 import org.springframework.context.event.ApplicationEventMulticaster;
028 import org.springframework.context.support.AbstractApplicationContext;
029 import org.springframework.richclient.application.ApplicationPage;
030 import org.springframework.richclient.application.ApplicationServicesLocator;
031 import org.springframework.richclient.application.ApplicationWindow;
032 import org.springframework.richclient.application.PageComponent;
033 import org.springframework.richclient.application.PageComponentDescriptor;
034 import org.springframework.richclient.application.PageComponentListener;
035 import org.springframework.richclient.application.PageComponentPane;
036 import org.springframework.richclient.application.PageComponentPaneFactory;
037 import org.springframework.richclient.application.PageDescriptor;
038 import org.springframework.richclient.application.View;
039 import org.springframework.richclient.application.ViewDescriptor;
040 import org.springframework.richclient.application.ViewDescriptorRegistry;
041 import org.springframework.richclient.factory.AbstractControlFactory;
042 import org.springframework.richclient.util.EventListenerListHelper;
043 import org.springframework.util.Assert;
044
045 /**
046 * Abstract "convenience" implementation of <code>ApplicationPage</code>.
047 *
048 * @author Peter De Bruycker
049 */
050 public abstract class AbstractApplicationPage extends AbstractControlFactory implements ApplicationPage {
051
052 private final EventListenerListHelper pageComponentListeners = new EventListenerListHelper(
053 PageComponentListener.class);
054
055 private ViewDescriptorRegistry viewDescriptorRegistry;
056
057 private PageComponentPaneFactory pageComponentPaneFactory;
058
059 private final List<PageComponent> pageComponents = new ArrayList<PageComponent>();
060
061 private PageComponent activeComponent;
062
063 private SharedCommandTargeter sharedCommandTargeter;
064
065 private PageDescriptor descriptor;
066
067 private ApplicationWindow window;
068
069 private boolean settingActiveComponent;
070
071 private ApplicationEventMulticaster applicationEventMulticaster;
072
073 private PropertyChangeListener pageComponentUpdater = new PropertyChangeListener() {
074 public void propertyChange(PropertyChangeEvent evt) {
075 if (evt.getSource() instanceof PageComponent) {
076 updatePageComponentProperties((PageComponent) evt.getSource());
077 }
078 }
079 };
080
081 public AbstractApplicationPage() {
082
083 }
084
085 public AbstractApplicationPage(ApplicationWindow window, PageDescriptor pageDescriptor) {
086 setApplicationWindow(window);
087 setDescriptor(pageDescriptor);
088 }
089
090 /**
091 * Called when the <code>PageComponent</code> changes any of its properties (display name, caption, icon, ...).
092 * <p>
093 * This method should be overridden when these changes must be reflected in the ui.
094 *
095 * @param pageComponent
096 * the <code>PageComponent</code> that has changed
097 */
098 protected void updatePageComponentProperties(PageComponent pageComponent) {
099 // do nothing by default
100 }
101
102 protected PageComponent findPageComponent(final String viewDescriptorId) {
103 for (PageComponent component : pageComponents) {
104 if (component.getId().equals(viewDescriptorId)) {
105 return component;
106 }
107 }
108
109 return null;
110 }
111
112 @SuppressWarnings("unchecked")
113 public <T extends View> T getView(String id) {
114 return (T) findPageComponent(id);
115 }
116
117 public void addPageComponentListener(PageComponentListener listener) {
118 pageComponentListeners.add(listener);
119 }
120
121 public void removePageComponentListener(PageComponentListener listener) {
122 pageComponentListeners.remove(listener);
123 }
124
125 protected void fireOpened(PageComponent component) {
126 component.componentOpened();
127 pageComponentListeners.fire("componentOpened", component);
128 }
129
130 protected void fireFocusGained(PageComponent component) {
131 component.componentFocusGained();
132 pageComponentListeners.fire("componentFocusGained", component);
133 }
134
135 /**
136 * Sets the first {@link PageComponent} as the active one.
137 */
138 protected void setActiveComponent() {
139 if (pageComponents.size() > 0) {
140 setActiveComponent((PageComponent) pageComponents.get(0));
141 }
142 }
143
144 protected ViewDescriptor getViewDescriptor(String viewDescriptorId) {
145 return getViewDescriptorRegistry().getViewDescriptor(viewDescriptorId);
146 }
147
148 /**
149 * Returns the active <code>PageComponent</code>, or <code>null</code> if none.
150 *
151 * @return the active <code>PageComponent</code>
152 */
153 public PageComponent getActiveComponent() {
154 return activeComponent;
155 }
156
157 /**
158 * Activates the given <code>PageComponent</code>. Does nothing if it is already the active one.
159 * <p>
160 * Does nothing if this <code>ApplicationPage</code> doesn't contain the given <code>PageComponent</code>.
161 *
162 * @param pageComponent
163 * the <code>PageComponent</code>
164 */
165 public void setActiveComponent(PageComponent pageComponent) {
166 if (!pageComponents.contains(pageComponent)) {
167 return;
168 }
169
170 // if pageComponent is already active, don't do anything
171 if (this.activeComponent == pageComponent || settingActiveComponent) {
172 return;
173 }
174
175 settingActiveComponent = true;
176
177 if (this.activeComponent != null) {
178 fireFocusLost(this.activeComponent);
179 }
180 giveFocusTo(pageComponent);
181 this.activeComponent = pageComponent;
182 fireFocusGained(this.activeComponent);
183
184 settingActiveComponent = false;
185 }
186
187 protected void fireFocusLost(PageComponent component) {
188 component.componentFocusLost();
189 pageComponentListeners.fire("componentFocusLost", component);
190 }
191
192 /**
193 * This method must add the given <code>PageComponent</code> in the ui.
194 * <p>
195 * Implementors may choose to add the <code>PageComponent</code>'s control directly, or add the
196 * <code>PageComponentPane</code>'s control.
197 *
198 * @param pageComponent
199 * the <code>PageComponent</code> to add
200 */
201 protected abstract void doAddPageComponent(PageComponent pageComponent);
202
203 /**
204 * This method must remove the given <code>PageComponent</code> from the ui.
205 *
206 * @param pageComponent
207 * the <code>PageComponent</code> to remove
208 */
209 protected abstract void doRemovePageComponent(PageComponent pageComponent);
210
211 /**
212 * This method must transfer the focus to the given <code>PageComponent</code>. This could involve making an
213 * internal frame visible, selecting a tab in a tabbed pane, ...
214 *
215 * @param pageComponent
216 * the <code>PageComponent</code>
217 * @return <code>true</code> if the operation was successful, <code>false</code> otherwise
218 */
219 protected abstract boolean giveFocusTo(PageComponent pageComponent);
220
221 protected PageComponentPane createPageComponentPane(PageComponent pageComponent) {
222 return getPageComponentPaneFactory().createPageComponentPane(pageComponent);
223 }
224
225 protected void fireClosed(PageComponent component) {
226 component.componentClosed();
227 pageComponentListeners.fire("componentClosed", component);
228 }
229
230 public String getId() {
231 return descriptor.getId();
232 }
233
234 public ApplicationWindow getWindow() {
235 return window;
236 }
237
238 /**
239 * Closes the given <code>PageComponent</code>. This method disposes the <code>PageComponent</code>, triggers all
240 * necessary events ("focus lost" and "closed"), and will activate another <code>PageComponent</code> (if there is
241 * one).
242 * <p>
243 * Returns <code>false</code> if this <code>ApplicationPage</code> doesn't contain the given
244 * <code>PageComponent</code>.
245 *
246 * @param pageComponent
247 * the <code>PageComponent</code>
248 * @return boolean <code>true</code> if pageComponent was successfully closed.
249 */
250 public boolean close(PageComponent pageComponent) {
251 if (!pageComponent.canClose()) {
252 return false;
253 }
254
255 if (!pageComponents.contains(pageComponent)) {
256 return false;
257 }
258
259 if (pageComponent == activeComponent) {
260 fireFocusLost(pageComponent);
261 activeComponent = null;
262 }
263
264 doRemovePageComponent(pageComponent);
265 pageComponents.remove(pageComponent);
266 pageComponent.removePropertyChangeListener(pageComponentUpdater);
267 if (pageComponent instanceof ApplicationListener && getApplicationEventMulticaster() != null) {
268 getApplicationEventMulticaster().removeApplicationListener((ApplicationListener) pageComponent);
269 }
270
271 pageComponent.dispose();
272 fireClosed(pageComponent);
273 if (activeComponent == null) {
274 setActiveComponent();
275 }
276 return true;
277 }
278
279 /**
280 * Closes this <code>ApplicationPage</code>. This method calls {@link #close(PageComponent)} for each open
281 * <code>PageComponent</code>.
282 *
283 * @return <code>true</code> if the operation was successful, <code>false</code> otherwise.
284 */
285 public boolean close() {
286 for (Iterator<PageComponent> iter = new HashSet<PageComponent>(pageComponents).iterator(); iter.hasNext();) {
287 PageComponent component = iter.next();
288 if (!close(component))
289 return false;
290 }
291 return true;
292 }
293
294 public View showView(String id) {
295 Assert.hasText(id, "id cannot be empty");
296
297 return showView(getViewDescriptor(id), false, null);
298 }
299
300 public View showView(String id, Object input) {
301 Assert.hasText(id, "id cannot be empty");
302
303 return showView(getViewDescriptor(id), true, input);
304 }
305
306 private View showView(ViewDescriptor viewDescriptor, boolean setInput, Object input) {
307 Assert.notNull(viewDescriptor, "viewDescriptor cannot be null");
308
309 View view = (View) findPageComponent(viewDescriptor.getId());
310 if (view == null) {
311 view = (View) createPageComponent(viewDescriptor);
312
313 if (setInput) {
314 // trigger control creation before input is set to avoid npe
315 view.getControl();
316
317 view.setInput(input);
318 }
319
320 addPageComponent(view);
321 } else {
322 if (setInput) {
323 view.setInput(input);
324 }
325 }
326 setActiveComponent(view);
327
328 return view;
329 }
330
331 public void openEditor(Object editorInput) {
332 // TODO implement editors
333 }
334
335 public boolean closeAllEditors() {
336 // TODO implement editors
337 return true;
338 }
339
340 /**
341 * Adds the pageComponent to the components list while registering listeners and firing appropriate events. (not yet
342 * setting the component as the active one)
343 *
344 * @param pageComponent
345 * the pageComponent to add.
346 */
347 protected void addPageComponent(PageComponent pageComponent) {
348 pageComponents.add(pageComponent);
349 doAddPageComponent(pageComponent);
350 pageComponent.addPropertyChangeListener(pageComponentUpdater);
351
352 fireOpened(pageComponent);
353 }
354
355 /**
356 * Creates a PageComponent for the given PageComponentDescriptor.
357 *
358 * @param descriptor
359 * the descriptor
360 * @return the created PageComponent
361 */
362 protected PageComponent createPageComponent(PageComponentDescriptor descriptor) {
363 PageComponent pageComponent = descriptor.createPageComponent();
364 pageComponent.setContext(new DefaultViewContext(this, createPageComponentPane(pageComponent)));
365 if (pageComponent instanceof ApplicationListener && getApplicationEventMulticaster() != null) {
366 getApplicationEventMulticaster().addApplicationListener((ApplicationListener) pageComponent);
367 }
368
369 return pageComponent;
370 }
371
372 public List<PageComponent> getPageComponents() {
373 return Collections.unmodifiableList(pageComponents);
374 }
375
376 public final void setApplicationWindow(ApplicationWindow window) {
377 Assert.notNull(window, "The containing window is required");
378 Assert.state(this.window == null, "Page window already set: it should only be set once, during initialization");
379 this.window = window;
380 sharedCommandTargeter = new SharedCommandTargeter(window);
381 addPageComponentListener(sharedCommandTargeter);
382 }
383
384 public final void setDescriptor(PageDescriptor descriptor) {
385 Assert.notNull(descriptor, "The page's descriptor is required");
386 Assert.state(this.descriptor == null,
387 "Page descriptor already set: it should only be set once, during initialization");
388 this.descriptor = descriptor;
389 }
390
391 protected PageDescriptor getPageDescriptor() {
392 return descriptor;
393 }
394
395 public ApplicationEventMulticaster getApplicationEventMulticaster() {
396 if ((applicationEventMulticaster == null) && (getApplicationContext() != null)) {
397 final String beanName = AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME;
398 if (getApplicationContext().containsBean(beanName)) {
399 applicationEventMulticaster = (ApplicationEventMulticaster) getApplicationContext().getBean(beanName);
400 }
401 }
402 return applicationEventMulticaster;
403 }
404
405 public void setViewDescriptorRegistry(ViewDescriptorRegistry viewDescriptorRegistry) {
406 this.viewDescriptorRegistry = viewDescriptorRegistry;
407 }
408
409 public ViewDescriptorRegistry getViewDescriptorRegistry() {
410 if (viewDescriptorRegistry == null) {
411 viewDescriptorRegistry = (ViewDescriptorRegistry) ApplicationServicesLocator.services().getService(
412 ViewDescriptorRegistry.class);
413 }
414
415 return viewDescriptorRegistry;
416 }
417
418 public void setPageComponentPaneFactory(PageComponentPaneFactory pageComponentPaneFactory) {
419 this.pageComponentPaneFactory = pageComponentPaneFactory;
420 }
421
422 public PageComponentPaneFactory getPageComponentPaneFactory() {
423 if (pageComponentPaneFactory == null) {
424 pageComponentPaneFactory = (PageComponentPaneFactory) ApplicationServicesLocator.services().getService(
425 PageComponentPaneFactory.class);
426 }
427
428 return pageComponentPaneFactory;
429 }
430 }