blob: 18712b58476ad0c794f99f45acee8628d267f21a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2006 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.ui.part;
import java.util.Arrays;
import java.util.List;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.internal.IWorkbenchGraphicConstants;
import org.eclipse.ui.internal.WorkbenchImages;
import org.eclipse.ui.internal.WorkbenchMessages;
/**
* Implements a simple web style navigation metaphor for a <code>TreeViewer</code>.
* Home, back, and "drill into" functions are supported for the viewer,
* <p>
* To use the <code>DrillDownAdapter</code> ..
* </p>
* <ul>
* <li>Create an instance of <code>TreeViewer</code>. </li>
* <li>Create a <code>DrillDownAdapter</code> for the viewer. </li>
* <li>Create a container for your viewer with a toolbar or a popup menu.
* Add actions for "goBack", "goHome", and "goInto" to either one by calling
* </code>addNavigationActions</code> with the popup menu or toolbar.</li>
* </ol>
* <p>
* If the input for the underlying viewer is changed by something other than the
* adapter the <code>reset</code> method should be called. This will clear
* the drill stack and update the navigation buttons to reflect the new
* state of the underlying viewer.
* </p>
* </p>
*/
public class DrillDownAdapter implements ISelectionChangedListener {
private TreeViewer fChildTree;
private DrillStack fDrillStack;
private Action homeAction;
private Action backAction;
private Action forwardAction;
/**
* Allocates a new DrillDownTreePart.
*
* @param tree the target tree for refocusing
*/
public DrillDownAdapter(TreeViewer tree) {
fDrillStack = new DrillStack();
fChildTree = tree;
}
/**
* Adds actions for "go back", "go home", and "go into" to a menu manager.
*
* @param manager is the target manager to update
*/
public void addNavigationActions(IMenuManager manager) {
createActions();
manager.add(homeAction);
manager.add(backAction);
manager.add(forwardAction);
updateNavigationButtons();
}
/**
* Adds actions for "go back", "go home", and "go into" to a tool bar manager.
*
* @param toolBar is the target manager to update
*/
public void addNavigationActions(IToolBarManager toolBar) {
createActions();
toolBar.add(homeAction);
toolBar.add(backAction);
toolBar.add(forwardAction);
updateNavigationButtons();
}
/**
* Returns whether expansion is possible for the current selection. This
* will only be true if it has children.
*
* @param element the object to test for expansion
* @return <code>true</code> if expansion is possible; otherwise
* return <code>false</code
*/
public boolean canExpand(Object element) {
return fChildTree.isExpandable(element);
}
/**
* Returns whether "go back" is possible for child tree. This is only possible
* if the client has performed one or more drilling operations.
*
* @return <code>true</code> if "go back" is possible; <code>false</code> otherwise
*/
public boolean canGoBack() {
return fDrillStack.canGoBack();
}
/**
* Returns whether "go home" is possible for child tree. This is only possible
* if the client has performed one or more drilling operations.
*
* @return <code>true</code> if "go home" is possible; <code>false</code> otherwise
*/
public boolean canGoHome() {
return fDrillStack.canGoHome();
}
/**
* Returns whether "go into" is possible for child tree. This is only possible
* if the current selection in the client has one item and it has children.
*
* @return <code>true</code> if "go into" is possible; <code>false</code> otherwise
*/
public boolean canGoInto() {
IStructuredSelection oSelection = (IStructuredSelection) fChildTree
.getSelection();
if (oSelection == null || oSelection.size() != 1) {
return false;
}
Object anElement = oSelection.getFirstElement();
return canExpand(anElement);
}
/**
* Create the actions for navigation.
*
* @param tree the target tree for refocusing
*/
private void createActions() {
// Only do this once.
if (homeAction != null) {
return;
}
// Home.
homeAction = new Action(WorkbenchMessages.GoHome_text) {
public void run() {
goHome();
}
};
homeAction
.setToolTipText(WorkbenchMessages.GoHome_toolTip);
homeAction
.setImageDescriptor(WorkbenchImages
.getImageDescriptor(IWorkbenchGraphicConstants.IMG_ETOOL_HOME_NAV));
// Back.
ISharedImages images = PlatformUI.getWorkbench().getSharedImages();
backAction = new Action(WorkbenchMessages.GoBack_text) {
public void run() {
goBack();
}
};
backAction
.setToolTipText(WorkbenchMessages.GoBack_toolTip);
backAction.setImageDescriptor(images
.getImageDescriptor(ISharedImages.IMG_TOOL_BACK));
backAction.setDisabledImageDescriptor(images
.getImageDescriptor(ISharedImages.IMG_TOOL_BACK_DISABLED));
// Forward.
forwardAction = new Action(WorkbenchMessages.GoInto_text) {
public void run() {
goInto();
}
};
forwardAction.setToolTipText(WorkbenchMessages.GoInto_toolTip);
forwardAction.setImageDescriptor(images
.getImageDescriptor(ISharedImages.IMG_TOOL_FORWARD));
forwardAction.setDisabledImageDescriptor(images
.getImageDescriptor(ISharedImages.IMG_TOOL_FORWARD_DISABLED));
// Update the buttons when a selection change occurs.
fChildTree.addSelectionChangedListener(this);
updateNavigationButtons();
}
/**
* Expands the given items in the tree. The list of items passed should be
* derived by calling <code>getExpanded</code>.
*
* @param items is a list of items within the tree which should be expanded
*/
private void expand(List items) {
fChildTree.setExpandedElements(items.toArray());
}
/**
* Returns a list of elements corresponding to expanded nodes in
* child tree.
*
* @return a list of expandd elements
*/
private List getExpanded() {
return Arrays.asList(fChildTree.getExpandedElements());
}
/**
* Reverts the input for the tree back to the state when <code>goInto</code>
* was last called.
* <p>
* A frame is removed from the drill stack. Then that frame is used to reset the
* input and expansion state for the child tree.
* </p>
*/
public void goBack() {
Object currentInput = fChildTree.getInput();
DrillFrame oFrame = fDrillStack.goBack();
Object input = oFrame.getElement();
fChildTree.setInput(input);
expand(oFrame.getExpansion());
// if there was a selection, it should have been preserved,
// but if not, select the element that was drilled into
if (fChildTree.getSelection().isEmpty()) {
fChildTree
.setSelection(new StructuredSelection(currentInput), true);
}
updateNavigationButtons();
}
/**
* Reverts the input for the tree back to the state when the adapter was
* created.
* <p>
* All of the frames are removed from the drill stack. Then the oldest frame is
* used to reset the input and expansion state for the child tree.
* </p>
*/
public void goHome() {
Object currentInput = fChildTree.getInput();
DrillFrame oFrame = fDrillStack.goHome();
Object input = oFrame.getElement();
fChildTree.setInput(input);
expand(oFrame.getExpansion());
// if there was a selection, it should have been preserved,
// but if not, select the element that was last drilled into
if (fChildTree.getSelection().isEmpty()) {
fChildTree
.setSelection(new StructuredSelection(currentInput), true);
}
updateNavigationButtons();
}
/**
* Sets the input for the tree to the current selection.
* <p>
* The current input and expansion state are saved in a frame and added to the
* drill stack. Then the input for the tree is changed to be the current selection.
* The expansion state for the tree is maintained during the operation.
* </p><p>
* On return the client may revert back to the previous state by invoking
* <code>goBack</code> or <code>goHome</code>.
* </p>
*/
public void goInto() {
IStructuredSelection sel = (IStructuredSelection) fChildTree
.getSelection();
Object element = sel.getFirstElement();
goInto(element);
}
/**
* Sets the input for the tree to a particular item in the tree.
* <p>
* The current input and expansion state are saved in a frame and added to the
* drill stack. Then the input for the tree is changed to be <code>newInput</code>.
* The expansion state for the tree is maintained during the operation.
* </p><p>
* On return the client may revert back to the previous state by invoking
* <code>goBack</code> or <code>goHome</code>.
* </p>
*
* @param newInput the new input element
*/
public void goInto(Object newInput) {
// If we can drill ..
if (canExpand(newInput)) {
// Save the old state.
Object oldInput = fChildTree.getInput();
List expandedList = getExpanded();
fDrillStack.add(new DrillFrame(oldInput, "null", expandedList));//$NON-NLS-1$
// Install the new state.
fChildTree.setInput(newInput);
expand(expandedList);
updateNavigationButtons();
}
}
/**
* Resets the drill down adapter.
* <p>
* This method is typically called when the input for the underlying view
* is reset by something other than the adapter.
* On return the drill stack has been cleared and the navigation buttons
* reflect the new state of the underlying viewer.
* </p>
*/
public void reset() {
fDrillStack.reset();
updateNavigationButtons();
}
/**
* Updates the navigation buttons when a selection change occurs
* in the tree.
*/
public void selectionChanged(SelectionChangedEvent event) {
updateNavigationButtons();
}
/**
* Updates the enabled state for each navigation button.
*/
protected void updateNavigationButtons() {
if (homeAction != null) {
homeAction.setEnabled(canGoHome());
backAction.setEnabled(canGoBack());
forwardAction.setEnabled(canGoInto());
}
}
}