| /******************************************************************************* |
| * Copyright (c) 2010-2015 BSI Business Systems Integration AG. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * BSI Business Systems Integration AG - initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.scout.rt.client.ui.desktop; |
| |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.LinkedHashSet; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.scout.rt.client.ModelContextProxy; |
| import org.eclipse.scout.rt.client.ModelContextProxy.ModelContext; |
| import org.eclipse.scout.rt.client.context.ClientRunContexts; |
| import org.eclipse.scout.rt.client.deeplink.DeepLinkException; |
| import org.eclipse.scout.rt.client.deeplink.IDeepLinks; |
| import org.eclipse.scout.rt.client.deeplink.OutlineDeepLinkHandler; |
| import org.eclipse.scout.rt.client.extension.ui.desktop.DesktopChains.DesktopBeforeClosingChain; |
| import org.eclipse.scout.rt.client.extension.ui.desktop.DesktopChains.DesktopClosingChain; |
| import org.eclipse.scout.rt.client.extension.ui.desktop.DesktopChains.DesktopDefaultViewChain; |
| import org.eclipse.scout.rt.client.extension.ui.desktop.DesktopChains.DesktopFormAboutToShowChain; |
| import org.eclipse.scout.rt.client.extension.ui.desktop.DesktopChains.DesktopGuiAttachedChain; |
| import org.eclipse.scout.rt.client.extension.ui.desktop.DesktopChains.DesktopGuiDetachedChain; |
| import org.eclipse.scout.rt.client.extension.ui.desktop.DesktopChains.DesktopInitChain; |
| import org.eclipse.scout.rt.client.extension.ui.desktop.DesktopChains.DesktopOpenedChain; |
| import org.eclipse.scout.rt.client.extension.ui.desktop.DesktopChains.DesktopOutlineChangedChain; |
| import org.eclipse.scout.rt.client.extension.ui.desktop.DesktopChains.DesktopPageDetailFormChangedChain; |
| import org.eclipse.scout.rt.client.extension.ui.desktop.DesktopChains.DesktopPageDetailTableChangedChain; |
| import org.eclipse.scout.rt.client.extension.ui.desktop.DesktopChains.DesktopPageSearchFormChangedChain; |
| import org.eclipse.scout.rt.client.extension.ui.desktop.DesktopChains.DesktopTablePageLoadedChain; |
| import org.eclipse.scout.rt.client.services.common.bookmark.internal.BookmarkUtility; |
| import org.eclipse.scout.rt.client.services.common.icon.IIconProviderService; |
| import org.eclipse.scout.rt.client.session.ClientSessionProvider; |
| import org.eclipse.scout.rt.client.transformation.IDeviceTransformationService; |
| import org.eclipse.scout.rt.client.ui.DataChangeListener; |
| import org.eclipse.scout.rt.client.ui.IDisplayParent; |
| import org.eclipse.scout.rt.client.ui.action.ActionFinder; |
| import org.eclipse.scout.rt.client.ui.action.ActionUtility; |
| import org.eclipse.scout.rt.client.ui.action.IAction; |
| import org.eclipse.scout.rt.client.ui.action.keystroke.IKeyStroke; |
| import org.eclipse.scout.rt.client.ui.action.menu.IMenu; |
| import org.eclipse.scout.rt.client.ui.action.tool.IToolButton; |
| import org.eclipse.scout.rt.client.ui.action.view.IViewButton; |
| import org.eclipse.scout.rt.client.ui.basic.filechooser.IFileChooser; |
| import org.eclipse.scout.rt.client.ui.basic.table.ITable; |
| import org.eclipse.scout.rt.client.ui.basic.tree.ITreeNode; |
| import org.eclipse.scout.rt.client.ui.basic.tree.TreeAdapter; |
| import org.eclipse.scout.rt.client.ui.basic.tree.TreeEvent; |
| import org.eclipse.scout.rt.client.ui.desktop.notification.IDesktopNotification; |
| import org.eclipse.scout.rt.client.ui.desktop.outline.AbstractOutlineViewButton; |
| import org.eclipse.scout.rt.client.ui.desktop.outline.IOutline; |
| import org.eclipse.scout.rt.client.ui.desktop.outline.pages.IPage; |
| import org.eclipse.scout.rt.client.ui.desktop.outline.pages.IPageWithTable; |
| import org.eclipse.scout.rt.client.ui.desktop.outline.pages.ISearchForm; |
| import org.eclipse.scout.rt.client.ui.form.IForm; |
| import org.eclipse.scout.rt.client.ui.form.IFormHandler; |
| import org.eclipse.scout.rt.client.ui.form.IFormMenu; |
| import org.eclipse.scout.rt.client.ui.form.fields.IFormField; |
| import org.eclipse.scout.rt.client.ui.form.fields.button.IButton; |
| import org.eclipse.scout.rt.client.ui.messagebox.IMessageBox; |
| import org.eclipse.scout.rt.client.ui.messagebox.MessageBoxes; |
| import org.eclipse.scout.rt.platform.BEANS; |
| import org.eclipse.scout.rt.platform.Order; |
| import org.eclipse.scout.rt.platform.OrderedComparator; |
| import org.eclipse.scout.rt.platform.annotations.ConfigOperation; |
| import org.eclipse.scout.rt.platform.annotations.ConfigProperty; |
| import org.eclipse.scout.rt.platform.exception.ExceptionHandler; |
| import org.eclipse.scout.rt.platform.exception.ProcessingException; |
| import org.eclipse.scout.rt.platform.exception.VetoException; |
| import org.eclipse.scout.rt.platform.holders.Holder; |
| import org.eclipse.scout.rt.platform.holders.IHolder; |
| import org.eclipse.scout.rt.platform.reflect.AbstractPropertyObserver; |
| import org.eclipse.scout.rt.platform.reflect.ConfigurationUtility; |
| import org.eclipse.scout.rt.platform.resource.BinaryResource; |
| import org.eclipse.scout.rt.platform.util.Assertions; |
| import org.eclipse.scout.rt.platform.util.CollectionUtility; |
| import org.eclipse.scout.rt.platform.util.EventListenerList; |
| import org.eclipse.scout.rt.platform.util.StringUtility; |
| import org.eclipse.scout.rt.platform.util.collection.OrderedCollection; |
| import org.eclipse.scout.rt.platform.util.concurrent.IRunnable; |
| import org.eclipse.scout.rt.shared.TEXTS; |
| import org.eclipse.scout.rt.shared.extension.AbstractExtension; |
| import org.eclipse.scout.rt.shared.extension.ContributionComposite; |
| import org.eclipse.scout.rt.shared.extension.ExtensionUtility; |
| import org.eclipse.scout.rt.shared.extension.IContributionOwner; |
| import org.eclipse.scout.rt.shared.extension.IExtensibleObject; |
| import org.eclipse.scout.rt.shared.extension.IExtension; |
| import org.eclipse.scout.rt.shared.extension.ObjectExtensions; |
| import org.eclipse.scout.rt.shared.services.common.bookmark.Bookmark; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * The desktop model (may) consist of |
| * <ul> |
| * <li>set of available outlines |
| * <li>active outline |
| * <li>active table view |
| * <li>active detail form |
| * <li>active search form |
| * <li>form stack (swing: dialogs on desktop as {@code JInternalFrame}s) |
| * <li>dialog stack of modal and non-modal dialogs (swing: dialogs as {@code JDialog}, {@code JFrame}) |
| * <li>active message box stack |
| * <li>menubar menus |
| * <li>toolbar and viewbar actions |
| * </ul> |
| * The Eclipse Scout SDK creates a subclass of this class that can be used as initial desktop. |
| */ |
| @SuppressWarnings("deprecation") |
| public abstract class AbstractDesktop extends AbstractPropertyObserver implements IDesktop, IContributionOwner, IExtensibleObject { |
| |
| private static final Logger LOG = LoggerFactory.getLogger(AbstractDesktop.class); |
| |
| private final IDesktopExtension m_localDesktopExtension; |
| private List<IDesktopExtension> m_desktopExtensions; |
| private final EventListenerList m_listenerList; |
| private int m_dataChanging; |
| private final List<Object[]> m_dataChangeEventBuffer; |
| private final Map<Object, EventListenerList> m_dataChangeListenerList; |
| private final IDesktopUIFacade m_uiFacade; |
| private List<IOutline> m_availableOutlines; |
| private IOutline m_outline; |
| private boolean m_outlineChanging = false; |
| private P_ActiveOutlineListener m_activeOutlineListener; |
| private ITable m_pageDetailTable; |
| private IForm m_pageDetailForm; |
| private IForm m_pageSearchForm; |
| private final FormStore m_formStore; |
| private final MessageBoxStore m_messageBoxStore; |
| private final FileChooserStore m_fileChooserStore; |
| private List<IMenu> m_menus; |
| private List<IViewButton> m_viewButtons; |
| private boolean m_autoPrefixWildcardForTextSearch; |
| private boolean m_desktopInitialized; |
| private boolean m_isForcedClosing = false; |
| private final List<Object> m_addOns; |
| private IContributionOwner m_contributionHolder; |
| private final ObjectExtensions<AbstractDesktop, org.eclipse.scout.rt.client.extension.ui.desktop.IDesktopExtension<? extends AbstractDesktop>> m_objectExtensions; |
| |
| /** |
| * do not instantiate a new desktop<br> |
| * get it via {@code ClientScoutSession.getSession().getModelManager()} |
| */ |
| public AbstractDesktop() { |
| this(true); |
| } |
| |
| public AbstractDesktop(boolean callInitializer) { |
| m_localDesktopExtension = new P_LocalDesktopExtension(); |
| m_listenerList = new EventListenerList(); |
| m_dataChangeListenerList = new HashMap<>(); |
| m_dataChangeEventBuffer = new ArrayList<>(); |
| m_formStore = BEANS.get(FormStore.class); |
| m_messageBoxStore = BEANS.get(MessageBoxStore.class); |
| m_fileChooserStore = BEANS.get(FileChooserStore.class); |
| m_uiFacade = BEANS.get(ModelContextProxy.class).newProxy(new P_UIFacade(), ModelContext.copyCurrent().withDesktop(this)); |
| m_addOns = new ArrayList<>(); |
| m_objectExtensions = new ObjectExtensions<>(this); |
| if (callInitializer) { |
| callInitializer(); |
| } |
| } |
| |
| protected final void callInitializer() { |
| // Run the initialization on behalf of this Desktop. |
| ClientRunContexts.copyCurrent().withDesktop(this).run(new IRunnable() { |
| @Override |
| public void run() throws Exception { |
| interceptInitConfig(); |
| } |
| }); |
| } |
| |
| @Override |
| public final List<Object> getAllContributions() { |
| return m_contributionHolder.getAllContributions(); |
| } |
| |
| @Override |
| public final <T> List<T> getContributionsByClass(Class<T> type) { |
| return m_contributionHolder.getContributionsByClass(type); |
| } |
| |
| @Override |
| public final <T> T getContribution(Class<T> contribution) { |
| return m_contributionHolder.getContribution(contribution); |
| } |
| |
| /* |
| * Configuration |
| */ |
| /** |
| * Configures the title of this desktop. The title is typically used as title for the main application window. |
| * <p> |
| * Subclasses can override this method. Default is {@code null}. |
| * |
| * @return the title of this desktop |
| */ |
| @ConfigProperty(ConfigProperty.TEXT) |
| @Order(10) |
| protected String getConfiguredTitle() { |
| return null; |
| } |
| |
| /** |
| * Configures the outlines associated with this desktop. If multiple outlines are configured, there is typically a |
| * need to provide some means of switching between different outlines, such as a {@link AbstractOutlineViewButton}. |
| * <p> |
| * Note that {@linkplain IDesktopExtension desktop extensions} might contribute additional outlines to this desktop. |
| * <p> |
| * Subclasses can override this method. Default is {@code null}. |
| * |
| * @return an array of outline type tokens |
| * @see IOutline |
| */ |
| @ConfigProperty(ConfigProperty.OUTLINES) |
| @Order(20) |
| protected List<Class<? extends IOutline>> getConfiguredOutlines() { |
| return null; |
| } |
| |
| /** |
| * @return <code>true</code> if UI key strokes to select view tabs are enabled, <code>false</code> otherwise. Default |
| * value is <code>true</code>. |
| */ |
| @ConfigProperty(ConfigProperty.BOOLEAN) |
| @Order(30) |
| protected boolean getConfiguredSelectViewTabsKeyStrokesEnabled() { |
| return true; |
| } |
| |
| /** |
| * @return optional modifier to use for UI key strokes to select view tabs (only relevant when |
| * {@link #isSelectViewTabsKeyStrokesEnabled()} is <code>true</code>). Default value is |
| * {@link IKeyStroke#CONTROL}. |
| */ |
| @ConfigProperty(ConfigProperty.STRING) |
| @Order(40) |
| protected String getConfiguredSelectViewTabsKeyStrokeModifier() { |
| return IKeyStroke.CONTROL; |
| } |
| |
| /** |
| * Configures the desktop style which defines the basic layout of the application in the UI. |
| * <p> |
| * Subclasses can override this method. Default is {@link IDesktop#DISPLAY_STYLE_DEFAULT} |
| */ |
| @ConfigProperty(ConfigProperty.STRING) |
| @Order(50) |
| protected String getConfiguredDisplayStyle() { |
| return DISPLAY_STYLE_DEFAULT; |
| } |
| |
| /** |
| * Configures the logo id. |
| * <p> |
| * If specified, the logo will be displayed on the top right corner of the desktop. |
| * <p> |
| * Subclasses can override this method. Default is {@code null}. |
| * |
| * @since 6.0 |
| * @see IIconProviderService |
| */ |
| protected String getConfiguredLogoId() { |
| return null; |
| } |
| |
| /** |
| * Configures whether the position of the splitter between the navigation and bench should be stored in the session |
| * storage, so that the position may be restored after a page reload. If set to false, the default position is used. |
| * |
| * @return {@code true} if the splitter position should be cached, false if not |
| */ |
| @ConfigProperty(ConfigProperty.BOOLEAN) |
| @Order(60) |
| protected boolean getConfiguredCacheSplitterPosition() { |
| return true; |
| } |
| |
| @ConfigProperty(ConfigProperty.BOOLEAN) |
| @Order(70) |
| protected boolean getConfiguredNavigationVisible() { |
| return true; |
| } |
| |
| @ConfigProperty(ConfigProperty.BOOLEAN) |
| @Order(75) |
| protected boolean getConfiguredNavigationHandleVisible() { |
| return true; |
| } |
| |
| @ConfigProperty(ConfigProperty.BOOLEAN) |
| @Order(80) |
| protected boolean getConfiguredHeaderVisible() { |
| return true; |
| } |
| |
| @ConfigProperty(ConfigProperty.BOOLEAN) |
| @Order(90) |
| protected boolean getConfiguredBenchVisible() { |
| return true; |
| } |
| |
| private List<Class<? extends IAction>> getConfiguredActions() { |
| Class[] dca = ConfigurationUtility.getDeclaredPublicClasses(getClass()); |
| List<Class<IAction>> fca = ConfigurationUtility.filterClasses(dca, IAction.class); |
| return ConfigurationUtility.removeReplacedClasses(fca); |
| } |
| |
| /** |
| * Called while this desktop is initialized. |
| * <p> |
| * Subclasses can override this method. The default does nothing. |
| */ |
| @ConfigOperation |
| @Order(10) |
| protected void execInit() { |
| } |
| |
| /** |
| * Called after this desktop was opened and displayed on the GUI. |
| * <p> |
| * Subclasses can override this method. The default does nothing. |
| */ |
| @ConfigOperation |
| @Order(20) |
| protected void execOpened() { |
| } |
| |
| /** |
| * Called just after the core desktop receives the request to close the desktop. |
| * <p> |
| * Subclasses can override this method to execute some custom code before the desktop gets into its closing state. The |
| * default behavior is to do nothing. By throwing an explicit {@link VetoException} the closing process will be |
| * stopped. |
| */ |
| @ConfigOperation |
| @Order(30) |
| protected void execBeforeClosing() { |
| } |
| |
| /** |
| * Called before this desktop is being closed. |
| * <p> |
| * Subclasses can override this method. The default does nothing. |
| */ |
| @ConfigOperation |
| @Order(40) |
| protected void execClosing() { |
| } |
| |
| /** |
| * Called after a UI has been attached to this desktop. The desktop is opened at this point. This method may be called |
| * more than once. It is called immediately after the desktop has been opened and also in case of a reload, when a new |
| * UiSession has been created. |
| * <p> |
| * Subclasses can override this method. The default does nothing. |
| */ |
| @ConfigOperation |
| @Order(50) |
| protected void execGuiAttached() { |
| } |
| |
| /** |
| * Called after a UI has been detached from this desktop. This desktop must not necessarily be open. |
| * <p> |
| * Subclasses can override this method. The default does nothing. |
| */ |
| @ConfigOperation |
| @Order(60) |
| protected void execGuiDetached() { |
| } |
| |
| /** |
| * Called whenever a new outline has been activated on this desktop. |
| * <p> |
| * Subclasses can override this method. The default does nothing. |
| * |
| * @param oldOutline |
| * old outline that was active before |
| * @param newOutline |
| * new outline that is active after the change |
| */ |
| @ConfigOperation |
| @Order(70) |
| protected void execOutlineChanged(IOutline oldOutline, IOutline newOutline) { |
| } |
| |
| /** |
| * Method invoked right before the given form is shown and therefore added to the desktop. That is before any UI is |
| * informed about the new form.<br/> |
| * Overwrite this method to modify the given form, or to replace it with another form instance. The default |
| * implementation simply returns the given form. |
| * |
| * @param form |
| * the form which is about to show. |
| * @return the form to show, or <code>null</code> to not show the form. |
| */ |
| @ConfigOperation |
| @Order(80) |
| protected IForm execFormAboutToShow(IForm form) { |
| return form; |
| } |
| |
| /** |
| * Called whenever a new page has been activated (selected) on this desktop. |
| * <p> |
| * Subclasses can override this method.<br/> |
| * This default implementation does nothing. |
| * |
| * @param oldForm |
| * is the search form of the old (not selected anymore) page or {@code null} |
| * @param newForm |
| * is the search form of the new (selected) page or {@code null} |
| */ |
| @Order(90) |
| @ConfigOperation |
| protected void execPageSearchFormChanged(IForm oldForm, IForm newForm) { |
| } |
| |
| /** |
| * Called whenever a new page has been activated (selected) on this desktop. |
| * <p> |
| * Subclasses can override this method.<br/> |
| * This default implementation does nothing. |
| * |
| * @param oldForm |
| * is the detail form of the old (not selected anymore) page or {@code null} |
| * @param newForm |
| * is the detail form of the new (selected) page or {@code null} |
| */ |
| @Order(100) |
| @ConfigOperation |
| protected void execPageDetailFormChanged(IForm oldForm, IForm newForm) { |
| } |
| |
| /** |
| * Called whenever a new page has been activated (selected) on this desktop. |
| * <p> |
| * Subclasses can override this method.<br/> |
| * This default implementation keeps track of the current outline table form and updates it accordingly (including |
| * visibility). See also {@link #getOutlineTableForm()}. |
| * |
| * @param oldTable |
| * is the table of the old (not selected anymore) table page or {@code null} |
| * @param newTable |
| * is the table of the new (selected) table page or {@code null} |
| */ |
| @Order(110) |
| @ConfigOperation |
| protected void execPageDetailTableChanged(ITable oldTable, ITable newTable) { |
| } |
| |
| /** |
| * Called after a table page was loaded or reloaded. |
| * <p> |
| * Subclasses can override this method.<br/> |
| * This default implementation minimizes the page search form when data has been found. |
| * |
| * @param tablePage |
| * the table page that has been (re)loaded |
| */ |
| @Order(120) |
| @ConfigOperation |
| protected void execTablePageLoaded(IPageWithTable<?> tablePage) { |
| ISearchForm searchForm = tablePage.getSearchFormInternal(); |
| if (searchForm != null) { |
| searchForm.setMinimized(tablePage.getTable().getRowCount() > 0); |
| } |
| } |
| |
| /** |
| * Called by activateDefaultView when Scout application was not started with a deep-link or when deep-link could not |
| * be executed. The default implementation does nothing. |
| */ |
| @Order(130) |
| @ConfigOperation |
| protected void execDefaultView() { |
| } |
| |
| public List<IDesktopExtension> getDesktopExtensions() { |
| return CollectionUtility.arrayList(m_desktopExtensions); |
| } |
| |
| /** |
| * @return the special extension that contributes the contents of this desktop itself |
| */ |
| protected IDesktopExtension getLocalDesktopExtension() { |
| return m_localDesktopExtension; |
| } |
| |
| protected org.eclipse.scout.rt.client.extension.ui.desktop.IDesktopExtension<? extends AbstractDesktop> createLocalExtension() { |
| return new LocalDesktopExtension<AbstractDesktop>(this); |
| } |
| |
| @Override |
| public final List<? extends org.eclipse.scout.rt.client.extension.ui.desktop.IDesktopExtension<? extends AbstractDesktop>> getAllExtensions() { |
| return m_objectExtensions.getAllExtensions(); |
| } |
| |
| @Override |
| public <T extends IExtension<?>> T getExtension(Class<T> c) { |
| return m_objectExtensions.getExtension(c); |
| } |
| |
| protected final void interceptInitConfig() { |
| m_objectExtensions.initConfig(createLocalExtension(), new Runnable() { |
| @Override |
| public void run() { |
| initConfig(); |
| } |
| }); |
| } |
| |
| protected void initConfig() { |
| BEANS.get(IDeviceTransformationService.class).install(this); |
| initDesktopExtensions(); |
| setTitle(getConfiguredTitle()); |
| setSelectViewTabsKeyStrokesEnabled(getConfiguredSelectViewTabsKeyStrokesEnabled()); |
| setSelectViewTabsKeyStrokeModifier(getConfiguredSelectViewTabsKeyStrokeModifier()); |
| setLogoId(getConfiguredLogoId()); |
| setNavigationVisible(getConfiguredNavigationVisible()); |
| setNavigationHandleVisible(getConfiguredNavigationHandleVisible()); |
| setBenchVisible(getConfiguredBenchVisible()); |
| setHeaderVisible(getConfiguredHeaderVisible()); |
| setDisplayStyle(getConfiguredDisplayStyle()); |
| initDisplayStyle(getDisplayStyle()); |
| setCacheSplitterPosition(getConfiguredCacheSplitterPosition()); |
| List<IDesktopExtension> extensions = getDesktopExtensions(); |
| m_contributionHolder = new ContributionComposite(this); |
| |
| // outlines |
| OrderedCollection<IOutline> outlines = new OrderedCollection<IOutline>(); |
| for (IDesktopExtension ext : extensions) { |
| try { |
| ext.contributeOutlines(outlines); |
| } |
| catch (Exception t) { |
| LOG.error("contributing outlines by {}", ext, t); |
| } |
| } |
| List<IOutline> contributedOutlines = m_contributionHolder.getContributionsByClass(IOutline.class); |
| outlines.addAllOrdered(contributedOutlines); |
| |
| // move outlines |
| ExtensionUtility.moveModelObjects(outlines); |
| m_availableOutlines = outlines.getOrderedList(); |
| |
| // actions (keyStroke, menu, viewButton, toolButton) |
| List<IAction> actionList = new ArrayList<IAction>(); |
| for (IDesktopExtension ext : extensions) { |
| try { |
| ext.contributeActions(actionList); |
| } |
| catch (Exception t) { |
| LOG.error("contributing actions by {}", ext, t); |
| } |
| } |
| List<IAction> contributedActions = m_contributionHolder.getContributionsByClass(IAction.class); |
| actionList.addAll(contributedActions); |
| |
| // build complete menu and viewButton lists |
| // only top level menus |
| OrderedComparator orderedComparator = new OrderedComparator(); |
| List<IMenu> menuList = new ActionFinder().findActions(actionList, IMenu.class, false); |
| ExtensionUtility.moveModelObjects(menuList); |
| Collections.sort(menuList, orderedComparator); |
| m_menus = menuList; |
| |
| List<IViewButton> viewButtonList = new ActionFinder().findActions(actionList, IViewButton.class, false); |
| ExtensionUtility.moveModelObjects(viewButtonList); |
| Collections.sort(viewButtonList, orderedComparator); |
| m_viewButtons = viewButtonList; |
| |
| // add dynamic keyStrokes |
| List<IKeyStroke> ksList = new ActionFinder().findActions(actionList, IKeyStroke.class, true); |
| for (IKeyStroke ks : ksList) { |
| try { |
| ks.initAction(); |
| } |
| catch (RuntimeException e) { |
| LOG.error("could not initialize key stroke '{}'.", ks, e); |
| } |
| } |
| addKeyStrokes(ksList.toArray(new IKeyStroke[ksList.size()])); |
| |
| // init outlines |
| for (IOutline o : m_availableOutlines) { |
| try { |
| o.initTree(); |
| } |
| catch (Exception e) { |
| LOG.error("Could not init outline {}", o, e); |
| } |
| } |
| addPropertyChangeListener(new P_LocalPropertyChangeListener()); |
| } |
| |
| protected final void interceptInit() { |
| List<? extends org.eclipse.scout.rt.client.extension.ui.desktop.IDesktopExtension<? extends org.eclipse.scout.rt.client.ui.desktop.AbstractDesktop>> extensions = getAllExtensions(); |
| DesktopInitChain chain = new DesktopInitChain(extensions); |
| chain.execInit(); |
| } |
| |
| private void initDesktopExtensions() { |
| m_desktopExtensions = new LinkedList<IDesktopExtension>(); |
| m_desktopExtensions.add(getLocalDesktopExtension()); |
| injectDesktopExtensions(m_desktopExtensions); |
| } |
| |
| /** |
| * Override to provide a set of extensions (modules) that contribute their content to this desktop. |
| * <p> |
| * The default list contains only the {@link #getLocalDesktopExtension()} |
| * </p> |
| * <p> |
| * The extension that are held by this desktop must call {@link IDesktopExtension#setCoreDesktop(this)} before using |
| * the extension. That way the extension can use and access this desktop's methods. |
| * </p> |
| * |
| * @param desktopExtensions |
| * a live list can be modified. |
| */ |
| protected void injectDesktopExtensions(List<IDesktopExtension> desktopExtensions) { |
| List<IDesktopExtension> extensions = BEANS.all(IDesktopExtension.class); |
| for (IDesktopExtension e : extensions) { |
| e.setCoreDesktop(this); |
| } |
| desktopExtensions.addAll(extensions); |
| } |
| |
| @Override |
| public void initDesktop() { |
| if (m_desktopInitialized) { |
| return; |
| } |
| // extensions |
| for (IDesktopExtension ext : getDesktopExtensions()) { |
| try { |
| ContributionCommand cc = ext.initDelegate(); |
| if (cc == ContributionCommand.Stop) { |
| break; |
| } |
| } |
| catch (Exception t) { |
| LOG.error("extension {} failed", ext, t); |
| } |
| } |
| ActionUtility.initActions(getActions()); |
| m_desktopInitialized = true; |
| } |
| |
| protected void initDisplayStyle(String style) { |
| if (DISPLAY_STYLE_BENCH.equals(style)) { |
| setNavigationVisible(false); |
| setNavigationHandleVisible(false); |
| setHeaderVisible(false); |
| setBenchVisible(true); |
| } |
| else if (DISPLAY_STYLE_COMPACT.equals(style)) { |
| setNavigationVisible(true); |
| setNavigationHandleVisible(false); |
| setHeaderVisible(false); |
| setBenchVisible(false); |
| } |
| else { |
| setNavigationVisible(true); |
| setNavigationHandleVisible(true); |
| setHeaderVisible(true); |
| setBenchVisible(true); |
| } |
| } |
| |
| @Override |
| public boolean isShowing(IForm form) { |
| if (form == null) { |
| return false; |
| } |
| |
| // wrapped form |
| if (form.getOuterForm() != null) { |
| return form.getOuterForm().isShowing(); |
| } |
| |
| // dialog or view |
| if (m_formStore.contains(form)) { |
| return true; |
| } |
| |
| // active detail or search Form |
| if (form == m_pageDetailForm || form == m_pageSearchForm) { |
| return true; |
| } |
| |
| // active menu Form |
| for (IMenu menu : getMenus()) { |
| if (menu instanceof IFormMenu && menu.isSelected() && ((IFormMenu) menu).getForm() == form) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| @Override |
| public <FORM extends IForm> FORM findForm(Class<FORM> formType) { |
| if (formType == null) { |
| return null; |
| } |
| |
| for (final IForm candidate : m_formStore.values()) { |
| if (formType.isAssignableFrom(candidate.getClass())) { |
| @SuppressWarnings("unchecked") |
| FORM form = (FORM) candidate; |
| return form; |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public <FORM extends IForm> List<FORM> findForms(final Class<FORM> formType) { |
| if (formType == null) { |
| return CollectionUtility.emptyArrayList(); |
| } |
| |
| final List<FORM> forms = new ArrayList<FORM>(); |
| for (final IForm candidate : m_formStore.values()) { |
| if (formType.isAssignableFrom(candidate.getClass())) { |
| @SuppressWarnings("unchecked") |
| FORM form = (FORM) candidate; |
| forms.add(form); |
| } |
| } |
| return forms; |
| } |
| |
| @Override |
| @SuppressWarnings("unchecked") |
| public <T extends IOutline> T findOutline(Class<T> outlineType) { |
| for (IOutline o : getAvailableOutlines()) { |
| if (outlineType.isAssignableFrom(o.getClass())) { |
| return (T) o; |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public <T extends IAction> T findAction(Class<T> actionType) { |
| return new ActionFinder().findAction(getActions(), actionType); |
| } |
| |
| @Override |
| @Deprecated |
| public <T extends IToolButton> T findToolButton(Class<T> toolButtonType) { |
| return findAction(toolButtonType); |
| } |
| |
| @Override |
| public <T extends IMenu> T findMenu(Class<T> menuType) { |
| return findAction(menuType); |
| } |
| |
| @Override |
| public <T extends IViewButton> T findViewButton(Class<T> viewButtonType) { |
| return findAction(viewButtonType); |
| } |
| |
| @Override |
| public String getDisplayStyle() { |
| return propertySupport.getPropertyString(PROP_DISPLAY_STYLE); |
| } |
| |
| @Override |
| public void setDisplayStyle(String displayStyle) { |
| propertySupport.setProperty(PROP_DISPLAY_STYLE, displayStyle); |
| } |
| |
| @Override |
| public String getLogoId() { |
| return propertySupport.getPropertyString(PROP_LOGO_ID); |
| } |
| |
| @Override |
| public void setLogoId(String id) { |
| propertySupport.setPropertyString(PROP_LOGO_ID, id); |
| } |
| |
| @Override |
| public IFormField getFocusOwner() { |
| return fireFindFocusOwner(); |
| } |
| |
| @Override |
| public IForm getActiveForm() { |
| return (IForm) propertySupport.getProperty(PROP_ACTIVE_FORM); |
| } |
| |
| protected void setActiveForm(IForm form) { |
| propertySupport.setProperty(PROP_ACTIVE_FORM, form); |
| } |
| |
| @Override |
| public List<IForm> getForms(IDisplayParent displayParent) { |
| return m_formStore.getByDisplayParent(displayParent); |
| } |
| |
| @Override |
| public List<IForm> getViewStack() { |
| return getViews(); |
| } |
| |
| @Override |
| public List<IForm> getViews() { |
| return m_formStore.getViews(); |
| } |
| |
| @Override |
| public List<IForm> getViews(IDisplayParent displayParent) { |
| return m_formStore.getViewsByDisplayParent(displayParent); |
| } |
| |
| @Override |
| public <F extends IForm, H extends IFormHandler> List<F> findAllOpenViews(Class<? extends F> formClass, Class<? extends H> handlerClass, Object exclusiveKey) { |
| List<F> forms = new ArrayList<>(); |
| if (exclusiveKey != null) { |
| for (IForm view : getViews()) { |
| if (getPageDetailForm() == view || getPageSearchForm() == view) { |
| continue; |
| } |
| Object candidateKey = view.computeExclusiveKey(); |
| if (candidateKey == null) { |
| continue; |
| } |
| else { |
| LOG.debug("form: {} vs {}", candidateKey, exclusiveKey); |
| if (exclusiveKey.equals(candidateKey) |
| && view.getClass() == formClass |
| && view.getHandler() != null |
| && view.getHandler().getClass() == handlerClass |
| && view.getHandler().isOpenExclusive()) { |
| forms.add(formClass.cast(view)); |
| } |
| } |
| } |
| } |
| return forms; |
| } |
| |
| @Override |
| public <F extends IForm, H extends IFormHandler> F findOpenView(Class<? extends F> formClass, Class<? extends H> handlerClass, Object exclusiveKey) { |
| return CollectionUtility.firstElement(findAllOpenViews(formClass, handlerClass, exclusiveKey)); |
| } |
| |
| @Override |
| public List<IForm> getDialogStack() { |
| return getDialogs(); |
| } |
| |
| @Override |
| public List<IForm> getDialogs() { |
| return m_formStore.getDialogs(); |
| } |
| |
| @Override |
| public List<IForm> getDialogs(final IDisplayParent displayParent, final boolean includeChildDialogs) { |
| final List<IForm> dialogs = new ArrayList<>(); |
| |
| for (final IForm dialog : m_formStore.getDialogsByDisplayParent(displayParent)) { |
| // Add the dialog's child dialogs first. |
| if (includeChildDialogs) { |
| dialogs.addAll(getDialogs(dialog, true)); |
| } |
| |
| // Add the dialog. |
| dialogs.add(dialog); |
| } |
| |
| return dialogs; |
| } |
| |
| @Override |
| public List<IMessageBox> getMessageBoxStack() { |
| return getMessageBoxes(); |
| } |
| |
| @Override |
| public List<IMessageBox> getMessageBoxes() { |
| return m_messageBoxStore.values(); |
| } |
| |
| @Override |
| public List<IMessageBox> getMessageBoxes(IDisplayParent displayParent) { |
| return m_messageBoxStore.getByDisplayParent(displayParent); |
| } |
| |
| @Override |
| // TODO [5.2] dwi: Clarify whether this method is still used. |
| public List<IForm> getSimilarViewForms(final IForm form) { |
| if (form == null) { |
| return CollectionUtility.emptyArrayList(); |
| } |
| |
| final Object formKey; |
| try { |
| formKey = form.computeExclusiveKey(); |
| } |
| catch (final RuntimeException e) { |
| BEANS.get(ExceptionHandler.class).handle(e); |
| return CollectionUtility.emptyArrayList(); |
| } |
| |
| if (formKey == null) { |
| return CollectionUtility.emptyArrayList(); |
| } |
| |
| final IForm currentDetailForm = getPageDetailForm(); |
| final IForm currentSearchForm = getPageSearchForm(); |
| |
| final List<IForm> similarForms = new ArrayList<>(); |
| for (final IForm candidateView : m_formStore.getViewsByKey(formKey)) { |
| if (candidateView == currentDetailForm) { |
| continue; |
| } |
| if (candidateView == currentSearchForm) { |
| continue; |
| } |
| |
| if (candidateView.getClass().equals(form.getClass())) { |
| similarForms.add(candidateView); |
| } |
| } |
| |
| return similarForms; |
| } |
| |
| @Override |
| public void ensureViewStackVisible() { // TODO [5.2] dwi: Clarify whether this method is still used. |
| for (IForm view : m_formStore.getViews()) { |
| activateForm(view); |
| } |
| } |
| |
| @Override |
| |
| public void ensureVisible(IForm form) { |
| activateForm(form); |
| } |
| |
| @Override |
| public void activateForm(IForm form) { |
| if (form == null) { |
| setActiveForm(form); |
| return; |
| } |
| |
| if (!m_formStore.contains(form)) { |
| return; // only dialogs or views can be activated. |
| } |
| |
| IDisplayParent displayParent = form.getDisplayParent(); |
| if (displayParent instanceof IForm) { |
| activateForm((IForm) displayParent); |
| } |
| else if (displayParent instanceof IOutline && !displayParent.equals(getOutline())) { |
| activateOutline(((IOutline) displayParent)); |
| } |
| setActiveForm(form); |
| fireFormActivate(form); |
| } |
| |
| @Override |
| public void activateOutline(IOutline outline) { |
| activateOutlineInternal(outline); |
| fireOutlineContentActivate(); |
| } |
| |
| protected void activateOutlineInternal(IOutline outline) { |
| final IOutline newOutline = resolveOutline(outline); |
| if (m_outline == newOutline || m_outlineChanging) { |
| return; |
| } |
| |
| ClientRunContexts.copyCurrent() |
| .withOutline(newOutline, true) |
| .run(new IRunnable() { |
| |
| @Override |
| public void run() throws Exception { |
| try { |
| m_outlineChanging = true; |
| setOutlineInternal(newOutline); |
| } |
| finally { |
| m_outlineChanging = false; |
| } |
| } |
| }); |
| } |
| |
| protected void setOutlineInternal(IOutline newOutline) { |
| if (m_outline == newOutline) { |
| return; |
| } |
| // |
| IOutline oldOutline = m_outline; |
| if (m_activeOutlineListener != null && oldOutline != null) { |
| oldOutline.removeTreeListener(m_activeOutlineListener); |
| oldOutline.removePropertyChangeListener(m_activeOutlineListener); |
| m_activeOutlineListener = null; |
| } |
| // set new outline to set facts |
| m_outline = newOutline; |
| // deactivate old page |
| if (oldOutline != null) { |
| oldOutline.clearContextPage(); |
| } |
| // |
| if (m_outline != null) { |
| m_activeOutlineListener = new P_ActiveOutlineListener(); |
| m_outline.addTreeListener(m_activeOutlineListener); |
| m_outline.addPropertyChangeListener(m_activeOutlineListener); |
| setBrowserHistoryEntry(BEANS.get(OutlineDeepLinkHandler.class).createBrowserHistoryEntry(m_outline)); |
| } |
| // <bsh 2010-10-15> |
| // Those three "setXyz(null)" statements used to be called unconditionally. Now, they |
| // are only called when the new outline is null. When the new outline is _not_ null, we |
| // will override the "null" anyway (see below). |
| // This change is needed for the "on/off semantics" of the tool tab buttons to work correctly. |
| if (m_outline == null) { |
| setPageDetailForm(null); |
| setPageDetailTable(null); |
| setPageSearchForm(null, true); |
| } |
| // </bsh> |
| updateActiveFormOnOutlineChanged(); |
| fireOutlineChanged(oldOutline, m_outline); |
| onOutlineChangedInternal(); |
| } |
| |
| // FIXME awe: review with C.GU - is it sufficient to set the active form to null? |
| // or do we have to find another suitable active form? I guess the UI will send |
| // the new active form later... |
| protected void updateActiveFormOnOutlineChanged() { |
| // If active form is not set or parent of active form is desktop |
| // we must do nothing |
| IForm activeForm = getActiveForm(); |
| if (activeForm == null || activeForm.getDisplayParent() == this) { |
| return; |
| } |
| |
| // Does the active form belong to the current outline? |
| // if not, we must set the active form to null otherwise we do nothing |
| List<IForm> formsByOutline = m_formStore.getByDisplayParent(m_outline); |
| if (!formsByOutline.contains(activeForm)) { |
| setActiveForm(null); |
| } |
| } |
| |
| /** |
| * Called after the outline has been changed |
| */ |
| protected void onOutlineChangedInternal() { |
| if (m_outline == null) { |
| return; |
| } |
| // reload selected page in case it is marked dirty |
| if (m_outline.getActivePage() != null) { |
| try { |
| m_outline.getActivePage().ensureChildrenLoaded(); |
| } |
| catch (RuntimeException e) { |
| BEANS.get(ExceptionHandler.class).handle(e); |
| } |
| } |
| m_outline.setNodeExpanded(m_outline.getRootNode(), true); |
| setPageDetailForm(m_outline.getDetailForm()); |
| setPageDetailTable(m_outline.getDetailTable()); |
| setPageSearchForm(m_outline.getSearchForm(), true); |
| m_outline.makeActivePageToContextPage(); |
| } |
| |
| @Override |
| public void addForm(IForm form) { |
| showForm(form); |
| } |
| |
| @Override |
| public void showForm(IForm form) { |
| // Let the desktop extensions to intercept the given form. |
| final IHolder<IForm> formHolder = new Holder<>(form); |
| for (IDesktopExtension extension : getDesktopExtensions()) { |
| try { |
| if (extension.formAboutToShowDelegate(formHolder) == ContributionCommand.Stop) { |
| break; |
| } |
| } |
| catch (Exception e) { |
| formHolder.setValue(form); |
| BEANS.get(ExceptionHandler.class).handle(e); |
| } |
| } |
| form = formHolder.getValue(); |
| |
| // Only show the form if not null nor already showing. |
| if (form == null || m_formStore.contains(form)) { |
| return; |
| } |
| |
| // Validate the Form's modality configuration. |
| Assertions.assertNotNull(form.getDisplayParent(), "Property 'displayParent' must not be null"); |
| boolean applicationModal = (form.isModal() && form.getDisplayParent() == this); |
| boolean view = (form.getDisplayHint() == IForm.DISPLAY_HINT_VIEW); |
| |
| // Ensure not to show a 'application-modal' view. Otherwise, no user interaction would be possible. |
| Assertions.assertFalse(view && applicationModal, "'desktop-modality' not supported for views"); |
| |
| // Ensure not to show a modal Form if the application is in 'application-modal' state. Otherwise, no user interaction would be possible. |
| if (view && form.isModal()) { |
| boolean applicationLocked = m_formStore.containsApplicationModalDialogs() || m_messageBoxStore.containsApplicationModalMessageBoxes() || m_fileChooserStore.containsApplicationModalFileChoosers(); |
| Assertions.assertFalse(applicationLocked, "Modal view cannot be showed because application is in 'desktop-modal' state; otherwise, no user interaction would be possible."); |
| } |
| |
| m_formStore.add(form); |
| activateForm(form); |
| fireFormShow(form); |
| } |
| |
| @Override |
| public void removeForm(IForm form) { |
| hideForm(form); |
| } |
| |
| @Override |
| public void hideForm(IForm form) { |
| if (form == null || !m_formStore.contains(form)) { |
| return; |
| } |
| |
| // Unset the currently active Form if being the given Form. |
| // The new active Form will be set by the UI or manually by IDesktop.activateForm(IForm). |
| if (getActiveForm() == form) { |
| setActiveForm(null); |
| } |
| m_formStore.remove(form); |
| fireFormHide(form); |
| } |
| |
| @Override |
| public void addMessageBox(final IMessageBox messageBox) { |
| showMessageBox(messageBox); |
| } |
| |
| @Override |
| public void showMessageBox(IMessageBox messageBox) { |
| if (messageBox == null || m_messageBoxStore.contains(messageBox)) { |
| return; |
| } |
| |
| // Ensure 'displayParent' to be set. |
| Assertions.assertNotNull(messageBox.getDisplayParent(), "Property 'displayParent' must not be null"); |
| |
| m_messageBoxStore.add(messageBox); |
| fireMessageBoxShow(messageBox); |
| } |
| |
| @Override |
| public void removeMessageBox(IMessageBox messageBox) { |
| hideMessageBox(messageBox); |
| } |
| |
| @Override |
| public void hideMessageBox(IMessageBox messageBox) { |
| if (messageBox == null || !m_messageBoxStore.contains(messageBox)) { |
| return; |
| } |
| |
| m_messageBoxStore.remove(messageBox); |
| fireMessageBoxHide(messageBox); |
| } |
| |
| @Override |
| public boolean isShowing(IMessageBox messageBox) { |
| return m_messageBoxStore.contains(messageBox); |
| } |
| |
| @Override |
| public List<IOutline> getAvailableOutlines() { |
| return CollectionUtility.arrayList(m_availableOutlines); |
| } |
| |
| @Override |
| public void setAvailableOutlines(List<? extends IOutline> availableOutlines) { |
| setOutline((IOutline) null); |
| m_availableOutlines = CollectionUtility.arrayList(availableOutlines); |
| } |
| |
| @Override |
| public IOutline getOutline() { |
| return m_outline; |
| } |
| |
| @Override |
| public boolean isOutlineChanging() { |
| return m_outlineChanging; |
| } |
| |
| @Override |
| public void setOutline(IOutline outline) { |
| activateOutline(outline); |
| } |
| |
| @Override |
| public void activateFirstPage() { |
| if (m_outline.isRootNodeVisible()) { |
| m_outline.selectNode(m_outline.getRootNode(), false); |
| } |
| else { |
| List<ITreeNode> children = m_outline.getRootNode().getChildNodes(); |
| if (CollectionUtility.hasElements(children)) { |
| for (ITreeNode node : children) { |
| if (node.isVisible() && node.isEnabled()) { |
| m_outline.selectNode(node, false); |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| private IOutline resolveOutline(IOutline outline) { |
| for (IOutline o : getAvailableOutlines()) { |
| if (o == outline) { |
| return o; |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public void setOutline(Class<? extends IOutline> outlineType) { |
| if (outlineType == null) { |
| return; |
| } |
| for (IOutline o : getAvailableOutlines()) { |
| if (outlineType.isInstance(o)) { |
| setOutline(o); |
| return; |
| } |
| } |
| } |
| |
| @Override |
| public Set<IKeyStroke> getKeyStrokes() { |
| return CollectionUtility.hashSet(propertySupport.<IKeyStroke> getPropertySet(PROP_KEY_STROKES)); |
| } |
| |
| @Override |
| public void setKeyStrokes(Collection<? extends IKeyStroke> ks) { |
| propertySupport.setPropertySet(PROP_KEY_STROKES, CollectionUtility.<IKeyStroke> hashSetWithoutNullElements(ks)); |
| } |
| |
| @Override |
| public void addKeyStrokes(IKeyStroke... keyStrokes) { |
| if (keyStrokes != null && keyStrokes.length > 0) { |
| Map<String, IKeyStroke> map = new HashMap<String, IKeyStroke>(); |
| for (IKeyStroke ks : getKeyStrokes()) { |
| map.put(ks.getKeyStroke(), ks); |
| } |
| for (IKeyStroke ks : keyStrokes) { |
| map.put(ks.getKeyStroke(), ks); |
| } |
| setKeyStrokes(map.values()); |
| } |
| } |
| |
| @Override |
| public void removeKeyStrokes(IKeyStroke... keyStrokes) { |
| if (keyStrokes != null && keyStrokes.length > 0) { |
| Map<String, IKeyStroke> map = new HashMap<String, IKeyStroke>(); |
| for (IKeyStroke ks : getKeyStrokes()) { |
| map.put(ks.getKeyStroke(), ks); |
| } |
| for (IKeyStroke ks : keyStrokes) { |
| map.remove(ks.getKeyStroke()); |
| } |
| setKeyStrokes(map.values()); |
| } |
| } |
| |
| @Override |
| public List<IMenu> getMenus() { |
| return CollectionUtility.arrayList(m_menus); |
| } |
| |
| @Override |
| public List<IAction> getActions() { |
| List<IAction> result = new ArrayList<IAction>(); |
| result.addAll(getKeyStrokes()); |
| result.addAll(getMenus()); |
| result.addAll(getViewButtons()); |
| return result; |
| } |
| |
| @Override |
| public <T extends IMenu> T getMenu(Class<? extends T> searchType) { |
| // ActionFinder performs instance-of checks. Hence the menu replacement mapping is not required |
| return new ActionFinder().findAction(getMenus(), searchType); |
| } |
| |
| @Deprecated |
| @Override |
| public <T extends IToolButton> T getToolButton(Class<? extends T> searchType) { |
| // ActionFinder performs instance-of checks. Hence the toolbutton replacement mapping is not required |
| return new ActionFinder().findAction(getMenus(), searchType); |
| } |
| |
| @Override |
| public List<IMenu> getToolButtons() { |
| return getMenus(); |
| } |
| |
| @Override |
| public <T extends IViewButton> T getViewButton(Class<? extends T> searchType) { |
| // ActionFinder performs instance-of checks. Hence the viewbutton replacement mapping is not required |
| return new ActionFinder().findAction(getViewButtons(), searchType); |
| } |
| |
| @Override |
| public List<IViewButton> getViewButtons() { |
| return CollectionUtility.arrayList(m_viewButtons); |
| } |
| |
| @Override |
| public IForm getPageDetailForm() { |
| return m_pageDetailForm; |
| } |
| |
| @Override |
| public void setPageDetailForm(IForm f) { |
| if (m_pageDetailForm != f) { |
| IForm oldForm = m_pageDetailForm; |
| m_pageDetailForm = f; |
| //extensions |
| for (IDesktopExtension ext : getDesktopExtensions()) { |
| try { |
| ContributionCommand cc = ext.pageDetailFormChangedDelegate(oldForm, m_pageDetailForm); |
| if (cc == ContributionCommand.Stop) { |
| break; |
| } |
| } |
| catch (Exception e) { |
| LOG.error("extension {}", ext, e); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public IForm getPageSearchForm() { |
| return m_pageSearchForm; |
| } |
| |
| @Override |
| public void setPageSearchForm(IForm f) { |
| setPageSearchForm(f, false); |
| } |
| |
| public void setPageSearchForm(IForm f, boolean force) { |
| if (force || m_pageSearchForm != f) { |
| IForm oldForm = m_pageSearchForm; |
| m_pageSearchForm = f; |
| //extensions |
| for (IDesktopExtension ext : getDesktopExtensions()) { |
| try { |
| ContributionCommand cc = ext.pageSearchFormChangedDelegate(oldForm, m_pageSearchForm); |
| if (cc == ContributionCommand.Stop) { |
| break; |
| } |
| } |
| catch (Exception t) { |
| LOG.error("extension {}", ext, t); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public ITable getPageDetailTable() { |
| return m_pageDetailTable; |
| } |
| |
| @Override |
| public void setPageDetailTable(ITable t) { |
| if (m_pageDetailTable != t) { |
| ITable oldTable = m_pageDetailTable; |
| m_pageDetailTable = t; |
| //extensions |
| for (IDesktopExtension ext : getDesktopExtensions()) { |
| try { |
| ContributionCommand cc = ext.pageDetailTableChangedDelegate(oldTable, m_pageDetailTable); |
| if (cc == ContributionCommand.Stop) { |
| break; |
| } |
| } |
| catch (Exception x) { |
| LOG.error("extension {}", ext, x); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public String getTitle() { |
| return propertySupport.getPropertyString(PROP_TITLE); |
| } |
| |
| @Override |
| public void setTitle(String s) { |
| propertySupport.setPropertyString(PROP_TITLE, s); |
| } |
| |
| @Override |
| public boolean isSelectViewTabsKeyStrokesEnabled() { |
| return propertySupport.getPropertyBool(PROP_SELECT_VIEW_TABS_KEY_STROKES_ENABLED); |
| } |
| |
| @Override |
| public void setSelectViewTabsKeyStrokesEnabled(boolean selectViewTabsKeyStrokesEnabled) { |
| propertySupport.setPropertyBool(PROP_SELECT_VIEW_TABS_KEY_STROKES_ENABLED, selectViewTabsKeyStrokesEnabled); |
| } |
| |
| @Override |
| public String getSelectViewTabsKeyStrokeModifier() { |
| return propertySupport.getPropertyString(PROP_SELECT_VIEW_TABS_KEY_STROKE_MODIFIER); |
| } |
| |
| @Override |
| public void setSelectViewTabsKeyStrokeModifier(String selectViewTabsKeyStrokeModifier) { |
| propertySupport.setPropertyString(PROP_SELECT_VIEW_TABS_KEY_STROKE_MODIFIER, selectViewTabsKeyStrokeModifier); |
| } |
| |
| @Override |
| public void addNotification(IDesktopNotification notification) { |
| fireNotification(DesktopEvent.TYPE_NOTIFICATION_ADDED, notification); |
| } |
| |
| @Override |
| public void removeNotification(IDesktopNotification notification) { |
| fireNotification(DesktopEvent.TYPE_NOTIFICATION_REMOVED, notification); |
| } |
| |
| @Override |
| public BrowserHistoryEntry getBrowserHistoryEntry() { |
| return (BrowserHistoryEntry) propertySupport.getProperty(PROP_BROWSER_HISTORY_ENTRY); |
| } |
| |
| @Override |
| public void setBrowserHistoryEntry(BrowserHistoryEntry browserHistory) { |
| propertySupport.setProperty(PROP_BROWSER_HISTORY_ENTRY, browserHistory); |
| } |
| |
| @Override |
| public boolean isCacheSplitterPosition() { |
| return propertySupport.getPropertyBool(PROP_CACHE_SPLITTER_POSITION); |
| } |
| |
| @Override |
| public void setCacheSplitterPosition(boolean b) { |
| propertySupport.setPropertyBool(PROP_CACHE_SPLITTER_POSITION, b); |
| } |
| |
| @Override |
| public List<IFileChooser> getFileChooserStack() { |
| return getFileChoosers(); |
| } |
| |
| @Override |
| public List<IFileChooser> getFileChoosers() { |
| return m_fileChooserStore.values(); |
| } |
| |
| @Override |
| public List<IFileChooser> getFileChoosers(IDisplayParent displayParent) { |
| return m_fileChooserStore.getByDisplayParent(displayParent); |
| } |
| |
| @Override |
| public void addFileChooser(final IFileChooser fileChooser) { |
| showFileChooser(fileChooser); |
| } |
| |
| @Override |
| public void showFileChooser(IFileChooser fileChooser) { |
| if (fileChooser == null || m_fileChooserStore.contains(fileChooser)) { |
| return; |
| } |
| |
| // Ensure 'displayParent' to be set. |
| Assertions.assertNotNull(fileChooser.getDisplayParent(), "Property 'displayParent' must not be null"); |
| |
| m_fileChooserStore.add(fileChooser); |
| fireFileChooserShow(fileChooser); |
| } |
| |
| @Override |
| public void hideFileChooser(IFileChooser fileChooser) { |
| if (fileChooser == null || !m_fileChooserStore.contains(fileChooser)) { |
| return; |
| } |
| |
| m_fileChooserStore.remove(fileChooser); |
| fireFileChooserHide(fileChooser); |
| } |
| |
| @Override |
| public boolean isShowing(IFileChooser fileChooser) { |
| return m_fileChooserStore.contains(fileChooser); |
| } |
| |
| @Override |
| public void openUri(String url, IOpenUriAction openUriAction) { |
| Assertions.assertNotNull(url); |
| Assertions.assertNotNull(openUriAction); |
| fireOpenUri(url, openUriAction); |
| } |
| |
| @Override |
| public void openUri(BinaryResource res, IOpenUriAction openUriAction) { |
| Assertions.assertNotNull(res); |
| Assertions.assertNotNull(openUriAction); |
| fireOpenUri(res, openUriAction); |
| } |
| |
| @Override |
| public boolean isAutoPrefixWildcardForTextSearch() { |
| return m_autoPrefixWildcardForTextSearch; |
| } |
| |
| @Override |
| public void setAutoPrefixWildcardForTextSearch(boolean b) { |
| m_autoPrefixWildcardForTextSearch = b; |
| } |
| |
| @Override |
| public boolean isOpened() { |
| return propertySupport.getPropertyBool(PROP_OPENED); |
| } |
| |
| private void setOpenedInternal(boolean b) { |
| propertySupport.setPropertyBool(PROP_OPENED, b); |
| } |
| |
| private void setGuiAvailableInternal(boolean guiAvailable) { |
| propertySupport.setPropertyBool(PROP_GUI_AVAILABLE, guiAvailable); |
| } |
| |
| @Override |
| public boolean isGuiAvailable() { |
| return propertySupport.getPropertyBool(PROP_GUI_AVAILABLE); |
| } |
| |
| @Override |
| public void addDesktopListener(DesktopListener l) { |
| m_listenerList.add(DesktopListener.class, l); |
| } |
| |
| @Override |
| public void addDesktopListenerAtExecutionEnd(DesktopListener l) { |
| m_listenerList.insertAtFront(DesktopListener.class, l); |
| } |
| |
| @Override |
| public void removeDesktopListener(DesktopListener l) { |
| m_listenerList.remove(DesktopListener.class, l); |
| } |
| |
| @Override |
| public void addDataChangeListener(DataChangeListener listener, Object... dataTypes) { |
| if (dataTypes == null || dataTypes.length == 0) { |
| EventListenerList list = m_dataChangeListenerList.get(null); |
| if (list == null) { |
| list = new EventListenerList(); |
| m_dataChangeListenerList.put(null, list); |
| } |
| list.add(DataChangeListener.class, listener); |
| } |
| else { |
| for (Object dataType : dataTypes) { |
| if (dataType != null) { |
| EventListenerList list = m_dataChangeListenerList.get(dataType); |
| if (list == null) { |
| list = new EventListenerList(); |
| m_dataChangeListenerList.put(dataType, list); |
| } |
| list.add(DataChangeListener.class, listener); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void removeDataChangeListener(DataChangeListener listener, Object... dataTypes) { |
| if (dataTypes == null || dataTypes.length == 0) { |
| for (Iterator<EventListenerList> it = m_dataChangeListenerList.values().iterator(); it.hasNext();) { |
| EventListenerList list = it.next(); |
| list.removeAll(DataChangeListener.class, listener); |
| if (list.getListenerCount(DataChangeListener.class) == 0) { |
| it.remove(); |
| } |
| } |
| } |
| else { |
| for (Object dataType : dataTypes) { |
| if (dataType != null) { |
| EventListenerList list = m_dataChangeListenerList.get(dataType); |
| if (list != null) { |
| list.remove(DataChangeListener.class, listener); |
| if (list.getListenerCount(DataChangeListener.class) == 0) { |
| m_dataChangeListenerList.remove(dataType); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| @Override |
| public boolean isDataChanging() { |
| return m_dataChanging > 0; |
| } |
| |
| @Override |
| public void setDataChanging(boolean b) { |
| if (b) { |
| m_dataChanging++; |
| } |
| else { |
| if (m_dataChanging > 0) { |
| m_dataChanging--; |
| if (m_dataChanging == 0) { |
| processDataChangeBuffer(); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void dataChanged(Object... dataTypes) { |
| if (isDataChanging()) { |
| if (dataTypes != null && dataTypes.length > 0) { |
| m_dataChangeEventBuffer.add(dataTypes); |
| } |
| } |
| else { |
| fireDataChangedImpl(dataTypes); |
| } |
| } |
| |
| private void processDataChangeBuffer() { |
| Set<Object> knownEvents = new HashSet<Object>(); |
| for (Object[] dataTypes : m_dataChangeEventBuffer) { |
| for (Object dataType : dataTypes) { |
| knownEvents.add(dataType); |
| } |
| } |
| m_dataChangeEventBuffer.clear(); |
| fireDataChangedImpl(knownEvents.toArray(new Object[knownEvents.size()])); |
| } |
| |
| private void fireDataChangedImpl(Object... dataTypes) { |
| if (dataTypes != null && dataTypes.length > 0) { |
| // Important: Use LinkedHashMaps to make event firing deterministic! |
| // (If listeners would be called in random order, bugs may not be reproduced very well.) |
| HashMap<DataChangeListener, Set<Object>> map = new LinkedHashMap<>(); |
| for (Object dataType : dataTypes) { |
| if (dataType != null) { |
| EventListenerList list = m_dataChangeListenerList.get(dataType); |
| if (list != null) { |
| for (DataChangeListener listener : list.getListeners(DataChangeListener.class)) { |
| Set<Object> typeSet = map.get(listener); |
| if (typeSet == null) { |
| typeSet = new LinkedHashSet<Object>(); |
| map.put(listener, typeSet); |
| } |
| typeSet.add(dataType); |
| } |
| } |
| } |
| } |
| for (Map.Entry<DataChangeListener, Set<Object>> e : map.entrySet()) { |
| DataChangeListener listener = e.getKey(); |
| Set<Object> typeSet = e.getValue(); |
| listener.dataChanged(typeSet.toArray()); |
| } |
| } |
| } |
| |
| private void fireDesktopClosed() { |
| DesktopEvent e = new DesktopEvent(this, DesktopEvent.TYPE_DESKTOP_CLOSED); |
| fireDesktopEvent(e); |
| } |
| |
| private void fireOutlineChanged(IOutline oldOutline, IOutline newOutline) { |
| if (oldOutline != newOutline) { |
| //extensions |
| for (IDesktopExtension ext : getDesktopExtensions()) { |
| try { |
| ContributionCommand cc = ext.outlineChangedDelegate(oldOutline, newOutline); |
| if (cc == ContributionCommand.Stop) { |
| break; |
| } |
| } |
| catch (RuntimeException e) { |
| BEANS.get(ExceptionHandler.class).handle(e); |
| } |
| } |
| } |
| // fire |
| DesktopEvent e = new DesktopEvent(this, DesktopEvent.TYPE_OUTLINE_CHANGED, newOutline); |
| fireDesktopEvent(e); |
| } |
| |
| private void fireOutlineContentActivate() { |
| DesktopEvent e = new DesktopEvent(this, DesktopEvent.TYPE_OUTLINE_CONTENT_ACTIVATE); |
| fireDesktopEvent(e); |
| } |
| |
| private void fireFormShow(IForm form) { |
| DesktopEvent e = new DesktopEvent(this, DesktopEvent.TYPE_FORM_SHOW, form); |
| fireDesktopEvent(e); |
| } |
| |
| private void fireFormActivate(IForm form) { |
| DesktopEvent e = new DesktopEvent(this, DesktopEvent.TYPE_FORM_ACTIVATE, form); |
| fireDesktopEvent(e); |
| } |
| |
| private void fireFormHide(IForm form) { |
| DesktopEvent e = new DesktopEvent(this, DesktopEvent.TYPE_FORM_HIDE, form); |
| fireDesktopEvent(e); |
| } |
| |
| private void fireMessageBoxShow(IMessageBox messageBox) { |
| DesktopEvent e = new DesktopEvent(this, DesktopEvent.TYPE_MESSAGE_BOX_SHOW, messageBox); |
| fireDesktopEvent(e); |
| } |
| |
| private void fireMessageBoxHide(IMessageBox messageBox) { |
| DesktopEvent e = new DesktopEvent(this, DesktopEvent.TYPE_MESSAGE_BOX_HIDE, messageBox); |
| fireDesktopEvent(e); |
| } |
| |
| private void fireFileChooserShow(IFileChooser fileChooser) { |
| DesktopEvent e = new DesktopEvent(this, DesktopEvent.TYPE_FILE_CHOOSER_SHOW, fileChooser); |
| fireDesktopEvent(e); |
| } |
| |
| private void fireFileChooserHide(IFileChooser fileChooser) { |
| DesktopEvent e = new DesktopEvent(this, DesktopEvent.TYPE_FILE_CHOOSER_HIDE, fileChooser); |
| fireDesktopEvent(e); |
| } |
| |
| private void fireOpenUri(String uri, IOpenUriAction openUriAction) { |
| DesktopEvent e = new DesktopEvent(this, DesktopEvent.TYPE_OPEN_URI, uri, openUriAction); |
| fireDesktopEvent(e); |
| } |
| |
| private void fireOpenUri(BinaryResource res, IOpenUriAction openUriAction) { |
| DesktopEvent e = new DesktopEvent(this, DesktopEvent.TYPE_OPEN_URI, res, openUriAction); |
| fireDesktopEvent(e); |
| } |
| |
| private IFormField fireFindFocusOwner() { |
| DesktopEvent e = new DesktopEvent(this, DesktopEvent.TYPE_FIND_FOCUS_OWNER); |
| fireDesktopEvent(e); |
| return e.getFocusedField(); |
| } |
| |
| private void fireNotification(int eventType, IDesktopNotification notification) { |
| DesktopEvent e = new DesktopEvent(this, eventType, notification); |
| fireDesktopEvent(e); |
| } |
| |
| // main handler |
| private void fireDesktopEvent(DesktopEvent event) { |
| DesktopListener[] listeners = m_listenerList.getListeners(DesktopListener.class); |
| if (listeners != null && listeners.length > 0) { |
| for (DesktopListener listener : listeners) { |
| listener.desktopChanged(event); |
| } |
| } |
| } |
| |
| @Override |
| public void activateBookmark(Bookmark bm) { |
| activateBookmark(bm, true); |
| } |
| |
| @Override |
| public void activateBookmark(Bookmark bm, boolean activateOutline) { |
| BookmarkUtility.activateBookmark(this, bm, activateOutline); |
| } |
| |
| @Override |
| public Bookmark createBookmark() { |
| return BookmarkUtility.createBookmark(this); |
| } |
| |
| @Override |
| public Bookmark createBookmark(IPage<?> page) { |
| return BookmarkUtility.createBookmark(page); |
| } |
| |
| @Override |
| public void refreshPages(Class<?>... pageTypes) { |
| for (IOutline outline : getAvailableOutlines()) { |
| outline.refreshPages(pageTypes); |
| } |
| } |
| |
| @Override |
| public void refreshPages(List<Class<? extends IPage>> pages) { |
| for (IOutline outline : getAvailableOutlines()) { |
| outline.refreshPages(pages); |
| } |
| } |
| |
| @Override |
| public void releaseUnusedPages() { |
| for (IOutline outline : getAvailableOutlines()) { |
| outline.releaseUnusedPages(); |
| } |
| } |
| |
| @Override |
| public void afterTablePageLoaded(IPageWithTable<?> tablePage) { |
| //extensions |
| for (IDesktopExtension ext : getDesktopExtensions()) { |
| try { |
| ContributionCommand cc = ext.tablePageLoadedDelegate(tablePage); |
| if (cc == ContributionCommand.Stop) { |
| break; |
| } |
| } |
| catch (Exception t) { |
| LOG.error("extension {}", ext, t); |
| } |
| } |
| } |
| |
| @Override |
| public void closeInternal() { |
| setOpenedInternal(false); |
| detachGui(); |
| |
| List<IForm> showedForms = new ArrayList<IForm>(); |
| // Remove showed forms |
| for (IForm form : m_formStore.values()) { |
| hideForm(form); |
| showedForms.add(form); |
| } |
| //extensions |
| for (IDesktopExtension ext : getDesktopExtensions()) { |
| try { |
| ContributionCommand cc = ext.desktopClosingDelegate(); |
| if (cc == ContributionCommand.Stop) { |
| break; |
| } |
| } |
| catch (RuntimeException t) { |
| LOG.error("extension {}", ext, t); |
| } |
| } |
| |
| // close open forms |
| for (IForm form : showedForms) { |
| if (form != null) { |
| try { |
| form.doClose(); |
| } |
| catch (RuntimeException e) { |
| LOG.error("Exception while closing form", e); |
| } |
| } |
| } |
| |
| // outlines |
| for (IOutline outline : getAvailableOutlines()) { |
| try { |
| outline.disposeTree(); |
| } |
| catch (RuntimeException e) { |
| LOG.warn("Exception while disposing outline.", e); |
| } |
| } |
| |
| ActionUtility.disposeActions(getActions()); |
| |
| fireDesktopClosed(); |
| } |
| |
| private void attachGui(String deepLinkPath) { |
| if (isGuiAvailable()) { |
| return; |
| } |
| setGuiAvailableInternal(true); |
| |
| // extensions |
| for (IDesktopExtension ext : getDesktopExtensions()) { |
| try { |
| ContributionCommand cc = ext.guiAttachedDelegate(); |
| if (cc == ContributionCommand.Stop) { |
| break; |
| } |
| } |
| catch (Exception e) { |
| BEANS.get(ExceptionHandler.class).handle(e); |
| } |
| } |
| |
| activateDefaultView(deepLinkPath); |
| } |
| |
| /** |
| * Activates the initial, default view of the Desktop. This method checks whether the Scout application has been |
| * started with a deep-link. If this applies, the deep-link is executed. Otherwise or if the deep-link has caused an |
| * error execDefaultView is called. |
| * |
| * @param deepLinkPath |
| * The deep-link path which was passed as URL parameter to the Scout application (if available). This |
| * parameter may be null. See: {@link IDeepLinks}. |
| */ |
| protected void activateDefaultView(String deepLinkPath) { |
| try { |
| if (!handleDeepLink(deepLinkPath)) { |
| interceptDefaultView(); |
| } |
| } |
| catch (DeepLinkException e) { |
| LOG.warn("Failed to execute deep-link '{}', reason: {}", deepLinkPath, e.getMessage()); |
| interceptDefaultView(); |
| showDeepLinkError(e); |
| } |
| } |
| |
| /** |
| * Checks whether the given path is a valid deep-link. If that is the case the deep-link is handled. When an error |
| * occurs while handling the deep-link a {@link DeepLinkException} is thrown, usually this exception is thrown when |
| * the business-logic failed to find the resource addressed by the deep-link (similar to the 404 HTTP error status). |
| * |
| * @param deepLinkPath |
| * @return |
| * @throws DeepLinkException |
| * when deep-link could not be executed |
| */ |
| protected boolean handleDeepLink(String deepLinkPath) throws DeepLinkException { |
| if (hasApplicationModalElement()) { |
| LOG.debug("Could not handle deep-link because modal element prevents changes on UI state. deepLinkPath={}", deepLinkPath); |
| return false; |
| } |
| |
| if (StringUtility.isNullOrEmpty(deepLinkPath)) { |
| return false; |
| } |
| |
| boolean handled = false; |
| IDeepLinks deepLinks = BEANS.get(IDeepLinks.class); |
| if (deepLinks.canHandleDeepLink(deepLinkPath)) { |
| handled = deepLinks.handleDeepLink(deepLinkPath); |
| } |
| return handled; |
| } |
| |
| protected boolean hasApplicationModalElement() { |
| if (m_messageBoxStore.containsApplicationModalMessageBoxes()) { |
| return true; |
| } |
| if (m_formStore.containsApplicationModalDialogs()) { |
| return true; |
| } |
| return m_fileChooserStore.containsApplicationModalFileChoosers(); |
| } |
| |
| /** |
| * Displays a message box showing a generic message that the deep-link could not be executed. |
| */ |
| protected void showDeepLinkError(DeepLinkException e) { |
| if (e.getCause() instanceof VetoException) { |
| // permission problems |
| BEANS.get(ExceptionHandler.class).handle(e.getCause()); |
| } |
| else { |
| // other problems |
| String errorMessage = TEXTS.get("DeepLinkError"); |
| MessageBoxes.createOk().withBody(errorMessage).show(); |
| } |
| } |
| |
| private void detachGui() { |
| if (!isGuiAvailable()) { |
| return; |
| } |
| setGuiAvailableInternal(false); |
| |
| // extensions |
| for (IDesktopExtension ext : getDesktopExtensions()) { |
| try { |
| ContributionCommand cc = ext.guiDetachedDelegate(); |
| if (cc == ContributionCommand.Stop) { |
| break; |
| } |
| } |
| catch (Exception e) { |
| BEANS.get(ExceptionHandler.class).handle(e); |
| } |
| } |
| } |
| |
| public boolean runMenu(Class<? extends IMenu> menuType) { |
| for (IMenu m : getMenus()) { |
| if (runMenuRec(m, menuType)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private boolean runMenuRec(IMenu m, Class<? extends IMenu> menuType) { |
| if (m.getClass() == menuType) { |
| if (m.isVisible() && m.isEnabled()) { |
| m.doAction(); |
| return true; |
| } |
| else { |
| return false; |
| } |
| } |
| // children |
| for (IMenu c : m.getChildActions()) { |
| if (runMenuRec(c, menuType)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public Collection<Object> getAddOns() { |
| return Collections.unmodifiableCollection(m_addOns); |
| } |
| |
| @Override |
| public void addAddOn(Object addOn) { |
| m_addOns.add(addOn); |
| } |
| |
| @Override |
| public IDesktopUIFacade getUIFacade() { |
| return m_uiFacade; |
| } |
| |
| private boolean isForcedClosing() { |
| return m_isForcedClosing; |
| } |
| |
| private void setForcedClosing(boolean forcedClosing) { |
| m_isForcedClosing = forcedClosing; |
| } |
| |
| @Override |
| public void setNavigationVisible(boolean visible) { |
| propertySupport.setProperty(PROP_NAVIGATION_VISIBLE, visible); |
| } |
| |
| @Override |
| public boolean isNavigationVisible() { |
| return (boolean) propertySupport.getProperty(PROP_NAVIGATION_VISIBLE); |
| } |
| |
| @Override |
| public void setNavigationHandleVisible(boolean visible) { |
| propertySupport.setProperty(PROP_NAVIGATION_HANDLE_VISIBLE, visible); |
| } |
| |
| @Override |
| public boolean isNavigationHandleVisible() { |
| return (boolean) propertySupport.getProperty(PROP_NAVIGATION_HANDLE_VISIBLE); |
| } |
| |
| @Override |
| public void setBenchVisible(boolean visible) { |
| propertySupport.setProperty(PROP_BENCH_VISIBLE, visible); |
| } |
| |
| @Override |
| public boolean isBenchVisible() { |
| return (boolean) propertySupport.getProperty(PROP_BENCH_VISIBLE); |
| } |
| |
| @Override |
| public void setHeaderVisible(boolean visible) { |
| propertySupport.setProperty(PROP_HEADER_VISIBLE, visible); |
| } |
| |
| @Override |
| public boolean isHeaderVisible() { |
| return (boolean) propertySupport.getProperty(PROP_HEADER_VISIBLE); |
| } |
| |
| /** |
| * local desktop extension that calls local exec methods and returns local contributions in this class itself |
| */ |
| private class P_LocalDesktopExtension implements IDesktopExtension { |
| @Override |
| public IDesktop getCoreDesktop() { |
| return AbstractDesktop.this; |
| } |
| |
| @Override |
| public void setCoreDesktop(IDesktop desktop) { |
| // NOP |
| } |
| |
| @Override |
| public void contributeOutlines(OrderedCollection<IOutline> outlines) { |
| List<Class<? extends IOutline>> configuredOutlines = getConfiguredOutlines(); |
| if (configuredOutlines != null) { |
| for (Class<?> element : configuredOutlines) { |
| try { |
| IOutline o = (IOutline) element.newInstance(); |
| outlines.addOrdered(o); |
| } |
| catch (Exception e) { |
| BEANS.get(ExceptionHandler.class).handle(new ProcessingException("error creating instance of class '" + element.getName() + "'.", e)); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void contributeActions(Collection<IAction> actions) { |
| for (Class<? extends IAction> actionClazz : getConfiguredActions()) { |
| actions.add(ConfigurationUtility.newInnerInstance(AbstractDesktop.this, actionClazz)); |
| } |
| } |
| |
| @Override |
| public ContributionCommand initDelegate() { |
| interceptInit(); |
| return ContributionCommand.Continue; |
| } |
| |
| @Override |
| public ContributionCommand desktopOpenedDelegate() { |
| interceptOpened(); |
| return ContributionCommand.Continue; |
| } |
| |
| @Override |
| public ContributionCommand desktopBeforeClosingDelegate() { |
| interceptBeforeClosing(); |
| return ContributionCommand.Continue; |
| } |
| |
| @Override |
| public ContributionCommand desktopClosingDelegate() { |
| interceptClosing(); |
| return ContributionCommand.Continue; |
| } |
| |
| @Override |
| public ContributionCommand guiAttachedDelegate() { |
| interceptGuiAttached(); |
| return ContributionCommand.Continue; |
| } |
| |
| @Override |
| public ContributionCommand guiDetachedDelegate() { |
| interceptGuiDetached(); |
| return ContributionCommand.Continue; |
| } |
| |
| @Override |
| public ContributionCommand outlineChangedDelegate(IOutline oldOutline, IOutline newOutline) { |
| interceptOutlineChanged(oldOutline, newOutline); |
| return ContributionCommand.Continue; |
| } |
| |
| @Override |
| public ContributionCommand formAboutToShowDelegate(IHolder<IForm> formHolder) { |
| formHolder.setValue(interceptFormAboutToShow(formHolder.getValue())); |
| return ContributionCommand.Continue; |
| } |
| |
| @Override |
| public ContributionCommand pageSearchFormChangedDelegate(IForm oldForm, IForm newForm) { |
| interceptPageSearchFormChanged(oldForm, newForm); |
| return ContributionCommand.Continue; |
| } |
| |
| @Override |
| public ContributionCommand pageDetailFormChangedDelegate(IForm oldForm, IForm newForm) { |
| interceptPageDetailFormChanged(oldForm, newForm); |
| return ContributionCommand.Continue; |
| } |
| |
| @Override |
| public ContributionCommand pageDetailTableChangedDelegate(ITable oldTable, ITable newTable) { |
| interceptPageDetailTableChanged(oldTable, newTable); |
| return ContributionCommand.Continue; |
| } |
| |
| @Override |
| public ContributionCommand tablePageLoadedDelegate(IPageWithTable<?> tablePage) { |
| interceptTablePageLoaded(tablePage); |
| return ContributionCommand.Continue; |
| } |
| } |
| |
| protected class P_UIFacade implements IDesktopUIFacade { |
| |
| @Override |
| public void historyEntryActivatedFromUI(String deepLinkPath) { |
| try { |
| handleDeepLink(deepLinkPath); |
| } |
| catch (DeepLinkException e) { |
| showDeepLinkError(e); |
| } |
| } |
| |
| @Override |
| public void fireGuiAttached(String deepLinkPath) { |
| attachGui(deepLinkPath); |
| } |
| |
| @Override |
| public void fireGuiDetached() { |
| detachGui(); |
| } |
| |
| @Override |
| public void openFromUI() { |
| setOpenedInternal(true); |
| //extensions |
| for (IDesktopExtension ext : getDesktopExtensions()) { |
| try { |
| ContributionCommand cc = ext.desktopOpenedDelegate(); |
| if (cc == ContributionCommand.Stop) { |
| break; |
| } |
| } |
| catch (Exception e) { |
| BEANS.get(ExceptionHandler.class).handle(e); |
| } |
| } |
| } |
| |
| @Override |
| public void closeFromUI(boolean forcedClosing) { |
| setForcedClosing(forcedClosing); |
| // necessary so that no forms can be opened anymore. |
| if (forcedClosing) { |
| setOpenedInternal(false); |
| } |
| ClientSessionProvider.currentSession().stop(); |
| } |
| |
| @Override |
| public void setNavigationVisibleFromUI(boolean visible) { |
| setNavigationVisible(visible); |
| } |
| |
| @Override |
| public void setHeaderVisibleFromUI(boolean visible) { |
| setHeaderVisible(visible); |
| } |
| |
| @Override |
| public void setBenchVisibleFromUI(boolean visible) { |
| setBenchVisible(visible); |
| } |
| } |
| |
| private class P_ActiveOutlineListener extends TreeAdapter implements PropertyChangeListener { |
| @Override |
| public void treeChanged(TreeEvent e) { |
| switch (e.getType()) { |
| case TreeEvent.TYPE_NODES_SELECTED: { |
| try { |
| ClientSessionProvider.currentSession().getMemoryPolicy().afterOutlineSelectionChanged(AbstractDesktop.this); |
| } |
| catch (Exception t) { |
| LOG.warn("MemoryPolicy.afterOutlineSelectionChanged", t); |
| } |
| break; |
| } |
| } |
| } |
| |
| @Override |
| public void propertyChange(PropertyChangeEvent e) { |
| if (e.getPropertyName().equals(IOutline.PROP_DETAIL_FORM)) { |
| setPageDetailForm(((IOutline) e.getSource()).getDetailForm()); |
| } |
| else if (e.getPropertyName().equals(IOutline.PROP_DETAIL_TABLE)) { |
| setPageDetailTable(((IOutline) e.getSource()).getDetailTable()); |
| } |
| else if (e.getPropertyName().equals(IOutline.PROP_SEARCH_FORM)) { |
| setPageSearchForm(((IOutline) e.getSource()).getSearchForm()); |
| } |
| } |
| } |
| |
| /** |
| * <p> |
| * Default behavior of the pre-hook is to delegate the before closing call to the each desktop extension. The method |
| * {@link IDesktopExtension#desktopBeforeClosingDelegate()} of each desktop extension will be called. If at least one |
| * of the desktop extension's delegate throws a {@link VetoException}, the closing process will be stopped. |
| * </p> |
| * <p> |
| * Additionally, before closing, a dialog is shown to allow the user to save unsaved forms. At this point it is also |
| * possible to cancel closing. |
| * </p> |
| * If the flag {@link AbstractDesktop#m_isForcedClosing} is set to <code>true</code>, closing is always continued, |
| * without considering desktopExtensions or unsaved forms. |
| */ |
| @Override |
| public boolean doBeforeClosingInternal() { |
| return isForcedClosing() || (continueClosingInDesktopExtensions() && continueClosingConsideringUnsavedForms()); |
| } |
| |
| protected boolean continueClosingConsideringUnsavedForms() { |
| List<IForm> forms = getUnsavedForms(); |
| if (forms.size() > 0) { |
| try { |
| UnsavedFormChangesForm f = new UnsavedFormChangesForm(forms); |
| f.startNew(); |
| f.waitFor(); |
| if (f.getCloseSystemType() == IButton.SYSTEM_TYPE_CANCEL) { |
| return false; |
| } |
| } |
| catch (RuntimeException e) { |
| LOG.error("Error closing forms", e); |
| } |
| } |
| return true; |
| } |
| |
| private boolean continueClosingInDesktopExtensions() { |
| boolean continueClosing = true; |
| List<IDesktopExtension> extensions = getDesktopExtensions(); |
| if (extensions != null) { |
| for (IDesktopExtension ext : extensions) { |
| try { |
| ContributionCommand cc = ext.desktopBeforeClosingDelegate(); |
| if (cc == ContributionCommand.Stop) { |
| break; |
| } |
| } |
| catch (VetoException e) { |
| continueClosing = false; |
| } |
| catch (RuntimeException e) { |
| BEANS.get(ExceptionHandler.class).handle(e); |
| } |
| } |
| } |
| return continueClosing; |
| } |
| |
| @Override |
| public List<IForm> getUnsavedForms() { |
| List<IForm> saveNeededForms = new ArrayList<IForm>(); |
| List<IForm> showedForms = m_formStore.values(); |
| // last element on the stack is the first that needs to be saved: iterate from end to start |
| for (int i = showedForms.size() - 1; i >= 0; i--) { |
| IForm f = showedForms.get(i); |
| if (f.isAskIfNeedSave() && f.isSaveNeeded()) { |
| saveNeededForms.add(f); |
| } |
| } |
| return saveNeededForms; |
| } |
| |
| @Override |
| public String getTheme() { |
| return propertySupport.getPropertyString(PROP_THEME); |
| } |
| |
| @Override |
| public void setTheme(String theme) { |
| propertySupport.setProperty(PROP_THEME, theme); |
| } |
| |
| /** |
| * The extension delegating to the local methods. This Extension is always at the end of the chain and will not call |
| * any further chain elements. |
| */ |
| protected static class LocalDesktopExtension<DESKTOP extends AbstractDesktop> extends AbstractExtension<DESKTOP> implements org.eclipse.scout.rt.client.extension.ui.desktop.IDesktopExtension<DESKTOP> { |
| |
| public LocalDesktopExtension(DESKTOP desktop) { |
| super(desktop); |
| } |
| |
| @Override |
| public void execInit(DesktopInitChain chain) { |
| getOwner().execInit(); |
| } |
| |
| @Override |
| public void execOpened(DesktopOpenedChain chain) { |
| getOwner().execOpened(); |
| } |
| |
| @Override |
| public void execBeforeClosing(DesktopBeforeClosingChain chain) { |
| getOwner().execBeforeClosing(); |
| } |
| |
| @Override |
| public void execPageDetailFormChanged(DesktopPageDetailFormChangedChain chain, IForm oldForm, IForm newForm) { |
| getOwner().execPageDetailFormChanged(oldForm, newForm); |
| } |
| |
| @Override |
| public void execTablePageLoaded(DesktopTablePageLoadedChain chain, IPageWithTable<?> tablePage) { |
| getOwner().execTablePageLoaded(tablePage); |
| } |
| |
| @Override |
| public void execOutlineChanged(DesktopOutlineChangedChain chain, IOutline oldOutline, IOutline newOutline) { |
| getOwner().execOutlineChanged(oldOutline, newOutline); |
| } |
| |
| @Override |
| public IForm execFormAboutToShow(DesktopFormAboutToShowChain chain, IForm form) { |
| return getOwner().execFormAboutToShow(form); |
| } |
| |
| @Override |
| public void execClosing(DesktopClosingChain chain) { |
| getOwner().execClosing(); |
| } |
| |
| @Override |
| public void execPageSearchFormChanged(DesktopPageSearchFormChangedChain chain, IForm oldForm, IForm newForm) { |
| getOwner().execPageSearchFormChanged(oldForm, newForm); |
| } |
| |
| @Override |
| public void execPageDetailTableChanged(DesktopPageDetailTableChangedChain chain, ITable oldTable, ITable newTable) { |
| getOwner().execPageDetailTableChanged(oldTable, newTable); |
| } |
| |
| @Override |
| public void execGuiAttached(DesktopGuiAttachedChain chain) { |
| getOwner().execGuiAttached(); |
| } |
| |
| @Override |
| public void execGuiDetached(DesktopGuiDetachedChain chain) { |
| getOwner().execGuiDetached(); |
| } |
| |
| @Override |
| public void execDefaultView(DesktopDefaultViewChain chain) { |
| getOwner().execDefaultView(); |
| } |
| |
| } |
| |
| protected final void interceptOpened() { |
| List<? extends org.eclipse.scout.rt.client.extension.ui.desktop.IDesktopExtension<? extends AbstractDesktop>> extensions = getAllExtensions(); |
| DesktopOpenedChain chain = new DesktopOpenedChain(extensions); |
| chain.execOpened(); |
| } |
| |
| protected final void interceptBeforeClosing() { |
| List<? extends org.eclipse.scout.rt.client.extension.ui.desktop.IDesktopExtension<? extends AbstractDesktop>> extensions = getAllExtensions(); |
| DesktopBeforeClosingChain chain = new DesktopBeforeClosingChain(extensions); |
| chain.execBeforeClosing(); |
| } |
| |
| protected final void interceptPageDetailFormChanged(IForm oldForm, IForm newForm) { |
| List<? extends org.eclipse.scout.rt.client.extension.ui.desktop.IDesktopExtension<? extends AbstractDesktop>> extensions = getAllExtensions(); |
| DesktopPageDetailFormChangedChain chain = new DesktopPageDetailFormChangedChain(extensions); |
| chain.execPageDetailFormChanged(oldForm, newForm); |
| } |
| |
| protected final void interceptTablePageLoaded(IPageWithTable<?> tablePage) { |
| List<? extends org.eclipse.scout.rt.client.extension.ui.desktop.IDesktopExtension<? extends AbstractDesktop>> extensions = getAllExtensions(); |
| DesktopTablePageLoadedChain chain = new DesktopTablePageLoadedChain(extensions); |
| chain.execTablePageLoaded(tablePage); |
| } |
| |
| protected final void interceptOutlineChanged(IOutline oldOutline, IOutline newOutline) { |
| List<? extends org.eclipse.scout.rt.client.extension.ui.desktop.IDesktopExtension<? extends AbstractDesktop>> extensions = getAllExtensions(); |
| DesktopOutlineChangedChain chain = new DesktopOutlineChangedChain(extensions); |
| chain.execOutlineChanged(oldOutline, newOutline); |
| } |
| |
| protected final IForm interceptFormAboutToShow(IForm form) { |
| List<? extends org.eclipse.scout.rt.client.extension.ui.desktop.IDesktopExtension<? extends AbstractDesktop>> extensions = getAllExtensions(); |
| DesktopFormAboutToShowChain chain = new DesktopFormAboutToShowChain(extensions); |
| return chain.execFormAboutToShow(form); |
| } |
| |
| protected final void interceptClosing() { |
| List<? extends org.eclipse.scout.rt.client.extension.ui.desktop.IDesktopExtension<? extends AbstractDesktop>> extensions = getAllExtensions(); |
| DesktopClosingChain chain = new DesktopClosingChain(extensions); |
| chain.execClosing(); |
| } |
| |
| protected final void interceptPageSearchFormChanged(IForm oldForm, IForm newForm) { |
| List<? extends org.eclipse.scout.rt.client.extension.ui.desktop.IDesktopExtension<? extends AbstractDesktop>> extensions = getAllExtensions(); |
| DesktopPageSearchFormChangedChain chain = new DesktopPageSearchFormChangedChain(extensions); |
| chain.execPageSearchFormChanged(oldForm, newForm); |
| } |
| |
| protected final void interceptPageDetailTableChanged(ITable oldTable, ITable newTable) { |
| List<? extends org.eclipse.scout.rt.client.extension.ui.desktop.IDesktopExtension<? extends AbstractDesktop>> extensions = getAllExtensions(); |
| DesktopPageDetailTableChangedChain chain = new DesktopPageDetailTableChangedChain(extensions); |
| chain.execPageDetailTableChanged(oldTable, newTable); |
| } |
| |
| protected final void interceptGuiAttached() { |
| List<? extends org.eclipse.scout.rt.client.extension.ui.desktop.IDesktopExtension<? extends AbstractDesktop>> extensions = getAllExtensions(); |
| DesktopGuiAttachedChain chain = new DesktopGuiAttachedChain(extensions); |
| chain.execGuiAttached(); |
| } |
| |
| protected final void interceptGuiDetached() { |
| List<? extends org.eclipse.scout.rt.client.extension.ui.desktop.IDesktopExtension<? extends AbstractDesktop>> extensions = getAllExtensions(); |
| DesktopGuiDetachedChain chain = new DesktopGuiDetachedChain(extensions); |
| chain.execGuiDetached(); |
| } |
| |
| protected final void interceptDefaultView() { |
| List<? extends org.eclipse.scout.rt.client.extension.ui.desktop.IDesktopExtension<? extends AbstractDesktop>> extensions = getAllExtensions(); |
| DesktopDefaultViewChain chain = new DesktopDefaultViewChain(extensions); |
| chain.execDefaultView(); |
| } |
| |
| protected class P_LocalPropertyChangeListener implements PropertyChangeListener { |
| |
| @Override |
| public void propertyChange(PropertyChangeEvent evt) { |
| if (evt.getPropertyName() == PROP_DISPLAY_STYLE) { |
| initDisplayStyle((String) evt.getNewValue()); |
| } |
| } |
| |
| } |
| } |