/** * 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.docking.editor; import java.awt.Image; import java.beans.PropertyChangeListener; import java.util.Locale; import javax.swing.Icon; import javax.swing.JComponent; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.MessageSource; import org.springframework.util.Assert; import org.valkyriercp.application.Editor; import org.valkyriercp.application.PageComponentContext; import org.valkyriercp.application.PageComponentDescriptor; import org.valkyriercp.application.config.ApplicationLifecycleAdvisor; import org.valkyriercp.application.docking.JideApplicationLifecycleAdvisor; import org.valkyriercp.command.support.AbstractActionCommandExecutor; import org.valkyriercp.command.support.CommandGroup; import org.valkyriercp.command.support.GlobalCommandIds; import org.valkyriercp.progress.ProgressMonitor; import org.valkyriercp.util.ValkyrieRepository; /** * Abstract base class for an editor. Provides ability to specify editor * specific toolbar and menubar as well as providing convience methods * for getting values from the descriptor, registering property change * listeners. Specifies a template method that should be overridden to * allow registration of editor specific command executors, and preregisters * a save and a saveAs executor that simply call the relevant editor methods * * @author Jonny Wray * */ public abstract class AbstractEditor implements Editor { private static final Logger logger = LoggerFactory.getLogger(AbstractEditor.class); private static final String TOOLBAR_SUFFIX = ".editorToolBar"; private static final String MENU_SUFFIX = ".editorMenuBar"; private String toolBarCommandGroupName = null; private String menuBarCommandGroupName = null; private PageComponentDescriptor descriptor; private PageComponentContext context; private Object editorObject; public void setEditorInput(Object editorObject){ this.editorObject = editorObject; initialize(editorObject); } public Object getEditorInput(){ return editorObject; } /** * This should be implemented to do the actual initialization * code with the specific editor object. Called after setEditorObject * is called. * * @param editorObject */ public abstract void initialize(Object editorObject); /** * Implement to provide editor specific control * * @return The component that is the central control in the * editor. It has toolbar * and menubar added to it by the framework if specified */ public abstract JComponent getControl(); /** * Implement to provide editor specific id. As one editor descriptor * can give rise to multiple editors this needs to be unique for each * editor instance. * * @return The identifing string of the editor instance. Each editor in * the collection displayed is unique. */ public abstract String getId(); /** * The display name is used as the editor tab title * * @return The display name, if not overridden comes from the descriptor */ public String getDisplayName() { return descriptor.getDisplayName(); } /** * The caption is used by the status bar as an opened message * * @return The caption, if not overridden comes from the descriptor */ public String getCaption() { return descriptor.getCaption(); } /** * The description is used by the editor tab tooltip * * @return The description, if not overridden comes from the descriptor */ public String getDescription() { return descriptor.getDescription(); } /** * Default comes from the descriptor */ public Image getImage() { return descriptor.getImage(); } /** * Default comes from the descriptor * * @return the icon used in the editor document tab */ public Icon getIcon() { return descriptor.getIcon(); } public PageComponentDescriptor getDescriptor(){ return descriptor; } public PageComponentContext getContext() { return context; } public boolean canClose(){ return true; } public void componentOpened() { } public void componentFocusGained() { } public void componentFocusLost() { } public void componentClosed() { } /** * Method to obtain a message from the message source * defined via the services locator, at the default locale. */ protected String getMessage(String key, Object[] params){ MessageSource messageSource = ValkyrieRepository.getInstance().getApplicationConfig().messageSource(); return messageSource.getMessage(key, params, Locale.getDefault()); } /** * Calls close on the active windows page */ public void close(){ ValkyrieRepository.getInstance().getApplicationConfig().windowManager().getActiveWindow().getPage().close(this); } /** * This method is called when an editor is removed from the * workspace. */ public void dispose(){ if(logger.isDebugEnabled()){ logger.debug("Disposing of editor "+getId()); } context.register(GlobalCommandIds.SAVE, null); context.register(GlobalCommandIds.SAVE_AS, null); //descriptor.removePropertyChangeListener((PropertyChangeListener)context.getPane()); concreteDispose(); } /** * This method is called by the dispose method as intended to be * overridden by the concrete implementations of actual editors. They * should unregister any listeners etc. to avoid memory leaks. * */ protected void concreteDispose(){} public void setDescriptor(PageComponentDescriptor descriptor) { Assert.notNull(descriptor, "The editor descriptor is required"); Assert.state(this.descriptor == null, "An editor's descriptor may only be set once"); this.descriptor = descriptor; } public final void setContext(PageComponentContext context) { Assert.notNull(context, "This editors's page component context is required"); Assert.state(this.context == null, "An editor's context may only be set once"); this.context = context; registerCommandExecutors(context); registerLocalCommandExecutors(context); } /* * Registers some editor specific command executors for save and * save as that just call the relevant methods within the editor. */ private void registerCommandExecutors(PageComponentContext context){ context.register(GlobalCommandIds.SAVE, new SaveCommandExecutor()); context.register(GlobalCommandIds.SAVE_AS, new SaveAsCommandExecutor()); } /** * Template method called once when this editor is initialized; allows * subclasses to register local executors for shared commands with the view * context. * * @param context the view context */ protected void registerLocalCommandExecutors(PageComponentContext context) { } public void addPropertyChangeListener(PropertyChangeListener listener) { descriptor.addPropertyChangeListener(listener); } public void addPropertyChangeListener(String name, PropertyChangeListener listener) { descriptor.addPropertyChangeListener(name, listener); } public void removePropertyChangeListener(PropertyChangeListener listener) { descriptor.removePropertyChangeListener(listener); } public void removePropertyChangeListener(String name, PropertyChangeListener listener) { descriptor.removePropertyChangeListener(name, listener); } /** * Default is true which means editors can always * be saved. */ public boolean isDirty() { return true; } /** * Null implementation does not do anything but the method * is registered to the local save executor and * GlobalCommandIds.SAVE */ public void save() { } /** * Null implementation does not do anything and is not * registered to an executor by default */ public void save(ProgressMonitor monitor) { } /** * Null implementation does not do anything but the method * is registered to the local save executor and * GlobalCommandIds.SAVE_AS */ public void saveAs() { } /** * Default is true */ public boolean isSaveAsSupported() { return true; } /** * Default is false */ public boolean isSaveOnCloseRecommended() { return false; } /* * The code below supports declarative editor specific commands * that can be turned into toolbars and/or menubars and added to * the editor window. * * TODO: This shares a lot of code with the same concept for * views. Either move to a common super class * (eg AbstractPageComponent) or to a helper class to * extend by composition. */ /** * Injects a user defined name for the toolbar command group. If not * definied then the default is constructed from * descriptor.getId() + ".editorToolBar". Note the descriptor id * is used as this is constant for one editor prototype whereas the * actual editor id will change with the instance and so cannot * be used here. * * @param toolBarCommandGroupName */ public void setToolBarCommandGroupName(String toolBarCommandGroupName){ this.toolBarCommandGroupName = toolBarCommandGroupName; } /** * Injects a user defined name for the menubar command group. If not * definied then the default is constructed from * descriptor.getId() + ".editorMenuBar". Note the descriptor id * is used as this is constant for one editor prototype whereas the * actual editor id will change with the instance and so cannot * be used here. * * @param menuBarCommandGroupName */ public void setMenuBarCommandGroupName(String menuBarCommandGroupName){ this.menuBarCommandGroupName = menuBarCommandGroupName; } public String getToolBarCommandGroupName(){ if(toolBarCommandGroupName == null){ return descriptor.getId() + TOOLBAR_SUFFIX; } return toolBarCommandGroupName; } public String getMenuBarCommandGroupName(){ if(menuBarCommandGroupName == null){ return descriptor.getId() + MENU_SUFFIX; } return menuBarCommandGroupName; } public CommandGroup getCommandGroup(String name){ ApplicationLifecycleAdvisor advisor = ValkyrieRepository.getInstance().getApplicationConfig().applicationLifecycleAdvisor(); if(advisor instanceof JideApplicationLifecycleAdvisor){ JideApplicationLifecycleAdvisor dockingAdvisor = (JideApplicationLifecycleAdvisor)advisor; CommandGroup commandGroup = dockingAdvisor.getSpecificCommandGroup(name); return commandGroup; } return null; } /** * Returns the view specific menu bar constructed from * the command group given by the menuBarCommandGroupName or * its default * * @return */ public JComponent getEditorMenuBar(){ CommandGroup commandGroup = getCommandGroup(getMenuBarCommandGroupName()); if(commandGroup == null){ return null; } return commandGroup.createMenuBar(); } /** * Returns the view specific menu bar constructed from * the command group given by the toolBarCommandGroupName or * its default * * @return */ public JComponent getEditorToolBar(){ CommandGroup commandGroup = getCommandGroup(getToolBarCommandGroupName()); if(commandGroup == null){ return null; } return commandGroup.createToolBar(); } /** * Local command executor that simply calls the saveAs command for the * underlying editor. It is the responsibility of the editor to manage * the save process, threads, progress monitoring etc. * * @author Jonny Wray * */ private class SaveAsCommandExecutor extends AbstractActionCommandExecutor { /** * Command is enabled if the underlying editor is dirty and save * as is supported */ public boolean isEnabled() { return isDirty() && isSaveAsSupported(); } /** * Calls saveAs on the editor */ public void execute() { saveAs(); } } private class SaveCommandExecutor extends AbstractActionCommandExecutor { /** * Command is enabled if the underlying editor is dirty */ public boolean isEnabled() { return isDirty(); } /** * Calls save on the editor */ public void execute() { save(); } } }