/*
* Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of Business Objects nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* GemCutterBrowserTreeExtensions.java
* Creation date: Oct 23, 2002.
* By: Edward Lam
*/
package org.openquark.gems.client;
import java.awt.Color;
import java.awt.Component;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JPopupMenu;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import org.openquark.cal.compiler.ModuleName;
import org.openquark.cal.module.Cal.Core.CAL_Prelude;
import org.openquark.cal.services.GemEntity;
import org.openquark.cal.services.ModuleRevision;
import org.openquark.cal.services.Vault;
import org.openquark.cal.services.VaultElementInfo;
import org.openquark.gems.client.AutoburnLogic.AutoburnUnifyStatus;
import org.openquark.gems.client.IntellicutManager.IntellicutInfo;
import org.openquark.gems.client.IntellicutManager.IntellicutMode;
import org.openquark.gems.client.browser.BrowserTree;
import org.openquark.gems.client.browser.BrowserTreeCellRenderer;
import org.openquark.gems.client.browser.BrowserTreeModel;
import org.openquark.gems.client.browser.BrowserTreeNode;
import org.openquark.gems.client.browser.GemDrawer;
import org.openquark.gems.client.browser.GemTreeNode;
import org.openquark.gems.client.browser.LeafNodeTriggeredEvent;
import org.openquark.gems.client.browser.LeafNodeTriggeredListener;
import org.openquark.util.UnsafeCast;
import org.openquark.util.ui.UIUtilities;
/**
* Extensions are declared as outer-inner classes in this file.
* @author Frank Worsley
*/
class GemCutterBrowserTreeExtensions {
/**
* The GemCutterVaultTreeCellRenderer is an extension to the BrowserTreeCellRenderer
* that displays additional visual cues for intellicut.
* @author Edward Lam
*/
static class CellRenderer extends BrowserTreeCellRenderer {
private static final long serialVersionUID = -7121628652443578657L;
/** The icons representing the scope of gem tree nodes, to be added to gem icons */
private static final ImageIcon SCOPE_PUBLIC_ICON = new ImageIcon(Object.class.getResource("/Resources/scope_public.gif"));
private static final ImageIcon SCOPE_PROTECTED_ICON = new ImageIcon(Object.class.getResource("/Resources/scope_protected.gif"));
private static final ImageIcon SCOPE_PRIVATE_ICON = new ImageIcon(Object.class.getResource("/Resources/scope_private.gif"));
/** The little decal we add to the gem icon, if the gem has a design available. */
private static final ImageIcon GEM_DESIGN_DECAL = new ImageIcon(CellRenderer.class.getResource("/Resources/gemDesignDecal.gif"));
/** The icon to use for gems that can't be connected at all. */
private static final ImageIcon CANNOT_CONNECT_ICON = new ImageIcon(CellRenderer.class.getResource("/Resources/noParking.gif"));
/** The icon to use for gems that can only be connected via burning. */
private static final ImageIcon BURN_ICON = TableTopPanel.burnImageIconSmall;
/** The icon to use for gems that can be connected directly and via burning. */
private static final ImageIcon AMBIGUOUS_ICON = TableTopPanel.burnQuestionImageIconSmall;
/** The text color for gems that can not be connected at all. */
private static final Color CANNOT_CONNECT_COLOR = UIManager.getColor("TextField.inactiveForeground");
/** The text color to use for gems that can only be connected by burning. */
private static final Color BURN_COLOR = Color.red;
/** The text color to use for gems that can be connected directly or via burning. */
private static final Color AMBIGUOUS_COLOR = Color.red;
/** The GemCutter this renderer is for. */
private final GemCutter gemCutter;
// Cache for the scope decorated icons
private final Map<Icon, Icon> imageCache_public = new HashMap<Icon, Icon>();
private final Map<Icon, Icon> imageCache_protected = new HashMap<Icon, Icon>();
private final Map<Icon, Icon> imageCache_private = new HashMap<Icon, Icon>();
/**
* Default GemCutterVaultTreeCellRenderer constructor.
* @param gemCutter the GemCutter this renderer is for
*/
public CellRenderer(GemCutter gemCutter) {
if (gemCutter == null) {
throw new NullPointerException();
}
this.gemCutter = gemCutter;
}
/**
* {@inheritDoc}
* We override this to draw items differently if Intellicut is active.
*/
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
BrowserTreeModel browserTreeModel = (BrowserTreeModel) tree.getModel();
IntellicutManager intellicutManager = gemCutter.getIntellicutManager();
if (intellicutManager.getIntellicutMode() == IntellicutMode.NOTHING) {
// If Intellicut is not active
if (value instanceof GemTreeNode) {
// Add scope icon (public, protected, private) to the gem icon on the bottom right corner
// Uses a cache to store icons already combined
GemEntity entity = (GemEntity) ((GemTreeNode) value).getUserObject();
Icon baseGemIcon = getIcon();
Icon overlayIcon = null;
Map<Icon, Icon> cache = null;
if (entity.getScope().isPublic()) {
cache = imageCache_public;
overlayIcon = SCOPE_PUBLIC_ICON;
} else if (entity.getScope().isProtected()) {
cache = imageCache_protected;
overlayIcon = SCOPE_PROTECTED_ICON;
} else { //entity.getScope().isPrivate()
cache = imageCache_private;
overlayIcon = SCOPE_PRIVATE_ICON;
}
Icon cachedImage = cache.get(baseGemIcon);
if (cachedImage != null) {
setIcon(cachedImage);
} else {
Icon newIcon = UIUtilities.combineIcons(baseGemIcon, overlayIcon);
setIcon(newIcon);
cache.put(baseGemIcon, newIcon);
}
if (((GemTreeNode) value).hasDesign()) {
// Add a design decal to the gem icon
baseGemIcon = getIcon();
Icon newIcon = UIUtilities.combineIcons(baseGemIcon, GEM_DESIGN_DECAL);
setIcon(newIcon);
}
}
return this;
}
// If Intellicut is active, use special Intellicut icons to indicate Intellicut status.
if (leaf) {
// This is a gem. Give it an icon and text color that indicates how intellicut can connect it.
GemEntity gemEntity = (GemEntity) ((GemTreeNode) value).getUserObject();
if (browserTreeModel.isVisibleGem(gemEntity)) {
IntellicutInfo intellicutInfo = intellicutManager.getIntellicutInfo(gemEntity);
AutoburnUnifyStatus autoburnStatus = intellicutInfo.getAutoburnUnifyStatus();
if (autoburnStatus == AutoburnUnifyStatus.UNAMBIGUOUS) {
setIcon(BURN_ICON);
setForeground(BURN_COLOR);
} else if (autoburnStatus == AutoburnUnifyStatus.AMBIGUOUS) {
setIcon(AMBIGUOUS_ICON);
setForeground(AMBIGUOUS_COLOR);
} else if (autoburnStatus == AutoburnUnifyStatus.NOT_POSSIBLE) {
setIcon(CANNOT_CONNECT_ICON);
setForeground(CANNOT_CONNECT_COLOR);
} else if (autoburnStatus == AutoburnUnifyStatus.UNAMBIGUOUS_NOT_NECESSARY &&
intellicutInfo.getBurnTypeCloseness() > intellicutInfo.getNoBurnTypeCloseness()) {
setIcon(BURN_ICON);
setForeground(BURN_COLOR);
} else if (autoburnStatus == AutoburnUnifyStatus.AMBIGUOUS_NOT_NECESSARY &&
intellicutInfo.getBurnTypeCloseness() > intellicutInfo.getNoBurnTypeCloseness()) {
setIcon(AMBIGUOUS_ICON);
setForeground(AMBIGUOUS_COLOR);
}
} else {
setIcon(CANNOT_CONNECT_ICON);
setForeground(CANNOT_CONNECT_COLOR);
}
} else {
// This is a folder. Determine if and how it should pulse.
// Assume we can't connect at all to start with.
Color folderColor = CANNOT_CONNECT_COLOR;
Enumeration<TreeNode> subTreeEnum = UnsafeCast.<Enumeration<TreeNode>>unsafeCast(((DefaultMutableTreeNode)value).breadthFirstEnumeration());
while (subTreeEnum.hasMoreElements()) {
DefaultMutableTreeNode childTreeNode = (DefaultMutableTreeNode) subTreeEnum.nextElement();
if (childTreeNode instanceof GemTreeNode) {
GemEntity gemEntity = (GemEntity) childTreeNode.getUserObject();
if (browserTreeModel.isVisibleGem(gemEntity)) {
IntellicutInfo intellicutInfo = intellicutManager.getIntellicutInfo(gemEntity);
AutoburnUnifyStatus autoburnStatus = intellicutInfo.getAutoburnUnifyStatus();
if (autoburnStatus == AutoburnUnifyStatus.NOT_NECESSARY) {
// Connections take precedence. If we find a connection then stop.
folderColor = null;
break;
} else if (autoburnStatus == AutoburnUnifyStatus.UNAMBIGUOUS ||
autoburnStatus == AutoburnUnifyStatus.AMBIGUOUS) {
folderColor = BURN_COLOR;
} else if ((autoburnStatus == AutoburnUnifyStatus.UNAMBIGUOUS_NOT_NECESSARY ||
autoburnStatus == AutoburnUnifyStatus.AMBIGUOUS_NOT_NECESSARY)) {
if (intellicutInfo.getBurnTypeCloseness() > intellicutInfo.getNoBurnTypeCloseness()) {
folderColor = BURN_COLOR;
} else {
folderColor = null;
break;
}
}
}
}
}
if (folderColor != null) {
setForeground(folderColor);
}
}
return this;
}
}
/**
* A customized popup menu provider for the BrowserTree. It adds GemCutter specific menu items.
* @author Frank Worsley
*/
static class PopupMenuProvider implements BrowserTree.PopupMenuProvider {
/** The GemCutter this provider is for. */
private final GemCutter gemCutter;
/**
* Constructor for a new GemCutterVaultTreePopupMenuProvider.
* @param gemCutter the GemCutter the provider is for
*/
public PopupMenuProvider(GemCutter gemCutter) {
if (gemCutter == null) {
throw new NullPointerException();
}
this.gemCutter = gemCutter;
}
/**
* @see org.openquark.gems.client.browser.BrowserTree.PopupMenuProvider#getPopupMenu(javax.swing.tree.TreePath)
*/
public JPopupMenu getPopupMenu(TreePath selectionPath) {
// Create the popup menu so we can add our custom items.
JPopupMenu popupMenu = new JPopupMenu();
BrowserTreeNode selectedNode = (selectionPath == null) ? null : (BrowserTreeNode) selectionPath.getLastPathComponent();
if (selectedNode instanceof GemDrawer) {
// The node represents a "module".
if (((GemDrawer)selectedNode).isNamespaceNode()) {
// a module namespace node is a 'phantom' node that does not really
// correspond to an actual module
} else {
// Add the 'Change Working Module' menu item.
ModuleName moduleName = ((GemDrawer) selectedNode).getModuleName();
Action changeModuleAction = getChangeModuleAction(moduleName);
popupMenu.add(GemCutter.makeNewMenuItem(changeModuleAction));
// Add a separator.
popupMenu.addSeparator();
// Add menu items for the workspace.
popupMenu.add(GemCutter.makeNewMenuItem(getSyncModuleAction(moduleName)));
popupMenu.add(GemCutter.makeNewMenuItem(getSyncModuleToRevisionAction(moduleName)));
popupMenu.add(GemCutter.makeNewMenuItem(getRevertModuleAction(moduleName)));
popupMenu.addSeparator();
popupMenu.add(GemCutter.makeNewMenuItem(getAddTypeDeclsToModuleAction(moduleName)));
popupMenu.add(GemCutter.makeNewMenuItem(getCleanModuleImportsAction(moduleName)));
popupMenu.add(GemCutter.makeNewMenuItem(getRenameModuleAction(moduleName)));
popupMenu.add(GemCutter.makeNewMenuItem(getRemoveModuleAction(moduleName)));
}
} else if (selectedNode instanceof GemTreeNode) {
// The node represents a gem.
// Add a 'Load Design...' menu item to load the gem design.
GemEntity gemEntity = (GemEntity) selectedNode.getUserObject();
popupMenu.add(GemCutter.makeNewMenuItem(getOpenDesignAction(gemEntity)));
popupMenu.add(GemCutter.makeNewMenuItem(getRenameGemAction(gemEntity)));
popupMenu.add(GemCutter.makeNewMenuItem(getSearchForGemAction(gemEntity)));
popupMenu.add(GemCutter.makeNewMenuItem(getSearchForGemDefinitionAction(gemEntity)));
}
if (popupMenu.getComponentCount() > 0) {
popupMenu.addSeparator();
}
// Add the default menu items.
gemCutter.getBrowserTree().getBrowserTreeActions().addDefaultMenuItems(popupMenu, selectedNode);
return popupMenu;
}
/**
* @param gemEntity the entity to load a saved design for
* @return an action to load a saved design.
*/
private Action getOpenDesignAction(final GemEntity gemEntity) {
Action loadDesignAction = new AbstractAction(GemCutter.getResourceString("OpenDesign")) {
private static final long serialVersionUID = -7655463979081031688L;
public void actionPerformed(ActionEvent e) {
gemCutter.openGemDesign(gemEntity);
}
};
loadDesignAction.putValue(Action.SMALL_ICON, new ImageIcon(GemCutter.class.getResource("/Resources/open.gif")));
loadDesignAction.putValue(Action.SHORT_DESCRIPTION, GemCutter.getResourceString("OpenDesignToolTip"));
loadDesignAction.setEnabled(gemCutter.getGUIState() == GemCutter.GUIState.EDIT && gemEntity.hasDesign());
return loadDesignAction;
}
private Action getRenameModuleAction(final ModuleName moduleName) {
Action renameModuleAction = new AbstractAction(GemCutter.getResourceString("RenameModule")) {
private static final long serialVersionUID = 4668938145711913122L;
public void actionPerformed(ActionEvent e) {
gemCutter.showRenameModuleDialog(moduleName);
}
};
renameModuleAction.putValue(Action.SHORT_DESCRIPTION, GemCutter.getResourceString("RenameModuleToolTip"));
renameModuleAction.setEnabled(gemCutter.getGUIState() == GemCutter.GUIState.EDIT &&
(!moduleName.equals(CAL_Prelude.MODULE_NAME) || gemCutter.isAllowPreludeRenamingMode()));
return renameModuleAction;
}
/**
* @param gemEntity the entity to rename
* @return an action to popup the rename refactoring dialog.
*/
private Action getRenameGemAction(final GemEntity gemEntity) {
Action renameGemAction = new AbstractAction(GemCutter.getResourceString("RenameGem")) {
private static final long serialVersionUID = 5715650083979358935L;
public void actionPerformed(ActionEvent e) {
gemCutter.showRenameEntityDialog(RenameRefactoringDialog.EntityType.Gem, gemEntity.getName().getQualifiedName());
}
};
renameGemAction.putValue(Action.SHORT_DESCRIPTION, GemCutter.getResourceString("RenameGemToolTip"));
renameGemAction.setEnabled(gemCutter.getGUIState() == GemCutter.GUIState.EDIT &&
((!gemEntity.getName().getModuleName().equals(CAL_Prelude.MODULE_NAME))
|| gemCutter.isAllowPreludeRenamingMode()));
return renameGemAction;
}
/**
* @param gemEntity the entity to search for
* @return an action to popup the search dialog with the qualified name of the
* currently-selected gem filled in.
*/
private Action getSearchForGemAction(final GemEntity gemEntity) {
Action searchForGemAction = new AbstractAction(GemCutter.getResourceString("SearchForGemPopup")) {
private static final long serialVersionUID = -6698686245610450051L;
public void actionPerformed(ActionEvent e) {
SearchDialog searchDialog = gemCutter.showSearchDialog();
searchDialog.performSearch(gemEntity.getName().getQualifiedName(), SearchDialog.SearchType.REFERENCES);
}
};
searchForGemAction.putValue(Action.MNEMONIC_KEY, Integer.valueOf(GemCutterActionKeys.MNEMONIC_SEARCH));
searchForGemAction.putValue(Action.SHORT_DESCRIPTION, GemCutter.getResourceString("SearchForGemPopupToolTip"));
searchForGemAction.setEnabled(!gemCutter.isSearchPending());
return searchForGemAction;
}
/**
* @param gemEntity the entity to search for the definition of
* @return an action to popup the search dialog with the qualified name of the
* currently-selected gem filled in.
*/
private Action getSearchForGemDefinitionAction(final GemEntity gemEntity) {
Action searchForGemDefinitionAction = new AbstractAction(GemCutter.getResourceString("SearchForGemDefinitionPopup")) {
private static final long serialVersionUID = 8119618611507146279L;
public void actionPerformed(ActionEvent e) {
SearchDialog searchDialog = gemCutter.showSearchDialog();
searchDialog.performSearch(gemEntity.getName().getQualifiedName(), SearchDialog.SearchType.DEFINITION);
}
};
searchForGemDefinitionAction.putValue(Action.SHORT_DESCRIPTION, GemCutter.getResourceString("SearchForGemDefinitionPopupToolTip"));
searchForGemDefinitionAction.setEnabled(!gemCutter.isSearchPending());
return searchForGemDefinitionAction;
}
/**
* Return a new action to change the module
* @param newModuleName the name of the module to which to change.
* @return Action the new action.
*/
private Action getChangeModuleAction(final ModuleName newModuleName) {
Action changeModuleAction = new AbstractAction (GemCutter.getResourceString("ChangeModulePopup")) {
private static final long serialVersionUID = 7260606500969827421L;
public void actionPerformed(ActionEvent evt) {
gemCutter.doChangeModuleUserAction(newModuleName);
}
};
changeModuleAction.putValue(Action.SHORT_DESCRIPTION, GemCutter.getResourceString("ChangeModulePopupToolTip"));
changeModuleAction.setEnabled(gemCutter.getGUIState() == GemCutter.GUIState.EDIT &&
!newModuleName.equals(gemCutter.getWorkingModuleName()));
return changeModuleAction;
}
/**
* Return a new action to perform the Add Type Declarations refactoring on a module
* @param targetModule String name of the module to refactor
* @return Action the new action
*/
private Action getAddTypeDeclsToModuleAction(final ModuleName targetModule) {
Action addTypeDeclsAction = new AbstractAction (GemCutter.getResourceString("AddTypedeclsPopup")) {
private static final long serialVersionUID = 4895749473356263923L;
public void actionPerformed(ActionEvent evt) {
gemCutter.doAddTypeDeclsUserAction(targetModule);
}
};
addTypeDeclsAction.putValue(Action.SHORT_DESCRIPTION, GemCutter.getResourceString("AddTypedeclsPopupToolTip"));
addTypeDeclsAction.setEnabled(gemCutter.getGUIState() == GemCutter.GUIState.EDIT);
return addTypeDeclsAction;
}
/**
* Return a new action to perform the Clean Imports refactoring on a module
* @param targetModule String name of the module to refactor
* @return Action the new action
*/
private Action getCleanModuleImportsAction(final ModuleName targetModule) {
Action cleanImportsAction = new AbstractAction (GemCutter.getResourceString("CleanImportsPopup")) {
private static final long serialVersionUID = 5792348192459545571L;
public void actionPerformed(ActionEvent evt) {
gemCutter.doCleanImportsUserAction(targetModule);
}
};
cleanImportsAction.putValue(Action.SHORT_DESCRIPTION, GemCutter.getResourceString("CleanImportsPopupToolTip"));
cleanImportsAction.setEnabled(gemCutter.getGUIState() == GemCutter.GUIState.EDIT);
return cleanImportsAction;
}
/**
* Return a new action to remove a module from the workspace.
* @param moduleNameToRemove the name of the module to remove from the workspace.
* @return Action the new action.
*/
private Action getRemoveModuleAction(final ModuleName moduleNameToRemove) {
Action syncModuleAction = new AbstractAction (GemCutter.getResourceString("RemoveModulePopup")) {
private static final long serialVersionUID = 7205929596263407738L;
public void actionPerformed(ActionEvent evt) {
gemCutter.doRemoveModuleUserAction(moduleNameToRemove);
}
};
syncModuleAction.putValue(Action.SHORT_DESCRIPTION, GemCutter.getResourceString("RemoveModulePopupToolTip"));
syncModuleAction.setEnabled(gemCutter.getGUIState() == GemCutter.GUIState.EDIT &&
!moduleNameToRemove.equals(CAL_Prelude.MODULE_NAME));
return syncModuleAction;
}
/**
* Return a new action to sync a module
* @param moduleNameToSync the name of the module to sync with its vault.
* @return Action the new action.
*/
private Action getSyncModuleAction(final ModuleName moduleNameToSync) {
Action syncModuleAction = new AbstractAction (GemCutter.getResourceString("SyncModulePopup")) {
private static final long serialVersionUID = -2098576892178968782L;
public void actionPerformed(ActionEvent evt) {
gemCutter.doSyncModulesToLatestUserAction(Collections.singletonList(moduleNameToSync));
}
};
syncModuleAction.putValue(Action.SHORT_DESCRIPTION, GemCutter.getResourceString("SyncModulePopupToolTip"));
syncModuleAction.setEnabled(gemCutter.getGUIState() == GemCutter.GUIState.EDIT);
return syncModuleAction;
}
/**
* Return a new action to sync a module to a given revision
* @param moduleNameToSync the name of the module to sync with its vault.
* @return Action the new action.
*/
private Action getSyncModuleToRevisionAction(final ModuleName moduleNameToSync) {
Action syncModuleToRevisionAction = new AbstractAction (GemCutter.getResourceString("SyncModuleToRevisionPopup")) {
private static final long serialVersionUID = 2447645113483308106L;
public void actionPerformed(ActionEvent evt) {
// Get the associated vault.
Vault vault = gemCutter.getWorkspace().getVault(moduleNameToSync);
// Show a dialog to get the revision with which to sync.
VaultRevisionChooser revisionChooserDialog = new VaultRevisionChooser(gemCutter, vault, true);
Integer selectedRevision = revisionChooserDialog.showDialog(moduleNameToSync.toSourceText());
// If any revision was selected, sync.
if (selectedRevision != null) {
int selectedRevisionNum = selectedRevision.intValue();
ModuleRevision moduleRevision = new ModuleRevision(moduleNameToSync, selectedRevisionNum);
gemCutter.doSyncModulesUserAction(Collections.singletonList(moduleRevision), false);
}
}
};
syncModuleToRevisionAction.putValue(Action.SHORT_DESCRIPTION, GemCutter.getResourceString("SyncModuleToRevisionPopupToolTip"));
syncModuleToRevisionAction.setEnabled(gemCutter.getGUIState() == GemCutter.GUIState.EDIT);
return syncModuleToRevisionAction;
}
/**
* Return a new action to sync a module to a given revision
* @param moduleNameToSync the name of the module to sync with its vault.
* @return Action the new action.
*/
private Action getRevertModuleAction(final ModuleName moduleNameToSync) {
final VaultElementInfo vaultInfo = gemCutter.getWorkspace().getVaultInfo(moduleNameToSync);
boolean hasVaultInfo = vaultInfo != null;
Action revertModuleAction = new AbstractAction (GemCutter.getResourceString("RevertModulePopup")) {
private static final long serialVersionUID = 5546677553785665358L;
public void actionPerformed(ActionEvent evt) {
int moduleRevision = vaultInfo.getRevision(); // shouldn't be null.
gemCutter.doSyncModulesUserAction(Collections.singletonList(new ModuleRevision(moduleNameToSync, moduleRevision)), true);
}
};
// Calculate whether the action should be enabled..
boolean enableRevert;
if (gemCutter.getGUIState() != GemCutter.GUIState.EDIT || !hasVaultInfo) {
enableRevert = false;
} else {
// Getting the vault status communicates with the vault.
// This might lead to the undesirable situation where invoking the popup prompts for log on to Enterprise.
// // enabled if the module is modified.
// VaultStatus moduleVaultStatus = gemCutter.getWorkspace().getVaultStatus(moduleNameToSync);
// enableRevert = moduleVaultStatus.isModuleModified(moduleNameToSync);
enableRevert = true;
}
revertModuleAction.putValue(Action.SHORT_DESCRIPTION, GemCutter.getResourceString("RevertModulePopupToolTip"));
revertModuleAction.setEnabled(enableRevert);
return revertModuleAction;
}
}
/**
* This listener will add gems from the gem browser to the table top. They are connected
* using intellicut if intellicut is active.
* @author Frank Worsley
*/
static class LeafNodeListener implements LeafNodeTriggeredListener {
/** The GemCutter this listener is for. */
private final GemCutter gemCutter;
/**
* Constructor for a new GemCutterVaultTreeLeafNodeListener.
* @param gemCutter the GemCutter the listener is for
*/
public LeafNodeListener(GemCutter gemCutter) {
if (gemCutter == null) {
throw new NullPointerException();
}
this.gemCutter = gemCutter;
}
/**
* @see org.openquark.gems.client.browser.LeafNodeTriggeredListener#leafNodeTriggered(org.openquark.gems.client.browser.LeafNodeTriggeredEvent)
*/
public void leafNodeTriggered(LeafNodeTriggeredEvent evt) {
IntellicutManager intellicutManager = gemCutter.getIntellicutManager();
TableTop tableTop = gemCutter.getTableTop();
BrowserTreeModel browserTreeModel = (BrowserTreeModel) gemCutter.getBrowserTree().getModel();
if (intellicutManager.getIntellicutMode() != IntellicutMode.NOTHING) {
// evt.getTriggeredObjects() could be empty if a module is selected
if (!evt.getTriggeredObjects().isEmpty()){
Object obj = evt.getTriggeredObjects().get(0);
if (obj instanceof GemEntity) {
GemEntity gemEntity = (GemEntity) obj;
IntellicutInfo intellicutInfo = intellicutManager.getIntellicutInfo(gemEntity);
AutoburnUnifyStatus autoburnStatus = intellicutInfo.getAutoburnUnifyStatus();
if (autoburnStatus != AutoburnUnifyStatus.NOT_POSSIBLE) {
tableTop.getUndoableEditSupport().beginUpdate();
// Add the new gem to the table top.
DisplayedGem dGem = gemCutter.getTableTop().createDisplayedFunctionalAgentGem(new Point(), gemEntity);
Point location = tableTop.findAvailableDisplayedGemLocation(dGem);
tableTop.doAddGemUserAction(dGem, location);
intellicutManager.attemptIntellicutAutoConnect(dGem);
intellicutManager.stopIntellicut();
// Set the proper undo name.
tableTop.getUndoableEditSupport().setEditName(GemCutterMessages.getString("UndoText_Add", dGem.getDisplayText()));
tableTop.getUndoableEditSupport().endUpdate();
}
}
}
} else {
gemCutter.getTableTop().getUndoableEditSupport().beginUpdate();
List<Object> triggeredObjects = evt.getTriggeredObjects();
for (final Object triggeredObj : triggeredObjects) {
if (triggeredObj instanceof GemEntity) {
GemEntity gemEntity = (GemEntity) triggeredObj;
if (browserTreeModel.isVisibleGem(gemEntity)) {
DisplayedGem dGem = gemCutter.getTableTop().createDisplayedFunctionalAgentGem(new Point(), gemEntity);
gemCutter.getTableTop().doAddGemUserAction(dGem);
}
// TODO: in future we may want to warn the user if they are trying to add an entity from a module
// which is not visible, and give them the option of importing it.
}
}
// Override the default undo name if more than one gem was added.
if (triggeredObjects.size() > 1) {
gemCutter.getTableTop().getUndoableEditSupport().setEditName(GemCutter.getResourceString("UndoText_AddGems"));
}
gemCutter.getTableTop().getUndoableEditSupport().endUpdate();
}
}
}
/**
* If intellicut is pulsing this will display a status message if
* the user hovers the mouse over a gem in the gem browser.
* @author Edward Lam
*/
static class MouseListener extends MouseMotionAdapter {
/** The GemCutter the mouse listener is for. */
private final GemCutter gemCutter;
/**
* Constructs a new GemCutterVaultTreeMouseListener
* @param gemCutter the GemCutter the listener is for
*/
public MouseListener(GemCutter gemCutter) {
if (gemCutter == null) {
throw new NullPointerException();
}
this.gemCutter = gemCutter;
}
/**
* @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)
*/
@Override
public void mouseMoved(MouseEvent e) {
IntellicutManager intellicutManager = gemCutter.getIntellicutManager();
StatusMessageDisplayer statusMessageDisplayer = gemCutter.getStatusMessageDisplayer();
TreePath path = gemCutter.getBrowserTree().getPathForLocation(e.getX(), e.getY());
Object lastPathComponent = path != null ? path.getLastPathComponent() : null;
if (!(lastPathComponent instanceof GemTreeNode)) {
statusMessageDisplayer.clearMessage(this);
} else {
// If we've hovering over a gem display an appropriate message
GemTreeNode treeNode = (GemTreeNode) lastPathComponent;
GemEntity gemEntity = (GemEntity) treeNode.getUserObject();
if (intellicutManager.getIntellicutMode() == IntellicutManager.IntellicutMode.NOTHING) {
statusMessageDisplayer.clearMessage(this);
} else if (((BrowserTreeModel) gemCutter.getBrowserTree().getModel()).isVisibleGem(gemEntity)) {
IntellicutInfo intellicutInfo = intellicutManager.getIntellicutInfo(gemEntity);
AutoburnUnifyStatus autoburnStatus = intellicutInfo.getAutoburnUnifyStatus();
if (autoburnStatus.isAutoConnectable()) {
statusMessageDisplayer.setMessageFromResource(this, "SM_IntellicutGemConnectable", StatusMessageDisplayer.MessageType.PERSISTENT);
} else {
statusMessageDisplayer.clearMessage(this);
}
} else {
statusMessageDisplayer.clearMessage(this);
}
}
}
}
}