001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 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.io.IOException;
015: import java.net.URL;
016: import java.util.HashMap;
017: import java.util.Map;
018:
019: import org.eclipse.core.runtime.IPath;
020: import org.eclipse.core.runtime.IProgressMonitor;
021: import org.eclipse.core.runtime.IStatus;
022: import org.eclipse.core.runtime.Path;
023: import org.eclipse.core.runtime.Platform;
024: import org.eclipse.core.runtime.Status;
025: import org.eclipse.core.runtime.jobs.Job;
026: import org.eclipse.jface.dialogs.DialogTray;
027: import org.eclipse.core.runtime.jobs.ISchedulingRule;
028: import org.eclipse.jface.dialogs.IDialogConstants;
029: import org.eclipse.jface.viewers.ArrayContentProvider;
030: import org.eclipse.jface.viewers.IBaseLabelProvider;
031: import org.eclipse.jface.viewers.ISelectionChangedListener;
032: import org.eclipse.jface.viewers.IStructuredSelection;
033: import org.eclipse.jface.viewers.ITableLabelProvider;
034: import org.eclipse.jface.viewers.LabelProvider;
035: import org.eclipse.jface.viewers.LabelProviderChangedEvent;
036: import org.eclipse.jface.viewers.SelectionChangedEvent;
037: import org.eclipse.jface.viewers.TableViewer;
038: import org.eclipse.jface.viewers.Viewer;
039: import org.eclipse.jface.viewers.ViewerComparator;
040: import org.eclipse.osgi.util.NLS;
041: import org.eclipse.swt.SWT;
042: import org.eclipse.swt.events.SelectionAdapter;
043: import org.eclipse.swt.events.SelectionEvent;
044: import org.eclipse.swt.graphics.Image;
045: import org.eclipse.swt.layout.GridData;
046: import org.eclipse.swt.layout.GridLayout;
047: import org.eclipse.swt.widgets.Button;
048: import org.eclipse.swt.widgets.Composite;
049: import org.eclipse.swt.widgets.Control;
050: import org.eclipse.swt.widgets.Label;
051: import org.eclipse.swt.widgets.Shell;
052: import org.eclipse.swt.widgets.TableColumn;
053: import org.eclipse.ui.PlatformUI;
054: import org.eclipse.ui.internal.IWorkbenchGraphicConstants;
055: import org.eclipse.ui.internal.IWorkbenchHelpContextIds;
056: import org.eclipse.ui.internal.WorkbenchImages;
057: import org.eclipse.ui.internal.WorkbenchMessages;
058: import org.eclipse.ui.internal.WorkbenchPlugin;
059: import org.eclipse.ui.internal.about.AboutBundleData;
060: import org.eclipse.ui.internal.misc.StatusUtil;
061: import org.eclipse.ui.internal.util.BundleUtility;
062: import org.eclipse.ui.statushandlers.StatusManager;
063: import org.osgi.framework.Bundle;
064:
065: /**
066: * Displays information about the product plugins.
067: *
068: * PRIVATE
069: * this class is internal to the ide
070: */
071: public class AboutPluginsDialog extends ProductInfoDialog {
072:
073: public class BundleTableLabelProvider extends LabelProvider
074: implements ITableLabelProvider {
075:
076: /**
077: * Scheduling rule for resolve jobs.
078: */
079: private ISchedulingRule resolveRule = new ISchedulingRule() {
080:
081: public boolean contains(ISchedulingRule rule) {
082: return rule == this ;
083: }
084:
085: public boolean isConflicting(ISchedulingRule rule) {
086: return rule == this ;
087: }
088: };
089:
090: /* (non-Javadoc)
091: * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(java.lang.Object, int)
092: */
093: public Image getColumnImage(Object element, int columnIndex) {
094: if (columnIndex == 0) {
095: if (element instanceof AboutBundleData) {
096: final AboutBundleData data = (AboutBundleData) element;
097: if (data.isSignedDetermined()) {
098: return WorkbenchImages
099: .getImage(data.isSigned() ? IWorkbenchGraphicConstants.IMG_OBJ_SIGNED_YES
100: : IWorkbenchGraphicConstants.IMG_OBJ_SIGNED_NO);
101: }
102: Job resolveJob = new Job(data.getId()) {
103:
104: protected IStatus run(IProgressMonitor monitor) {
105:
106: data.isSigned();
107: Shell dialogShell = getShell();
108: if (dialogShell == null
109: || dialogShell.isDisposed())
110: return Status.OK_STATUS;
111:
112: dialogShell.getDisplay().asyncExec(
113: new Runnable() {
114:
115: public void run() {
116: fireLabelProviderChanged(new LabelProviderChangedEvent(
117: BundleTableLabelProvider.this ,
118: data));
119: }
120: });
121:
122: return Status.OK_STATUS;
123: }
124: };
125: resolveJob.setRule(resolveRule);
126: resolveJob.setSystem(true);
127: resolveJob.schedule();
128: return WorkbenchImages
129: .getImage(IWorkbenchGraphicConstants.IMG_OBJ_SIGNED_UNKNOWN);
130: }
131: }
132: return null;
133: }
134:
135: /* (non-Javadoc)
136: * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(java.lang.Object, int)
137: */
138: public String getColumnText(Object element, int columnIndex) {
139: if (element instanceof AboutBundleData) {
140: AboutBundleData data = (AboutBundleData) element;
141: switch (columnIndex) {
142: case 1:
143: return data.getProviderName();
144: case 2:
145: return data.getName();
146: case 3:
147: return data.getVersion();
148: case 4:
149: return data.getId();
150: }
151: }
152: return ""; //$NON-NLS-1$
153: }
154: }
155:
156: /**
157: * Table height in dialog units (value 200).
158: */
159: private static final int TABLE_HEIGHT = 200;
160:
161: private static final IPath baseNLPath = new Path("$nl$"); //$NON-NLS-1$
162:
163: private static final String PLUGININFO = "about.html"; //$NON-NLS-1$
164:
165: private final static int MORE_ID = IDialogConstants.CLIENT_ID + 1;
166: private final static int SIGNING_ID = MORE_ID + 1;
167:
168: private TableViewer vendorInfo;
169:
170: private Button moreInfo, signingInfo;
171:
172: private String title;
173:
174: private String message;
175:
176: private String helpContextId;
177:
178: private String columnTitles[] = {
179: WorkbenchMessages.AboutPluginsDialog_signed,
180: WorkbenchMessages.AboutPluginsDialog_provider,
181: WorkbenchMessages.AboutPluginsDialog_pluginName,
182: WorkbenchMessages.AboutPluginsDialog_version,
183: WorkbenchMessages.AboutPluginsDialog_pluginId,
184:
185: };
186:
187: private String productName;
188:
189: private AboutBundleData[] bundleInfos;
190:
191: /**
192: * Constructor for AboutPluginsDialog.
193: *
194: * @param parentShell the parent shell
195: * @param productName the product name
196: */
197: public AboutPluginsDialog(Shell parentShell, String productName) {
198: this (parentShell, productName, WorkbenchPlugin.getDefault()
199: .getBundles(), null, null,
200: IWorkbenchHelpContextIds.ABOUT_PLUGINS_DIALOG);
201: WorkbenchPlugin.class.getSigners();
202: }
203:
204: /**
205: * Constructor for AboutPluginsDialog.
206: *
207: * @param parentShell
208: * the parent shell
209: * @param productName
210: * must not be null
211: * @param bundles
212: * must not be null
213: * @param title
214: * the title
215: * @param message
216: * the message
217: * @param helpContextId
218: * the help context id
219: */
220: public AboutPluginsDialog(Shell parentShell, String productName,
221: Bundle[] bundles, String title, String message,
222: String helpContextId) {
223: super (parentShell);
224: this .title = title;
225: this .message = message;
226: this .helpContextId = helpContextId;
227: this .productName = productName;
228:
229: // create a data object for each bundle, remove duplicates, and include only resolved bundles (bug 65548)
230: Map map = new HashMap();
231: for (int i = 0; i < bundles.length; ++i) {
232: AboutBundleData data = new AboutBundleData(bundles[i]);
233: if (BundleUtility.isReady(data.getState())
234: && !map.containsKey(data.getVersionedId())) {
235: map.put(data.getVersionedId(), data);
236: }
237: }
238: bundleInfos = (AboutBundleData[]) map.values().toArray(
239: new AboutBundleData[0]);
240: }
241:
242: /*
243: * (non-Javadoc) Method declared on Dialog.
244: */
245: protected void buttonPressed(int buttonId) {
246: switch (buttonId) {
247: case MORE_ID:
248: handleMoreInfoPressed();
249: break;
250: case SIGNING_ID:
251: handleSigningInfoPressed();
252: break;
253: default:
254: super .buttonPressed(buttonId);
255: break;
256: }
257: }
258:
259: /**
260: */
261: private void handleSigningInfoPressed() {
262: DialogTray existingTray = getTray();
263: if (existingTray instanceof BundleSigningTray) {
264: // hide
265: getButton(SIGNING_ID)
266: .setText(
267: WorkbenchMessages.AboutPluginsDialog_signingInfo_show);
268: closeTray();
269: } else {
270: //show
271: getButton(SIGNING_ID)
272: .setText(
273: WorkbenchMessages.AboutPluginsDialog_signingInfo_hide);
274: if (existingTray != null)
275: closeTray();
276: AboutBundleData bundleInfo = (AboutBundleData) ((IStructuredSelection) vendorInfo
277: .getSelection()).getFirstElement();
278: BundleSigningTray tray = new BundleSigningTray(this );
279: tray.setData(bundleInfo);
280: openTray(tray);
281: }
282:
283: }
284:
285: /*
286: * (non-Javadoc) Method declared on Window.
287: */
288: protected void configureShell(Shell newShell) {
289: super .configureShell(newShell);
290:
291: //signImage = new Image( this.getParentShell().getDisplay(), AboutPluginsDialog.class.getResourceAsStream("Signed.gif")); //$NON-NLS-1$
292:
293: if (title == null && productName != null) {
294: title = NLS.bind(
295: WorkbenchMessages.AboutPluginsDialog_shellTitle,
296: productName);
297: }
298:
299: if (title != null) {
300: newShell.setText(title);
301: }
302:
303: PlatformUI.getWorkbench().getHelpSystem().setHelp(newShell,
304: helpContextId);
305: }
306:
307: /**
308: * Add buttons to the dialog's button bar.
309: *
310: * Subclasses should override.
311: *
312: * @param parent
313: * the button bar composite
314: */
315: protected void createButtonsForButtonBar(Composite parent) {
316: parent.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
317:
318: moreInfo = createButton(parent, MORE_ID,
319: WorkbenchMessages.AboutPluginsDialog_moreInfo, false);
320: moreInfo.setEnabled(false);
321:
322: signingInfo = createButton(parent, SIGNING_ID,
323: WorkbenchMessages.AboutPluginsDialog_signingInfo_show,
324: false);
325: signingInfo.setEnabled(false);
326:
327: Label l = new Label(parent, SWT.NONE);
328: l.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
329: GridLayout layout = (GridLayout) parent.getLayout();
330: layout.numColumns++;
331: layout.makeColumnsEqualWidth = false;
332:
333: createButton(parent, IDialogConstants.OK_ID,
334: IDialogConstants.OK_LABEL, true);
335: }
336:
337: /**
338: * Create the contents of the dialog (above the button bar).
339: *
340: * Subclasses should overide.
341: *
342: * @param parent
343: * the parent composite to contain the dialog area
344: * @return the dialog area control
345: */
346: protected Control createDialogArea(Composite parent) {
347: Composite outer = (Composite) super .createDialogArea(parent);
348:
349: if (message != null) {
350: Label label = new Label(outer, SWT.NONE);
351: label.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
352: label.setFont(parent.getFont());
353: label.setText(message);
354: }
355:
356: createTable(outer);
357:
358: return outer;
359: }
360:
361: /**
362: * Create the table part of the dialog.
363: *
364: * @param parent
365: * the parent composite to contain the dialog area
366: */
367: protected void createTable(Composite parent) {
368: vendorInfo = new TableViewer(parent, SWT.H_SCROLL
369: | SWT.V_SCROLL | SWT.SINGLE | SWT.FULL_SELECTION
370: | SWT.BORDER);
371: vendorInfo.setUseHashlookup(true);
372: vendorInfo.getTable().setHeaderVisible(true);
373: vendorInfo.getTable().setLinesVisible(true);
374: vendorInfo.getTable().setFont(parent.getFont());
375: vendorInfo
376: .addSelectionChangedListener(new ISelectionChangedListener() {
377:
378: public void selectionChanged(
379: SelectionChangedEvent event) {
380: // enable if there is an item selected and that
381: // item has additional info
382: IStructuredSelection selection = (IStructuredSelection) event
383: .getSelection();
384: if (selection.getFirstElement() instanceof AboutBundleData) {
385: AboutBundleData selected = (AboutBundleData) selection
386: .getFirstElement();
387: moreInfo
388: .setEnabled(selectionHasInfo(selected));
389: signingInfo.setEnabled(true);
390: if (getTray() instanceof BundleSigningTray) {
391: ((BundleSigningTray) getTray())
392: .setData(selected);
393: }
394: } else {
395: moreInfo.setEnabled(false);
396: signingInfo.setEnabled(false);
397: }
398: }
399: });
400:
401: final TableComparator comparator = new TableComparator();
402: comparator.setSortColumn(1); // sort on name initially
403:
404: int[] columnWidths = {
405: convertHorizontalDLUsToPixels(30), //signature
406: convertHorizontalDLUsToPixels(120),
407: convertHorizontalDLUsToPixels(120),
408: convertHorizontalDLUsToPixels(70),
409: convertHorizontalDLUsToPixels(130), };
410:
411: // create table headers
412: for (int i = 0; i < columnTitles.length; i++) {
413: TableColumn column = new TableColumn(vendorInfo.getTable(),
414: SWT.NULL);
415: column.setWidth(columnWidths[i]);
416: column.setText(columnTitles[i]);
417: final int columnIndex = i;
418: column.addSelectionListener(new SelectionAdapter() {
419: public void widgetSelected(SelectionEvent e) {
420: if (columnIndex == comparator.getSortColumn()) {
421: comparator.setAscending(!comparator
422: .isAscending());
423: }
424: comparator.setSortColumn(columnIndex);
425: vendorInfo.refresh();
426: }
427: });
428: }
429:
430: vendorInfo.setComparator(comparator);
431: vendorInfo.setContentProvider(new ArrayContentProvider());
432: vendorInfo.setLabelProvider(new BundleTableLabelProvider());
433:
434: GridData gridData = new GridData(GridData.FILL, GridData.FILL,
435: true, true);
436: gridData.heightHint = convertVerticalDLUsToPixels(TABLE_HEIGHT);
437: vendorInfo.getTable().setLayoutData(gridData);
438:
439: vendorInfo.setInput(bundleInfos);
440: }
441:
442: /**
443: * Check if the currently selected plugin has additional information to
444: * show.
445: * @param bundleInfo
446: *
447: * @return true if the selected plugin has additional info available to
448: * display
449: */
450: private boolean selectionHasInfo(AboutBundleData bundleInfo) {
451:
452: URL infoURL = getMoreInfoURL(bundleInfo, false);
453:
454: // only report ini problems if the -debug command line argument is used
455: if (infoURL == null && WorkbenchPlugin.DEBUG) {
456: WorkbenchPlugin.log("Problem reading plugin info for: " //$NON-NLS-1$
457: + bundleInfo.getName());
458: }
459:
460: return infoURL != null;
461: }
462:
463: /**
464: * The More Info button was pressed. Open a browser showing the license information
465: * for the selected bundle or an error dialog if the browser cannot be opened.
466: */
467: protected void handleMoreInfoPressed() {
468: if (vendorInfo == null) {
469: return;
470: }
471:
472: if (vendorInfo.getSelection().isEmpty())
473: return;
474:
475: AboutBundleData bundleInfo = (AboutBundleData) ((IStructuredSelection) vendorInfo
476: .getSelection()).getFirstElement();
477:
478: if (!openBrowser(getMoreInfoURL(bundleInfo, true))) {
479: String message = NLS
480: .bind(
481: WorkbenchMessages.AboutPluginsDialog_unableToOpenFile,
482: PLUGININFO, bundleInfo.getId());
483: StatusUtil
484: .handleStatus(
485: WorkbenchMessages.AboutPluginsDialog_errorTitle
486: + ": " + message, StatusManager.SHOW, getShell()); //$NON-NLS-1$
487: }
488: }
489:
490: /**
491: * Return an url to the plugin's about.html file (what is shown when "More info" is
492: * pressed) or null if no such file exists. The method does nl lookup to allow for
493: * i18n.
494: *
495: * @param bundleInfo the bundle info
496: * @param makeLocal whether to make the about content local
497: * @return the url or <code>null</code>
498: */
499: private URL getMoreInfoURL(AboutBundleData bundleInfo,
500: boolean makeLocal) {
501: Bundle bundle = Platform.getBundle(bundleInfo.getId());
502: if (bundle == null) {
503: return null;
504: }
505:
506: URL aboutUrl = Platform.find(bundle, baseNLPath
507: .append(PLUGININFO), null);
508: if (!makeLocal) {
509: return aboutUrl;
510: }
511: if (aboutUrl != null) {
512: try {
513: URL result = Platform.asLocalURL(aboutUrl);
514: try {
515: // Make local all content in the "about" directory.
516: // This is needed to handle jar'ed plug-ins.
517: // See Bug 88240 [About] About dialog needs to extract subdirs.
518: URL about = new URL(aboutUrl, "about_files"); //$NON-NLS-1$
519: if (about != null) {
520: Platform.asLocalURL(about);
521: }
522: } catch (IOException e) {
523: // skip the about dir if its not found or there are other problems.
524: }
525: return result;
526: } catch (IOException e) {
527: // do nothing
528: }
529: }
530: return null;
531: }
532: }
533:
534: class TableComparator extends ViewerComparator {
535:
536: private int sortColumn = 0;
537: private boolean ascending = true;
538:
539: /* (non-Javadoc)
540: * @see org.eclipse.jface.viewers.ViewerComparator#compare(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
541: */
542: public int compare(Viewer viewer, Object e1, Object e2) {
543: if (viewer instanceof TableViewer) {
544: TableViewer tableViewer = (TableViewer) viewer;
545: IBaseLabelProvider baseLabel = tableViewer
546: .getLabelProvider();
547: if (baseLabel instanceof ITableLabelProvider) {
548: ITableLabelProvider tableProvider = (ITableLabelProvider) baseLabel;
549: String e1p = tableProvider
550: .getColumnText(e1, sortColumn);
551: String e2p = tableProvider
552: .getColumnText(e2, sortColumn);
553: int result = getComparator().compare(e1p, e2p);
554: return ascending ? result : (-1) * result;
555: }
556: }
557:
558: return super .compare(viewer, e1, e2);
559: }
560:
561: /**
562: * @return Returns the sortColumn.
563: */
564: public int getSortColumn() {
565: return sortColumn;
566: }
567:
568: /**
569: * @param sortColumn The sortColumn to set.
570: */
571: public void setSortColumn(int sortColumn) {
572: this .sortColumn = sortColumn;
573: }
574:
575: /**
576: * @return Returns the ascending.
577: */
578: public boolean isAscending() {
579: return ascending;
580: }
581:
582: /**
583: * @param ascending The ascending to set.
584: */
585: public void setAscending(boolean ascending) {
586: this.ascending = ascending;
587: }
588: }
|