/* ==============================================
* Simtools : The tools library used in JSynoptic
* ==============================================
*
* Project Info: http://jsynoptic.sourceforge.net/index.html
*
* This library is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation;
* either version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* (C) Copyright 1999-2003, by :
* Corporate:
* Astrium SAS
* EADS CRC
* Individual:
* Claude Cazenave
* Nicolas Brodu
*
*
* $Id: DesktopCardPanel.java,v 1.16 2009/01/19 16:35:31 ogor Exp $
*
* Changes
* -------
* 25-Sep-2003 : Initial public release (NB);
* 18-mai-2005 : External desktop mode
*
*/
package simtools.ui;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.LayoutManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.ResourceBundle;
import java.util.Stack;
import java.util.Vector;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JSeparator;
import javax.swing.JTabbedPane;
import javax.swing.JViewport;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import simtools.util.ListenerManager;
/**
* This class manages a list of swing components inside either internal frames
* (one per component) on a desktop or a single panel which displays only the
* selected component. Component selection is performed through a tab pane which
* displays components names using Component.getName(). The selected component
* is inside a frame which is at the top of the desktop when desktop mode is
* used. The selected component is displayed inside a panel under the tab pane
* when the single panel mode is used. A maximize action on one of the frame of
* the desktop leads to the single panel mode with the selected component Using
* the menu provided by the getMenu fonction, one can switch to the desktop pane
* by clicking on the cascade item This menu displays also all the available
* components and menu items to select components one after each other.
*
* @author Claude Cazenave
*
* @version 1.0 1999
*/
public class DesktopCardPanel extends JPanel implements ChangeListener,
MouseListener, ActionListener {
//
// members
//
/** resources */
protected static MenuResourceBundle _resources;
static {
try {
_resources = (MenuResourceBundle) ResourceBundle.getBundle(
"simtools.ui.resources.DesktopCardPanelResource",
CustomizedLocale.get());
} catch (Exception e) {
System.err.println("Can't load DesktopCardPanel resources");
System.exit(0);
}
}
/** constant for the card display mode */
public static final int CARD_MODE = 0;
/** constant for the desktop display mode */
public static final int DESKTOP_MODE = 1;
/** constant for the external frame display mode */
public static final int EXTERNAL_MODE = 2;
/** the scrolling mode as defined in JViewPort */
public static int SCROLLING_MODE=JViewport.BLIT_SCROLL_MODE;
/** current layout mode */
private int _currentMode = -1;
/** tab panel for components names and selection */
private JTabbedPane _namesPane;
/** list of components */
protected Vector _components;
/** true if scrollable pane and frames are required */
private boolean _scrollable;
/** true if the inner frame can be closed */
private boolean _closable;
/** Selection menu */
private JMenu _menu;
/** Next menu item */
private JMenuItem _miNext;
/** Previous menu item */
private JMenuItem _miPrevious;
/** Cascade menu item */
private JMenuItem _miCascade;
/** Close All menu item */
private JMenuItem _miCloseAll;
/** Maximize menu item */
private JMenuItem _miMaximize;
/** External menu item */
private JMenuItem _miExternal;
/** button group to manage components menu items */
private ButtonGroup _bgComponents;
/** backward sheet button*/
private JButton _bBackward;
/** forward sheet button*/
private JButton _bForward;
/**
* popup menu to be displayed when the right mouse is clicked above a card
* item
*/
protected JPopupMenu _cardPopup;
/** a list of listener on component selection changes */
private ListenerManager _listeners = new ListenerManager();
protected DesktopCardPanelMode _desktopCardPanelMode;
/**
* Two stacks to manage previous and next sheet actions.
* Contains pointers to existing sheets.
*/
protected Stack backwardStack, forwardStack;
//
// public methods
//
/**
* Creates a new DesktopCardPanel The single pane mode is selected The list
* of components is empty
*
* @param scrollable
* true if scrollable pane and frames are required
*/
public DesktopCardPanel(boolean scrollable) {
this(scrollable, true);
}
/**
* Creates a new DesktopCardPanel The single pane mode is selected The list
* of components is empty
*
* @param scrollable
* true if scrollable pane and frames are required
* @param closable
* true if inner frames created will be closable
*/
public DesktopCardPanel(boolean scrollable, boolean closable) {
super(new BorderLayout());
backwardStack = new Stack();
forwardStack = new Stack();
_scrollable = scrollable;
_closable = closable;
createCardPopupMenu();
_namesPane = new JTabbedPane();
_namesPane.addChangeListener(this);
_namesPane.addMouseListener(this);
add(BorderLayout.NORTH, _namesPane);
_components = new Vector();
_miNext = null;
_miPrevious = null;
_miCascade = null;
_miCloseAll = null;
_miMaximize = null;
_bgComponents = null;
_bForward = null;
_bBackward= null;
setMode(CARD_MODE);
}
/**
*
* @return true if inner frames created will be closable
*/
public boolean is_closable() {
return this._closable;
}
/**
*
* @return true if pane and frames are scrollable
*/
public boolean is_scrollable() {
return this._scrollable;
}
protected void createCardPopupMenu() {
_cardPopup = new CardPopupMenu();
}
/**
* @deprecated use setMode(int mode) instead
*
* Changes desktop mode
*
* @param state
* true if desktop is selected
*/
public void setDesktopState(boolean state) {
setMode(state ? DESKTOP_MODE : CARD_MODE);
}
/**
* set the desktop display mode
*
* @param mode CARD_MODE, DESKTOP_MODE or EXTERNAL_MODE
*/
public void setMode(int mode) {
if (_currentMode == mode) {
return;
}
_currentMode = mode;
switch (mode) {
case CARD_MODE:
setDesktopCardPanelMode(new CardMode(this));
break;
case DESKTOP_MODE:
setDesktopCardPanelMode(new DesktopMode(this));
break;
case EXTERNAL_MODE:
setDesktopCardPanelMode(new ExternalMode(this));
break;
}
}
/**
* set the implementation of the _desktopCardPanelMode wich handles the
* display of the desktop components
*
* @param newMode the new implementation of DesktopCardPanelMode
*/
protected void setDesktopCardPanelMode(DesktopCardPanelMode newMode) {
if (_desktopCardPanelMode != null) {
Container contentPane = _desktopCardPanelMode.getContentPane();
if (contentPane != null) {
remove(contentPane);
}
}
Container newContentPane = newMode.getContentPane();
if (newContentPane != null) {
add(BorderLayout.CENTER, newContentPane);
}
validate();
for (int i = 0; i < _components.size(); i++) {
JComponent d = (JComponent) _components.elementAt(i);
_desktopCardPanelMode.removeComponent(d);
newMode.addComponent(d);
}
_desktopCardPanelMode = newMode;
selectComponent(getSelectedComponent());
if (_miCascade != null) {
_miCascade.setEnabled(_currentMode != DESKTOP_MODE);
}
if (_miMaximize != null) {
_miMaximize.setEnabled(_currentMode != CARD_MODE);
}
if (_miExternal != null) {
_miExternal.setEnabled(_currentMode != EXTERNAL_MODE);
}
validate();
repaint();
}
/**
* Gets desktop current mode
*
* @return the current mode
*/
public int getMode() {
return this._currentMode;
}
/**
* @deprecated use get mode instead
*
* Gets desktop mode
*
* @return true if desktop is selected
*/
public boolean getDesktopState() {
return _currentMode == DESKTOP_MODE;
}
/**
* Gets the index of a component
*
* @param d
* the component
* @return the component index or -1 if it doesn't exist
*/
public int indexOfComponent(JComponent d) {
for (int i = 0; i < _components.size(); i++) {
JComponent c = (JComponent) _components.elementAt(i);
if (c == d) {
return i;
}
}
return -1;
}
/**
* Gets an iterator on the component list
*
* @return the iterator
*/
public Iterator getComponentIterator() {
return _components.iterator();
}
public JComponent getComponentAt(int index) {
return (JComponent) _components.elementAt(index);
}
/**
* returns the ComponentContainer that contains the specified component
* @param d the component
*
* @return the container
*/
public Container getContainer(JComponent d) {
return this._desktopCardPanelMode.getContainer(d);
}
/**
* Adds a component
*
* @param d
* the component
*/
public void addComponent(JComponent d) {
_components.addElement(d);
JPanel dummy = new JPanel(new PanelsPaneLayout(this));
_namesPane.addTab(d.getName(), dummy);
_desktopCardPanelMode.addComponent(d);
setComponentsMenuItems();
selectComponent(d);
repaint();
}
/**
* Updates the name of a component
*
* @param d
* the component
*/
public void updateComponentName(JComponent d) {
int index = _components.indexOf(d);
if (index < 0) {
return;
}
_namesPane.setTitleAt(index, d.getName());
_desktopCardPanelMode.updateComponentName(d);
setComponentsMenuItems();
// update menu if required
if (_bgComponents != null) {
Enumeration e = _bgComponents.getElements();
while (e.hasMoreElements()) {
ComponentRadioButton crb = (ComponentRadioButton) e
.nextElement();
if (d == crb._component) {
if (!crb.isSelected()) {
crb.setSelected(true);
}
break;
}
}
}
repaint();
}
/**
* Removes a component
*
* @param d
* the component
*/
public void removeComponent(JComponent d) {
int index = _components.indexOf(d);
if (index < 0) {
return;
}
_namesPane.removeTabAt(index);
_components.removeElementAt(index);
_desktopCardPanelMode.removeComponent(d);
// Remove from forwardStack and backwardStack
while (forwardStack.contains(d)){
forwardStack.remove(forwardStack.indexOf(d));
}
while (backwardStack.contains(d)){
backwardStack.remove(backwardStack.indexOf(d));
}
setComponentsMenuItems();
// new selection is previous component if any
if (_components.isEmpty()) {
selectComponent(null);
} else {
if (index >= _components.size()) {
selectComponent((JComponent) _components.lastElement());
} else {
selectComponent((JComponent) _components.elementAt(index));
}
}
repaint();
}
/**
* Removes all components
*/
public void removeAllComponents() {
Object[] components = _components.toArray();
for (int i = 0; i < components.length; i++){
JComponent d = (JComponent) components[i];
removeComponent(d);
}
repaint();
}
/**
* Removes all components
*/
public void removeOtherComponents(JComponent jc) {
Object[] components = _components.toArray();
for (int i = 0; i < components.length; i++){
JComponent d = (JComponent) components[i];
if (!d.equals(jc))
removeComponent(d);
}
repaint();
}
/**
* Save all components.
*/
public void saveAllComponents() {
}
/**
* Gets selected component
*
* @return the component or null if none
*/
public JComponent getSelectedComponent() {
int index = _namesPane.getSelectedIndex();
if ((index < 0) || (index > _components.size())) {
return null;
}
return (JComponent) _components.elementAt(index);
}
/**
* Gets selected component index
*
* @return -1 if none
*/
public int getSelectedIndex() {
int index = _namesPane.getSelectedIndex();
if ((index < 0) || (index > _components.size())) {
return -1;
}
return index;
}
/**
* Selects a component
*
* @param d the component
*/
public void selectComponent(JComponent d) {
_desktopCardPanelMode.selectComponent(d);
// update tab pane
for (int i = 0; i < _components.size(); i++) {
if (d == _components.elementAt(i)) {
if (_namesPane.getSelectedIndex() != i) {
_namesPane.setSelectedIndex(i);
}
break;
}
}
// update menu if required
if (_bgComponents != null) {
Enumeration e = _bgComponents.getElements();
while (e.hasMoreElements()) {
ComponentRadioButton crb = (ComponentRadioButton) e
.nextElement();
if (d == crb._component) {
if (!crb.isSelected()) {
crb.setSelected(true);
}
break;
}
}
}
// propagate component selection to listeners
synchronized (_listeners) {
int n = _listeners.size(); // only one call outside loop
for (int i = 0; i < n; ++i) {
DesktopCardPanelListener dcpl = (DesktopCardPanelListener) _listeners
.get(i);
if (dcpl != null)
dcpl.componentSelected(d);
}
}
// Update backward and foreward buttons
if (_bForward!=null)
_bForward.setEnabled(!forwardStack.isEmpty());
if (_bBackward!=null)
_bBackward.setEnabled(backwardStack.size()>=2);
repaint();
}
/**
* Adds a new Listener on component selection
*/
public void addListener(DesktopCardPanelListener dcpl) {
_listeners.add(dcpl);
}
/**
* Removes a Listener on component selection
*/
public void removeListener(DesktopCardPanelListener dcpl) {
_listeners.remove(dcpl);
}
/**
* Removes all Listeners on component selection
*/
public void removeAllListeners() {
_listeners.clear();
}
/**
* @return the previous sheet button
*/
public JButton createPreviousButton(){
_bBackward = _resources.getBox("prev", this);
return _bBackward;
}
/**
* @return the next sheet button
*/
public JButton createNextButton(){
_bForward = _resources.getBox("next", this);
return _bForward;
}
/**
* Creates a menu to switch between components Each component is displayed
* in the menu using toString()
*
* @param the
* menu name
* @return a new menu or null is one already created
*/
public JMenu createMenu(String name) {
return createMenu(name, true);
}
/**
* Creates a menu to switch between components Each component is displayed
* in the menu using toString()
*
* @param name
* the menu name
* @param enableCloseAll
* a boolean indicating if the 'Close all' menu item is showed
* @return a new menu or null is one already created
*/
public JMenu createMenu(String name, boolean enableCloseAll) {
if (_menu != null) {
return null;
}
if (name != null) {
_menu = new JMenu(name);
_menu.setMnemonic(name.charAt(0));
} else {
_menu = _resources.getMenu("windows");
}
_menu.add(_miNext = _resources.getItem("next", this));
_menu.add(_miPrevious = _resources.getItem("previous", this));
_menu.add(new JSeparator());
_menu.add(_miMaximize = _resources.getItem("maximize", this));
if (_currentMode == CARD_MODE) {
_miMaximize.setEnabled(false);
}
_menu.add(_miCascade = _resources.getItem("cascade", this));
if (_currentMode == DESKTOP_MODE) {
_miCascade.setEnabled(false);
}
_menu.add(_miExternal = _resources.getItem("external", this));
if (_currentMode == EXTERNAL_MODE) {
_miCascade.setEnabled(false);
}
_menu.add(new JSeparator());
if (enableCloseAll) {
_menu.add(_miCloseAll = _resources.getItem("closeAll", this));
_menu.add(new JSeparator());
}
_bgComponents = new ButtonGroup();
setComponentsMenuItems();
return _menu;
}
/**
* Constructs the menu to navigate between components
*/
private void setComponentsMenuItems() {
if (_bgComponents == null) {
return;
}
// remove all items
Enumeration e = _bgComponents.getElements();
while (e.hasMoreElements()) {
JMenuItem mi = (JMenuItem) e.nextElement();
_menu.remove(mi);
}
_bgComponents = new ButtonGroup();
// add new ones
for (int i = 0; i < _components.size(); i++) {
ComponentRadioButton crb = new ComponentRadioButton(
(JComponent) _components.elementAt(i));
_menu.add(crb);
_bgComponents.add(crb);
}
}
//
// MouseListener interface
//
public void mouseClicked(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
public void mousePressed(MouseEvent e) {
if ((e.getModifiers() & MouseEvent.BUTTON3_MASK) == MouseEvent.BUTTON3_MASK) {
if (_cardPopup != null) {
_cardPopup.show(this, e.getX(), e.getY());
}
}
}
public void mouseReleased(MouseEvent e) {
}
// Action listener interface
//
public void actionPerformed(ActionEvent e) {
if ((e.getSource() == _miNext) || (e.getSource() == _miPrevious)) {
// TODO
int index = _namesPane.getSelectedIndex();
int s = _components.size();
if (s == 0) {
return;
}
if (e.getSource() == _miNext){
index++;
if (index >= s) {
index = 0;
}
} else {
index--;
if (index < 0) {
index = s - 1;
}
}
selectComponent((JComponent) _components.elementAt(index));
} else if (e.getSource() == _bForward){
next();
} else if (e.getSource() == _bBackward){
back();
} else if (e.getSource() == _miCascade) {
setMode(DESKTOP_MODE);
} else if (e.getSource() == _miCloseAll) {
removeAllComponents();
} else if (e.getSource() == _miMaximize) {
setMode(CARD_MODE);
} else if (e.getSource() == _miExternal) {
setMode(EXTERNAL_MODE);
}
}
protected void back(){
if (backwardStack.size()>=2){
forwardStack.push((JComponent)backwardStack.pop());
JComponent prevComp = (JComponent)backwardStack.pop();
selectComponent(prevComp);
}
}
protected void next(){
if (!forwardStack.isEmpty()){
JComponent nextComp = (JComponent)forwardStack.pop();
//backwardStack.push(nextComp);
selectComponent(nextComp);
}
}
//
// Change listener interface
//
public void stateChanged(ChangeEvent e) {
int index = _namesPane.getSelectedIndex();
if ((index < 0) || (index > _components.size())) {
return;
}
JComponent d = (JComponent) _components.elementAt(index);
backwardStack.push(d);
selectComponent(d);
}
/**
* The radio button to choose a component in the menu
*/
public class ComponentRadioButton extends JRadioButtonMenuItem implements
ActionListener {
/** the component */
JComponent _component;
/**
* Constructs a new RadioButton for the given component
*
* @param component
* the linked component
*/
public ComponentRadioButton(JComponent component) {
super();
_component = component;
setText(_component.getName());
addActionListener(this);
}
//
// Action listener interface
//
public void actionPerformed(ActionEvent e) {
if (isSelected()) {
selectComponent(_component);
}
}
}
/**
* The Layout for the panel which holds the component panels
*/
public class PanelsPaneLayout implements LayoutManager {
private JComponent _mirrored;
/**
* Creates a layout which size fit the size of a mirrored component
*
* @param mirrored
* the mirrored component
*/
PanelsPaneLayout(JComponent mirrored) {
_mirrored = mirrored;
}
/**
* Adds the specified component to the layout. Not used by this class.
*
* @param name
* the name of the component
* @param comp
* the component to be added
*/
public void addLayoutComponent(String name, Component comp) {
}
/**
* Removes the specified component from the layout. Not used by this
* class.
*
* @param comp
* the component to remove
*/
public void removeLayoutComponent(Component comp) {
}
/**
* Returns the preferred dimensions for this layout
*
* @param target
* the component which needs to be laid out
* @return the preferred dimensions to lay out
*/
public Dimension preferredLayoutSize(Container target) {
if (_mirrored != null) {
int w = _mirrored.getSize().width;
return new Dimension(w, 0);
}
if (target.getComponentCount() == 0) {
return new Dimension(0, 0);
}
return target.getSize();
}
/**
* Returns the minimum dimensions for this layout
*
* @param target
* the component which needs to be laid out
* @return the minimum dimensions to lay out
*/
public Dimension minimumLayoutSize(Container target) {
if (target.getComponentCount() == 0) {
return new Dimension(0, 0);
}
return target.getComponent(0).getMinimumSize();
}
/**
* Lays out the container. This method reshape to its container size the
* component it holds
*
* @param target
* the specified component being laid out.
* @see Container
* @see java.awt.Container#doLayout
*/
public void layoutContainer(Container target) {
if (target.getComponentCount() == 0) {
return;
}
Component c = target.getComponent(0);
Dimension d = target.getSize();
c.setSize(d.width, d.height);
c.setLocation(0, 0);
}
}
/**
* A popup menu to be displayed when a card is selected
*/
public class CardPopupMenu extends JPopupMenu implements ActionListener {
protected JMenuItem _miClose, _miCloseOther, _miCloseAll;
/**
* Constructs a new CardPopupMenu instance
*/
public CardPopupMenu() {
super();
add(_miClose = _resources.getItem("closeCard", this));
add(_miCloseOther = _resources.getItem("closeOther", this));
add(_miCloseAll = _resources.getItem("closeAll", this));
}
//
// ActionListener interface
//
public void actionPerformed(ActionEvent e) {
if (e.getSource() == _miClose)
removeComponent(getSelectedComponent());
}
}
}