/* ========================
* JSynoptic : a free Synoptic editor
* ========================
*
* Project Info: http://jsynoptic.sourceforge.net/index.html
*
* This program 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 program 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
* program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*
* (C) Copyright 2001-2003, by :
* Corporate:
* Astrium SAS
* EADS CRC
* Individual:
* Nicolas Brodu
* Jean-Baptiste Lièvremont
*
* $Id: ActionPopup.java,v 1.16 2008/02/25 12:40:05 ogor Exp $
*
* Changes
* -------
* 25-Sep-2003 : Initial public release (NB);
* 15-Feb-2005 : Selection properties support (JBL);
*
*/
package jsynoptic.ui;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Vector;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.event.UndoableEditEvent;
import javax.swing.undo.CompoundEdit;
import jsynoptic.base.*;
import simtools.diagram.DiagramComponent;
import simtools.ui.BasicMessageWriter;
import simtools.ui.ResourceFinder;
/**
*
* @author Nicolas Brodu
*
* @version 1.0 2001
*/
public class ActionPopup extends JPopupMenu implements ActionListener {
public static BasicMessageWriter messageWriter = ResourceFinder.getMessages(ActionPopup.class);
protected DiagramComponent component;
protected HashMap menuItems;
protected HashMap providers;
protected double actionX, actionY;
protected Object actionTarget;
public ActionPopup(DiagramComponent component, double x, double y, Object o, int context) {
super();
this.component = component;
Vector sel = component.getSelection();
if (sel.size()>1) return;
boolean isEmpty = true;
menuItems = new HashMap();
providers = new HashMap();
if (sel.size()==1) {
Object obj = sel.get(0);
if (obj instanceof ContextualActionProvider) {
ContextualActionProvider provider = (ContextualActionProvider)obj;
String[] actions = provider.getActions(x,y,o,context);
if ((actions!=null) && (actions.length!=0)) {
boolean locked = JSynoptic.gui.getLockedShapes().contains(provider);
for (int i=0; i<actions.length; ++i) {
// Display action in a hirarchical sub menus.
JMenuItem jmi = createMenuItem(this, actions[i], null); //TODO Set icon if provided
//JMenuItem jmi = new JMenuItem(actions[i]);
if (locked || !provider.canDoAction(x,y,o,actions[i],context))
jmi.setEnabled(false);
else
jmi.addActionListener(this);
//add(jmi);
menuItems.put(jmi, actions[i]);
providers.put(jmi, provider);
}
}
}
if (obj instanceof Linkable) {
final Linkable l = (Linkable)obj;
final String link = l.getLink();
if (!isEmpty) addSeparator();
JMenuItem jmi;
boolean followItem = false;
if ((link==null) || (link.equals(""))) {
jmi = new JMenuItem(messageWriter.print0args("AddLink"));
} else {
followItem = true;
jmi = new JMenuItem(messageWriter.print0args("EditLink"));
}
jmi.addActionListener(this);
menuItems.put(jmi, jmi.getText());
add(jmi);
providers.put(jmi, new ContextualActionProvider() {
public String[] getActions(double x, double y, Object o, int context) {return null;}
public boolean canDoAction(double x, double y, Object o, String action, int context) {return true;}
public boolean doAction(double x, double y, Object o, String action, CompoundEdit undoableEdit) {
JFileChooser fc;
if(JSynoptic.gui instanceof JSynopticPanels){
fc =((JSynopticPanels)JSynoptic.gui).getFileChooser();
}else{
fc = new JFileChooser();
}
if (link!=null) fc.setSelectedFile(new File(link));
int res = fc.showDialog(JSynoptic.gui.getOwner(), messageWriter.print0args("SelectLink"));
if (res!=JFileChooser.APPROVE_OPTION) return false;
File f = fc.getSelectedFile();
if ((f==null) || (!f.exists())) {
JSynoptic.setStatus(messageWriter.print1args("CannotSetLinkInvalidFile",f.getName()));
return false;
}
try {
l.setLink(f.getCanonicalPath());
} catch (IOException e) {
l.setLink(f.getAbsolutePath());
}
ActionPopup.this.component.repaint();
return true;
}
});
if (followItem) {
jmi = new JMenuItem(messageWriter.print0args("OpenLink"));
jmi.addActionListener(this);
menuItems.put(jmi, jmi.getText());
add(jmi);
providers.put(jmi, new ContextualActionProvider() {
public String[] getActions(double x, double y, Object o, int context) {return null;}
public boolean canDoAction(double x, double y, Object o, String action, int context) {return true;}
public boolean doAction(double x, double y, Object o, String action, CompoundEdit undoableEdit) {
if (link!=null) JSynoptic.gui.open(new File(link));
return true;
}
});
}
isEmpty = false;
}
}
if (isEmpty) {
if (sel.size()==0) JSynoptic.setStatus(messageWriter.print0args("selectFirst"));
else JSynoptic.setStatus(messageWriter.print0args("noAction"));
}
actionX=x; actionY=y; actionTarget=o;
}
/**
* Build a menu arboresence from an action name and add it to ActionPopup.
* Action name contains the list of sub menus, seperated with ";" (ex: "Format;Paragraph;Center")
* @param currentMenu
* @param action
* @return
*/
public static JMenuItem createMenuItem(JComponent currentMenu, String action, Icon icon){
String[] subMenus = action.split(";");
if (subMenus.length==1){
JMenuItem item = new JMenuItem(subMenus[0], icon);
currentMenu.add(item);
return item;
}else{
String subMenu = subMenus[0];
String suffix = action.substring(subMenu.length()+1);
// Search in currentMenu components, if the subMenu does exist already
Component c[] = currentMenu.getComponents();
for (int i=0; i<c.length; ++i) {
if ((c[i] instanceof JMenu) && ((JMenu)c[i]).getText().equals(subMenu)){
return createMenuItem(((JMenu)c[i]), suffix, icon);
}
}
//Otherwise, add a new sub menu
JMenu sm = new JMenu(subMenu);
currentMenu.add(sm);
return createMenuItem(sm, suffix, icon);
}
}
/*
* Get the base class of all the shapes in the selection
*/
private static Class getCommonAncestor(Vector sel) {
// 1. Initialize what will be the common class hierarchy of the
// selection with the one of the first element
Vector commonHierarchy = new Vector();
Class tmpClass = sel.get(0).getClass();
while(tmpClass != null) {
commonHierarchy.add(0,tmpClass);
tmpClass = tmpClass.getSuperclass();
}
// 2. Compare the class hierarchy of all elements and keep only the
// common path
Object element;
Vector hierarchy = new Vector();
for (Iterator it = sel.iterator(); it.hasNext(); ) {
element = it.next();
hierarchy.clear();
// Get the elements hierarchy
tmpClass = element.getClass();
while(tmpClass != null) {
hierarchy.insertElementAt(tmpClass, 0);
tmpClass = tmpClass.getSuperclass();
}
// Take each element of commonHierarchy
// and see if hierarchy contains it
boolean ok = true;
int i;
for(i=0; i<commonHierarchy.size() && ok; i++) {
tmpClass = (Class)commonHierarchy.get(i);
ok = hierarchy.contains(tmpClass);
}
if(!ok) {
int iFound = commonHierarchy.indexOf(tmpClass);
while(commonHierarchy.size()>iFound) {
commonHierarchy.removeElementAt(iFound);
}
}
}
return (Class) (commonHierarchy.lastElement());
}
//
// ActionListener interface
//
public void actionPerformed(ActionEvent e) {
if (!(e.getSource() instanceof JMenuItem)) return;
JMenuItem jmi = (JMenuItem)e.getSource();
String action = (String)menuItems.get(jmi);
if (action==null) return;
Object o = providers.get(jmi);
if (o instanceof ContextualActionProvider) {
CompoundEdit ce=new CompoundEdit();
ContextualActionProvider provider = (ContextualActionProvider)o;
provider.doAction(actionX, actionY, actionTarget, action, ce);
ce.end();
if (ce.isSignificant()) {
ShapesContainer.ShapesComponent sc=JSynoptic.gui.getActiveComponent();
sc.fireUndoableEditUpdate(new UndoableEditEvent(sc, ce));
}
}
}
}