All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.openbp.jaspira.gui.plugin.JaspiraPage Maven / Gradle / Ivy

The newest version!
/*
 *   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.openbp.jaspira.gui.plugin;

import java.awt.Component;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.lang.ref.WeakReference;
import java.util.Iterator;
import java.util.List;

import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.FocusManager;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;

import org.openbp.jaspira.plugin.Plugin;
import org.openbp.jaspira.plugin.PluginMgr;

/**
 * A page in a application.
 * A page will be displayed in a {@link JaspiraPageContainer} which may contain one or more pages.
 *
 * @author Jens Ferchland
 */
public abstract class JaspiraPage extends AbstractVisiblePlugin
	implements PluginContainer
{
	/** Divider for the contained plugins */
	private PluginDivider divider;

	/**
	 * Used to store the plugin that had the focus last (for {@link #setFocusedPlugin}/{@link #restoreFocus}).
	 * This is a temporary variable only, so we use weak references here in order to prevent memory leaks.
	 */
	protected WeakReference focusedPluginRef;

	/**
	 * Creates a new Jaspira page.
	 */
	public JaspiraPage()
	{
	}

	/**
	 * Returns a string representation of this object.
	 */
	public String toString()
	{
		String s = super.toString();

		for (Iterator it = getPlugins().iterator(); it.hasNext();)
		{
			s += "\nContained plugin:" + it.next();
		}

		return s;
	}

	/**
	 */
	protected void initializeComponents()
	{
		setPluginDivider(new PluginDivider());

		layoutDefaultContent();

		setupKeyBindings();
	}

	/**
	 */
	protected void pluginUninstalled()
	{
		for (Iterator it = getPlugins().iterator(); it.hasNext();)
		{
			PluginMgr.getInstance().removeInstance((Plugin) it.next());
		}
	}

	/**
	 * This method will be called to generate the default layout of a Jaspira page.
	 * The normal JaspiraPage has no default layout so this method does nothing.
	 *
	 * If you create a new page which has to look special override this method
	 * and set your layout here.
	 *
	 * This method will be called automatically - you needn't to call it of your own.
	 */
	public void layoutDefaultContent()
	{
	}

	/**
	 * Set the divider of the page.
	 * @param divider Divider to set
	 */
	public void setPluginDivider(PluginDivider divider)
	{
		this.divider = divider;
		getContentPane().removeAll();
		getContentPane().add(divider);
	}

	/**
	 * Returns the divider of the page.
	 */
	public PluginDivider getPluginDivider()
	{
		return divider;
	}

	/**
	 * Returns the JaspiraPageContainer of this page or null.
	 */
	public Window getWindow()
	{
		return SwingUtilities.getWindowAncestor(getPluginComponent());
	}

	////////////////////////////////////////////////////////////
	// @@ PluginContainer implementation
	////////////////////////////////////////////////////////////

	public void addPlugin(VisiblePlugin p)
	{
		divider.addPlugin(p);
	}

	public void removePlugin(VisiblePlugin p)
	{
		divider.removePlugin(p);
		divider.revalidate();
	}

	public List getPlugins()
	{
		return divider.getPlugins();
	}

	public List getVisiblePlugins()
	{
		return divider.getVisiblePlugins();
	}

	public VisiblePlugin getActivePlugin()
	{
		return divider.getActivePlugin();
	}

	/**
	 * A page has no plugin container, so this returns null.
	 */
	public PluginContainer getParentContainer()
	{
		Window w = SwingUtilities.getWindowAncestor(getPluginComponent());
		if (w != null)
		{
			if (w instanceof JaspiraPageContainer)
			{
				return (JaspiraPageContainer) w;
			}
		}
		return null;
	}

	/**
	 */
	public void showPlugin(boolean changePage)
	{
		// Bring the page to the foreground only if we really should
		if (changePage && !getPluginComponent().isShowing())
		{
			Window w = SwingUtilities.getWindowAncestor(getPluginComponent());
			if (w != null)
			{
				if (w instanceof JaspiraPageContainer)
				{
					((JaspiraPageContainer) w).setPageActive(this);
				}
				w.toFront();
			}
		}
	}

	public void sliceContainer(PluginContainer toInsert, PluginContainer currentContainer, String constraint)
	{
		// matches the slice tagOrientation our splitdirection?
		// if so, we simply need to add the slicer directly
		// (if we are horiz. aligned and slice at EAST or WEST or the opposite)

		if (divider.getOrientation() == PluginDivider.HORIZONTAL_SPLIT)
		{
			// We are Horizontal
			if (PluginContainer.SOUTH.equals(constraint))
			{
				// Add to the end
				divider.insertContainerAt(toInsert, -1);
			}
			else if (PluginContainer.NORTH.equals(constraint))
			{
				// Add to the beginning
				divider.insertContainerAt(toInsert, 0);
			}
			else if (PluginContainer.WEST.equals(constraint))
			{
				PluginDivider sub = new PluginDivider(PluginDivider.VERTICAL_SPLIT);
				sub.addClient((Component) toInsert);
				sub.addClient(divider);
				setPluginDivider(sub);
			}
			else if (PluginContainer.EAST.equals(constraint))
			{
				PluginDivider sub2 = new PluginDivider(PluginDivider.VERTICAL_SPLIT);
				sub2.addClient(divider);
				sub2.addClient((Component) toInsert);
				setPluginDivider(sub2);
			}
		}
		else
		{
			// We are Vertical
			if (PluginContainer.EAST.equals(constraint))
			{
				// Add to the end
				divider.insertContainerAt(toInsert, -1);
			}
			else if (PluginContainer.WEST.equals(constraint))
			{
				// Add to the beginning
				divider.insertContainerAt(toInsert, 0);
			}
			else if (PluginContainer.NORTH.equals(constraint))
			{
				PluginDivider sub = new PluginDivider(PluginDivider.HORIZONTAL_SPLIT);
				sub.addClient((Component) toInsert);
				sub.addClient(divider);
				setPluginDivider(sub);
			}
			else if (PluginContainer.SOUTH.equals(constraint))
			{
				PluginDivider sub2 = new PluginDivider(PluginDivider.HORIZONTAL_SPLIT);
				sub2.addClient(divider);
				sub2.addClient((Component) toInsert);
				setPluginDivider(sub2);
			}
		}
	}

	//////////////////////////////////////////////////
	// @@ Focus support
	//////////////////////////////////////////////////

	/**
	 * Returns the focus component of this plugin, i\.e\. the component
	 * that is to initially receive the focus.
	 * @return The return value defaults to the first component below the content pane of the plugin.
	 * If this component is a scroll pane, the method returns the view component of the pane.
	 * If there is no focusable component, the method returns null.
	 */
	public Component getPluginFocusComponent()
	{
		Component comp = super.getPluginFocusComponent();

		if (comp == null)
		{
			// If we don't have a focusable component ourself, try the component of the currently focused plugin
			if (focusedPluginRef != null)
			{
				VisiblePlugin p = (VisiblePlugin) focusedPluginRef.get();
				if (p != null && p != this)
				{
					comp = p.getPluginFocusComponent();
				}
			}
		}

		return comp;
	}

	/**
	 * Stores the given plugin as last focused component of this page.
	 *
	 * @param plugin The focused plugin
	 */
	public void setFocusedPlugin(VisiblePlugin plugin)
	{
		if (plugin != null)
		{
			if (plugin == this)
			{
				focusedPluginRef = null;

				if (getPluginFocusComponent() == null)
				{
					// We cannot set the focus to ourself, so try the next best plugin
					List plugins = getVisiblePlugins();
					if (plugins != null && plugins.size() > 0)
					{
						plugin = (VisiblePlugin) plugins.get(0);
					}
				}
			}

			if (plugin != null)
			{
				focusedPluginRef = new WeakReference(plugin);
			}
			else
			{
				focusedPluginRef = null;
			}
		}
		else
		{
			focusedPluginRef = null;
		}
	}

	/**
	 * Restores the focus of this page.
	 * Sets the focus to the plugin that had the focus so far or simply tries to determine the component that follows the page component in the focus order.
	 */
	public void restoreFocus()
	{
		boolean hasRestored = false;

		if (focusedPluginRef != null)
		{
			// Try to restore the focus to the stored plugin if it still exists
			VisiblePlugin p = (VisiblePlugin) focusedPluginRef.get();

			if (p != null && p.getPluginHolder() != null)
			{
				// However, don't switch pages.
				p.focusPlugin();
				hasRestored = true;
			}
			else
			{
				// Plugin was garbage collected or removed from the container, kill the reference
				focusedPluginRef = null;
			}
		}

		if (!hasRestored)
		{
			// Unable to restore, choose the next component of the page component instead
			FocusManager.getCurrentManager().focusNextComponent(this.getPluginComponent());
		}
	}

	/**
	 * Advances the focus of this page.
	 *
	 * @param forward
	 *		true	Moves the focus to the next plugin.
* false Moves the focus to the previous plugin. * @param visibleOnly * true Considers currently visible plugins only.
* false Considers also plugins that are currently hidden * (i. e. non-active tabs in tabbed plugin containers) */ public void advanceFocus(boolean forward, boolean visibleOnly) { int currentIndex = -1; // Get a list of all (or all currently visible) plugins of this page // We assume the order delivered by the page is also the tab order List plugins = visibleOnly ? getVisiblePlugins() : getPlugins(); if (plugins == null) return; int n = plugins.size(); if (n == 0) return; for (int i = 0; i < n; ++i) { VisiblePlugin plugin = (VisiblePlugin) plugins.get(i); if (plugin.isPluginFocused()) { // Save the index of the currently focused plugin currentIndex = i; } } // Advance the focus to the next/previous plugin. // This works also if no plugin is currently selected. int newIndex = currentIndex; if (forward) { if (++newIndex >= n) newIndex = 0; } else { if (--newIndex < 0) newIndex = n - 1; } if (newIndex != currentIndex) { // Set the focus to the determined plugin VisiblePlugin vp = (VisiblePlugin) plugins.get(newIndex); vp.focusPlugin(); } } ///////////////////////////////////////////////////////////////////////// // @@ Keyboard support ///////////////////////////////////////////////////////////////////////// /** * Sets up the key bindings of the page. * Maps CTRL+TAB/SHIFT+CTRL+TAB to cycle the plugins of this page. */ protected void setupKeyBindings() { InputMap inputmap = getContentPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); ActionMap actionmap = getContentPane().getActionMap(); // Map F11 to 'Next visible plugin' inputmap.put(KeyStroke.getKeyStroke(KeyEvent.VK_F11, 0), "NextVisiblePlugin"); actionmap.put("NextVisiblePlugin", new CycleAction(true, true)); // Map SHIFT+F11 to 'Previous visible plugin' inputmap.put(KeyStroke.getKeyStroke(KeyEvent.VK_F11, KeyEvent.SHIFT_MASK), "PrevVisiblePlugin"); actionmap.put("PrevVisiblePlugin", new CycleAction(false, true)); // Map F12 to 'Next plugin' inputmap.put(KeyStroke.getKeyStroke(KeyEvent.VK_F12, 0), "NextPlugin"); actionmap.put("NextPlugin", new CycleAction(true, false)); // Map SHIFT+F12 to 'Previous plugin' inputmap.put(KeyStroke.getKeyStroke(KeyEvent.VK_F12, KeyEvent.SHIFT_MASK), "PrevPlugin"); actionmap.put("PrevPlugin", new CycleAction(false, false)); } /** * Advances the plugin focus */ class CycleAction extends AbstractAction { /** Direction flag */ private boolean forward; /** Cycle through the visible plugins only */ private boolean visibleOnly; /** * Constructor. * * @param forward * true Moves the focus to the next plugin.
* false Moves the focus to the previous plugin. * @param visibleOnly * true Considers currently visible plugins only.
* false Considers also plugins that are currently hidden * (i. e. non-active tabs in tabbed plugin containers) */ public CycleAction(boolean forward, boolean visibleOnly) { this.forward = forward; this.visibleOnly = visibleOnly; } public void actionPerformed(ActionEvent e) { advanceFocus(forward, visibleOnly); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy