blob: bdbad28c749eba288c1de156821e89f6f5bdf458 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008 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.statushandlers;
import java.net.URL;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.action.ContributionItem;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.DialogTray;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.ErrorSupportProvider;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.jface.dialogs.TrayDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.util.Policy;
import org.eclipse.jface.viewers.IContentProvider;
import org.eclipse.jface.viewers.ILabelProviderListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.PaletteData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.internal.WorkbenchMessages;
import org.eclipse.ui.internal.WorkbenchPlugin;
import org.eclipse.ui.internal.progress.ProgressManager;
import org.eclipse.ui.internal.progress.ProgressManagerUtil;
import org.eclipse.ui.internal.progress.ProgressMessages;
import org.eclipse.ui.internal.statushandlers.DefaultDetailsArea;
import org.eclipse.ui.internal.statushandlers.StackTraceSupportArea;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.eclipse.ui.progress.IProgressConstants;
import com.ibm.icu.text.DateFormat;
/**
* <p>
* A dialog to display one or more messages (errors, warnings or infos) to the
* user. The dialog supplies the Details button that opens/closes the details
* area. The default detail area displays a tree of statuses related to the
* selected item on the messages list. The dialog supplies the Support button
* that opens/closes the support area. The Support button is disabled and
* invisible by default. To enable it
* {@link WorkbenchStatusDialog#enableDefaultSupportArea(boolean)} should be
* used.
* </p>
*
* <p>
* The default details area can be replaced using
* {@link WorkbenchStatusDialog#setDetailsAreaProvider(AbstractStatusAreaProvider)}
* </p>
*
* <p>
* The default support area can be replaced using
* {@link WorkbenchStatusDialog#setSupportAreaProvider(AbstractStatusAreaProvider)}
* or {@link Policy#setErrorSupportProvider(ErrorSupportProvider)}.
* </p>
*
* <p>
* The dialog can switch from non-modal to modal mode. See
* {@link #addError(StatusAdapter, boolean)}
* </p>
*
* <p>
* IMPORTANT: This class is <em>not</em> intended to be subclassed.
* </p>
*
* <p>
* <strong>EXPERIMENTAL</strong>. This class or interface has been added as
* part of a work in progress. There is no guarantee that this API will work or
* that it will remain the same. Please do not use this API without consulting
* with the eclipseUI team.
* </p>
*
* @see Policy
* @see ErrorSupportProvider
* @see AbstractStatusAreaProvider
* @since 3.4
*/
public class WorkbenchStatusDialog extends TrayDialog {
/**
* Preference used to indicate whether the user should be prompted to
* confirm the execution of the job's goto action
*/
private static final String PREF_SKIP_GOTO_ACTION_PROMPT = "pref_skip_goto_action_prompt"; //$NON-NLS-1$
/**
* The id of the goto action button
*/
private static final int GOTO_ACTION_ID = IDialogConstants.CLIENT_ID + 1;
/**
* Stores statuses
*/
private Collection errors = Collections.synchronizedSet(new HashSet());
/**
* Stores information, which statuses should be displayed in modal window
*/
private HashMap modals = new HashMap();
/**
* This field stores the real dialog that appears to the user.
*/
private InternalDialog dialog;
/**
* On this composite are placed all non-header dialog components.
*/
private Composite workArea;
/**
* This composite is responsible for displaying statuses to the user. It
* switches between two different representations: for single status only
* simple message is presented, for many statuses the list is presented
*/
private Composite statusArea;
/**
* This is necessary because we need to switch between different
* representations when new status arrives
*/
private StackLayout statusAreaStackLayout;
/**
* On this composite are presented elements for displaying single status
*/
private Composite singleStatusDisplayArea;
/**
* This label is used to represent single status
*/
private Label label;
/**
* On this composite is presented a list, from which the user can select the
* status that should be displayed in the support area and details area
*/
private Composite multipleStatusDisplayArea;
/**
* A list from which the user selects statuses.
*/
private TableViewer statusListViewer;
/**
* A list label provider
*/
private ITableLabelProvider statusListLabelProvider = new ITableLabelProvider() {
Map imageTable = new HashMap();
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.IBaseLabelProvider#addListener(org.eclipse.jface.viewers.ILabelProviderListener)
*/
public void addListener(ILabelProviderListener listener) {
// Do nothing
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.IBaseLabelProvider#dispose()
*/
public void dispose() {
if (!imageTable.isEmpty()) {
for (Iterator iter = imageTable.values().iterator(); iter
.hasNext();) {
Image image = (Image) iter.next();
image.dispose();
}
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(java.lang.Object,
* int)
*/
public Image getColumnImage(Object element, int columnIndex) {
if (element != null) {
StatusAdapter statusAdapter = ((StatusAdapter) element);
Job job = (Job) (statusAdapter.getAdapter(Job.class));
if (job != null) {
return getIcon(job);
}
}
return null;
}
/*
* Get the icon for the job. Code copied from NewProgressViewer
*/
private Image getIcon(Job job) {
if (job != null) {
Object property = job
.getProperty(IProgressConstants.ICON_PROPERTY);
// If we already have an image cached, return it
Image im = (Image) imageTable.get(property);
if (im != null) {
return im;
}
// Create an image from the job's icon property or family
Display display = getShell().getDisplay();
if (property instanceof ImageDescriptor) {
im = ((ImageDescriptor) property).createImage(display);
imageTable.put(property, im); // Cache for disposal
} else if (property instanceof URL) {
im = ImageDescriptor.createFromURL((URL) property)
.createImage(display);
imageTable.put(property, im); // Cache for disposal
} else {
im = ProgressManager.getInstance().getIconFor(job);
// No need to cache since the progress manager will
}
return im;
}
return null;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(java.lang.Object,
* int)
*/
public String getColumnText(Object element, int columnIndex) {
StatusAdapter statusAdapter = (StatusAdapter) element;
String text = statusAdapter.getStatus().getMessage();
Job job = (Job) (statusAdapter.getAdapter(Job.class));
if (job != null) {
text = job.getName();
}
// if we display only one message we should not display the same
// information twice
if (text.equals(getTitleMessage(statusAdapter))
&& errors.size() == 1) {
text = WorkbenchMessages.WorkbenchStatusDialog_SeeDetails;
}
Long timestamp = (Long) statusAdapter
.getProperty(IStatusAdapterConstants.TIMESTAMP_PROPERTY);
String date = WorkbenchMessages.WorkbenchStatusDialog_TimestampNotAvailable;
if (timestamp != null) {
date = DateFormat.getDateTimeInstance(DateFormat.LONG,
DateFormat.LONG)
.format(new Date(timestamp.longValue()));
}
return NLS.bind(ProgressMessages.JobInfo_Error, (new Object[] {
text, date }));
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.IBaseLabelProvider#isLabelProperty(java.lang.Object,
* java.lang.String)
*/
public boolean isLabelProperty(Object element, String property) {
return false;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.IBaseLabelProvider#removeListener(org.eclipse.jface.viewers.ILabelProviderListener)
*/
public void removeListener(ILabelProviderListener listener) {
// Do nothing
}
};
/**
* The Details button.
*/
private Button detailsButton;
/**
* This variable holds current details area provider.
*/
private DetailsAreaManager detailsManager = new DetailsAreaManager();
private DisposeListener disposeListener = new StatusDialogDisposeListener();
/**
* The title of the dialog.
*/
private String title;
/**
* The current clipboard. To be disposed when closing the dialog.
*/
private Clipboard clipboard;
/**
* Filter mask for determining which status items to display. Default allows
* for displaying all statuses.
*/
private int displayMask = 0xFFFF;
/**
* Currently selected status adapter.
*/
private StatusAdapter statusAdapter;
/**
* In this support tray status support providers are displayed.
*/
private SupportTray supportTray = new WorkbenchStatusDialog.SupportTray();
/**
* This is the image that is placed on launchTrayButton
*/
private Image launchTrayImage;
/**
* This item is used to launch support tray
*/
private ToolItem launchTrayButton;
/**
* This flag indicates if the dialog is switching modality. For now it is
* possible only to change from non-modal to modal.
*/
private boolean modalitySwitch = false;
/**
* This fields holds the information about dialog position and size when
* switching the modality.
*/
private Rectangle shellBounds;
/**
* This flag indicates if the details area was opened before switching the
* modality or not.
*/
private boolean detailsOpened = false;
/**
* This flag indicates if the support area was opened before switching the
* modality or not.
*/
private boolean trayOpened = false;
/**
* This field contains the support provider set by the user.
*/
private AbstractStatusAreaProvider userSupportProvider;
/* This flag contains information if we are under junit tests */
private boolean testingMode = false;
/**
* Main dialog image holder.
*/
private Label titleImageLabel;
/**
* Title in the header.
*/
private Label titleLabel;
/**
* Message in the header.
*/
private Label messageLabel;
/**
* Header area.
*/
private Composite titleArea;
/**
* Creates workbench status dialog.
*
* This dialog can be configured with custom support area.
*
* @param parentShell
* a parent shell for the dialog
* @param displayMask
* should be logical sum of status severities that should be
* handled
* @param dialogTitle
* A title of the dialog. If null, than default will be used.
*/
public WorkbenchStatusDialog(Shell parentShell, int displayMask,
String dialogTitle) {
super(parentShell);
this.displayMask = displayMask;
this.title = dialogTitle == null ? JFaceResources
.getString("Problem_Occurred") : //$NON-NLS-1$
dialogTitle;
prepareImages(parentShell);
}
/**
* Loads into this class necessary images
*
* @param parentShell
* a parent shell for the dialog
*/
private void prepareImages(Shell parentShell) {
ImageDescriptor imdesc = AbstractUIPlugin.imageDescriptorFromPlugin(
PlatformUI.PLUGIN_ID, "icons/full/dtool16/show_support.gif"); //$NON-NLS-1$
if (imdesc != null)
launchTrayImage = imdesc.createImage();
parentShell.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
if (launchTrayImage != null) {
launchTrayImage.dispose();
}
}
});
}
/**
* This dialog can be configured with custom support area.
*
* @param parentShell
* a parent shell for the dialog
* @param dialogTitle
* A title of the dialog. If null, than default will be used.
*/
public WorkbenchStatusDialog(Shell parentShell, String dialogTitle) {
this(parentShell, IStatus.INFO | IStatus.WARNING | IStatus.ERROR,
dialogTitle);
}
/**
* Add a new status to the list.
*
* @param modal
* indicates if the dialog should be modal(when true) or not
* (when false).
*
* @param statusAdapter
* the error to be displayed
*/
public void addError(final StatusAdapter statusAdapter, final boolean modal) {
if (ErrorDialog.AUTOMATED_MODE == true && !testingMode) {
return;
}
if (!PlatformUI.isWorkbenchRunning()) {
// we are shutting down, so just log
WorkbenchPlugin.log(statusAdapter.getStatus());
return;
}
// Add the error in the UI thread to ensure thread safety in the
// dialog
if (dialog == null || dialog.getShell().isDisposed()) {
errors.add(statusAdapter);
modals.put(statusAdapter, new Boolean(modal));
// Delay prompting if the status adapter property is set
if (shouldPrompt(statusAdapter)) {
executeAsync(new Runnable() {
public void run() {
if (dialog == null) {
dialog = new InternalDialog(getParentShell(),
WorkbenchStatusDialog.this, shouldBeModal());
setSelectedStatusAdapter(statusAdapter);
dialog.open();
dialog.getShell().addDisposeListener(
disposeListener);
}
refresh();
refreshDialogSize();
}
});
}
} else {
if (statusAdapter
.getProperty(IProgressConstants.NO_IMMEDIATE_ERROR_PROMPT_PROPERTY) != null) {
statusAdapter.setProperty(
IProgressConstants.NO_IMMEDIATE_ERROR_PROMPT_PROPERTY,
Boolean.FALSE);
}
executeAsync(new Runnable() {
public void run() {
openStatusDialog(modal, statusAdapter);
}
});
}
}
/**
* Checks if the user should be prompted immediately about
* {@link StatusAdapter}
*
* @param statusAdapter
* to be checked.
* @return true if the statusAdapter should be prompted, false otherwise.
*/
private boolean shouldPrompt(final StatusAdapter statusAdapter) {
Object noPromptProperty = statusAdapter
.getProperty(IProgressConstants.NO_IMMEDIATE_ERROR_PROMPT_PROPERTY);
boolean prompt = true;
if (noPromptProperty instanceof Boolean) {
prompt = !((Boolean) noPromptProperty).booleanValue();
}
return prompt;
}
/**
* Get the currently registered errors in the receiver.
*
* @return Collection of ErrorInfo
*/
Collection getErrors() {
return errors;
}
/**
* Opens status dialog with particular statusAdapter selected.
*
* @param modal
* decides if window is modal or not.
* @param statusAdapter
* status adapter to be selected.
*/
private void openStatusDialog(final boolean modal,
final StatusAdapter statusAdapter) {
errors.add(statusAdapter);
modals.put(statusAdapter, new Boolean(modal));
boolean shouldBeModal = shouldBeModal();
if (shouldBeModal ^ dialog.isModal()) {
dialog.getShell().removeDisposeListener(disposeListener);
modalitySwitch = true;
close();
setSelectedStatusAdapter(statusAdapter);
dialog = new InternalDialog(getParentShell(), this, modal);
open();
dialog.getShell().addDisposeListener(disposeListener);
modalitySwitch = false;
}
refresh();
}
/**
* Decides if dialog should be modal. Dialog will be modal if any of the
* statuses contained by StatusAdapters had been reported with
* {@link StatusManager#BLOCK} flag.
*
* @return true if any StatusHandler should be displayed in modal window
*/
private boolean shouldBeModal() {
for (Iterator it = modals.keySet().iterator(); it.hasNext();) {
Object o = it.next();
Object value = modals.get(o);
if (value instanceof Boolean) {
Boolean b = (Boolean) value;
if (b.booleanValue()) {
return true;
}
}
}
return false;
}
/**
* Method which should be invoked when new errors become available for
* display.
*/
private void refresh() {
// Do not refresh if we are in the process of
// opening or shutting down
if (dialogArea == null || dialogArea.isDisposed()) {
return;
}
updateTitleArea();
updateStatusArea();
updateEnablements();
((Composite) dialogArea).layout();
}
/**
* This methods switches StatusAdapters presentation depending if there is
* one status or more.
*/
protected void updateStatusArea() {
if (errors.size() > 1) {
if (multipleStatusDisplayArea == null
|| multipleStatusDisplayArea.isDisposed()) {
multipleStatusDisplayArea = createMultipleStatusesDisplayArea(statusArea);
getShell().setSize(
getShell().computeSize(SWT.DEFAULT, SWT.DEFAULT));
}
statusAreaStackLayout.topControl = multipleStatusDisplayArea;
refreshMultipleStatusArea();
} else {
statusAreaStackLayout.topControl = singleStatusDisplayArea;
refreshSingleStatusArea();
}
statusArea.layout();
}
/**
* Updated title area. Adjust title, title message and title image according
* to selected {@link StatusAdapter}.
*/
protected void updateTitleArea() {
Image image = getImage();
titleLabel.setText(getAccessibleMessageFor(image));
titleImageLabel.setImage(image);
if (statusAdapter != null) {
messageLabel.setText(getTitleMessage(statusAdapter));
}
titleArea.layout();
}
/**
* Update the button enablements
*/
private void updateEnablements() {
Button details = getButton(IDialogConstants.DETAILS_ID);
if (details != null) {
details.setEnabled(true);
}
Button gotoButton = getButton(GOTO_ACTION_ID);
if (gotoButton != null) {
IAction gotoAction = getGotoAction();
boolean hasValidGotoAction = gotoAction != null;
String text = gotoButton.getText();
String newText = null;
if (hasValidGotoAction) {
newText = gotoAction.getText();
}
if (newText == null) {
hasValidGotoAction = false;
newText = ProgressMessages.JobErrorDialog_CustomJobText;
}
if (!newText.equals(text)) {
gotoButton.setText(newText);
}
gotoButton.setEnabled(hasValidGotoAction);
gotoButton.setVisible(hasValidGotoAction);
}
// and tray enablement button
if (launchTrayButton != null) {
launchTrayButton.setEnabled(supportTray.providesSupport());
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.ErrorDialog#buttonPressed(int)
*/
protected void buttonPressed(int id) {
if (id == GOTO_ACTION_ID) {
IAction gotoAction = getGotoAction();
if (gotoAction != null) {
if (isPromptToClose()) {
okPressed(); // close the dialog
gotoAction.run(); // run the goto action
}
}
}
if (id == IDialogConstants.DETAILS_ID) {
// was the details button pressed?
detailsOpened = toggleDetailsArea();
} else {
super.buttonPressed(id);
}
}
/*
* Prompt to inform the user that the dialog will close and the errors
* cleared.
*/
private boolean isPromptToClose() {
IPreferenceStore store = WorkbenchPlugin.getDefault()
.getPreferenceStore();
if (!store.contains(PREF_SKIP_GOTO_ACTION_PROMPT)
|| !store.getString(PREF_SKIP_GOTO_ACTION_PROMPT).equals(
MessageDialogWithToggle.ALWAYS)) {
MessageDialogWithToggle dialog = MessageDialogWithToggle
.openOkCancelConfirm(
getShell(),
ProgressMessages.JobErrorDialog_CloseDialogTitle,
ProgressMessages.JobErrorDialog_CloseDialogMessage,
ProgressMessages.JobErrorDialog_DoNotShowAgainMessage,
false, store, PREF_SKIP_GOTO_ACTION_PROMPT);
return dialog.getReturnCode() == OK;
}
return true;
}
/**
* Returns {@link IAction} associated with selected StatusAdapter.
*
* @return {@link IAction} that is set as {@link StatusAdapter} property
* with Job.class key.
*/
private IAction getGotoAction() {
Object property = null;
Job job = (Job) (statusAdapter.getAdapter(Job.class));
if (job != null) {
property = job.getProperty(IProgressConstants.ACTION_PROPERTY);
}
if (property instanceof IAction) {
return (IAction) property;
}
return null;
}
private String getTitleMessage(StatusAdapter statusAdapter) {
// if there was nonempty title set, display the title
Object property = statusAdapter
.getProperty(IStatusAdapterConstants.TITLE_PROPERTY);
if (property instanceof String) {
String header = (String) property;
if (header.trim().length() > 0) {
return header;
}
}
// if there was message set in the status
IStatus status = statusAdapter.getStatus();
if (status.getMessage() != null
&& status.getMessage().trim().length() > 0) {
return status.getMessage();
}
// if status has children
if (status.getChildren().length > 0) {
return WorkbenchMessages.WorkbenchStatusDialog_StatusWithChildren;
}
// check the exception
Throwable t = status.getException();
if (t != null) {
if (t.getMessage() != null && t.getMessage().trim().length() > 0) {
return t.getMessage();
}
return t.getClass().getName();
}
return WorkbenchMessages.WorkbenchStatusDialog_NoMessageAvailable;
}
/**
* Create an area which allows the user to view the status if only one is
* created or to select one of reported statuses when there are many.
*
* @param parent -
* A parent composite on which all components should be placed.
* @see WorkbenchStatusDialog#refresh()
*/
protected void createStatusArea(Composite parent) {
statusArea = new Composite(parent, SWT.NONE);
statusArea.setLayoutData(new GridData(GridData.FILL_BOTH
| GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL));
statusAreaStackLayout = new StackLayout();
statusArea.setLayout(statusAreaStackLayout);
singleStatusDisplayArea = createSingleStatusDisplayArea(statusArea);
}
/**
* This method creates display area for {@link StatusAdapter} when only one
* is available.
*
* @param parent
* A parent composite on which all components should be placed.
* @return composite the composite on which are all components for
* displaying status when only one is available.
*/
private Composite createSingleStatusDisplayArea(Composite parent) {
parent = new Composite(parent, SWT.NONE);
GridLayout gridLayout = new GridLayout();
// the position of the text should be the same even if we do not display
// list viewer. In that case we need to add 5 pixels of default margin.
gridLayout.marginWidth += 5;
gridLayout.marginWidth += 5;
parent.setLayout(gridLayout);
GridData gd = new GridData(GridData.FILL_BOTH
| GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);
parent.setLayoutData(gd);
label = new Label(parent, SWT.WRAP);
label.addMouseListener(new MouseListener() {
public void mouseDown(MouseEvent e) {
showDetailsArea();
}
public void mouseUp(MouseEvent e) {
}
public void mouseDoubleClick(MouseEvent e) {
}
});
return parent;
}
/**
* Refreshes the single status area. Is called only when there is one and
* only one error.
*/
private void refreshSingleStatusArea() {
Image image = statusListLabelProvider.getColumnImage(statusAdapter, 0);
label.setImage(image);
String description = statusListLabelProvider.getColumnText(
statusAdapter, 0);
label.setText(description);
singleStatusDisplayArea.layout();
getShell().setText(title);
}
/**
* This method creates display area for {@link StatusAdapter}s when more is
* available.
*
* @param parent
* A parent composite on which all components should be placed.
* @return composite the composite on which are all components for
* displaying status when only one is available.
*/
private Composite createMultipleStatusesDisplayArea(Composite parent) {
parent = new Composite(parent, SWT.NONE);
GridLayout gridLayout = new GridLayout();
parent.setLayout(gridLayout);
GridData gd = new GridData(GridData.FILL_BOTH
| GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);
parent.setLayoutData(gd);
statusListViewer = new TableViewer(parent, SWT.SINGLE | SWT.H_SCROLL
| SWT.V_SCROLL | SWT.BORDER);
statusListViewer.setComparator(getViewerComparator());
Control control = statusListViewer.getControl();
GridData data = new GridData(GridData.FILL_BOTH
| GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);
data.heightHint = convertHeightInCharsToPixels(5);
statusListViewer.addSelectionChangedListener(supportTray);
control.setLayoutData(data);
initContentProvider();
initLabelProvider();
statusListViewer
.addSelectionChangedListener(new ISelectionChangedListener() {
public void selectionChanged(SelectionChangedEvent event) {
handleSelectionChange();
}
});
applyDialogFont(parent);
return parent;
}
/**
* Refresh the contents of the viewer.
*/
private void refreshMultipleStatusArea() {
if (statusListViewer != null
&& !statusListViewer.getControl().isDisposed()) {
statusListViewer.refresh();
if (statusListViewer.getTable().getItemCount() > 1) {
getShell()
.setText(
WorkbenchMessages.WorkbenchStatusDialog_MultipleProblemsHaveOccured);
} else {
getShell().setText(this.title);
}
}
}
/**
* Return a viewer sorter for looking at the jobs.
*
* @return ViewerSorter
*/
private ViewerComparator getViewerComparator() {
return new ViewerComparator() {
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.ViewerComparator#compare(org.eclipse.jface.viewers.Viewer,
* java.lang.Object, java.lang.Object)
*/
public int compare(Viewer testViewer, Object o1, Object o2) {
if (o1 instanceof StatusAdapter && o2 instanceof StatusAdapter) {
return compare((StatusAdapter) o1, (StatusAdapter) o2);
}
// should not happen
if (o1.hashCode() < o2.hashCode()) {
return -1;
}
if (o2.hashCode() > o2.hashCode()) {
return 1;
}
return 0;
}
private int compare(StatusAdapter s1, StatusAdapter s2) {
Long timestamp1 = ((Long) s1
.getProperty(IStatusAdapterConstants.TIMESTAMP_PROPERTY));
Long timestamp2 = ((Long) s2
.getProperty(IStatusAdapterConstants.TIMESTAMP_PROPERTY));
if (timestamp1 == null || timestamp2 == null
|| (timestamp1.equals(timestamp2))) {
String text1 = statusListLabelProvider.getColumnText(s1, 0);
String text2 = statusListLabelProvider.getColumnText(s2, 0);
return text1.compareTo(text2);
}
if (timestamp1.longValue() < timestamp2.longValue()) {
return -1;
}
if (timestamp1.longValue() > timestamp2.longValue()) {
return 1;
}
// should be never called
return 0;
}
};
}
private void refreshDialogSize() {
Point newSize = getShell().computeSize(SWT.DEFAULT, SWT.DEFAULT);
getShell().setSize(newSize);
}
/**
* Sets initial label provider.
*/
private void initLabelProvider() {
statusListViewer.setLabelProvider(statusListLabelProvider);
}
/**
* Sets the content provider for the viewer.
*/
private void initContentProvider() {
IContentProvider provider = new IStructuredContentProvider() {
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.IContentProvider#dispose()
*/
public void dispose() {
// Nothing of interest here
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
*/
public Object[] getElements(Object inputElement) {
return getErrors().toArray();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
* java.lang.Object, java.lang.Object)
*/
public void inputChanged(Viewer viewer, Object oldInput,
Object newInput) {
if (newInput != null) {
refreshMultipleStatusArea();
}
}
};
statusListViewer.setContentProvider(provider);
statusListViewer.setInput(this);
statusListViewer.setSelection(new StructuredSelection(statusAdapter));
}
/**
* Sets current status adapter.
*
* @param statusAdapter
* The statusAdapter to set.
*/
private void setSelectedStatusAdapter(StatusAdapter statusAdapter) {
if (this.statusAdapter != statusAdapter) {
this.statusAdapter = statusAdapter;
}
}
/**
* This method sets new label provider for status list.
*
* @param labelProvider
* a label provider to be used when displaying status adapters.
* It must not be null.
*/
public void setStatusListLabelProvider(ITableLabelProvider labelProvider) {
if (statusListLabelProvider != null) {
throw new NullPointerException("Label provider cannot be null"); //$NON-NLS-1$
}
statusListLabelProvider = labelProvider;
}
/**
* Get the single selection. Return null if the selection is not just one
* element.
*
* @return StatusAdapter or <code>null</code>.
*/
private StatusAdapter getSingleSelection() {
ISelection rawSelection = statusListViewer.getSelection();
if (rawSelection != null
&& rawSelection instanceof IStructuredSelection) {
IStructuredSelection selection = (IStructuredSelection) rawSelection;
if (selection.size() == 1) {
return (StatusAdapter) selection.getFirstElement();
}
}
return null;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.Dialog#initializeBounds()
*/
final protected void initializeBounds() {
refreshDialogSize();
if (!modalitySwitch) {
Rectangle shellPosition = getShell().getBounds();
ProgressManagerUtil.animateUp(shellPosition);
} else {
getShell().setBounds(shellBounds);
}
}
/**
* The selection in the multiple job list has changed. Update widget
* enablements and repopulate the list.
*/
protected void handleSelectionChange() {
StatusAdapter newSelection = getSingleSelection();
if (newSelection != null) {
setSelectedStatusAdapter(newSelection);
refresh();
showDetailsArea();
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.ErrorDialog#shouldShowDetailsButton()
*/
final protected boolean shouldShowDetailsButton() {
return true;
}
/*
* (non-Javadoc) Method declared in Window.
*/
final protected void configureShell(Shell shell) {
super.configureShell(shell);
if (title != null) {
shell.setText(title);
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.TitleAreaDialog#createDialogArea(org.eclipse.swt.widgets.Composite)
*/
final protected Control createDialogArea(Composite parent) {
createTitleArea(parent);
createStatusArea(parent);
workArea = parent;
return parent;
}
/**
* Creates title area.
*
* @param parent
* A composite on which the title area should be created.
*/
protected void createTitleArea(Composite parent) {
titleArea = new Composite(parent, SWT.NONE);
titleArea.setLayoutData(new GridData(GridData.FILL_HORIZONTAL
| GridData.GRAB_HORIZONTAL));
GridLayout layout = new GridLayout();
layout.numColumns = 2;
layout.horizontalSpacing = 10;
layout.marginLeft = 10;
layout.marginTop = 10;
layout.marginBottom = 8;
titleArea.setLayout(layout);
titleImageLabel = new Label(titleArea, SWT.NONE);
titleImageLabel.setImage(getErrorImage());
GridData layoutData = new GridData();
layoutData.verticalSpan = 2;
titleImageLabel.setLayoutData(layoutData);
titleLabel = new Label(titleArea, SWT.NONE);
titleLabel.setFont(JFaceResources.getBannerFont());
GridData messageData = new GridData(GridData.FILL_BOTH
| GridData.GRAB_HORIZONTAL);
messageLabel = new Label(titleArea, SWT.NONE);
messageLabel.setLayoutData(messageData);
}
/**
* Gets {@link Image} associated with current {@link StatusAdapter}
* severity.
*
* @return {@link Image} associated with current {@link StatusAdapter}
* severity.
*/
protected Image getImage() {
if (statusAdapter != null) {
IStatus status = statusAdapter.getStatus();
if (status != null) {
if (status.getSeverity() == IStatus.WARNING) {
return getWarningImage();
}
if (status.getSeverity() == IStatus.INFO) {
return getInfoImage();
}
}
}
// If it was not a warning or an error then return the error image
return getErrorImage();
}
/**
* Returns whether the given StatusAdapter object should be displayed.
*
* @param statusAdapter
* a status object
* @param mask
* a mask as per <code>IStatus.matches</code>
* @return <code>true</code> if the given status should be displayed, and
* <code>false</code> otherwise
* @see org.eclipse.core.runtime.IStatus#matches(int)
*/
protected static boolean shouldDisplay(StatusAdapter statusAdapter, int mask) {
IStatus status = statusAdapter.getStatus();
IStatus[] children = status.getChildren();
if (children == null || children.length == 0) {
return status.matches(mask);
}
for (int i = 0; i < children.length; i++) {
if (children[i].matches(mask)) {
return true;
}
}
return false;
}
/**
* Toggles the unfolding of the details area. This is triggered by the user
* pressing the details button.
*
*/
private boolean toggleDetailsArea() {
boolean opened = false;
Point windowSize = getShell().getSize();
Point oldSize = getShell().computeSize(SWT.DEFAULT, SWT.DEFAULT);
if (detailsManager.isOpen()) {
detailsManager.close();
detailsButton.setText(IDialogConstants.SHOW_DETAILS_LABEL);
opened = false;
} else {
detailsManager.createDetailsArea(workArea, statusAdapter);
detailsButton.setText(IDialogConstants.HIDE_DETAILS_LABEL);
opened = true;
}
Point newSize = getShell().computeSize(SWT.DEFAULT, SWT.DEFAULT);
getShell()
.setSize(
new Point(windowSize.x, windowSize.y
+ (newSize.y - oldSize.y)));
return opened;
}
/**
* Show the details portion of the dialog if it is not already visible. This
* method will only work when it is invoked after the control of the dialog
* has been set. In other words, after the <code>createContents</code>
* method has been invoked and has returned the control for the content area
* of the dialog. Invoking the method before the content area has been set
* or after the dialog has been disposed will have no effect.
*/
protected final void showDetailsArea() {
if (workArea != null && !workArea.isDisposed()) {
if (detailsManager.isOpen()) {
Point windowSize = getShell().getSize();
detailsManager.close();
detailsManager.createDetailsArea(workArea, statusAdapter);
getShell().setSize(windowSize);
workArea.layout();
} else {
toggleDetailsArea();
detailsOpened = true;
}
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.Dialog#isResizable()
*/
protected boolean isResizable() {
return true;
}
/**
* Return a message associated with particular {@link Image}. The message
* is displayed in the title area.
*
* @param image
* An {@link Image} for which a message should be retrieved.
* @return a message to be displayed for particular {@link Image}.
*/
private String getAccessibleMessageFor(Image image) {
if (image.equals(getErrorImage())) {
return JFaceResources.getString("error");//$NON-NLS-1$
}
if (image.equals(getWarningImage())) {
return JFaceResources.getString("warning");//$NON-NLS-1$
}
if (image.equals(getInfoImage())) {
return JFaceResources.getString("info");//$NON-NLS-1$
}
return null;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.Dialog#createButtonBar(org.eclipse.swt.widgets.Composite)
*/
protected Control createButtonBar(Composite parent) {
Composite composite = new Composite(parent, SWT.NONE);
GridLayout layout = new GridLayout();
layout.marginWidth = 0;
layout.marginHeight = 0;
layout.horizontalSpacing = 0;
composite.setLayout(layout);
composite
.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
composite.setFont(parent.getFont());
// create help control if needed
if (isHelpAvailable()) {
Control reportControl = createSupportControl(composite);
((GridData) reportControl.getLayoutData()).horizontalIndent = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
}
Composite buttonSection = new Composite(composite, SWT.NONE);
// create a layout with spacing and margins appropriate for the font
// size.
layout = new GridLayout();
layout.numColumns = 0; // this is incremented by createButton
layout.marginWidth = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_MARGIN);
layout.marginHeight = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_MARGIN);
layout.horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
layout.verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
buttonSection.setLayout(layout);
GridData data = new GridData(GridData.HORIZONTAL_ALIGN_END
| GridData.VERTICAL_ALIGN_CENTER);
buttonSection.setLayoutData(data);
buttonSection.setFont(composite.getFont());
// Add the buttons to the button bar.
createButtonsForButtonBar(buttonSection);
((GridData) buttonSection.getLayoutData()).grabExcessHorizontalSpace = true;
return composite;
}
/**
* Creates a new control that provides access to support providers.
* <p>
* The <code>WorkbenchStatusDialog</code> implementation of this method
* creates the control, registers it for selection events including
* selection, Note that the parent's layout is assumed to be a
* <code>GridLayout</code> and the number of columns in this layout is
* incremented. Subclasses may override.
* </p>
*
* @param parent
* A parent composite on which all components should be placed.
* @return the report control
*/
protected Control createSupportControl(Composite parent) {
return createSupportImageButton(parent, launchTrayImage);
}
/*
* Creates a button with a report image. This is only used if there is an
* image available.
*/
private ToolBar createSupportImageButton(Composite parent, Image image) {
ToolBar toolBar = new ToolBar(parent, SWT.FLAT | SWT.NO_FOCUS);
((GridLayout) parent.getLayout()).numColumns++;
toolBar.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_CENTER));
final Cursor cursor = new Cursor(parent.getDisplay(), SWT.CURSOR_HAND);
toolBar.setCursor(cursor);
toolBar.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
cursor.dispose();
}
});
launchTrayButton = new ToolItem(toolBar, SWT.NONE);
launchTrayButton.setImage(image);
launchTrayButton
.setToolTipText(WorkbenchMessages.WorkbenchStatusDialog_Support);
launchTrayButton.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent e) {
openTray(supportTray);
}
});
return toolBar;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(org.eclipse.swt.widgets.Composite)
*/
protected void createButtonsForButtonBar(Composite parent) {
IAction gotoAction = getGotoAction();
String text = null;
if (gotoAction != null) {
text = gotoAction.getText();
}
if (text == null) {
// Text is set to this initially but will be changed for active job
text = ProgressMessages.JobErrorDialog_CustomJobText;
}
createButton(parent, GOTO_ACTION_ID, text, false);
createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL,
true);
createDetailsButton(parent);
}
/**
* Create the details button if it should be included.
*
* @param parent
* A parent composite on which all components should be placed.
*/
protected void createDetailsButton(Composite parent) {
if (shouldShowDetailsButton()) {
detailsButton = createButton(parent, IDialogConstants.DETAILS_ID,
IDialogConstants.SHOW_DETAILS_LABEL, false);
}
}
/**
* Return the <code>Image</code> to be used when displaying an error.
*
* @return image the error image
*/
protected Image getErrorImage() {
return getSWTImage(SWT.ICON_ERROR);
}
/**
* Return the <code>Image</code> to be used when displaying a warning.
*
* @return image the warning image
*/
protected Image getWarningImage() {
return getSWTImage(SWT.ICON_WARNING);
}
/**
* Return the <code>Image</code> to be used when displaying information.
*
* @return image the information image
*/
protected Image getInfoImage() {
return getSWTImage(SWT.ICON_INFORMATION);
}
/**
* This method enables testing mode: it assumes that all following operation
* are in UI Thread, so Display.(a)syncExec will be not called.
*
* @param enable -
* true if testing mode should be enabled, false otherwise.
*/
void enableTestingMode(boolean enable) {
testingMode = enable;
}
private void executeSync(Runnable r) {
if (testingMode) {
r.run();
} else {
Display.getDefault().syncExec(r);
}
}
private void executeAsync(Runnable r) {
if (testingMode) {
r.run();
} else {
Display.getDefault().asyncExec(r);
}
}
/**
* Get an <code>Image</code> from the provide SWT image constant.
*
* @param imageID
* the SWT image constant
* @return image the image
*/
private Image getSWTImage(final int imageID) {
Shell shell = getShell();
final Display display;
if (shell == null) {
shell = getParentShell();
}
if (shell == null) {
display = Display.getCurrent();
} else {
display = shell.getDisplay();
}
final Image[] image = new Image[1];
executeSync(new Runnable() {
public void run() {
image[0] = display.getSystemImage(imageID);
}
});
return image[0];
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.TrayDialog#closeTray()
*/
public void closeTray() throws IllegalStateException {
this.dialog.closeTray();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.TrayDialog#getTray()
*/
public DialogTray getTray() {
return this.dialog.getTray();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.TrayDialog#openTray(org.eclipse.jface.dialogs.DialogTray)
*/
public void openTray(DialogTray tray) throws IllegalStateException,
UnsupportedOperationException {
this.dialog.openTray(tray);
trayOpened = true;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.window.Window#getShell()
*/
public Shell getShell() {
return this.dialog.getShell();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.TrayDialog#close()
*/
public boolean close() {
if (detailsOpened) {
toggleDetailsArea();
}
if (trayOpened) {
closeTray();
}
shellBounds = getShell().getBounds();
if (clipboard != null) {
clipboard.dispose();
}
statusListViewer = null;
boolean result = this.dialog.close();
if (!modalitySwitch) {
ProgressManagerUtil.animateDown(shellBounds);
}
return result;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.window.Window#open()
*/
public int open() {
if (shouldDisplay(statusAdapter, displayMask)) {
int result = this.dialog.open();
if (modalitySwitch) {
if (detailsOpened) {
showDetailsArea();
}
if (trayOpened) {
openTray(supportTray);
}
}
return result;
}
setReturnCode(OK);
return OK;
}
/**
* Does nothing.
*
* @see org.eclipse.jface.dialogs.Dialog#create()
*/
final public void create() {
// do nothing
}
/**
* Enables the {@link StackTraceSupportArea} when no other support provider
* is defined.
*
* @param enable
* true enables, false disables default support
*/
public void enableDefaultSupportArea(boolean enable) {
supportTray.enableDefaultSupportArea(enable);
}
/**
* This method sets the support provider.
*
* Support providers are displayed according to following logic:
* <ol>
* <li>if support provider was set via Policy, than it is used</li>
* <li>if support provider was set by this method, it will override this
* set up via Policy</li>
* <li>if default support area is enabled and nothing was set before, then
* default support area should be displayed</li>
* </ol>
*
* @param provider
* Support provider to be set.
*/
public void setSupportAreaProvider(AbstractStatusAreaProvider provider) {
userSupportProvider = provider;
}
/**
* This method sets the details area provider. If null is set, the default
* area provider should be used if enabled.
*
* @param provider
* A details area provider to be set.
*/
public void setDetailsAreaProvider(AbstractStatusAreaProvider provider) {
this.detailsManager.setDetailsAreaProvider(provider);
}
/**
* This class is responsible for managing details area.
*
* @since 3.4
*
*/
private final class DetailsAreaManager {
private AbstractStatusAreaProvider provider = null;
private Control control = null;
/**
* This method sets the details area provider. If null is set, the
* default area provider (status tree) will be used.
*
* @param provider
* A provider that will create contents for details area.
*/
public void setDetailsAreaProvider(AbstractStatusAreaProvider provider) {
this.provider = provider;
}
/**
* Closes the details area
*/
public void close() {
if (control != null && !control.isDisposed()) {
control.dispose();
}
}
/**
* This method allows to check if the details area is open (physically
* constructed).
*
* @return true if the area is open, false otherwise
*/
public boolean isOpen() {
if (control == null || control.isDisposed()) {
return false;
}
return true;
}
/**
* This method is responsible for creating details area on the specified
* Composite and displaying specified StatusAdapter
*
* @param parent
* A composite on which should be the details area created.
* @param statusAdapter
* StatusAdapter for which should be the details area
* created.
*/
public void createDetailsArea(Composite parent,
StatusAdapter statusAdapter) {
control = getProvider().createSupportArea(parent, statusAdapter);
}
/**
* Returns current detail area provider.
*
* @return current detail area provider.
*/
private AbstractStatusAreaProvider getProvider() {
if (provider == null) {
provider = new DefaultDetailsArea();
}
return provider;
}
}
/**
* This class is responsible for disposing dialog elements when the dialog
* is closed.
*
* @since 3.4
*
*/
private final class StatusDialogDisposeListener implements DisposeListener {
/*
* (non-Javadoc)
*
* @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent)
*/
public void widgetDisposed(org.eclipse.swt.events.DisposeEvent e) {
dialog = null;
statusListViewer = null;
statusAdapter = null;
errors.clear();
modals.clear();
}
}
/**
* This class is responsible for displaying the support area on the right
* side of the status dialog.
*
* @since 3.4
*
*/
private class SupportTray extends DialogTray implements
ISelectionChangedListener {
private IContributionItem closeAction;
private Image normal;
private Image hover;
private Composite supportArea;
private Composite supportAreaContent;
private StatusAdapter lastSelectedStatus;
private boolean defaultSupportAreaEnabled;
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.DialogTray#createContents(org.eclipse.swt.widgets.Composite)
*/
protected Control createContents(Composite parent) {
Composite container = new Composite(parent, SWT.NONE);
// nothing to display. Should never happen, cause button is disabled
// when nothing to display.
if (!providesSupport()) {
return container;
}
GridLayout layout = new GridLayout();
layout.marginWidth = layout.marginHeight = 0;
layout.verticalSpacing = 0;
container.setLayout(layout);
container.addListener(SWT.Dispose, new Listener() {
public void handleEvent(Event event) {
// dispose event
}
});
ToolBarManager toolBarManager = new ToolBarManager(SWT.FLAT);
toolBarManager.createControl(container);
GridData gd = new GridData(GridData.HORIZONTAL_ALIGN_END);
gd.grabExcessHorizontalSpace = true;
toolBarManager.getControl().setLayoutData(gd);
Label separator = new Label(container, SWT.SEPARATOR
| SWT.HORIZONTAL);
gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
gd.heightHint = 1;
separator.setLayoutData(gd);
createActions();
toolBarManager.add(closeAction);
toolBarManager.update(true);
supportArea = new Composite(container, SWT.NONE);
layout = new GridLayout();
layout.marginWidth = layout.marginHeight = 0;
layout.verticalSpacing = 0;
supportArea.setLayout(layout);
gd = new GridData(GridData.HORIZONTAL_ALIGN_FILL
| GridData.VERTICAL_ALIGN_FILL);
gd.horizontalSpan = 1;
gd.grabExcessHorizontalSpace = true;
gd.grabExcessVerticalSpace = true;
supportArea.setLayoutData(gd);
if (lastSelectedStatus != null)
createSupportArea(supportArea, lastSelectedStatus);
Dialog.applyDialogFont(container);
return container;
}
/**
* Checks if the support dialog has any support areas.
*
* @return true if support dialog has any support areas to display,
* false otherwise
*
*/
private boolean providesSupport() {
if (Policy.getErrorSupportProvider() != null) {
return true;
}
if (userSupportProvider != null) {
return true;
}
return defaultSupportAreaEnabled;
}
/**
* This method manages the enablement of the default support area.
*
* @param enable
* true enables, false disables.
*/
public void enableDefaultSupportArea(boolean enable) {
this.defaultSupportAreaEnabled = enable;
}
/**
* Create the area for extra error support information.
*
* @param parent
* A composite on which should be the support area created.
* @param statusAdapter
* StatusAdapter for which should be the support area
* created.
*/
private void createSupportArea(Composite parent,
StatusAdapter statusAdapter) {
ErrorSupportProvider provider = Policy.getErrorSupportProvider();
if (userSupportProvider != null) {
provider = userSupportProvider;
}
if (defaultSupportAreaEnabled && provider == null) {
provider = new StackTraceSupportArea();
}
// default support area was disabled
if (provider == null)
return;
if (supportAreaContent != null)
supportAreaContent.dispose();
supportAreaContent = new Composite(parent, SWT.FILL);
GridData supportData = new GridData(SWT.FILL, SWT.FILL, true, true);
supportAreaContent.setLayoutData(supportData);
if (supportAreaContent.getLayout() == null) {
GridLayout layout = new GridLayout();
layout.marginWidth = 0;
layout.marginHeight = 0;
supportAreaContent.setLayout(layout); // Give it a default
// layout
}
if (provider instanceof AbstractStatusAreaProvider) {
((AbstractStatusAreaProvider) provider).createSupportArea(
supportAreaContent, statusAdapter);
} else {
provider.createSupportArea(supportAreaContent, statusAdapter
.getStatus());
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
*/
public void selectionChanged(SelectionChangedEvent event) {
lastSelectedStatus = getStatusAdapterFromEvent(event);
if (supportArea != null && !supportArea.isDisposed()) {
if (lastSelectedStatus != null) {
createSupportArea(supportArea, lastSelectedStatus);
supportArea.layout(true);
}
}
}
private StatusAdapter getStatusAdapterFromEvent(
SelectionChangedEvent event) {
ISelection selection = event.getSelection();
if (selection instanceof StructuredSelection) {
StructuredSelection structuredSelection = (StructuredSelection) selection;
Object element = structuredSelection.getFirstElement();
if (element instanceof StatusAdapter) {
return (StatusAdapter) element;
}
}
return null;
}
/**
* Creates any actions needed by the tray.
*/
private void createActions() {
createImages();
closeAction = new ContributionItem() {
public void fill(ToolBar parent, int index) {
final ToolItem item = new ToolItem(parent, SWT.PUSH);
item.setImage(normal);
item.setHotImage(hover);
item.setToolTipText(JFaceResources.getString("close")); //$NON-NLS-1$
item.addListener(SWT.Selection, new Listener() {
public void handleEvent(Event event) {
trayOpened = false;
// close the tray
closeTray();
// set focus back to shell
getShell().setFocus();
}
});
}
};
}
/**
* Creates any custom needed by the tray, such as the close button.
*/
private void createImages() {
Display display = Display.getCurrent();
int[] shape = new int[] { 3, 3, 5, 3, 7, 5, 8, 5, 10, 3, 12, 3, 12,
5, 10, 7, 10, 8, 12, 10, 12, 12, 10, 12, 8, 10, 7, 10, 5,
12, 3, 12, 3, 10, 5, 8, 5, 7, 3, 5 };
/*
* Use magenta as transparency color since it is used infrequently.
*/
Color border = display.getSystemColor(SWT.COLOR_WIDGET_DARK_SHADOW);
Color background = display
.getSystemColor(SWT.COLOR_LIST_BACKGROUND);
Color backgroundHot = new Color(display, new RGB(252, 160, 160));
Color transparent = display.getSystemColor(SWT.COLOR_MAGENTA);
PaletteData palette = new PaletteData(new RGB[] {
transparent.getRGB(), border.getRGB(), background.getRGB(),
backgroundHot.getRGB() });
ImageData data = new ImageData(16, 16, 8, palette);
data.transparentPixel = 0;
normal = new Image(display, data);
normal.setBackground(transparent);
GC gc = new GC(normal);
gc.setBackground(background);
gc.fillPolygon(shape);
gc.setForeground(border);
gc.drawPolygon(shape);
gc.dispose();
hover = new Image(display, data);
hover.setBackground(transparent);
gc = new GC(hover);
gc.setBackground(backgroundHot);
gc.fillPolygon(shape);
gc.setForeground(border);
gc.drawPolygon(shape);
gc.dispose();
backgroundHot.dispose();
}
}
/**
* Parent window actually does not use its Shell to build dialog on. The
* window passes the shell to the InternalDialog, and it can do switching
* modality and recreate the window silently.
*
* @since 3.4
*/
private class InternalDialog extends TrayDialog {
private WorkbenchStatusDialog statusDialog;
/**
* Instantiates the internal dialog on the given shell. Created dialog
* uses statusDialog methods to create its contents.
*
* @param parentShell -
* a parent shell for the dialog
* @param statusDialog -
* a dialog from which methods should be used to create
* contents
* @param modal -
* true if created dialog should be modal, false otherwise.
*/
public InternalDialog(Shell parentShell,
WorkbenchStatusDialog statusDialog, boolean modal) {
super(parentShell);
this.statusDialog = statusDialog;
setShellStyle(SWT.RESIZE | SWT.MAX | SWT.MIN | getShellStyle());
setBlockOnOpen(false);
if (!modal) {
setShellStyle(~SWT.APPLICATION_MODAL & getShellStyle());
}
}
/**
* This function checks if the dialog is modal.
*
* @return true if the dialog is modal, false otherwise
*
*/
public boolean isModal() {
return ((getShellStyle() & SWT.APPLICATION_MODAL) == SWT.APPLICATION_MODAL);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.Dialog#createContents(org.eclipse.swt.widgets.Composite)
*/
protected Control createContents(Composite parent) {
return this.statusDialog.createContents(parent);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.Dialog#create()
*/
public void create() {
super.create();
this.statusDialog.create();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.dialogs.Dialog#initializeBounds()
*/
protected void initializeBounds() {
super.initializeBounds();
this.statusDialog.initializeBounds();
}
}
}