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: * Sebastian Davids - bug 128526, bug 128529
011: *******************************************************************************/package org.eclipse.ui.internal.dialogs;
012:
013: import java.util.ArrayList;
014: import java.util.Iterator;
015:
016: import org.eclipse.jface.dialogs.Dialog;
017: import org.eclipse.jface.dialogs.IDialogConstants;
018: import org.eclipse.jface.dialogs.IDialogSettings;
019: import org.eclipse.jface.dialogs.PopupDialog;
020: import org.eclipse.jface.viewers.DoubleClickEvent;
021: import org.eclipse.jface.viewers.IDoubleClickListener;
022: import org.eclipse.jface.viewers.ISelectionChangedListener;
023: import org.eclipse.jface.viewers.IStructuredSelection;
024: import org.eclipse.jface.viewers.ITreeContentProvider;
025: import org.eclipse.jface.viewers.ITreeSelection;
026: import org.eclipse.jface.viewers.SelectionChangedEvent;
027: import org.eclipse.jface.viewers.StructuredSelection;
028: import org.eclipse.jface.viewers.TreeViewer;
029: import org.eclipse.swt.SWT;
030: import org.eclipse.swt.events.DisposeEvent;
031: import org.eclipse.swt.events.DisposeListener;
032: import org.eclipse.swt.events.FocusAdapter;
033: import org.eclipse.swt.events.FocusEvent;
034: import org.eclipse.swt.events.KeyAdapter;
035: import org.eclipse.swt.events.KeyEvent;
036: import org.eclipse.swt.graphics.Color;
037: import org.eclipse.swt.graphics.Point;
038: import org.eclipse.swt.graphics.RGB;
039: import org.eclipse.swt.layout.GridData;
040: import org.eclipse.swt.widgets.Button;
041: import org.eclipse.swt.widgets.Composite;
042: import org.eclipse.swt.widgets.Control;
043: import org.eclipse.swt.widgets.Display;
044: import org.eclipse.swt.widgets.Label;
045: import org.eclipse.swt.widgets.Shell;
046: import org.eclipse.swt.widgets.Text;
047: import org.eclipse.ui.IWorkbenchWindow;
048: import org.eclipse.ui.PlatformUI;
049: import org.eclipse.ui.dialogs.FilteredTree;
050: import org.eclipse.ui.dialogs.PatternFilter;
051: import org.eclipse.ui.internal.IWorkbenchHelpContextIds;
052: import org.eclipse.ui.internal.WorkbenchMessages;
053: import org.eclipse.ui.internal.WorkbenchPlugin;
054: import org.eclipse.ui.internal.registry.ViewRegistry;
055: import org.eclipse.ui.views.IViewCategory;
056: import org.eclipse.ui.views.IViewDescriptor;
057: import org.eclipse.ui.views.IViewRegistry;
058:
059: /**
060: * The Show View dialog.
061: */
062: public class ShowViewDialog extends Dialog implements
063: ISelectionChangedListener, IDoubleClickListener {
064:
065: private static final String DIALOG_SETTING_SECTION_NAME = "ShowViewDialog"; //$NON-NLS-1$
066:
067: private static final int LIST_HEIGHT = 300;
068:
069: private static final int LIST_WIDTH = 250;
070:
071: private static final String STORE_EXPANDED_CATEGORIES_ID = DIALOG_SETTING_SECTION_NAME
072: + ".STORE_EXPANDED_CATEGORIES_ID"; //$NON-NLS-1$
073:
074: private static final String STORE_SELECTED_VIEW_ID = DIALOG_SETTING_SECTION_NAME
075: + ".STORE_SELECTED_VIEW_ID"; //$NON-NLS-1$
076:
077: private FilteredTree filteredTree;
078:
079: private Button okButton;
080:
081: private IViewDescriptor[] viewDescs = new IViewDescriptor[0];
082:
083: private IViewRegistry viewReg;
084:
085: private IWorkbenchWindow window;
086:
087: private Color dimmedForeground;
088:
089: /**
090: * Constructs a new ShowViewDialog.
091: *
092: * @param window the workbench window
093: * @param viewReg the view registry
094: */
095: public ShowViewDialog(IWorkbenchWindow window, IViewRegistry viewReg) {
096: super (window.getShell());
097: this .window = window;
098: this .viewReg = viewReg;
099: }
100:
101: /**
102: * This method is called if a button has been pressed.
103: */
104: protected void buttonPressed(int buttonId) {
105: if (buttonId == IDialogConstants.OK_ID) {
106: saveWidgetValues();
107: }
108: super .buttonPressed(buttonId);
109: }
110:
111: /**
112: * Notifies that the cancel button of this dialog has been pressed.
113: */
114: protected void cancelPressed() {
115: viewDescs = new IViewDescriptor[0];
116: super .cancelPressed();
117: }
118:
119: /*
120: * (non-Javadoc)
121: *
122: * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
123: */
124: protected void configureShell(Shell shell) {
125: super .configureShell(shell);
126: shell.setText(WorkbenchMessages.ShowView_shellTitle);
127: PlatformUI.getWorkbench().getHelpSystem().setHelp(shell,
128: IWorkbenchHelpContextIds.SHOW_VIEW_DIALOG);
129: }
130:
131: /**
132: * Adds buttons to this dialog's button bar.
133: * <p>
134: * The default implementation of this framework method adds standard ok and
135: * cancel buttons using the <code>createButton</code> framework method.
136: * Subclasses may override.
137: * </p>
138: *
139: * @param parent the button bar composite
140: */
141: protected void createButtonsForButtonBar(Composite parent) {
142: okButton = createButton(parent, IDialogConstants.OK_ID,
143: IDialogConstants.OK_LABEL, true);
144: createButton(parent, IDialogConstants.CANCEL_ID,
145: IDialogConstants.CANCEL_LABEL, false);
146: updateButtons();
147: }
148:
149: /**
150: * Creates and returns the contents of the upper part of this dialog (above
151: * the button bar).
152: *
153: * @param parent the parent composite to contain the dialog area
154: * @return the dialog area control
155: */
156: protected Control createDialogArea(Composite parent) {
157: // Run super.
158: Composite composite = (Composite) super
159: .createDialogArea(parent);
160: composite.setFont(parent.getFont());
161:
162: createFilteredTreeViewer(composite);
163:
164: layoutTopControl(filteredTree);
165:
166: // Use F2... label
167: Label label = new Label(composite, SWT.WRAP);
168: label.setText(WorkbenchMessages.ShowView_selectViewHelp);
169: label.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true,
170: false));
171:
172: // Restore the last state
173: restoreWidgetValues();
174:
175: applyDialogFont(composite);
176:
177: // Return results.
178: return composite;
179: }
180:
181: /**
182: * Blends c1 and c2 based in the provided ratio.
183: *
184: * @param c1
185: * first color
186: * @param c2
187: * second color
188: * @param ratio
189: * percentage of the first color in the blend (0-100)
190: * @return the RGB value of the blended color
191: *
192: * copied from FormColors.java
193: */
194: private static RGB blend(RGB c1, RGB c2, int ratio) {
195: int r = blend(c1.red, c2.red, ratio);
196: int g = blend(c1.green, c2.green, ratio);
197: int b = blend(c1.blue, c2.blue, ratio);
198: return new RGB(r, g, b);
199: }
200:
201: private static int blend(int v1, int v2, int ratio) {
202: int b = (ratio * v1 + (100 - ratio) * v2) / 100;
203: return Math.min(255, b);
204: }
205:
206: /**
207: * Create a new filtered tree viewer in the parent.
208: *
209: * @param parent the parent <code>Composite</code>.
210: */
211: private void createFilteredTreeViewer(Composite parent) {
212: PatternFilter filter = new ViewPatternFilter();
213: int styleBits = SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL
214: | SWT.BORDER;
215: filteredTree = new FilteredTree(parent, styleBits, filter);
216: filteredTree.setBackground(parent.getDisplay().getSystemColor(
217: SWT.COLOR_WIDGET_BACKGROUND));
218:
219: TreeViewer treeViewer = filteredTree.getViewer();
220: Control treeControl = treeViewer.getControl();
221: RGB dimmedRGB = blend(treeControl.getForeground().getRGB(),
222: treeControl.getBackground().getRGB(), 60);
223: dimmedForeground = new Color(treeControl.getDisplay(),
224: dimmedRGB);
225: treeControl.addDisposeListener(new DisposeListener() {
226: public void widgetDisposed(DisposeEvent e) {
227: dimmedForeground.dispose();
228: }
229: });
230:
231: treeViewer.setLabelProvider(new ViewLabelProvider(window,
232: dimmedForeground));
233: treeViewer.setContentProvider(new ViewContentProvider());
234: treeViewer.setComparator(new ViewComparator(
235: (ViewRegistry) viewReg));
236: treeViewer.setInput(viewReg);
237: treeViewer.addSelectionChangedListener(this );
238: treeViewer.addDoubleClickListener(this );
239: treeViewer.addFilter(new CapabilityFilter());
240: treeViewer.getControl().addKeyListener(new KeyAdapter() {
241: public void keyPressed(KeyEvent e) {
242: handleTreeViewerKeyPressed(e);
243: }
244: });
245:
246: // if the tree has only one or zero views, disable the filter text control
247: if (hasAtMostOneView(filteredTree.getViewer())) {
248: Text filterText = filteredTree.getFilterControl();
249: if (filterText != null) {
250: filterText.setEnabled(false);
251: }
252: }
253: }
254:
255: /**
256: * Return whether or not there are less than two views in the list.
257: *
258: * @param tree
259: * @return <code>true</code> if there are less than two views in the list.
260: */
261: private boolean hasAtMostOneView(TreeViewer tree) {
262: ITreeContentProvider contentProvider = (ITreeContentProvider) tree
263: .getContentProvider();
264: Object[] children = contentProvider
265: .getElements(tree.getInput());
266:
267: if (children.length <= 1) {
268: if (children.length == 0) {
269: return true;
270: }
271: return !contentProvider.hasChildren(children[0]);
272: }
273: return false;
274: }
275:
276: /*
277: * (non-Javadoc)
278: *
279: * @see org.eclipse.jface.viewers.IDoubleClickListener#doubleClick(org.eclipse.jface.viewers.DoubleClickEvent)
280: */
281: public void doubleClick(DoubleClickEvent event) {
282: IStructuredSelection s = (IStructuredSelection) event
283: .getSelection();
284: Object element = s.getFirstElement();
285: if (filteredTree.getViewer().isExpandable(element)) {
286: filteredTree.getViewer()
287: .setExpandedState(
288: element,
289: !filteredTree.getViewer().getExpandedState(
290: element));
291: } else if (viewDescs.length > 0) {
292: saveWidgetValues();
293: setReturnCode(OK);
294: close();
295: }
296: }
297:
298: /**
299: * Return the dialog store to cache values into
300: */
301: protected IDialogSettings getDialogSettings() {
302: IDialogSettings workbenchSettings = WorkbenchPlugin
303: .getDefault().getDialogSettings();
304: IDialogSettings section = workbenchSettings
305: .getSection(DIALOG_SETTING_SECTION_NAME);
306: if (section == null) {
307: section = workbenchSettings
308: .addNewSection(DIALOG_SETTING_SECTION_NAME);
309: }
310: return section;
311: }
312:
313: /**
314: * Returns the descriptors for the selected views.
315: *
316: * @return the descriptors for the selected views
317: */
318: public IViewDescriptor[] getSelection() {
319: return viewDescs;
320: }
321:
322: /**
323: * Layout the top control.
324: *
325: * @param control the control.
326: */
327: private void layoutTopControl(Control control) {
328: GridData spec = new GridData(GridData.FILL_BOTH);
329: spec.widthHint = LIST_WIDTH;
330: spec.heightHint = LIST_HEIGHT;
331: control.setLayoutData(spec);
332: }
333:
334: /**
335: * Use the dialog store to restore widget values to the values that they
336: * held last time this dialog was used to completion.
337: */
338: protected void restoreWidgetValues() {
339: IDialogSettings settings = getDialogSettings();
340:
341: String[] expandedCategoryIds = settings
342: .getArray(STORE_EXPANDED_CATEGORIES_ID);
343: if (expandedCategoryIds == null) {
344: return;
345: }
346:
347: ViewRegistry reg = (ViewRegistry) viewReg;
348: ArrayList categoriesToExpand = new ArrayList(
349: expandedCategoryIds.length);
350: for (int i = 0; i < expandedCategoryIds.length; i++) {
351: IViewCategory category = reg
352: .findCategory(expandedCategoryIds[i]);
353: if (category != null) {
354: categoriesToExpand.add(category);
355: }
356: }
357:
358: if (!categoriesToExpand.isEmpty()) {
359: filteredTree.getViewer().setExpandedElements(
360: categoriesToExpand.toArray());
361: }
362:
363: String selectedViewId = settings.get(STORE_SELECTED_VIEW_ID);
364: if (selectedViewId != null) {
365: IViewDescriptor viewDesc = reg.find(selectedViewId);
366: if (viewDesc != null) {
367: filteredTree.getViewer().setSelection(
368: new StructuredSelection(viewDesc), true);
369: }
370: }
371: }
372:
373: /**
374: * Since OK was pressed, write widget values to the dialog store so that
375: * they will persist into the next invocation of this dialog
376: */
377: protected void saveWidgetValues() {
378: IDialogSettings settings = getDialogSettings();
379:
380: // Collect the ids of the all expanded categories
381: Object[] expandedElements = filteredTree.getViewer()
382: .getExpandedElements();
383: String[] expandedCategoryIds = new String[expandedElements.length];
384: for (int i = 0; i < expandedElements.length; ++i) {
385: expandedCategoryIds[i] = ((IViewCategory) expandedElements[i])
386: .getId();
387: }
388:
389: // Save them for next time.
390: settings.put(STORE_EXPANDED_CATEGORIES_ID, expandedCategoryIds);
391:
392: String selectedViewId = ""; //$NON-NLS-1$
393: if (viewDescs.length > 0) {
394: // in the case of a multi-selection, it's probably less confusing
395: // to store just the first rather than the whole multi-selection
396: selectedViewId = viewDescs[0].getId();
397: }
398: settings.put(STORE_SELECTED_VIEW_ID, selectedViewId);
399: }
400:
401: /**
402: * Notifies that the selection has changed.
403: *
404: * @param event event object describing the change
405: */
406: public void selectionChanged(SelectionChangedEvent event) {
407: updateSelection(event);
408: updateButtons();
409: }
410:
411: /**
412: * Update the button enablement state.
413: */
414: protected void updateButtons() {
415: if (okButton != null) {
416: okButton.setEnabled(getSelection().length > 0);
417: }
418: }
419:
420: /**
421: * Update the selection object.
422: */
423: protected void updateSelection(SelectionChangedEvent event) {
424: ArrayList descs = new ArrayList();
425: IStructuredSelection sel = (IStructuredSelection) event
426: .getSelection();
427: for (Iterator i = sel.iterator(); i.hasNext();) {
428: Object o = i.next();
429: if (o instanceof IViewDescriptor) {
430: descs.add(o);
431: }
432: }
433: viewDescs = new IViewDescriptor[descs.size()];
434: descs.toArray(viewDescs);
435: }
436:
437: /* (non-Javadoc)
438: * @see org.eclipse.jface.window.Dialog#getDialogBoundsSettings()
439: *
440: * @since 3.4
441: */
442: protected IDialogSettings getDialogBoundsSettings() {
443: return getDialogSettings();
444: }
445:
446: void handleTreeViewerKeyPressed(KeyEvent event) {
447: // popup the description for the selected view
448: if (event.keyCode == SWT.F2 && event.stateMask == 0) {
449: ITreeSelection selection = (ITreeSelection) filteredTree
450: .getViewer().getSelection();
451: // only show description if one view is selected
452: if (selection.size() == 1) {
453: Object o = selection.getFirstElement();
454: if (o instanceof IViewDescriptor) {
455: String description = ((IViewDescriptor) o)
456: .getDescription();
457: if (description.length() == 0)
458: description = WorkbenchMessages.ShowView_noDesc;
459: popUp(description);
460: }
461: }
462: }
463: }
464:
465: private void popUp(final String description) {
466: new PopupDialog(filteredTree.getShell(),
467: PopupDialog.HOVER_SHELLSTYLE, true, false, false,
468: false, null, null) {
469: private static final int CURSOR_SIZE = 15;
470:
471: protected Point getInitialLocation(Point initialSize) {
472: //show popup relative to cursor
473: Display display = getShell().getDisplay();
474: Point location = display.getCursorLocation();
475: location.x += CURSOR_SIZE;
476: location.y += CURSOR_SIZE;
477: return location;
478: }
479:
480: protected Control createDialogArea(Composite parent) {
481: Label label = new Label(parent, SWT.WRAP);
482: label.setText(description);
483: label.addFocusListener(new FocusAdapter() {
484: public void focusLost(FocusEvent event) {
485: close();
486: }
487: });
488: // Use the compact margins employed by PopupDialog.
489: GridData gd = new GridData(GridData.BEGINNING
490: | GridData.FILL_BOTH);
491: gd.horizontalIndent = PopupDialog.POPUP_HORIZONTALSPACING;
492: gd.verticalIndent = PopupDialog.POPUP_VERTICALSPACING;
493: label.setLayoutData(gd);
494: return label;
495: }
496: }.open();
497: }
498:
499: /*
500: * (non-Javadoc)
501: * @see org.eclipse.jface.dialogs.Dialog#isResizable()
502: */
503: protected boolean isResizable() {
504: return true;
505: }
506: }
|