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 <sdavids@gmx.de> - Fix for bug 19346 - Dialog
011: * font should be activated and used by other components.
012: *******************************************************************************/package org.eclipse.ui.internal.dialogs;
013:
014: import java.util.HashMap;
015: import java.util.Iterator;
016: import java.util.Map;
017:
018: import org.eclipse.core.runtime.IBundleGroup;
019: import org.eclipse.jface.dialogs.IDialogConstants;
020: import org.eclipse.jface.dialogs.MessageDialog;
021: import org.eclipse.jface.resource.ImageDescriptor;
022: import org.eclipse.jface.window.Window;
023: import org.eclipse.osgi.util.NLS;
024: import org.eclipse.swt.SWT;
025: import org.eclipse.swt.custom.StyledText;
026: import org.eclipse.swt.events.DisposeEvent;
027: import org.eclipse.swt.events.DisposeListener;
028: import org.eclipse.swt.events.SelectionAdapter;
029: import org.eclipse.swt.events.SelectionEvent;
030: import org.eclipse.swt.graphics.Cursor;
031: import org.eclipse.swt.graphics.Font;
032: import org.eclipse.swt.graphics.Image;
033: import org.eclipse.swt.layout.GridData;
034: import org.eclipse.swt.layout.GridLayout;
035: import org.eclipse.swt.widgets.Button;
036: import org.eclipse.swt.widgets.Composite;
037: import org.eclipse.swt.widgets.Control;
038: import org.eclipse.swt.widgets.Label;
039: import org.eclipse.swt.widgets.Shell;
040: import org.eclipse.swt.widgets.Table;
041: import org.eclipse.swt.widgets.TableColumn;
042: import org.eclipse.swt.widgets.TableItem;
043: import org.eclipse.ui.PlatformUI;
044: import org.eclipse.ui.internal.IWorkbenchHelpContextIds;
045: import org.eclipse.ui.internal.WorkbenchMessages;
046: import org.eclipse.ui.internal.about.AboutBundleGroupData;
047: import org.eclipse.ui.internal.about.AboutData;
048: import org.osgi.framework.Bundle;
049:
050: /**
051: * Displays information about the product plugins.
052: *
053: * PRIVATE
054: * This class is internal to the workbench and must not be called outside
055: * the workbench.
056: */
057: public class AboutFeaturesDialog extends ProductInfoDialog {
058:
059: /**
060: * Table height in dialog units (value 150).
061: */
062: private static final int TABLE_HEIGHT = 150;
063:
064: private static final int INFO_HEIGHT = 100;
065:
066: private final static int MORE_ID = IDialogConstants.CLIENT_ID + 1;
067:
068: private final static int PLUGINS_ID = IDialogConstants.CLIENT_ID + 2;
069:
070: private Table table;
071:
072: private Label imageLabel;
073:
074: private StyledText text;
075:
076: private Composite infoArea;
077:
078: private Map cachedImages = new HashMap();
079:
080: private String columnTitles[] = {
081: WorkbenchMessages.AboutFeaturesDialog_provider,
082: WorkbenchMessages.AboutFeaturesDialog_featureName,
083: WorkbenchMessages.AboutFeaturesDialog_version,
084: WorkbenchMessages.AboutFeaturesDialog_featureId, };
085:
086: private String productName;
087:
088: private AboutBundleGroupData[] bundleGroupInfos;
089:
090: private int lastColumnChosen = 0; // initially sort by provider
091:
092: private boolean reverseSort = false; // initially sort ascending
093:
094: private AboutBundleGroupData lastSelection = null;
095:
096: private Button moreButton;
097:
098: private Button pluginsButton;
099:
100: private static Map featuresMap;
101:
102: /**
103: * Constructor for AboutFeaturesDialog.
104: *
105: * @param parentShell the parent shell
106: * @param productName the product name
107: * @param bundleGroupInfos the bundle info
108: */
109: public AboutFeaturesDialog(Shell parentShell, String productName,
110: AboutBundleGroupData[] bundleGroupInfos) {
111: super (parentShell);
112: this .productName = productName;
113:
114: // the order of the array may be changed due to sorting, so create a
115: // copy
116: this .bundleGroupInfos = new AboutBundleGroupData[bundleGroupInfos.length];
117: System.arraycopy(bundleGroupInfos, 0, this .bundleGroupInfos, 0,
118: bundleGroupInfos.length);
119:
120: AboutData.sortByProvider(reverseSort, this .bundleGroupInfos);
121: }
122:
123: /**
124: * The More Info button was pressed. Open a browser with the license for the
125: * selected item or an information dialog if there is no license, or the browser
126: * cannot be opened.
127: */
128: private void handleMoreInfoPressed() {
129: TableItem[] items = table.getSelection();
130: if (items.length <= 0) {
131: return;
132: }
133:
134: AboutBundleGroupData info = (AboutBundleGroupData) items[0]
135: .getData();
136: if (info == null || !openBrowser(info.getLicenseUrl())) {
137: MessageDialog
138: .openInformation(
139: getShell(),
140: WorkbenchMessages.AboutFeaturesDialog_noInfoTitle,
141: WorkbenchMessages.AboutFeaturesDialog_noInformation);
142: }
143: }
144:
145: /**
146: * The Plugins button was pressed. Open an about dialog on the plugins for
147: * the selected feature.
148: */
149: private void handlePluginInfoPressed() {
150: TableItem[] items = table.getSelection();
151: if (items.length <= 0) {
152: return;
153: }
154:
155: AboutBundleGroupData info = (AboutBundleGroupData) items[0]
156: .getData();
157: IBundleGroup bundleGroup = info.getBundleGroup();
158: Bundle[] bundles = bundleGroup == null ? new Bundle[0]
159: : bundleGroup.getBundles();
160:
161: AboutPluginsDialog d = new AboutPluginsDialog(
162: getShell(),
163: productName,
164: bundles,
165: WorkbenchMessages.AboutFeaturesDialog_pluginInfoTitle,
166: NLS
167: .bind(
168: WorkbenchMessages.AboutFeaturesDialog_pluginInfoMessage,
169: bundleGroup.getIdentifier()),
170: IWorkbenchHelpContextIds.ABOUT_FEATURES_PLUGINS_DIALOG);
171: d.open();
172: }
173:
174: /*
175: * (non-Javadoc) Method declared on Dialog.
176: */
177: protected void buttonPressed(int buttonId) {
178: switch (buttonId) {
179: case MORE_ID:
180: handleMoreInfoPressed();
181: break;
182: case PLUGINS_ID:
183: handlePluginInfoPressed();
184: break;
185: default:
186: super .buttonPressed(buttonId);
187: break;
188: }
189: }
190:
191: /*
192: * (non-Javadoc) Method declared on Window.
193: */
194: protected void configureShell(Shell newShell) {
195: super .configureShell(newShell);
196: if (productName != null) {
197: newShell.setText(NLS.bind(
198: WorkbenchMessages.AboutFeaturesDialog_shellTitle,
199: productName));
200: }
201:
202: PlatformUI.getWorkbench().getHelpSystem().setHelp(newShell,
203: IWorkbenchHelpContextIds.ABOUT_FEATURES_DIALOG);
204: }
205:
206: /**
207: * Add buttons to the dialog's button bar.
208: *
209: * Subclasses should override.
210: *
211: * @param parent
212: * the button bar composite
213: */
214: protected void createButtonsForButtonBar(Composite parent) {
215: parent.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
216:
217: moreButton = createButton(parent, MORE_ID,
218: WorkbenchMessages.AboutFeaturesDialog_moreInfo, false);
219: pluginsButton = createButton(parent, PLUGINS_ID,
220: WorkbenchMessages.AboutFeaturesDialog_pluginsInfo,
221: false);
222: Label l = new Label(parent, SWT.NONE);
223: l.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
224: GridLayout layout = (GridLayout) parent.getLayout();
225: layout.numColumns++;
226: layout.makeColumnsEqualWidth = false;
227:
228: Button b = createButton(parent, IDialogConstants.OK_ID,
229: IDialogConstants.OK_LABEL, true);
230: b.setFocus();
231:
232: TableItem[] items = table.getSelection();
233: if (items.length > 0) {
234: updateButtons((AboutBundleGroupData) items[0].getData());
235: }
236: }
237:
238: /**
239: * Create the contents of the dialog (above the button bar).
240: *
241: * Subclasses should overide.
242: *
243: * @param parent the parent composite to contain the dialog area
244: * @return the dialog area control
245: */
246: protected Control createDialogArea(Composite parent) {
247: setHandCursor(new Cursor(parent.getDisplay(), SWT.CURSOR_HAND));
248: setBusyCursor(new Cursor(parent.getDisplay(), SWT.CURSOR_WAIT));
249: getShell().addDisposeListener(new DisposeListener() {
250: public void widgetDisposed(DisposeEvent e) {
251: if (getHandCursor() != null) {
252: getHandCursor().dispose();
253: }
254: if (getBusyCursor() != null) {
255: getBusyCursor().dispose();
256: }
257: }
258: });
259:
260: Composite outer = (Composite) super .createDialogArea(parent);
261:
262: createTable(outer);
263: createInfoArea(outer);
264:
265: return outer;
266: }
267:
268: /**
269: * Create the info area containing the image and text
270: */
271: protected void createInfoArea(Composite parent) {
272: Font font = parent.getFont();
273:
274: infoArea = new Composite(parent, SWT.NULL);
275: GridData data = new GridData(GridData.FILL, GridData.FILL,
276: true, true);
277: // need to provide space for arbitrary feature infos, not just the
278: // one selected by default
279: data.heightHint = convertVerticalDLUsToPixels(INFO_HEIGHT);
280: infoArea.setLayoutData(data);
281:
282: GridLayout layout = new GridLayout();
283: layout.numColumns = 2;
284: infoArea.setLayout(layout);
285:
286: imageLabel = new Label(infoArea, SWT.NONE);
287: data = new GridData(GridData.FILL, GridData.BEGINNING, false,
288: false);
289: data.widthHint = 32;
290: data.heightHint = 32;
291: imageLabel.setLayoutData(data);
292: imageLabel.setFont(font);
293:
294: // text on the right
295: text = new StyledText(infoArea, SWT.MULTI | SWT.READ_ONLY);
296: text.setCaret(null);
297: text.setFont(parent.getFont());
298: data = new GridData(GridData.FILL, GridData.FILL, true, true);
299: text.setLayoutData(data);
300: text.setFont(font);
301: text.setCursor(null);
302: text.setBackground(infoArea.getBackground());
303: addListeners(text);
304:
305: TableItem[] items = table.getSelection();
306: if (items.length > 0) {
307: updateInfoArea((AboutBundleGroupData) items[0].getData());
308: }
309: }
310:
311: /**
312: * Create the table part of the dialog.
313: *
314: * @param parent the parent composite to contain the dialog area
315: */
316: protected void createTable(Composite parent) {
317: table = new Table(parent, SWT.H_SCROLL | SWT.V_SCROLL
318: | SWT.SINGLE | SWT.FULL_SELECTION | SWT.BORDER);
319:
320: GridData gridData = new GridData(GridData.FILL, GridData.FILL,
321: true, true);
322: gridData.heightHint = convertVerticalDLUsToPixels(TABLE_HEIGHT);
323: table.setLayoutData(gridData);
324: table.setHeaderVisible(true);
325:
326: table.setLinesVisible(true);
327: table.setFont(parent.getFont());
328: table.addSelectionListener(new SelectionAdapter() {
329: public void widgetSelected(SelectionEvent e) {
330: AboutBundleGroupData info = (AboutBundleGroupData) e.item
331: .getData();
332: updateInfoArea(info);
333: updateButtons(info);
334: }
335: });
336:
337: int[] columnWidths = { convertHorizontalDLUsToPixels(120),
338: convertHorizontalDLUsToPixels(120),
339: convertHorizontalDLUsToPixels(70),
340: convertHorizontalDLUsToPixels(130) };
341:
342: for (int i = 0; i < columnTitles.length; i++) {
343: TableColumn tableColumn = new TableColumn(table, SWT.NULL);
344: tableColumn.setWidth(columnWidths[i]);
345: tableColumn.setText(columnTitles[i]);
346: final int columnIndex = i;
347: tableColumn.addSelectionListener(new SelectionAdapter() {
348: public void widgetSelected(SelectionEvent e) {
349: sort(columnIndex);
350: }
351: });
352: }
353:
354: // create a table row for each bundle group
355: String selId = lastSelection == null ? null : lastSelection
356: .getId();
357: int sel = 0;
358: for (int i = 0; i < bundleGroupInfos.length; i++) {
359: if (bundleGroupInfos[i].getId().equals(selId)) {
360: sel = i;
361: }
362:
363: TableItem item = new TableItem(table, SWT.NULL);
364: item.setText(createRow(bundleGroupInfos[i]));
365: item.setData(bundleGroupInfos[i]);
366: }
367:
368: // if an item was specified during construction, it should be
369: // selected when the table is created
370: if (bundleGroupInfos.length > 0) {
371: table.setSelection(sel);
372: table.showSelection();
373: }
374: }
375:
376: /**
377: * @see Window#close()
378: */
379: public boolean close() {
380: boolean ret = super .close();
381:
382: Iterator iter = cachedImages.values().iterator();
383: while (iter.hasNext()) {
384: Image image = (Image) iter.next();
385: image.dispose();
386: }
387:
388: return ret;
389: }
390:
391: /**
392: * Update the button enablement
393: */
394: private void updateButtons(AboutBundleGroupData info) {
395: if (info == null) {
396: moreButton.setEnabled(false);
397: pluginsButton.setEnabled(false);
398: return;
399: }
400:
401: // Creating the feature map is too much just to determine enablement, so if
402: // it doesn't already exist, just enable the buttons. If this was the wrong
403: // choice, then when the button is actually pressed an dialog will be opened.
404: if (featuresMap == null) {
405: moreButton.setEnabled(true);
406: pluginsButton.setEnabled(true);
407: return;
408: }
409:
410: moreButton.setEnabled(info.getLicenseUrl() != null);
411: pluginsButton.setEnabled(true);
412: }
413:
414: /**
415: * Update the info area
416: */
417: private void updateInfoArea(AboutBundleGroupData info) {
418: if (info == null) {
419: imageLabel.setImage(null);
420: text.setText(""); //$NON-NLS-1$
421: return;
422: }
423:
424: ImageDescriptor desc = info.getFeatureImage();
425: Image image = (Image) cachedImages.get(desc);
426: if (image == null && desc != null) {
427: image = desc.createImage();
428: cachedImages.put(desc, image);
429: }
430: imageLabel.setImage(image);
431:
432: String aboutText = info.getAboutText();
433: setItem(null);
434: if (aboutText != null) {
435: setItem(scan(aboutText));
436: }
437:
438: if (getItem() == null) {
439: text
440: .setText(WorkbenchMessages.AboutFeaturesDialog_noInformation);
441: } else {
442: text.setText(getItem().getText());
443: text.setCursor(null);
444: setLinkRanges(text, getItem().getLinkRanges());
445: }
446: }
447:
448: /**
449: * Select the initial selection
450: *
451: * @param info the info
452: */
453: public void setInitialSelection(AboutBundleGroupData info) {
454: lastSelection = info;
455: }
456:
457: /**
458: * Sort the rows of the table based on the selected column.
459: *
460: * @param column
461: * index of table column selected as sort criteria
462: */
463: private void sort(int column) {
464: if (lastColumnChosen == column) {
465: reverseSort = !reverseSort;
466: } else {
467: reverseSort = false;
468: lastColumnChosen = column;
469: }
470:
471: if (table.getItemCount() <= 1) {
472: return;
473: }
474:
475: // Remember the last selection
476: int sel = table.getSelectionIndex();
477: if (sel != -1) {
478: lastSelection = bundleGroupInfos[sel];
479: }
480:
481: switch (column) {
482: case 0:
483: AboutData.sortByProvider(reverseSort, bundleGroupInfos);
484: break;
485: case 1:
486: AboutData.sortByName(reverseSort, bundleGroupInfos);
487: break;
488: case 2:
489: AboutData.sortByVersion(reverseSort, bundleGroupInfos);
490: break;
491: case 3:
492: AboutData.sortById(reverseSort, bundleGroupInfos);
493: break;
494: }
495:
496: refreshTable();
497: }
498:
499: /**
500: * Refresh the rows of the table based on the selected column. Maintain
501: * selection from before sort action request.
502: */
503: private void refreshTable() {
504: TableItem[] items = table.getItems();
505:
506: // create new order of table items
507: for (int i = 0; i < items.length; i++) {
508: items[i].setText(createRow(bundleGroupInfos[i]));
509: items[i].setData(bundleGroupInfos[i]);
510: }
511:
512: // Maintain the original selection
513: int sel = -1;
514: if (lastSelection != null) {
515: String oldId = lastSelection.getId();
516: for (int k = 0; k < bundleGroupInfos.length; k++) {
517: if (oldId.equalsIgnoreCase(bundleGroupInfos[k].getId())) {
518: sel = k;
519: }
520: }
521:
522: table.setSelection(sel);
523: table.showSelection();
524: }
525:
526: updateInfoArea(lastSelection);
527: }
528:
529: /**
530: * Return an array of strings containing the argument's information in the
531: * proper order for this table's columns.
532: *
533: * @param info
534: * the source information for the new row, must not be null
535: */
536: private static String[] createRow(AboutBundleGroupData info) {
537: return new String[] { info.getProviderName(), info.getName(),
538: info.getVersion(), info.getId() };
539: }
540: }
|