/* $Id: FindDialog.java 18647 2010-08-17 00:25:51Z bobtarling $
*****************************************************************************
* Copyright (c) 2009 Contributors - see below
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* tfmorris
*****************************************************************************
*
* Some portions of this file was previously release using the BSD License:
*/
// Copyright (c) 1996-2008 The Regents of the University of California. All
// Rights Reserved. Permission to use, copy, modify, and distribute this
// software and its documentation without fee, and without a written
// agreement is hereby granted, provided that the above copyright notice
// and this paragraph appear in all copies. This software program and
// documentation are copyrighted by The Regents of the University of
// California. The software program and documentation are supplied "AS
// IS", without any accompanying services from The Regents. The Regents
// does not warrant that the operation of the program will be
// uninterrupted or error-free. The end-user understands that the program
// was developed for research purposes and is advised not to rely
// exclusively on the program for any reason. IN NO EVENT SHALL THE
// UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
// SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
// ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
// THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
// SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
// PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
// CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT,
// UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
package org.argouml.ui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.border.EmptyBorder;
import org.argouml.application.api.AbstractArgoJPanel;
import org.argouml.i18n.Translator;
import org.argouml.kernel.ProjectManager;
import org.argouml.model.Model;
import org.argouml.swingext.SpacerPanel;
import org.argouml.uml.PredicateSearch;
import org.argouml.util.ArgoDialog;
import org.argouml.util.Predicate;
import org.argouml.util.PredicateStringMatch;
import org.argouml.util.PredicateType;
/**
* Main dialog for Find function.
*
* This is one of the few dialog boxes in Argo that is
* non-modal (so that the user can keep the results on
* the screen while they work with them).<p>
*
* The search is buggy and needs work.
* <p>
* NOTE: An incompatible change was made to the public API for this class
* before the release of ArgoUML 0.26 to remove exposed internal implementation
* details (GEF).
*/
public class FindDialog extends ArgoDialog
implements ActionListener, MouseListener {
private static FindDialog instance;
private static int nextResultNum = 1;
private static int numFinds;
/**
* Insets in pixels.
*/
private static final int INSET_PX = 3;
private JButton search =
new JButton(
Translator.localize("dialog.find.button.find"));
private JButton clearTabs =
new JButton(
Translator.localize("dialog.find.button.clear-tabs"));
// private JTabbedPane tabs = new JTabbedPane();
private JPanel nameLocTab = new JPanel();
// private JPanel modifiedTab = new JPanel();
// private JPanel tagValsTab = new JPanel();
// private JPanel constraintsTab = new JPanel();
private JComboBox elementName = new JComboBox();
private JComboBox diagramName = new JComboBox();
private JComboBox location = new JComboBox();
private JComboBox type = new JComboBox();
private JPanel typeDetails = new JPanel();
private JTabbedPane results = new JTabbedPane();
private JPanel help = new JPanel();
private List<TabResults> resultTabs = new ArrayList<TabResults>();
/**
* @return the instance of this dialog
*/
public static FindDialog getInstance() {
if (instance == null) {
instance = new FindDialog();
}
return instance;
}
/**
* The constructor.
*
*/
public FindDialog() {
super(Translator.localize("dialog.find.title"),
ArgoDialog.OK_CANCEL_OPTION, false);
JPanel mainPanel = new JPanel(new BorderLayout());
initNameLocTab();
// tabs.addTab(Translator.localize("dialog.find.tab.name-and-location"),
// nameLocTab);
// penyaskito says: According to issue 2501, I have removed the tabs.
// initModifiedTab();
// tabs.addTab(Translator.localize("dialog.find.tab.last-modified"),
// modifiedTab);
// tabs.setEnabledAt(1, false);
// initTagValsTab();
// tabs.addTab(Translator.localize("dialog.find.tab.tagged-values"),
// tagValsTab);
// tabs.setEnabledAt(2, false);
// initConstraintsTab();
// tabs.addTab(Translator.localize("tab.constraints"),
// constraintsTab);
// tabs.setEnabledAt(3, false);
// tabs.setMinimumSize(new Dimension(300, 250));
// JPanel north = new JPanel();
// north.setLayout(new BorderLayout());
// north.add(tabs, BorderLayout.CENTER);
// mainPanel.add(north, BorderLayout.NORTH);
mainPanel.add(nameLocTab, BorderLayout.NORTH);
initHelpTab();
results.addTab(Translator.localize("dialog.find.tab.help"), help);
mainPanel.add(results, BorderLayout.CENTER);
search.addActionListener(this);
results.addMouseListener(this);
clearTabs.addActionListener(this);
clearTabs.setEnabled(false);
setContent(mainPanel);
getOkButton().setEnabled(false);
}
/**
* Initialise the tab "Name and Location".
*/
private void initNameLocTab() {
elementName.setEditable(true);
// TODO: Don't use hardcoded colors here - tfm
elementName.getEditor()
.getEditorComponent().setBackground(Color.white);
diagramName.setEditable(true);
diagramName.getEditor()
.getEditorComponent().setBackground(Color.white);
elementName.addItem("*");
diagramName.addItem("*");
// TODO: add recent patterns
GridBagLayout gb = new GridBagLayout();
nameLocTab.setLayout(gb);
JLabel elementNameLabel =
new JLabel(
Translator.localize("dialog.find.label.element-name"));
JLabel diagramNameLabel =
new JLabel(
Translator.localize("dialog.find.label.in-diagram"));
JLabel typeLabel =
new JLabel(
Translator.localize("dialog.find.label.element-type"));
JLabel locLabel =
new JLabel(
Translator.localize("dialog.find.label.find-in"));
location.addItem(
Translator.localize("dialog.find.comboboxitem.entire-project"));
initTypes();
typeDetails.setMinimumSize(new Dimension(200, 100));
typeDetails.setPreferredSize(new Dimension(200, 100));
typeDetails.setSize(new Dimension(200, 100));
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
c.ipadx = 3; c.ipady = 3;
c.gridwidth = 1;
c.gridx = 0; c.gridy = 0;
c.weightx = 0.0;
gb.setConstraints(elementNameLabel, c);
nameLocTab.add(elementNameLabel);
c.gridx = 1; c.gridy = 0;
c.weightx = 1.0;
gb.setConstraints(elementName, c);
nameLocTab.add(elementName);
c.gridx = 0; c.gridy = 1;
c.weightx = 0.0;
gb.setConstraints(diagramNameLabel, c);
nameLocTab.add(diagramNameLabel);
c.gridx = 1; c.gridy = 1;
c.weightx = 1.0;
gb.setConstraints(diagramName, c);
nameLocTab.add(diagramName);
// open space at gridy = 2;
c.gridx = 0; c.gridy = 3;
c.weightx = 0.0;
gb.setConstraints(locLabel, c);
nameLocTab.add(locLabel);
c.gridx = 1; c.gridy = 3;
c.weightx = 1.0;
gb.setConstraints(location, c);
nameLocTab.add(location);
SpacerPanel spacer = new SpacerPanel();
c.gridx = 2; c.gridy = 0;
c.weightx = 0.0;
gb.setConstraints(spacer, c);
nameLocTab.add(spacer);
c.gridx = 3; c.gridy = 0;
c.weightx = 0.0;
gb.setConstraints(typeLabel, c);
nameLocTab.add(typeLabel);
c.gridx = 4; c.gridy = 0;
c.weightx = 1.0;
gb.setConstraints(type, c);
nameLocTab.add(type);
c.gridx = 3; c.gridy = 1;
c.gridwidth = 2; c.gridheight = 5;
gb.setConstraints(typeDetails, c);
nameLocTab.add(typeDetails);
JPanel searchPanel = new JPanel();
searchPanel.setLayout(new GridLayout(1, 2, 5, 5));
searchPanel.add(clearTabs);
searchPanel.add(search);
searchPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
c.gridx = 0; c.gridy = 4;
c.weightx = 0.0; c.weighty = 0.0;
c.gridwidth = 2; c.gridheight = 1;
gb.setConstraints(searchPanel, c);
nameLocTab.add(searchPanel);
}
/**
* Initialise the help tab.
*/
private void initHelpTab() {
help.setLayout(new BorderLayout());
JTextArea helpText = new JTextArea();
helpText.setText(Translator.localize("dialog.find.helptext"));
helpText.setEditable(false);
helpText.setMargin(new Insets(INSET_PX, INSET_PX, INSET_PX, INSET_PX));
help.add(new JScrollPane(helpText), BorderLayout.CENTER);
}
/**
* Init the modelelement types that we can look for.
*/
private void initTypes() {
type.addItem(PredicateMType.create()); // Any type
type.addItem(PredicateMType.create(
Model.getMetaTypes().getUMLClass()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getInterface()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getAction()));
// Not in UML 2.x (or metatypes)
// type.addItem(PredicateMType.create(
// Model.getMetaTypes().getActivityGraph()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getActor()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getAssociation()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getAssociationClass()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getAssociationEndRole()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getAssociationRole()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getArtifact()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getAttribute()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getClassifier()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getClassifierRole()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getCollaboration()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getComment()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getComponent()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getCompositeState()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getConstraint()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getDataType()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getDependency()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getElementImport()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getEnumeration()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getEnumerationLiteral()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getException()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getExtend()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getExtensionPoint()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getGuard()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getGeneralization()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getInclude()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getInstance()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getInteraction()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getInterface()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getLink()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getMessage()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getModel()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getNode()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getPackage()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getParameter()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getPartition()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getPseudostate()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getOperation()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getSignal()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getState()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getStateMachine()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getStateVertex()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getStereotype()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getTagDefinition()));
// Not in UML 2.x (or Metatypes)
// type.addItem(PredicateMType.create(
// Model.getMetaTypes().getTemplateArgument()));
// type.addItem(PredicateMType.create(
// Model.getMetaTypes().getTemplateParameter()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getTransition()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getUseCase()));
// TODO: Can we move this knowledge behind model facade rather than have a condition here?
if (Model.getFacade().getUmlVersion().charAt(0) == '1') {
type.addItem(PredicateMType.create(
Model.getMetaTypes().getSimpleState()));
type.addItem(PredicateMType.create(
Model.getMetaTypes().getTaggedValue()));
}
}
/*
* @see org.tigris.swidgets.Dialog#nameButtons()
*/
@Override
protected void nameButtons() {
super.nameButtons();
nameButton(getOkButton(), "button.go-to-selection");
nameButton(getCancelButton(), "button.close");
}
/*
* @see java.awt.event.ActionListener#actionPerformed(
* java.awt.event.ActionEvent)
*/
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == search) {
doSearch();
} else if (e.getSource() == clearTabs) {
doClearTabs();
} else if (e.getSource() == getOkButton()) {
doGoToSelection();
} else {
super.actionPerformed(e);
}
}
/**
* Do the search.
*/
private void doSearch() {
numFinds++;
String eName = "";
if (elementName.getSelectedItem() != null) {
eName += elementName.getSelectedItem();
elementName.removeItem(eName);
elementName.insertItemAt(eName, 0);
elementName.setSelectedItem(eName);
}
String dName = "";
if (diagramName.getSelectedItem() != null) {
dName += diagramName.getSelectedItem();
diagramName.removeItem(dName);
diagramName.insertItemAt(dName, 0);
diagramName.setSelectedItem(dName);
}
String name = eName;
if (dName.length() > 0) {
Object[] msgArgs = {name, dName };
name =
Translator.messageFormat(
"dialog.find.comboboxitem.element-in-diagram", msgArgs);
//name += " in " + dName;
}
String typeName = type.getSelectedItem().toString();
if (!typeName.equals("Any Type")) {
name += " " + typeName;
}
if (name.length() == 0) {
name =
Translator.localize("dialog.find.tabname") + (nextResultNum++);
}
if (name.length() > 15) {
// TODO: Localize
name = name.substring(0, 12) + "...";
}
String pName = "";
Predicate eNamePred = PredicateStringMatch.create(eName);
Predicate pNamePred = PredicateStringMatch.create(pName);
Predicate dNamePred = PredicateStringMatch.create(dName);
Predicate typePred = (Predicate) type.getSelectedItem();
PredicateSearch pred =
new PredicateSearch(eNamePred, pNamePred, dNamePred, typePred);
ChildGenSearch gen = new ChildGenSearch();
Object root = ProjectManager.getManager().getCurrentProject();
TabResults newResults = new TabResults();
newResults.setTitle(name);
newResults.setPredicate(pred);
newResults.setRoot(root);
newResults.setGenerator(gen);
resultTabs.add(newResults);
results.addTab(name, newResults);
clearTabs.setEnabled(true);
getOkButton().setEnabled(true);
results.setSelectedComponent(newResults);
Object[] msgArgs = {name };
location.addItem(Translator.messageFormat(
"dialog.find.comboboxitem.in-tab", msgArgs));
invalidate();
results.invalidate();
validate();
newResults.run();
newResults.requestFocus();
newResults.selectResult(0);
}
/**
* Reset the user interface to its initial state. Use this if a new project
* has been loaded and the Find dialog is being displayed with stale
* results.
*/
public void reset() {
doClearTabs();
doResetFields(true);
}
/**
* Clear the tabs.
*/
private void doClearTabs() {
int numTabs = resultTabs.size();
for (int i = 0; i < numTabs; i++) {
results.remove(resultTabs.get(i));
}
resultTabs.clear();
clearTabs.setEnabled(false);
getOkButton().setEnabled(false);
doResetFields(false);
}
/**
* Reset the fields.
*
* @param complete if true, reset all 3 fields (element name, diagram name,
* and location), otherwise only the last is reset.
*/
private void doResetFields(boolean complete) {
if (complete) {
elementName.removeAllItems();
diagramName.removeAllItems();
elementName.addItem("*");
diagramName.addItem("*");
}
location.removeAllItems();
location.addItem(
Translator.localize("dialog.find.comboboxitem.entire-project"));
}
/**
* Execute the GoTo selection command.
*/
private void doGoToSelection() {
if (results.getSelectedComponent() instanceof TabResults) {
((TabResults) results.getSelectedComponent()).doDoubleClick();
}
}
////////////////////////////////////////////////////////////////
// MouseListener implementation
/*
* @see java.awt.event.MouseListener#mousePressed(java.awt.event.MouseEvent)
*/
public void mousePressed(MouseEvent me) {
// empty implementation - we only handle mouseClicked
}
/*
* @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
*/
public void mouseReleased(MouseEvent me) {
// empty implementation - we only handle mouseClicked
}
/*
* @see java.awt.event.MouseListener#mouseEntered(java.awt.event.MouseEvent)
*/
public void mouseEntered(MouseEvent me) {
// empty implementation - we only handle mouseClicked
}
/*
* @see java.awt.event.MouseListener#mouseExited(java.awt.event.MouseEvent)
*/
public void mouseExited(MouseEvent me) {
// empty implementation - we only handle mouseClicked
}
/*
* @see java.awt.event.MouseListener#mouseClicked(java.awt.event.MouseEvent)
*/
public void mouseClicked(MouseEvent me) {
int tab = results.getSelectedIndex();
if (tab != -1) {
Rectangle tabBounds = results.getBoundsAt(tab);
if (!tabBounds.contains(me.getX(), me.getY())) {
return;
}
if (tab >= 1 && me.getClickCount() >= 2) {
myDoubleClick(tab - 1); //help tab is 0
}
}
}
/**
* Double click on tab detaches it in a free floating window.
*
* MVW: This is the only place where spawning is still enabled.
*
* @param tab the given tab
*/
private void myDoubleClick(int tab) {
JPanel t = resultTabs.get(tab);
if (t instanceof AbstractArgoJPanel) {
if (((AbstractArgoJPanel) t).spawn() != null) {
resultTabs.remove(tab);
location.removeItem("In Tab: "
+ ((AbstractArgoJPanel) t).getTitle());
}
}
}
/**
* The UID.
*/
private static final long serialVersionUID = 9209251878896557216L;
} /* end class FindDialog */
/**
* PredicateMType is a small helper class which converts the
* string representation of the Type from internal form.
* Now that Type names aren't prefixed with NSUML's "M" the
* name of the class is somewhat of a misnomer.
*/
class PredicateMType extends PredicateType {
/**
* The constructor.
*
* @param pats The classes.
*/
protected PredicateMType(Class[] pats) {
super(pats, pats.length);
}
/**
* The constructor.
*
* @param pats The classes (or <code>null</code> if numPats is 0.
* @param numPats The count of classes.
*/
protected PredicateMType(Class[] pats, int numPats) {
super(pats, numPats);
}
/**
* Create without classes.
*
* @return A newly created PredicateType.
*/
public static PredicateType create() {
return new PredicateMType(null, 0);
}
/**
* Create with one class.
*
* @param c0 First class.
* @return A newly created PredicateType.
*/
public static PredicateType create(Object c0) {
Class[] classes = new Class[1];
classes[0] = (Class) c0;
return new PredicateMType(classes);
}
/**
* Create with two classes.
*
* @param c0 First class.
* @param c1 Second class.
* @return A newly created PredicateType.
*/
public static PredicateType create(Object c0, Object c1) {
Class[] classes = new Class[2];
classes[0] = (Class) c0;
classes[1] = (Class) c1;
return new PredicateMType(classes);
}
/**
* Create with three classes.
*
* @param c0 First class.
* @param c1 Second class.
* @param c2 Third class.
* @return A newly created PredicateType.
*/
public static PredicateType create(Object c0, Object c1, Object c2) {
Class[] classes = new Class[3];
classes[0] = (Class) c0;
classes[1] = (Class) c1;
classes[2] = (Class) c2;
return new PredicateMType(classes);
}
@Override
public String toString() {
String result = super.toString();
// TODO: This shouldn't know the internal form of type names,
// but I'm not sure what GEF's PredicateType does, so I'm fixing it
// here - tfm
if (result.startsWith("Uml")) {
result = result.substring(3);
}
return result;
}
/**
* The UID.
*/
private static final long serialVersionUID = 901828109709882796L;
} /* end class PredicateMType */