/**
* 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;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.DefaultKeyboardFocusManager;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.HashSet;
import java.util.Set;
import javax.swing.Icon;
import javax.swing.JComponent;
import com.jidesoft.docking.DockContext;
import com.jidesoft.docking.DockableFrame;
import com.jidesoft.docking.DockingManager;
import com.jidesoft.docking.Workspace;
import com.jidesoft.docking.event.DockableFrameAdapter;
import com.jidesoft.docking.event.DockableFrameEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.valkyriercp.application.*;
import org.valkyriercp.application.docking.editor.AbstractEditor;
import org.valkyriercp.application.docking.editor.EditorComponentPane;
import org.valkyriercp.application.docking.editor.EditorDescriptor;
import org.valkyriercp.application.docking.editor.WorkspaceView;
import org.valkyriercp.application.docking.view.JideAbstractView;
import org.valkyriercp.application.docking.view.JideViewDescriptor;
import org.valkyriercp.application.event.LifecycleApplicationEvent;
import org.valkyriercp.application.perspective.PerspectiveManager;
import org.valkyriercp.application.support.DefaultApplicationPage;
import org.valkyriercp.application.support.DefaultViewContext;
import org.valkyriercp.command.CommandManager;
import org.valkyriercp.command.support.ActionCommand;
import org.valkyriercp.image.IconSource;
import org.valkyriercp.util.ValkyrieRepository;
/**
*
* Spring RCP ApplicationPage implementation that simply delegates
* most things to the underlying docking manager. The page is
* constructed from an instance of DockingPageDescriptor that specifies
* a list of Spring RCP View instances that are added as docked windows, and
* a Spring RCP View instance for the workspace.
*
* @author Jonny Wray
*
*/
public class JideApplicationPage extends DefaultApplicationPage {
private static final Logger log = LoggerFactory.getLogger(JideApplicationPage.class);
private Set<String> pageViews = new HashSet<>();
private JideApplicationWindow window;
private WorkspaceView workspaceComponent;
private JComponent control;
private FocusOwnerChangeListener focusOwnerListener = new FocusOwnerChangeListener();
public JideApplicationPage(ApplicationWindow window, PageDescriptor pageDescriptor) {
super(window, pageDescriptor);
if(log.isInfoEnabled()){
log.info("Constructing Application page " +pageDescriptor.getId());
}
this.window = (JideApplicationWindow)window;
DefaultKeyboardFocusManager.getCurrentKeyboardFocusManager().addPropertyChangeListener("focusOwner", focusOwnerListener);
}
/**
* Overridden close method to avoid memory leaks by Mikael Valot
*/
public boolean close(){
super.close();
DefaultKeyboardFocusManager.getCurrentKeyboardFocusManager().removePropertyChangeListener("focusOwner", focusOwnerListener);
focusOwnerListener = null;
return true;
}
/**
* Returns the workspace view, or null if it is not configured.
*
*/
public WorkspaceView getWorkspaceView(){
return workspaceComponent;
}
public PerspectiveManager getPerspectiveManager(){
return ((JidePageDescriptor)getPageDescriptor()).getPerspectiveManager();
}
public JComponent createControl(){
if (control == null){
this.getPageDescriptor().buildInitialLayout(this);
DockingManager manager = window.getDockingManager();
control = manager.getDockedFrameContainer();
Object initialEditorContents = ((JidePageDescriptor) getPageDescriptor())
.getInitialEditorContents();
if (initialEditorContents != null)
{
openEditor(initialEditorContents);
}
}
return control;
}
protected void doRemovePageComponent( PageComponent pageComponent ) {
if(log.isDebugEnabled()){
log.debug("Removing "+pageComponent.getId());
}
if(pageComponent instanceof Editor){
workspaceComponent.remove(pageComponent);
}
else{
DockingManager manager = window.getDockingManager();
manager.removeFrame(pageComponent.getId());
}
}
/**
* This sets the visible flag on all show view commands that
* are registered with the command manager. If the page contains
* the view the command is visible, otherwise not. The registration
* of the show view command with the command manager is the
* responsibility of the view descriptor.
*
*/
public void updateShowViewCommands(){
ViewDescriptorRegistry viewDescriptorRegistry = ValkyrieRepository.getInstance().getApplicationConfig().viewDescriptorRegistry();
ViewDescriptor[] views = viewDescriptorRegistry.getViewDescriptors();
for (ViewDescriptor view : views) {
String id = view.getId();
CommandManager commandManager = window.getCommandManager();
/*if(commandManager.containsActionCommand(id)){
ActionCommand command =
commandManager.getActionCommand(id);
command.setVisible(pageViews.contains(views[i].getId()));
}*/
if (commandManager.isTypeMatch(id, ActionCommand.class)) {
ActionCommand command =
(ActionCommand) commandManager.getCommand(id, ActionCommand.class);
command.setVisible(pageViews.contains(view.getId()));
}
}
}
protected boolean giveFocusTo(PageComponent pageComponent) {
if(log.isDebugEnabled()){
log.debug("Giving focus to "+pageComponent.getId());
}
PageComponentPane pane = pageComponent.getContext().getPane();
pane.getControl().requestFocusInWindow();
fireFocusGained(pageComponent);
return true;
}
public View showView(ViewDescriptor viewDescriptor){
if (log.isDebugEnabled())
{
log.debug("Adding view for " + viewDescriptor.getId());
}
View view = super.showView(viewDescriptor);
registerPageComponent(viewDescriptor.getId());
return view;
}
public void addView(String viewDescriptorId){
showView(viewDescriptorId);
}
/**
* Calls openEditor with activateAfterOpen to true
*/
public void openEditor(Object editorInput){
openEditor(editorInput, true);
}
/**
* Implementation of the openEditor command using the editor factory
* injected into the page descriptor. The editor input is used as the
* key the factory uses to get the requires editor descriptor. If not
* editor factory is injected into the page descriptor then the default
* factory is used which simply returns null for every editor object
* resulting in no editor being opened.
*
* @param editorInput the editor contents
* @param activateAfterOpen specifies if the document should be activated after opening
*/
public void openEditor(Object editorInput, boolean activateAfterOpen){
if(log.isDebugEnabled()){
log.debug("Attempting to open editor for "+editorInput.getClass().getName());
}
if(workspaceComponent == null){
log.debug("WorkspaceComponent is null");
return;
}
PageDescriptor descriptor = getPageDescriptor();
if(descriptor instanceof JidePageDescriptor){
if(editorInput.getClass().isArray()){
Object[] array = (Object[])editorInput;
for (Object editorObject : array) {
processEditorObject(editorObject, activateAfterOpen);
}
}
else{
processEditorObject(editorInput, activateAfterOpen);
}
}
}
private void processEditorObject(Object editorObject, boolean activateAfterOpen){
JidePageDescriptor pageDescriptor = (JidePageDescriptor)getPageDescriptor();
EditorDescriptor editorDescriptor =
pageDescriptor.getEditorFactory().getEditorDescriptor(editorObject);
if(editorDescriptor != null){
PageComponent pageComponent = editorDescriptor.createPageComponent();
AbstractEditor editor = (AbstractEditor)pageComponent;
editor.setEditorInput(editorObject);
EditorComponentPane editorPane = new EditorComponentPane(pageComponent);
pageComponent.setContext(new DefaultViewContext(this, editorPane));
WorkspaceView workspace = workspaceComponent;
workspace.addDocumentComponent(pageComponent, activateAfterOpen);
// Fires lifecycle event so listeners can react to editor
// being opened.
LifecycleApplicationEvent event = new LifecycleApplicationEvent(
LifecycleApplicationEvent.CREATED, editor);
ValkyrieRepository.getInstance().getApplicationConfig().applicationContext().publishEvent(event);
}
}
private void registerPageComponent(String viewDescriptorId){
PageComponent pageComponent = findPageComponent(viewDescriptorId);
ViewDescriptor descriptor = getViewDescriptor(viewDescriptorId);
if (descriptor instanceof JideViewDescriptor)
{
JideViewDescriptor viewDescriptor = (JideViewDescriptor) descriptor;
if (viewDescriptor.isWorkspace()){
registerWorkspaceView(pageComponent);
}
else {
registerNormalView(pageComponent, viewDescriptor);
pageViews.add(viewDescriptorId);
}
}
else{
registerNormalView(pageComponent, null);
pageViews.add(viewDescriptorId);
}
}
private void registerWorkspaceView(PageComponent pageComponent){
if (log.isInfoEnabled())
{
log.info("Registering workspace view " + pageComponent.getId());
}
DockingManager manager = window.getDockingManager();
manager.getWorkspace().add(pageComponent.getControl());
workspaceComponent = (WorkspaceView)pageComponent;
}
private void registerNormalView(PageComponent pageComponent, JideViewDescriptor viewDescriptor) {
if (log.isInfoEnabled()) {
log.info("Registering view " + pageComponent.getId());
}
DockingManager manager = window.getDockingManager();
String frameName = pageComponent.getId();
if (manager.getAllFrameNames().contains(frameName)) {
if (log.isDebugEnabled()) {
log.debug("Showing existing docked frame " + frameName);
}
DockContext currentContext = manager.getContextOf(frameName);
if (currentContext.isHidden()) {
if (viewDescriptor.isFloatOnShow()) {
manager.floatFrame(frameName, viewDescriptor.getFloatBounds(), true);
}
else {
manager.showFrame(frameName);
}
}
else {
manager.activateFrame(frameName);
}
}
else {
if (log.isDebugEnabled()) {
log.debug("Adding new dockable frame " + frameName);
}
DockableFrame frame = createDockableFrame(pageComponent, viewDescriptor);
manager.addFrame(frame);
if(viewDescriptor.getShowTitleBar() != null){
frame.setShowTitleBar(viewDescriptor.getShowTitleBar());
}
}
}
private DockableFrame createDockableFrame(final PageComponent pageComponent,
JideViewDescriptor viewDescriptor) {
if (log.isInfoEnabled()) {
log.info("Creating dockable frame for page component "+ pageComponent.getId());
}
Icon icon = pageComponent.getIcon();
if (icon == null) {
IconSource iconSource = ValkyrieRepository.getInstance().getApplicationConfig().iconSource();
icon = iconSource.getIcon("applicationInfo.image");
}
DockableFrame dockableFrame = new DockableFrame(pageComponent.getId(), icon);
dockableFrame.setTitle(pageComponent.getDisplayName());
dockableFrame.setTabTitle(pageComponent.getDisplayName());
dockableFrame.setFrameIcon(icon);
if(viewDescriptor != null){
dockableFrame.getContext().setInitMode(viewDescriptor.getInitMode());
dockableFrame.getContext().setInitSide(viewDescriptor.getInitSide());
dockableFrame.getContext().setInitIndex(viewDescriptor.getInitIndex());
if(viewDescriptor.getAutohidable() != null){
dockableFrame.setAutohidable(viewDescriptor.getAutohidable());
}
if(viewDescriptor.getFloatable() != null){
dockableFrame.setFloatable(viewDescriptor.getFloatable());
}
if(viewDescriptor.getDockable() != null){
dockableFrame.setDockable(viewDescriptor.getDockable());
}
if(viewDescriptor.getHidable() != null){
dockableFrame.setHidable(viewDescriptor.getHidable());
}
if(viewDescriptor.getSideDockAllowed() != null){
dockableFrame.setSideDockAllowed(viewDescriptor.getSideDockAllowed());
}
if(viewDescriptor.getSlidingAutohide() != null){
dockableFrame.setSlidingAutohide(viewDescriptor.getSlidingAutohide());
}
if(viewDescriptor.getTabDockAllowed() != null){
dockableFrame.setTabDockAllowed(viewDescriptor.getTabDockAllowed());
}
if(viewDescriptor.getShowGripper() != null){
dockableFrame.setShowGripper(viewDescriptor.getShowGripper());
}
if(viewDescriptor.getMaximizable() != null){
dockableFrame.setMaximizable(viewDescriptor.getMaximizable());
}
if(viewDescriptor.getRearrangable() != null){
dockableFrame.setRearrangable(viewDescriptor.getRearrangable());
}
}
else{
dockableFrame.getContext().setInitMode(DockContext.STATE_FRAMEDOCKED);
dockableFrame.getContext().setInitSide(DockContext.DOCK_SIDE_EAST);
dockableFrame.getContext().setInitIndex(0);
}
dockableFrame.addDockableFrameListener(new DockableFrameAdapter() {
public void dockableFrameRemoved(DockableFrameEvent event) {
if(log.isDebugEnabled()){
log.debug("Frame removed event on "+pageComponent.getId());
}
fireClosed(pageComponent);
}
public void dockableFrameActivated(DockableFrameEvent e) {
if(log.isDebugEnabled()){
log.debug("Frame activated event on "+pageComponent.getId());
}
fireFocusLost(workspaceComponent);
fireFocusGained(pageComponent);
}
public void dockableFrameDeactivated(DockableFrameEvent e){
if(log.isDebugEnabled()){
log.debug("Frame deactivated event on "+pageComponent.getId());
}
fireFocusLost(pageComponent);
}
});
dockableFrame.getContentPane().setLayout(new BorderLayout());
dockableFrame.getContentPane().add(pageComponent.getControl());
// This is where the view specific toolbar and menu bar get added. Note,
// that this is different from the editors. With the views they are part
// of the dockable frame, but with editors we add them to the editor
// pane itself in EditorComponentPane
if(pageComponent instanceof JideAbstractView){
JideAbstractView view = (JideAbstractView)pageComponent;
dockableFrame.setTitleBarComponent(view.getViewToolBar());
dockableFrame.setJMenuBar(view.getViewMenuBar());
}
return dockableFrame;
}
/*
* These next four methods change the access from protected to public allowing
* non children to fire these events. This is needs to allow the Jide events to
* translate into spring events
*/
public void fireClosed(PageComponent component) {
super.fireClosed(component);
}
public void fireOpened(PageComponent component) {
super.fireOpened(component);
}
public void fireFocusLost(PageComponent component) {
if(component != null){
super.fireFocusLost(component);
}
}
public void fireFocusGained(PageComponent component) {
if(component != null){
super.fireFocusGained(component);
}
}
/*
* This listener (registered in the constructor on the DefaultKeyboardFocusManager) is
* here to deal with one odd case in the JIDE to Spring RCP event translation. If a
* document tab is clicked after a specific view was active, a documentComponentActivated
* event is not fired (I'm not really sure why not) causing the spring rcp command
* framework not to follow the active frame/document. This overcomes that problem. The
* link documents the JIDE forum thread discussing this.
*
* http://www.jidesoft.com/forum/viewtopic.php?t=2722
*/
private class FocusOwnerChangeListener implements PropertyChangeListener{
public void propertyChange(PropertyChangeEvent evt) {
DefaultKeyboardFocusManager manager = (DefaultKeyboardFocusManager)evt.getSource();
Component focusOwner = manager.getFocusOwner();
Workspace workspace = (Workspace)getDockableWorkspace(focusOwner);
if( workspace != null){
if(log.isDebugEnabled()){
log.debug("About to fire focus gained on the active workspace component");
}
WorkspaceView workspaceView = workspaceComponent;
workspaceView.fireFocusGainedOnActiveComponent();
}
}
private Component getDockableWorkspace(Component c) {
if (c == null) return null;
if (c instanceof Workspace) return c;
do {
c = c.getParent();
if (c == null) return null;
}
while (!( c instanceof Workspace));
return c;
}
}
}