package rocks.inspectit.ui.rcp.validation; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang.ObjectUtils; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.TreeEditor; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.TreeItem; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import rocks.inspectit.ui.rcp.util.SafeExecutor; /** * Manager for the {@link TreeItemControlDecoration}s. * * @author Ivan Senic, Alexander Wert * */ public class TreeItemControlDecorationManager { /** * Logger. */ private static final Logger LOGGER = LoggerFactory.getLogger(TreeItemControlDecorationManager.class); /** * List of decorations. */ private final List<TreeItemControlDecoration> treeItemControlDecorations = new ArrayList<>(); /** * Shows the error decoration data. * * @param treeViewer * tree viewer * @param data * Data bounded to the tree item. * @param message * Message to display. */ public void showTreeItemControlDecoration(TreeViewer treeViewer, Object data, String message) { if (null == treeViewer) { return; } // first check if we have it, if so shown for (TreeItemControlDecoration decoration : treeItemControlDecorations) { if (data == decoration.getData()) { // NOPMD == on purpose decoration.show(); decoration.setDescriptionText(message); return; } } // if not find appropriate table item to place it for (TreeItem treeItem : getAllTreeItems(treeViewer)) { if (treeItem.getData() == data) { // NOPMD == on purpose TreeItemControlDecoration decoration = new TreeItemControlDecoration(treeItem); decoration.show(); decoration.setDescriptionText(message); treeItemControlDecorations.add(decoration); return; } } } /** * Returns all tree items (also the nested ones) of the tree associated with the * {@link #treeViewer}. * * @param treeViewer * the tree view to retrieve the items for * * @return List of all tree items. */ private List<TreeItem> getAllTreeItems(TreeViewer treeViewer) { List<TreeItem> treeItems = new ArrayList<>(); for (TreeItem item : treeViewer.getTree().getItems()) { treeItems.add(item); } int i = 0; while (i < treeItems.size()) { TreeItem parent = treeItems.get(i); for (TreeItem item : parent.getItems()) { treeItems.add(item); } i++; } return treeItems; } /** * Hides the error decoration for the sensor assignment. * * @param treeViewer * tree viewer * @param data * Data bounded to the tree item. */ public void hideTreeItemControlDecoration(TreeViewer treeViewer, Object data) { if (null == treeViewer) { return; } // remove if it's there for (TreeItemControlDecoration decoration : treeItemControlDecorations) { if (data == decoration.getData()) { // NOPMD == on purpose decoration.hide(); return; } } } /** * Class to help with displaying control decorations on the tree items. * * @author Alexander Wert */ public class TreeItemControlDecoration extends AbstractItemControlDecoration<TreeItem, TreeEditor> { /** * Parent decoration to show message for the child error. */ private TreeItemControlDecoration parentControlDecoration; /** * Valid state of this decoration. */ private boolean valid; /** * Collapse listener on tree items. */ private final Listener collapseListener; /** * Expand listener on tree items. */ private final Listener expandListener; /** * Constructor. * * @param treeItem * TreeItem to create decoration for. */ public TreeItemControlDecoration(final TreeItem treeItem) { super(treeItem, treeItem.getParent()); TreeEditor treeEditor = new TreeEditor(treeItem.getParent()); treeEditor.horizontalAlignment = SWT.LEFT; treeEditor.verticalAlignment = SWT.BOTTOM; treeEditor.setEditor(getControl(), treeItem, 0); initItemEditor(treeEditor); // hide and dispose decoration on disposal of the corresponding tree item treeItem.addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(DisposeEvent e) { // in any case hide, dispose and remove treeItemControlDecorations.remove(TreeItemControlDecoration.this); hide(); dispose(); } }); // collapseListener = new Listener() { @Override public void handleEvent(Event event) { TreeItem parent = treeItem.getParentItem(); while (null != parent) { if (ObjectUtils.equals(parent, event.item)) { if (isVisible()) { TreeItemControlDecoration.super.hide(); } break; } parent = parent.getParentItem(); } if (ObjectUtils.equals(treeItem, event.item)) { SafeExecutor.asyncExec(new Runnable() { @Override public void run() { update(); } }); } } }; expandListener = new Listener() { @Override public void handleEvent(Event event) { TreeItem parent = treeItem.getParentItem(); while (null != parent) { if (ObjectUtils.equals(parent, event.item)) { if (!valid) { TreeItemControlDecoration.super.show(); } SafeExecutor.asyncExec(new Runnable() { @Override public void run() { update(); } }); break; } parent = parent.getParentItem(); } if (ObjectUtils.equals(treeItem, event.item)) { SafeExecutor.asyncExec(new Runnable() { @Override public void run() { update(); } }); } } }; treeItem.getParent().addListener(SWT.Collapse, collapseListener); treeItem.getParent().addListener(SWT.Expand, expandListener); TreeItem parent = treeItem.getParentItem(); if (null != parent) { parentControlDecoration = new TreeItemControlDecoration(parent); parentControlDecoration.setDescriptionText("One of the child items has an error."); } } /** * {@inheritDoc} */ @Override public void hide() { valid = true; try { super.hide(); } catch (Exception exception) { // ignore exception on purpose if (LOGGER.isDebugEnabled()) { LOGGER.debug("Ignoring Exception on hiding TreeItemControlDecoration.", exception); } } if (null != parentControlDecoration) { parentControlDecoration.hide(); } } /** * {@inheritDoc} */ @Override public void show() { valid = false; super.show(); if (null != parentControlDecoration) { parentControlDecoration.show(); } } /** * Updates the decoration. */ public void updateDecoration() { update(); } /** * {@inheritDoc} */ @Override public void dispose() { getItem().getParent().removeListener(SWT.Collapse, collapseListener); getItem().getParent().removeListener(SWT.Expand, expandListener); super.dispose(); } } }