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 }