package rocks.inspectit.ui.rcp.editor.tree; import java.util.List; import java.util.Map; import org.eclipse.swt.SWT; import org.eclipse.swt.events.FocusAdapter; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.ui.forms.widgets.FormToolkit; import rocks.inspectit.ui.rcp.InspectIT; import rocks.inspectit.ui.rcp.InspectITImages; import rocks.inspectit.ui.rcp.editor.preferences.IPreferenceGroup; import rocks.inspectit.ui.rcp.editor.preferences.PreferenceEventCallback.PreferenceEvent; import rocks.inspectit.ui.rcp.editor.preferences.PreferenceId; import rocks.inspectit.ui.rcp.editor.tree.input.SteppingTreeInputController; import rocks.inspectit.ui.rcp.util.ElementOccurrenceCount; /** * View that enables locating the element in the tree via {@link SteppingControl}. * * @author Ivan Senic * */ public class SteppingTreeSubView extends TreeSubView { /** * Main composite for this view. It holds the {@link org.eclipse.jface.viewers.TreeViewer} and * additionally {@link SteppingControl} if necessary. */ private Composite subComposite; /** * Stepping control. */ private SteppingControl steppingControl; /** * Input controller for this view. */ private SteppingTreeInputController steppingTreeInputController; /** * Default constructor. * * @param treeInputController * Stepping tree input controller. * @see TreeSubView#TreeSubView(rocks.inspectit.ui.rcp.editor.tree.input.TreeInputController) */ public SteppingTreeSubView(SteppingTreeInputController treeInputController) { super(treeInputController); this.steppingTreeInputController = treeInputController; } /** * {@inheritDoc} */ @Override public void createPartControl(Composite parent, FormToolkit toolkit) { subComposite = toolkit.createComposite(parent); GridLayout layout = new GridLayout(1, true); layout.marginWidth = 0; layout.marginHeight = 0; subComposite.setLayout(layout); GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true); subComposite.setLayoutData(gd); super.createPartControl(subComposite, toolkit); gd = new GridData(SWT.FILL, SWT.FILL, true, true); getTreeViewer().getTree().setLayoutData(gd); if (steppingControl == null) { steppingControl = new SteppingControl(subComposite, toolkit, steppingTreeInputController.getSteppingObjectList()); } if (steppingTreeInputController.initSteppingControlVisible() && (null != steppingTreeInputController.getSteppingObjectList()) && !steppingTreeInputController.getSteppingObjectList().isEmpty()) { steppingControl.showControl(); } // the focus has to be passed to the subComposite, because it can not register it getTreeViewer().getTree().addFocusListener(new FocusAdapter() { @Override public void focusGained(FocusEvent e) { subComposite.notifyListeners(SWT.FocusIn, null); } }); } /** * {@inheritDoc} */ @Override public Control getControl() { return subComposite; } /** * {@inheritDoc} */ @Override public void setDataInput(List<? extends Object> data) { super.setDataInput(data); steppingControl.inputChanged(); } /** * {@inheritDoc} */ @Override public void preferenceEventFired(PreferenceEvent preferenceEvent) { super.preferenceEventFired(preferenceEvent); switch (preferenceEvent.getPreferenceId()) { case STEPPABLE_CONTROL: Map<IPreferenceGroup, Object> preferenceMap = preferenceEvent.getPreferenceMap(); Object isChecked = preferenceMap.get(PreferenceId.SteppableControl.BUTTON_STEPPABLE_CONTROL_ID); if (isChecked instanceof Boolean) { Boolean makeControlVisible = (Boolean) isChecked; if (makeControlVisible) { steppingControl.showControl(); } else { steppingControl.hideControl(); } } break; case CLEAR_BUFFER: case FILTERDATATYPE: case INVOCFILTEREXCLUSIVETIME: case INVOCFILTERTOTALTIME: steppingControl.inputChanged(); break; default: break; } } /** * Adds new element to the stepping control. This method will also register the new object with * the {@link SteppingTreeInputController}. * * @param element * Object to be added. */ public void addObjectToSteppingControl(Object element) { steppingTreeInputController.addObjectToSteppingObjectList(element); if (steppingControl.isControlShown()) { steppingControl.inputChanged(); steppingControl.selectObject(element); } else { steppingControl.showControl(); } } /** * Alters the state of the stepping control button on preference panel. * * @param checked * Should button be checked or not. */ private void setSwitchSteppingControlButtonChecked(boolean checked) { this.getRootEditor().getPreferencePanel().setSteppingControlChecked(checked); } /** * Tries to expand the tree viewer to the wanted occurrence of wanted element. If the wanted * occurrence is not reachable, nothing is done. Otherwise the tree is expanded and element * selected. * * @param template * Element to reach. * @param occurrence * Wanted occurrence in the tree. */ private void expandToObject(Object template, int occurrence) { Object realElement = steppingTreeInputController.getElement(template, occurrence, getTreeViewer().getFilters()); if (null != realElement) { ((DeferredTreeViewer) getTreeViewer()).expandToObjectAndSelect(realElement, 0); } } /** * Counts total occurrences found for given element. This method is just delegating the call to * the {@link SteppingTreeInputController}. Result depends on the filters that are currently * active for the tree. * * @param element * Element to count occurrences. * @return Total number of elements found. */ private ElementOccurrenceCount countOccurrences(Object element) { return steppingTreeInputController.countOccurrences(element, getTreeViewer().getFilters()); } /** * Is input set for this sub view. * * @return True is input is not null or if it is not empty. Otherwise false. */ @SuppressWarnings("unchecked") private boolean isInputSet() { List<Object> input = (List<Object>) getTreeViewer().getInput(); if ((input == null) || input.isEmpty()) { return false; } return true; } /** * Stepping control class. * * @author Ivan Senic * */ private class SteppingControl { /** * Composite where stepping control will be created. */ private Composite parent; /** * Toolkit. */ private FormToolkit toolkit; /** * List of objects that are able to be located in the tree. */ private List<Object> steppableObjects; /** * List of objects that are able currently in the combo. */ private List<Object> objectsInCombo; /** * Main composite of stepping control. */ private Composite mainComposite; /** * Combo for object selection. */ private Combo objectSelection; /** * Next button. */ private Button next; /** * Previous button. */ private Button previous; /** * Clear all button. */ private Button clearAllButton; /** * Information label. */ private Label info; /** * Flag for defining is the control show or not. */ private boolean controlShown = false; /** * The currently selected object that is to be found in the tree. */ private Object selectedObject; /** * Current displayed occurrence of selected object. */ private int occurrence; /** * Visible occurrence of the selected object that could be reached. */ private int visibleOccurrences; /** * Filtered occurrence of the selected object that could not be reached. */ private int filteredOccurrences; /** * Default constructor. * * @param parent * Composite where stepping control will be created. * @param toolkit * Toolkit. * @param objectList * List of objects that are able to be located in the tree. */ public SteppingControl(Composite parent, FormToolkit toolkit, List<Object> objectList) { super(); this.parent = parent; this.toolkit = toolkit; this.steppableObjects = objectList; } /** * Creates stepping control. * * @param parent * Composite where stepping control will be created. * @param toolkit * Toolkit. */ private void createPartControl(Composite parent, FormToolkit toolkit) { mainComposite = toolkit.createComposite(parent); GridLayout layout = new GridLayout(7, false); mainComposite.setLayout(layout); mainComposite.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false)); toolkit.createLabel(mainComposite, "Object to locate:"); objectSelection = new Combo(mainComposite, SWT.SIMPLE | SWT.DROP_DOWN | SWT.READ_ONLY); GridData gd = new GridData(); gd.grabExcessHorizontalSpace = true; gd.horizontalAlignment = GridData.FILL; gd.minimumWidth = 200; objectSelection.setLayoutData(gd); previous = toolkit.createButton(mainComposite, "Previous", SWT.PUSH | SWT.NO_BACKGROUND); previous.setEnabled(false); previous.setImage(InspectIT.getDefault().getImage(InspectITImages.IMG_PREVIOUS)); next = toolkit.createButton(mainComposite, "Next", SWT.PUSH | SWT.NO_BACKGROUND); next.setEnabled(false); next.setImage(InspectIT.getDefault().getImage(InspectITImages.IMG_NEXT)); info = toolkit.createLabel(mainComposite, "No invocation loaded"); // added additional composite to the right, so that minimizing and maximizing the window // can look better Composite helpComposite = toolkit.createComposite(mainComposite); gd = new GridData(); gd.grabExcessHorizontalSpace = true; gd.horizontalAlignment = GridData.FILL; gd.minimumWidth = 0; gd.heightHint = 0; gd.widthHint = 0; helpComposite.setLayoutData(gd); clearAllButton = toolkit.createButton(mainComposite, "", SWT.PUSH | SWT.NO_BACKGROUND); clearAllButton.setEnabled(false); clearAllButton.setImage(InspectIT.getDefault().getImage(InspectITImages.IMG_TRASH)); clearAllButton.setToolTipText("Empty steppable objects list"); objectSelection.addListener(SWT.Modify, new Listener() { @Override public void handleEvent(Event event) { int selectionIndex = objectSelection.getSelectionIndex(); if (selectionIndex != -1) { Object selObject = objectsInCombo.get(selectionIndex); selectedObject = selObject; if (isInputSet()) { occurrence = 0; ElementOccurrenceCount elementOccurrenceCount = countOccurrences(selectedObject); visibleOccurrences = elementOccurrenceCount.getVisibleOccurrences(); filteredOccurrences = elementOccurrenceCount.getFilteredOccurrences(); if (visibleOccurrences > 0) { expandToObject(selectedObject, ++occurrence); } if (visibleOccurrences <= occurrence) { next.setEnabled(false); } else { next.setEnabled(true); } if (occurrence <= 1) { previous.setEnabled(false); } else { previous.setEnabled(true); } } } else { next.setEnabled(false); previous.setEnabled(false); } updateInfoBox(); } }); next.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { expandToObject(selectedObject, ++occurrence); if (visibleOccurrences <= occurrence) { next.setEnabled(false); } if (occurrence <= 1) { previous.setEnabled(false); } else { previous.setEnabled(true); } updateInfoBox(); } }); previous.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { expandToObject(selectedObject, --occurrence); next.setEnabled(true); if (occurrence <= 1) { previous.setEnabled(false); } updateInfoBox(); } }); clearAllButton.addListener(SWT.Selection, new Listener() { @Override public void handleEvent(Event event) { clearAll(); } }); controlShown = true; } /** * Clears all objects from the list. */ private void clearAll() { steppableObjects.clear(); objectSelection.removeAll(); objectsInCombo.clear(); selectedObject = null; // NOPMD occurrence = 0; inputChanged(); } /** * * * @param object * One of the objects that are to be located in the tree. * @return Returns the string to be inserted into the combo box for supplied object. */ private String getTextualString(Object object) { String representation = steppingTreeInputController.getElementTextualRepresentation(object); // Assure that string is not too long if (representation.length() > 120) { representation = representation.substring(0, 118) + ".."; } ElementOccurrenceCount elementOccurrenceCount = countOccurrences(object); return representation + " (" + elementOccurrenceCount.getVisibleOccurrences() + " visible, " + elementOccurrenceCount.getFilteredOccurrences() + " filtered)"; } /** * Selects the given object in the stepping control, if the object is currently in the * combo-box. * * @param element * Element to select. */ public void selectObject(Object element) { if (controlShown) { int index = objectsInCombo.indexOf(element); if (index != -1) { objectSelection.select(index); } } } /** * Hides stepping control. */ public void hideControl() { if (controlShown) { mainComposite.dispose(); subComposite.layout(); controlShown = false; setSwitchSteppingControlButtonChecked(false); } } /** * Shows stepping control. */ public void showControl() { if (!controlShown) { createPartControl(parent, toolkit); subComposite.layout(); controlShown = true; inputChanged(); setSwitchSteppingControlButtonChecked(true); } } /** * Resets stepping control. */ public void inputChanged() { if (controlShown) { if (isInputSet()) { objectsInCombo = steppableObjects; objectSelection.removeAll(); if (!objectsInCombo.isEmpty()) { clearAllButton.setEnabled(true); for (Object object : objectsInCombo) { objectSelection.add(getTextualString(object)); } objectSelection.pack(true); if ((null != selectedObject) && objectsInCombo.contains(selectedObject)) { objectSelection.select(objectsInCombo.indexOf(selectedObject)); } else { objectSelection.select(0); } } else { next.setEnabled(false); previous.setEnabled(false); clearAllButton.setEnabled(false); updateInfoBox(); } } else { objectSelection.removeAll(); next.setEnabled(false); previous.setEnabled(false); updateInfoBox(); } mainComposite.layout(); } } /** * Updates the text in the info box based on the current status of the stepping control. */ private void updateInfoBox() { if (controlShown) { String msg = ""; if (isInputSet() && (objectSelection.getSelectionIndex() != -1)) { if ((occurrence == 0) && (visibleOccurrences != 0)) { msg = "Found " + visibleOccurrences + " occurrence"; if (visibleOccurrences > 1) { msg += "s"; } } else if (occurrence != 0) { msg = occurrence + "/" + visibleOccurrences; } else { msg = "No occurrences found"; } if (filteredOccurrences > 0) { msg += " (" + filteredOccurrences + " filtered out)"; } } else if (objectSelection.getItemCount() == 0) { msg = "No object to locate"; } else { msg = "No invocation loaded"; } info.setText(msg); mainComposite.layout(); } } /** * @return the controlShown */ public boolean isControlShown() { return controlShown; } } }