001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.part;
011:
012: import java.util.Arrays;
013: import java.util.List;
014:
015: import org.eclipse.jface.action.Action;
016: import org.eclipse.jface.action.IMenuManager;
017: import org.eclipse.jface.action.IToolBarManager;
018: import org.eclipse.jface.viewers.ISelectionChangedListener;
019: import org.eclipse.jface.viewers.IStructuredSelection;
020: import org.eclipse.jface.viewers.SelectionChangedEvent;
021: import org.eclipse.jface.viewers.StructuredSelection;
022: import org.eclipse.jface.viewers.TreeViewer;
023: import org.eclipse.ui.ISharedImages;
024: import org.eclipse.ui.PlatformUI;
025: import org.eclipse.ui.internal.IWorkbenchGraphicConstants;
026: import org.eclipse.ui.internal.WorkbenchImages;
027: import org.eclipse.ui.internal.WorkbenchMessages;
028:
029: /**
030: * Implements a simple web style navigation metaphor for a <code>TreeViewer</code>.
031: * Home, back, and "drill into" functions are supported for the viewer,
032: * <p>
033: * To use the <code>DrillDownAdapter</code> ..
034: * </p>
035: * <ul>
036: * <li>Create an instance of <code>TreeViewer</code>. </li>
037: * <li>Create a <code>DrillDownAdapter</code> for the viewer. </li>
038: * <li>Create a container for your viewer with a toolbar or a popup menu.
039: * Add actions for "goBack", "goHome", and "goInto" to either one by calling
040: * </code>addNavigationActions</code> with the popup menu or toolbar.</li>
041: * </ol>
042: * <p>
043: * If the input for the underlying viewer is changed by something other than the
044: * adapter the <code>reset</code> method should be called. This will clear
045: * the drill stack and update the navigation buttons to reflect the new
046: * state of the underlying viewer.
047: * </p>
048: * </p>
049: */
050: public class DrillDownAdapter implements ISelectionChangedListener {
051: private TreeViewer fChildTree;
052:
053: private DrillStack fDrillStack;
054:
055: private Action homeAction;
056:
057: private Action backAction;
058:
059: private Action forwardAction;
060:
061: /**
062: * Allocates a new DrillDownTreePart.
063: *
064: * @param tree the target tree for refocusing
065: */
066: public DrillDownAdapter(TreeViewer tree) {
067: fDrillStack = new DrillStack();
068: fChildTree = tree;
069: }
070:
071: /**
072: * Adds actions for "go back", "go home", and "go into" to a menu manager.
073: *
074: * @param manager is the target manager to update
075: */
076: public void addNavigationActions(IMenuManager manager) {
077: createActions();
078: manager.add(homeAction);
079: manager.add(backAction);
080: manager.add(forwardAction);
081: updateNavigationButtons();
082: }
083:
084: /**
085: * Adds actions for "go back", "go home", and "go into" to a tool bar manager.
086: *
087: * @param toolBar is the target manager to update
088: */
089: public void addNavigationActions(IToolBarManager toolBar) {
090: createActions();
091: toolBar.add(homeAction);
092: toolBar.add(backAction);
093: toolBar.add(forwardAction);
094: updateNavigationButtons();
095: }
096:
097: /**
098: * Returns whether expansion is possible for the current selection. This
099: * will only be true if it has children.
100: *
101: * @param element the object to test for expansion
102: * @return <code>true</code> if expansion is possible; otherwise
103: * return <code>false</code
104: */
105: public boolean canExpand(Object element) {
106: return fChildTree.isExpandable(element);
107: }
108:
109: /**
110: * Returns whether "go back" is possible for child tree. This is only possible
111: * if the client has performed one or more drilling operations.
112: *
113: * @return <code>true</code> if "go back" is possible; <code>false</code> otherwise
114: */
115: public boolean canGoBack() {
116: return fDrillStack.canGoBack();
117: }
118:
119: /**
120: * Returns whether "go home" is possible for child tree. This is only possible
121: * if the client has performed one or more drilling operations.
122: *
123: * @return <code>true</code> if "go home" is possible; <code>false</code> otherwise
124: */
125: public boolean canGoHome() {
126: return fDrillStack.canGoHome();
127: }
128:
129: /**
130: * Returns whether "go into" is possible for child tree. This is only possible
131: * if the current selection in the client has one item and it has children.
132: *
133: * @return <code>true</code> if "go into" is possible; <code>false</code> otherwise
134: */
135: public boolean canGoInto() {
136: IStructuredSelection oSelection = (IStructuredSelection) fChildTree
137: .getSelection();
138: if (oSelection == null || oSelection.size() != 1) {
139: return false;
140: }
141: Object anElement = oSelection.getFirstElement();
142: return canExpand(anElement);
143: }
144:
145: /**
146: * Create the actions for navigation.
147: *
148: * @param tree the target tree for refocusing
149: */
150: private void createActions() {
151: // Only do this once.
152: if (homeAction != null) {
153: return;
154: }
155:
156: // Home.
157: homeAction = new Action(WorkbenchMessages.GoHome_text) {
158: public void run() {
159: goHome();
160: }
161: };
162: homeAction.setToolTipText(WorkbenchMessages.GoHome_toolTip);
163: homeAction
164: .setImageDescriptor(WorkbenchImages
165: .getImageDescriptor(IWorkbenchGraphicConstants.IMG_ETOOL_HOME_NAV));
166:
167: // Back.
168: ISharedImages images = PlatformUI.getWorkbench()
169: .getSharedImages();
170: backAction = new Action(WorkbenchMessages.GoBack_text) {
171: public void run() {
172: goBack();
173: }
174: };
175: backAction.setToolTipText(WorkbenchMessages.GoBack_toolTip);
176: backAction.setImageDescriptor(images
177: .getImageDescriptor(ISharedImages.IMG_TOOL_BACK));
178: backAction
179: .setDisabledImageDescriptor(images
180: .getImageDescriptor(ISharedImages.IMG_TOOL_BACK_DISABLED));
181:
182: // Forward.
183: forwardAction = new Action(WorkbenchMessages.GoInto_text) {
184: public void run() {
185: goInto();
186: }
187: };
188: forwardAction.setToolTipText(WorkbenchMessages.GoInto_toolTip);
189: forwardAction.setImageDescriptor(images
190: .getImageDescriptor(ISharedImages.IMG_TOOL_FORWARD));
191: forwardAction
192: .setDisabledImageDescriptor(images
193: .getImageDescriptor(ISharedImages.IMG_TOOL_FORWARD_DISABLED));
194:
195: // Update the buttons when a selection change occurs.
196: fChildTree.addSelectionChangedListener(this );
197: updateNavigationButtons();
198: }
199:
200: /**
201: * Expands the given items in the tree. The list of items passed should be
202: * derived by calling <code>getExpanded</code>.
203: *
204: * @param items is a list of items within the tree which should be expanded
205: */
206: private void expand(List items) {
207: fChildTree.setExpandedElements(items.toArray());
208: }
209:
210: /**
211: * Returns a list of elements corresponding to expanded nodes in
212: * child tree.
213: *
214: * @return a list of expandd elements
215: */
216: private List getExpanded() {
217: return Arrays.asList(fChildTree.getExpandedElements());
218: }
219:
220: /**
221: * Reverts the input for the tree back to the state when <code>goInto</code>
222: * was last called.
223: * <p>
224: * A frame is removed from the drill stack. Then that frame is used to reset the
225: * input and expansion state for the child tree.
226: * </p>
227: */
228: public void goBack() {
229: Object currentInput = fChildTree.getInput();
230: DrillFrame oFrame = fDrillStack.goBack();
231: Object input = oFrame.getElement();
232: fChildTree.setInput(input);
233: expand(oFrame.getExpansion());
234: // if there was a selection, it should have been preserved,
235: // but if not, select the element that was drilled into
236: if (fChildTree.getSelection().isEmpty()) {
237: fChildTree.setSelection(new StructuredSelection(
238: currentInput), true);
239: }
240: updateNavigationButtons();
241: }
242:
243: /**
244: * Reverts the input for the tree back to the state when the adapter was
245: * created.
246: * <p>
247: * All of the frames are removed from the drill stack. Then the oldest frame is
248: * used to reset the input and expansion state for the child tree.
249: * </p>
250: */
251: public void goHome() {
252: Object currentInput = fChildTree.getInput();
253: DrillFrame oFrame = fDrillStack.goHome();
254: Object input = oFrame.getElement();
255: fChildTree.setInput(input);
256: expand(oFrame.getExpansion());
257: // if there was a selection, it should have been preserved,
258: // but if not, select the element that was last drilled into
259: if (fChildTree.getSelection().isEmpty()) {
260: fChildTree.setSelection(new StructuredSelection(
261: currentInput), true);
262: }
263: updateNavigationButtons();
264: }
265:
266: /**
267: * Sets the input for the tree to the current selection.
268: * <p>
269: * The current input and expansion state are saved in a frame and added to the
270: * drill stack. Then the input for the tree is changed to be the current selection.
271: * The expansion state for the tree is maintained during the operation.
272: * </p><p>
273: * On return the client may revert back to the previous state by invoking
274: * <code>goBack</code> or <code>goHome</code>.
275: * </p>
276: */
277: public void goInto() {
278: IStructuredSelection sel = (IStructuredSelection) fChildTree
279: .getSelection();
280: Object element = sel.getFirstElement();
281: goInto(element);
282: }
283:
284: /**
285: * Sets the input for the tree to a particular item in the tree.
286: * <p>
287: * The current input and expansion state are saved in a frame and added to the
288: * drill stack. Then the input for the tree is changed to be <code>newInput</code>.
289: * The expansion state for the tree is maintained during the operation.
290: * </p><p>
291: * On return the client may revert back to the previous state by invoking
292: * <code>goBack</code> or <code>goHome</code>.
293: * </p>
294: *
295: * @param newInput the new input element
296: */
297: public void goInto(Object newInput) {
298: // If we can drill ..
299: if (canExpand(newInput)) {
300: // Save the old state.
301: Object oldInput = fChildTree.getInput();
302: List expandedList = getExpanded();
303: fDrillStack.add(new DrillFrame(oldInput,
304: "null", expandedList));//$NON-NLS-1$
305:
306: // Install the new state.
307: fChildTree.setInput(newInput);
308: expand(expandedList);
309: updateNavigationButtons();
310: }
311: }
312:
313: /**
314: * Resets the drill down adapter.
315: * <p>
316: * This method is typically called when the input for the underlying view
317: * is reset by something other than the adapter.
318: * On return the drill stack has been cleared and the navigation buttons
319: * reflect the new state of the underlying viewer.
320: * </p>
321: */
322: public void reset() {
323: fDrillStack.reset();
324: updateNavigationButtons();
325: }
326:
327: /**
328: * Updates the navigation buttons when a selection change occurs
329: * in the tree.
330: */
331: public void selectionChanged(SelectionChangedEvent event) {
332: updateNavigationButtons();
333: }
334:
335: /**
336: * Updates the enabled state for each navigation button.
337: */
338: protected void updateNavigationButtons() {
339: if (homeAction != null) {
340: homeAction.setEnabled(canGoHome());
341: backAction.setEnabled(canGoBack());
342: forwardAction.setEnabled(canGoInto());
343: }
344: }
345: }
|