/**
* Copyright (C) 2015 Valkyrie RCP
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.valkyriercp.application.support;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.util.Assert;
import org.valkyriercp.application.*;
import org.valkyriercp.factory.AbstractControlFactory;
import org.valkyriercp.util.EventListenerListHelper;
import org.valkyriercp.util.ValkyrieRepository;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.*;
/**
* Abstract "convenience" implementation of <code>ApplicationPage</code>.
*
* @author Peter De Bruycker
*/
public abstract class AbstractApplicationPage extends AbstractControlFactory implements ApplicationPage {
private final EventListenerListHelper pageComponentListeners = new EventListenerListHelper(
PageComponentListener.class);
private ViewDescriptorRegistry viewDescriptorRegistry;
@Autowired
private PageComponentPaneFactory pageComponentPaneFactory;
private final List<PageComponent> pageComponents = new ArrayList<PageComponent>();
private PageComponent activeComponent;
private SharedCommandTargeter sharedCommandTargeter;
private PageDescriptor descriptor;
private ApplicationWindow window;
private boolean settingActiveComponent;
private ApplicationEventMulticaster applicationEventMulticaster;
private PropertyChangeListener pageComponentUpdater = new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getSource() instanceof PageComponent) {
updatePageComponentProperties((PageComponent) evt.getSource());
}
}
};
public AbstractApplicationPage() {
}
public AbstractApplicationPage(ApplicationWindow window, PageDescriptor pageDescriptor) {
setApplicationWindow(window);
setDescriptor(pageDescriptor);
}
/**
* Called when the <code>PageComponent</code> changes any of its properties (display name, caption, icon, ...).
* <p>
* This method should be overridden when these changes must be reflected in the ui.
*
* @param pageComponent
* the <code>PageComponent</code> that has changed
*/
protected void updatePageComponentProperties(PageComponent pageComponent) {
// do nothing by default
}
protected PageComponent findPageComponent(final String viewDescriptorId) {
for (PageComponent component : pageComponents) {
if (component.getId().equals(viewDescriptorId)) {
return component;
}
}
return null;
}
@SuppressWarnings("unchecked")
public <T extends View> T getView(String id) {
return (T) findPageComponent(id);
}
public void addPageComponentListener(PageComponentListener listener) {
pageComponentListeners.add(listener);
}
public void removePageComponentListener(PageComponentListener listener) {
pageComponentListeners.remove(listener);
}
protected void fireOpened(PageComponent component) {
component.componentOpened();
pageComponentListeners.fire("componentOpened", component);
}
protected void fireFocusGained(PageComponent component) {
component.componentFocusGained();
pageComponentListeners.fire("componentFocusGained", component);
}
/**
* Sets the first {@link PageComponent} as the active one.
*/
protected void setActiveComponent() {
if (pageComponents.size() > 0) {
setActiveComponent((PageComponent) pageComponents.get(0));
}
}
protected ViewDescriptor getViewDescriptor(String viewDescriptorId) {
return getViewDescriptorRegistry().getViewDescriptor(viewDescriptorId);
}
/**
* Returns the active <code>PageComponent</code>, or <code>null</code> if none.
*
* @return the active <code>PageComponent</code>
*/
public PageComponent getActiveComponent() {
return activeComponent;
}
/**
* Activates the given <code>PageComponent</code>. Does nothing if it is already the active one.
* <p>
* Does nothing if this <code>ApplicationPage</code> doesn't contain the given <code>PageComponent</code>.
*
* @param pageComponent
* the <code>PageComponent</code>
*/
public void setActiveComponent(PageComponent pageComponent) {
if (!pageComponents.contains(pageComponent)) {
return;
}
// if pageComponent is already active, don't do anything
if (this.activeComponent == pageComponent || settingActiveComponent) {
return;
}
settingActiveComponent = true;
try {
if (this.activeComponent != null) {
fireFocusLost(this.activeComponent);
}
giveFocusTo(pageComponent);
this.activeComponent = pageComponent;
fireFocusGained(this.activeComponent);
} finally {
// If this is not done in a finally, any exception thrown in fireFocusGained
// will prevent the user from leaving the screen
settingActiveComponent = false;
}
}
protected void fireFocusLost(PageComponent component) {
component.componentFocusLost();
pageComponentListeners.fire("componentFocusLost", component);
}
/**
* This method must add the given <code>PageComponent</code> in the ui.
* <p>
* Implementors may choose to add the <code>PageComponent</code>'s control directly, or add the
* <code>PageComponentPane</code>'s control.
*
* @param pageComponent
* the <code>PageComponent</code> to add
*/
protected abstract void doAddPageComponent(PageComponent pageComponent);
/**
* This method must remove the given <code>PageComponent</code> from the ui.
*
* @param pageComponent
* the <code>PageComponent</code> to remove
*/
protected abstract void doRemovePageComponent(PageComponent pageComponent);
/**
* This method must transfer the focus to the given <code>PageComponent</code>. This could involve making an
* internal frame visible, selecting a tab in a tabbed pane, ...
*
* @param pageComponent
* the <code>PageComponent</code>
* @return <code>true</code> if the operation was successful, <code>false</code> otherwise
*/
protected abstract boolean giveFocusTo(PageComponent pageComponent);
protected PageComponentPane createPageComponentPane(PageComponent pageComponent) {
return getPageComponentPaneFactory().createPageComponentPane(pageComponent);
}
protected void fireClosed(PageComponent component) {
component.componentClosed();
pageComponentListeners.fire("componentClosed", component);
}
public String getId() {
return descriptor.getId();
}
public ApplicationWindow getWindow() {
return window;
}
/**
* Closes the given <code>PageComponent</code>. This method disposes the <code>PageComponent</code>, triggers all
* necessary events ("focus lost" and "closed"), and will activate another <code>PageComponent</code> (if there is
* one).
* <p>
* Returns <code>false</code> if this <code>ApplicationPage</code> doesn't contain the given
* <code>PageComponent</code>.
*
* @param pageComponent
* the <code>PageComponent</code>
* @return boolean <code>true</code> if pageComponent was successfully closed.
*/
public boolean close(PageComponent pageComponent) {
if (!pageComponent.canClose()) {
return false;
}
if (!pageComponents.contains(pageComponent)) {
return false;
}
if (pageComponent == activeComponent) {
fireFocusLost(pageComponent);
activeComponent = null;
}
doRemovePageComponent(pageComponent);
pageComponents.remove(pageComponent);
pageComponent.removePropertyChangeListener(pageComponentUpdater);
if (pageComponent instanceof ApplicationListener && getApplicationEventMulticaster() != null) {
getApplicationEventMulticaster().removeApplicationListener((ApplicationListener) pageComponent);
}
pageComponent.dispose();
fireClosed(pageComponent);
if (activeComponent == null) {
setActiveComponent();
}
return true;
}
/**
* Closes this <code>ApplicationPage</code>. This method calls {@link #close(PageComponent)} for each open
* <code>PageComponent</code>.
*
* @return <code>true</code> if the operation was successful, <code>false</code> otherwise.
*/
public boolean close() {
for (Iterator<PageComponent> iter = new HashSet<PageComponent>(pageComponents).iterator(); iter.hasNext();) {
PageComponent component = iter.next();
if (!close(component))
return false;
}
return true;
}
public View showView(String id) {
Assert.hasText(id, "id cannot be empty");
return showView(getViewDescriptor(id), false, null);
}
public View showView(String id, Object input) {
Assert.hasText(id, "id cannot be empty");
return showView(getViewDescriptor(id), true, input);
}
@Override
public View showView(ViewDescriptor viewDescriptor) {
return showView(viewDescriptor, false, null);
}
@Override
public View showView(ViewDescriptor viewDescriptor, Object input) {
return showView(viewDescriptor, true, input);
}
private View showView(ViewDescriptor viewDescriptor, boolean setInput, Object input) {
Assert.notNull(viewDescriptor, "viewDescriptor cannot be null");
// applicationConfig.applicationObjectConfigurer().configure(viewDescriptor, viewDescriptor.getId());
View view = (View) findPageComponent(viewDescriptor.getId());
if (view == null) {
view = (View) createPageComponent(viewDescriptor);
if (setInput) {
// trigger control creation before input is set to avoid npe
view.getControl();
view.setInput(input);
}
addPageComponent(view);
} else {
if (setInput) {
view.setInput(input);
}
}
setActiveComponent(view);
return view;
}
public void openEditor(Object editorInput) {
// TODO implement editors
}
public boolean closeAllEditors() {
// TODO implement editors
return true;
}
/**
* Adds the pageComponent to the components list while registering listeners and firing appropriate events. (not yet
* setting the component as the active one)
*
* @param pageComponent
* the pageComponent to add.
*/
protected void addPageComponent(PageComponent pageComponent) {
pageComponents.add(pageComponent);
doAddPageComponent(pageComponent);
pageComponent.addPropertyChangeListener(pageComponentUpdater);
fireOpened(pageComponent);
}
/**
* Creates a PageComponent for the given PageComponentDescriptor.
*
* @param descriptor
* the descriptor
* @return the created PageComponent
*/
protected PageComponent createPageComponent(PageComponentDescriptor descriptor) {
PageComponent pageComponent = descriptor.createPageComponent();
pageComponent.setContext(new DefaultViewContext(this, createPageComponentPane(pageComponent)));
if (pageComponent instanceof ApplicationListener && getApplicationEventMulticaster() != null) {
getApplicationEventMulticaster().addApplicationListener((ApplicationListener) pageComponent);
}
return pageComponent;
}
public List<PageComponent> getPageComponents() {
return Collections.unmodifiableList(pageComponents);
}
public final void setApplicationWindow(ApplicationWindow window) {
Assert.notNull(window, "The containing window is required");
Assert.state(this.window == null, "Page window already set: it should only be set once, during initialization");
this.window = window;
sharedCommandTargeter = new SharedCommandTargeter(window);
addPageComponentListener(sharedCommandTargeter);
}
public final void setDescriptor(PageDescriptor descriptor) {
Assert.notNull(descriptor, "The page's descriptor is required");
Assert.state(this.descriptor == null,
"Page descriptor already set: it should only be set once, during initialization");
this.descriptor = descriptor;
}
protected PageDescriptor getPageDescriptor() {
return descriptor;
}
public ApplicationEventMulticaster getApplicationEventMulticaster() {
if ((applicationEventMulticaster == null) && (getApplicationConfig() != null)) {
final String beanName = AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME;
if (getApplicationConfig().applicationContext().containsBean(beanName)) {
applicationEventMulticaster = (ApplicationEventMulticaster) getApplicationConfig().applicationContext().getBean(beanName);
}
}
return applicationEventMulticaster;
}
public void setViewDescriptorRegistry(ViewDescriptorRegistry viewDescriptorRegistry) {
this.viewDescriptorRegistry = viewDescriptorRegistry;
}
public ViewDescriptorRegistry getViewDescriptorRegistry() {
if (viewDescriptorRegistry == null) {
viewDescriptorRegistry = getApplicationConfig().viewDescriptorRegistry();
}
return viewDescriptorRegistry;
}
public void setPageComponentPaneFactory(PageComponentPaneFactory pageComponentPaneFactory) {
this.pageComponentPaneFactory = pageComponentPaneFactory;
}
public PageComponentPaneFactory getPageComponentPaneFactory() {
if(pageComponentPaneFactory == null)
return ValkyrieRepository.getInstance().getApplicationConfig().pageComponentPaneFactory();
return pageComponentPaneFactory;
}
}