1 /*******************************************************************************
2  * Copyright (c) 2000, 2015 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Konstantin Scheglov <scheglov_ke@nlmk.ru > - Fix for bug 41172
14  *     [Dialogs] Bug with Image in TitleAreaDialog
15  *     Sebastian Davids <sdavids@gmx.de> - Fix for bug 82064
16  *     [Dialogs] TitleAreaDialog#setTitleImage cannot be called before open()
17  *******************************************************************************/
18 package org.eclipse.jface.dialogs;
19 
20 import org.eclipse.jface.resource.JFaceColors;
21 import org.eclipse.jface.resource.JFaceResources;
22 import org.eclipse.jface.window.ToolTip;
23 import org.eclipse.swt.SWT;
24 import org.eclipse.swt.accessibility.ACC;
25 import org.eclipse.swt.accessibility.AccessibleAttributeAdapter;
26 import org.eclipse.swt.accessibility.AccessibleAttributeEvent;
27 import org.eclipse.swt.graphics.Color;
28 import org.eclipse.swt.graphics.Image;
29 import org.eclipse.swt.graphics.Point;
30 import org.eclipse.swt.graphics.RGB;
31 import org.eclipse.swt.graphics.Rectangle;
32 import org.eclipse.swt.layout.FormAttachment;
33 import org.eclipse.swt.layout.FormData;
34 import org.eclipse.swt.layout.FormLayout;
35 import org.eclipse.swt.layout.GridData;
36 import org.eclipse.swt.layout.GridLayout;
37 import org.eclipse.swt.widgets.Composite;
38 import org.eclipse.swt.widgets.Control;
39 import org.eclipse.swt.widgets.Display;
40 import org.eclipse.swt.widgets.Event;
41 import org.eclipse.swt.widgets.Label;
42 import org.eclipse.swt.widgets.Shell;
43 import org.eclipse.swt.widgets.Text;
44 
45 /**
46  * A dialog that has a title area for displaying a title and an image as well as
47  * a common area for displaying a description, a message, or an error message.
48  * <p>
49  * This dialog class may be subclassed.
50  */
51 public class TitleAreaDialog extends TrayDialog {
52 	/**
53 	 * Image registry key for error message image.
54 	 */
55 	public static final String DLG_IMG_TITLE_ERROR = DLG_IMG_MESSAGE_ERROR;
56 
57 	/**
58 	 * Image registry key for banner image (value
59 	 * <code>"dialog_title_banner_image"</code>).
60 	 */
61 	public static final String DLG_IMG_TITLE_BANNER = "dialog_title_banner_image";//$NON-NLS-1$
62 
63 	/**
64 	 * Message type constant used to display an info icon with the message.
65 	 *
66 	 * @since 2.0
67 	 * @deprecated
68 	 */
69 	@Deprecated
70 	public static final String INFO_MESSAGE = "INFO_MESSAGE"; //$NON-NLS-1$
71 
72 	/**
73 	 * Message type constant used to display a warning icon with the message.
74 	 *
75 	 * @since 2.0
76 	 * @deprecated
77 	 */
78 	@Deprecated
79 	public static final String WARNING_MESSAGE = "WARNING_MESSAGE"; //$NON-NLS-1$
80 
81 	// Space between an image and a label
82 	private static final int H_GAP_IMAGE = 5;
83 
84 	// Minimum dialog width (in dialog units)
85 	private static final int MIN_DIALOG_WIDTH = 350;
86 
87 	// Minimum dialog height (in dialog units)
88 	private static final int MIN_DIALOG_HEIGHT = 150;
89 
90 	private Label titleLabel;
91 
92 	private Label titleImageLabel;
93 
94 	private Label bottomFillerLabel;
95 
96 	private Label leftFillerLabel;
97 
98 	private RGB titleAreaRGB;
99 
100 	Color titleAreaColor;
101 
102 	private String message = ""; //$NON-NLS-1$
103 
104 	private String errorMessage;
105 
106 	private Text messageLabel;
107 
108 	private Composite workArea;
109 
110 	private Label messageImageLabel;
111 
112 	private Image messageImage;
113 
114 	private boolean showingError = false;
115 
116 	private boolean titleImageLargest = true;
117 
118 	private int messageLabelHeight;
119 
120 	private Image titleAreaImage;
121 
122 	private int xTrim;
123 
124 	private int yTrim;
125 
126 	/**
127 	 * Instantiate a new title area dialog.
128 	 *
129 	 * @param parentShell
130 	 *            the parent SWT shell
131 	 */
TitleAreaDialog(Shell parentShell)132 	public TitleAreaDialog(Shell parentShell) {
133 		super(parentShell);
134 	}
135 
136 	/*
137 	 * @see Dialog.createContents(Composite)
138 	 */
139 	@Override
createContents(Composite parent)140 	protected Control createContents(Composite parent) {
141 		// create the overall composite
142 		Composite contents = new Composite(parent, SWT.NONE);
143 		contents.setLayoutData(new GridData(GridData.FILL_BOTH));
144 		// initialize the dialog units
145 		initializeDialogUnits(contents);
146 		FormLayout layout = new FormLayout();
147 		contents.setLayout(layout);
148 		// Now create a work area for the rest of the dialog
149 		workArea = new Composite(contents, SWT.NONE);
150 		GridLayout childLayout = new GridLayout();
151 		childLayout.marginHeight = 0;
152 		childLayout.marginWidth = 0;
153 		childLayout.verticalSpacing = 0;
154 		workArea.setLayout(childLayout);
155 		Control top = createTitleArea(contents);
156 		resetWorkAreaAttachments(top);
157 		workArea.setFont(JFaceResources.getDialogFont());
158 		// initialize the dialog units
159 		initializeDialogUnits(workArea);
160 		// create the dialog area and button bar
161 		dialogArea = createDialogArea(workArea);
162 		buttonBar = createButtonBar(workArea);
163 
164 		// computing trim for later
165 		Rectangle rect = messageLabel.computeTrim(0, 0, 100, 100);
166 		xTrim = rect.width - 100;
167 		yTrim = rect.height - 100;
168 
169 		// need to react to new size of title area
170 		getShell().addListener(SWT.Resize, event -> layoutForNewMessage(true));
171 		return contents;
172 	}
173 
174 	/**
175 	 * Creates and returns the contents of the upper part of this dialog (above
176 	 * the button bar).
177 	 * <p>
178 	 * The <code>Dialog</code> implementation of this framework method creates
179 	 * and returns a new <code>Composite</code> with no margins and spacing.
180 	 * Subclasses should override.
181 	 * </p>
182 	 *
183 	 * @param parent
184 	 *            The parent composite to contain the dialog area
185 	 * @return the dialog area control
186 	 */
187 	@Override
createDialogArea(Composite parent)188 	protected Control createDialogArea(Composite parent) {
189 		// create the top level composite for the dialog area
190 		Composite composite = new Composite(parent, SWT.NONE);
191 		GridLayout layout = new GridLayout();
192 		layout.marginHeight = 0;
193 		layout.marginWidth = 0;
194 		layout.verticalSpacing = 0;
195 		layout.horizontalSpacing = 0;
196 		composite.setLayout(layout);
197 		composite.setLayoutData(new GridData(GridData.FILL_BOTH));
198 		composite.setFont(parent.getFont());
199 		// Build the separator line
200 		Label titleBarSeparator = new Label(composite, SWT.HORIZONTAL
201 				| SWT.SEPARATOR);
202 		titleBarSeparator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
203 		return composite;
204 	}
205 
206 	/**
207 	 * Creates the dialog's title area.
208 	 *
209 	 * @param parent
210 	 *            the SWT parent for the title area widgets
211 	 * @return Control with the highest x axis value.
212 	 */
createTitleArea(Composite parent)213 	private Control createTitleArea(Composite parent) {
214 
215 		// add a dispose listener
216 		parent.addDisposeListener(e -> {
217 			if (titleAreaColor != null) {
218 				titleAreaColor.dispose();
219 			}
220 		});
221 		// Determine the background color of the title bar
222 		Display display = parent.getDisplay();
223 		Color background;
224 		Color foreground;
225 		if (titleAreaRGB != null) {
226 			titleAreaColor = new Color(display, titleAreaRGB);
227 			background = titleAreaColor;
228 			foreground = null;
229 		} else {
230 			background = JFaceColors.getBannerBackground(display);
231 			foreground = JFaceColors.getBannerForeground(display);
232 		}
233 
234 		parent.setBackground(background);
235 		int verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
236 		int horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
237 		// Dialog image @ right
238 		titleImageLabel = new Label(parent, SWT.CENTER);
239 		titleImageLabel.setBackground(background);
240 		if (titleAreaImage == null)
241 			titleImageLabel.setImage(JFaceResources
242 					.getImage(DLG_IMG_TITLE_BANNER));
243 		else
244 			titleImageLabel.setImage(titleAreaImage);
245 
246 		FormData imageData = new FormData();
247 		imageData.top = new FormAttachment(0, 0);
248 		// Note: do not use horizontalSpacing on the right as that would be a
249 		// regression from
250 		// the R2.x style where there was no margin on the right and images are
251 		// flush to the right
252 		// hand side. see reopened comments in 41172
253 		imageData.right = new FormAttachment(100, 0); // horizontalSpacing
254 		titleImageLabel.setLayoutData(imageData);
255 		// Title label @ top, left
256 		titleLabel = new Label(parent, SWT.LEFT);
257 		JFaceColors.setColors(titleLabel, foreground, background);
258 		titleLabel.setFont(JFaceResources.getBannerFont());
259 		titleLabel.setText(" ");//$NON-NLS-1$
260 		FormData titleData = new FormData();
261 		titleData.top = new FormAttachment(0, verticalSpacing);
262 		titleData.right = new FormAttachment(titleImageLabel);
263 		titleData.left = new FormAttachment(0, horizontalSpacing);
264 		titleLabel.setLayoutData(titleData);
265 		// Message image @ bottom, left
266 		messageImageLabel = new Label(parent, SWT.CENTER);
267 		messageImageLabel.setBackground(background);
268 		// Message label @ bottom, center
269 		messageLabel = new Text(parent, SWT.WRAP | SWT.READ_ONLY);
270 		JFaceColors.setColors(messageLabel, foreground, background);
271 		messageLabel.setText(" \n "); // two lines//$NON-NLS-1$
272 		messageLabel.setFont(JFaceResources.getDialogFont());
273 		// Bug 248410 -  This snippet will only work with Windows screen readers.
274 		messageLabel.getAccessible().addAccessibleAttributeListener(
275 				new AccessibleAttributeAdapter() {
276 					@Override
277 					public void getAttributes(AccessibleAttributeEvent e) {
278 						e.attributes = new String[] { "container-live", //$NON-NLS-1$
279 								"polite", "live", "polite",   //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
280 								"container-live-role", "status", }; //$NON-NLS-1$ //$NON-NLS-2$
281 					}
282 				});
283 		messageLabelHeight = messageLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
284 		// Filler labels
285 		leftFillerLabel = new Label(parent, SWT.CENTER);
286 		leftFillerLabel.setBackground(background);
287 		bottomFillerLabel = new Label(parent, SWT.CENTER);
288 		bottomFillerLabel.setBackground(background);
289 		setLayoutsForNormalMessage(verticalSpacing, horizontalSpacing);
290 		determineTitleImageLargest();
291 		if (titleImageLargest)
292 			return titleImageLabel;
293 		return messageLabel;
294 	}
295 
296 	/**
297 	 * Determine if the title image is larger than the title message and message
298 	 * area. This is used for layout decisions.
299 	 */
determineTitleImageLargest()300 	private void determineTitleImageLargest() {
301 		int titleY = titleImageLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
302 		int verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
303 		int labelY = titleLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
304 		labelY += verticalSpacing;
305 		labelY += messageLabelHeight;
306 		labelY += verticalSpacing;
307 		titleImageLargest = titleY > labelY;
308 	}
309 
310 	/**
311 	 * Set the layout values for the messageLabel, messageImageLabel and
312 	 * fillerLabel for the case where there is a normal message.
313 	 *
314 	 * @param verticalSpacing
315 	 *            int The spacing between widgets on the vertical axis.
316 	 * @param horizontalSpacing
317 	 *            int The spacing between widgets on the horizontal axis.
318 	 */
setLayoutsForNormalMessage(int verticalSpacing, int horizontalSpacing)319 	private void setLayoutsForNormalMessage(int verticalSpacing,
320 			int horizontalSpacing) {
321 		FormData messageImageData = new FormData();
322 		messageImageData.top = new FormAttachment(titleLabel, verticalSpacing);
323 		messageImageData.left = new FormAttachment(0, H_GAP_IMAGE);
324 		messageImageLabel.setLayoutData(messageImageData);
325 		FormData messageLabelData = new FormData();
326 		messageLabelData.top = new FormAttachment(titleLabel, verticalSpacing);
327 		messageLabelData.right = new FormAttachment(titleImageLabel);
328 		messageLabelData.left = new FormAttachment(messageImageLabel,
329 				horizontalSpacing);
330 		messageLabelData.height = messageLabelHeight;
331 		if (titleImageLargest)
332 			messageLabelData.bottom = new FormAttachment(titleImageLabel, 0,
333 					SWT.BOTTOM);
334 		messageLabel.setLayoutData(messageLabelData);
335 		FormData fillerData = new FormData();
336 		fillerData.left = new FormAttachment(0, horizontalSpacing);
337 		fillerData.top = new FormAttachment(messageImageLabel, 0);
338 		fillerData.bottom = new FormAttachment(messageLabel, 0, SWT.BOTTOM);
339 		bottomFillerLabel.setLayoutData(fillerData);
340 		FormData data = new FormData();
341 		data.top = new FormAttachment(messageImageLabel, 0, SWT.TOP);
342 		data.left = new FormAttachment(0, 0);
343 		data.bottom = new FormAttachment(messageImageLabel, 0, SWT.BOTTOM);
344 		data.right = new FormAttachment(messageImageLabel, 0);
345 		leftFillerLabel.setLayoutData(data);
346 	}
347 
348 	/**
349 	 * The <code>TitleAreaDialog</code> implementation of this
350 	 * <code>Window</code> methods returns an initial size which is at least
351 	 * some reasonable minimum.
352 	 *
353 	 * @return the initial size of the dialog
354 	 */
355 	@Override
getInitialSize()356 	protected Point getInitialSize() {
357 		Point shellSize = super.getInitialSize();
358 		return new Point(Math.max(
359 				convertHorizontalDLUsToPixels(MIN_DIALOG_WIDTH), shellSize.x),
360 				Math.max(convertVerticalDLUsToPixels(MIN_DIALOG_HEIGHT),
361 						shellSize.y));
362 	}
363 
364 	/**
365 	 * Retained for backward compatibility.
366 	 *
367 	 * Returns the title area composite. There is no composite in this
368 	 * implementation so the shell is returned.
369 	 *
370 	 * @return Composite
371 	 * @deprecated
372 	 */
373 	@Deprecated
getTitleArea()374 	protected Composite getTitleArea() {
375 		return getShell();
376 	}
377 
378 	/**
379 	 * Returns the title image label.
380 	 *
381 	 * @return the title image label
382 	 */
getTitleImageLabel()383 	protected Label getTitleImageLabel() {
384 		return titleImageLabel;
385 	}
386 
387 	/**
388 	 * Display the given error message. The currently displayed message is saved
389 	 * and will be redisplayed when the error message is set to
390 	 * <code>null</code>.
391 	 *
392 	 * @param newErrorMessage
393 	 *            the newErrorMessage to display or <code>null</code>
394 	 */
setErrorMessage(String newErrorMessage)395 	public void setErrorMessage(String newErrorMessage) {
396 		// Any change?
397 		if (errorMessage == null ? newErrorMessage == null : errorMessage
398 				.equals(newErrorMessage))
399 			return;
400 		errorMessage = newErrorMessage;
401 
402 		// Clear or set error message.
403 		if (errorMessage == null) {
404 			if (showingError) {
405 				// we were previously showing an error
406 				showingError = false;
407 			}
408 			// show the message
409 			// avoid calling setMessage in case it is overridden to call
410 			// setErrorMessage,
411 			// which would result in a recursive infinite loop
412 			if (message == null) // this should probably never happen since
413 				// setMessage does this conversion....
414 				message = ""; //$NON-NLS-1$
415 			updateMessage(message);
416 			messageImageLabel.setImage(messageImage);
417 			setImageLabelVisible(messageImage != null);
418 		} else {
419 			// Add in a space for layout purposes but do not
420 			// change the instance variable
421 			String displayedErrorMessage = " " + errorMessage; //$NON-NLS-1$
422 			updateMessage(displayedErrorMessage);
423 			if (!showingError) {
424 				// we were not previously showing an error
425 				showingError = true;
426 				messageImageLabel.setImage(JFaceResources
427 						.getImage(DLG_IMG_TITLE_ERROR));
428 				setImageLabelVisible(true);
429 			}
430 		}
431 		layoutForNewMessage(false);
432 	}
433 
434 	/**
435 	 * Re-layout the labels for the new message.
436 	 *
437 	 * @param forceLayout
438 	 *            <code>true</code> to force a layout of the shell
439 	 */
layoutForNewMessage(boolean forceLayout)440 	private void layoutForNewMessage(boolean forceLayout) {
441 		int verticalSpacing = convertVerticalDLUsToPixels(IDialogConstants.VERTICAL_SPACING);
442 		int horizontalSpacing = convertHorizontalDLUsToPixels(IDialogConstants.HORIZONTAL_SPACING);
443 		// If there are no images then layout as normal
444 		if (errorMessage == null && messageImage == null) {
445 			setImageLabelVisible(false);
446 			setLayoutsForNormalMessage(verticalSpacing, horizontalSpacing);
447 		} else {
448 			messageImageLabel.setVisible(true);
449 			bottomFillerLabel.setVisible(true);
450 			leftFillerLabel.setVisible(true);
451 			/**
452 			 * Note that we do not use horizontalSpacing here as when the
453 			 * background of the messages changes there will be gaps between the
454 			 * icon label and the message that are the background color of the
455 			 * shell. We add a leading space elsewhere to compendate for this.
456 			 */
457 			FormData data = new FormData();
458 			data.left = new FormAttachment(0, H_GAP_IMAGE);
459 			data.top = new FormAttachment(titleLabel, verticalSpacing);
460 			messageImageLabel.setLayoutData(data);
461 			data = new FormData();
462 			data.top = new FormAttachment(messageImageLabel, 0);
463 			data.left = new FormAttachment(0, 0);
464 			data.bottom = new FormAttachment(messageLabel, 0, SWT.BOTTOM);
465 			data.right = new FormAttachment(messageImageLabel, 0, SWT.RIGHT);
466 			bottomFillerLabel.setLayoutData(data);
467 			data = new FormData();
468 			data.top = new FormAttachment(messageImageLabel, 0, SWT.TOP);
469 			data.left = new FormAttachment(0, 0);
470 			data.bottom = new FormAttachment(messageImageLabel, 0, SWT.BOTTOM);
471 			data.right = new FormAttachment(messageImageLabel, 0);
472 			leftFillerLabel.setLayoutData(data);
473 			FormData messageLabelData = new FormData();
474 			messageLabelData.top = new FormAttachment(titleLabel,
475 					verticalSpacing);
476 			messageLabelData.right = new FormAttachment(titleImageLabel);
477 			messageLabelData.left = new FormAttachment(messageImageLabel, 0);
478 			messageLabelData.height = messageLabelHeight;
479 			if (titleImageLargest)
480 				messageLabelData.bottom = new FormAttachment(titleImageLabel,
481 						0, SWT.BOTTOM);
482 			messageLabel.setLayoutData(messageLabelData);
483 		}
484 
485 		if (forceLayout) {
486 			getShell().layout();
487 		} else {
488 			// Do not layout before the dialog area has been created
489 			// to avoid incomplete calculations.
490 			if (dialogArea != null)
491 				workArea.getParent().layout(true);
492 		}
493 
494 		int messageLabelUnclippedHeight = messageLabel.computeSize(messageLabel.getSize().x - xTrim, SWT.DEFAULT, true).y;
495 		boolean messageLabelClipped = messageLabelUnclippedHeight > messageLabel.getSize().y - yTrim;
496 		if (messageLabel.getData() instanceof ToolTip) {
497 			ToolTip toolTip = (ToolTip) messageLabel.getData();
498 			toolTip.hide();
499 			toolTip.deactivate();
500 			messageLabel.setData(null);
501 		}
502 		if (messageLabelClipped) {
503 			ToolTip tooltip = new ToolTip(messageLabel, ToolTip.NO_RECREATE, false) {
504 
505 				@Override
506 				protected Composite createToolTipContentArea(Event event, Composite parent) {
507 					Composite result = new Composite(parent, SWT.NONE);
508 					result.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
509 					result.setLayout(new GridLayout());
510 					Text text = new Text(result, SWT.WRAP);
511 					text.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
512 					text.setForeground(parent.getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND));
513 					text.setText(messageLabel.getText());
514 					GridData gridData = new GridData();
515 					gridData.widthHint = messageLabel.getSize().x;
516 					text.setLayoutData(gridData);
517 					Dialog.applyDialogFont(result);
518 					return result;
519 				}
520 				@Override
521 				public Point getLocation(Point tipSize, Event event) {
522 					return messageLabel.getShell().toDisplay(messageLabel.getLocation());
523 				}
524 			};
525 			messageLabel.setData(tooltip);
526 			tooltip.setPopupDelay(0);
527 			tooltip.activate();
528 		}
529 	}
530 
531 	/**
532 	 * Set the message text. If the message line currently displays an error,
533 	 * the message is saved and will be redisplayed when the error message is
534 	 * set to <code>null</code>.
535 	 * <p>
536 	 * Shortcut for <code>setMessage(newMessage, IMessageProvider.NONE)</code>
537 	 * </p>
538 	 * This method should be called after the dialog has been opened as it
539 	 * updates the message label immediately.
540 	 *
541 	 * @param newMessage
542 	 *            the message, or <code>null</code> to clear the message
543 	 */
setMessage(String newMessage)544 	public void setMessage(String newMessage) {
545 		setMessage(newMessage, IMessageProvider.NONE);
546 	}
547 
548 	/**
549 	 * Sets the message for this dialog with an indication of what type of
550 	 * message it is.
551 	 * <p>
552 	 * The valid message types are one of <code>NONE</code>,
553 	 * <code>INFORMATION</code>,<code>WARNING</code>, or
554 	 * <code>ERROR</code>.
555 	 * </p>
556 	 * <p>
557 	 * Note that for backward compatibility, a message of type
558 	 * <code>ERROR</code> is different than an error message (set using
559 	 * <code>setErrorMessage</code>). An error message overrides the current
560 	 * message until the error message is cleared. This method replaces the
561 	 * current message and does not affect the error message.
562 	 * </p>
563 	 *
564 	 * @param newMessage
565 	 *            the message, or <code>null</code> to clear the message
566 	 * @param newType
567 	 *            the message type
568 	 * @since 2.0
569 	 */
setMessage(String newMessage, int newType)570 	public void setMessage(String newMessage, int newType) {
571 		Image newImage = null;
572 		if (newMessage != null) {
573 			switch (newType) {
574 			case IMessageProvider.NONE:
575 				break;
576 			case IMessageProvider.INFORMATION:
577 				newImage = JFaceResources.getImage(DLG_IMG_MESSAGE_INFO);
578 				break;
579 			case IMessageProvider.WARNING:
580 				newImage = JFaceResources.getImage(DLG_IMG_MESSAGE_WARNING);
581 				break;
582 			case IMessageProvider.ERROR:
583 				newImage = JFaceResources.getImage(DLG_IMG_MESSAGE_ERROR);
584 				break;
585 			}
586 		}
587 		showMessage(newMessage, newImage);
588 	}
589 
590 	/**
591 	 * Show the new message and image.
592 	 *
593 	 * @param newMessage
594 	 * @param newImage
595 	 */
showMessage(String newMessage, Image newImage)596 	private void showMessage(String newMessage, Image newImage) {
597 		// https://bugs.eclipse.org/bugs/show_bug.cgi?id=249915
598 		if (newMessage == null)
599 			newMessage = ""; //$NON-NLS-1$
600 
601 		// Any change?
602 		if (message.equals(newMessage) && messageImage == newImage) {
603 			return;
604 		}
605 		message = newMessage;
606 
607 		// Message string to be shown - if there is an image then add in
608 		// a space to the message for layout purposes
609 		String shownMessage = (newImage == null) ? message : " " + message; //$NON-NLS-1$
610 		messageImage = newImage;
611 		if (!showingError) {
612 			// we are not showing an error
613 			updateMessage(shownMessage);
614 			messageImageLabel.setImage(messageImage);
615 			setImageLabelVisible(messageImage != null);
616 			layoutForNewMessage(false);
617 		}
618 	}
619 
620 	/**
621 	 * Update the contents of the messageLabel.
622 	 *
623 	 * @param newMessage
624 	 *            the message to use
625 	 */
updateMessage(String newMessage)626 	private void updateMessage(String newMessage) {
627 		String oldMessage = messageLabel.getText();
628 		messageLabel.setText(newMessage);
629 		// Bug 248410 -  This snippet will only work with Windows screen readers.
630 		messageLabel.getAccessible().sendEvent(ACC.EVENT_ATTRIBUTE_CHANGED,
631 				null);
632 		messageLabel.getAccessible().sendEvent(
633 				ACC.EVENT_TEXT_CHANGED,
634 				new Object[] { Integer.valueOf(ACC.TEXT_DELETE), Integer.valueOf(0),
635 						Integer.valueOf(oldMessage.length()), oldMessage });
636 		messageLabel.getAccessible().sendEvent(
637 				ACC.EVENT_TEXT_CHANGED,
638 				new Object[] { Integer.valueOf(ACC.TEXT_INSERT), Integer.valueOf(0),
639 						Integer.valueOf(newMessage.length()), newMessage });
640 	}
641 
642 	/**
643 	 * Sets the title to be shown in the title area of this dialog.
644 	 *
645 	 * @param newTitle
646 	 *            the title show
647 	 */
setTitle(String newTitle)648 	public void setTitle(String newTitle) {
649 		if (titleLabel == null)
650 			return;
651 		String title = newTitle;
652 		if (title == null)
653 			title = "";//$NON-NLS-1$
654 		titleLabel.setText(title);
655 	}
656 
657 	/**
658 	 * Sets the title bar color for this dialog.
659 	 *
660 	 * @param color
661 	 *            the title bar color
662 	 */
setTitleAreaColor(RGB color)663 	public void setTitleAreaColor(RGB color) {
664 		titleAreaRGB = color;
665 	}
666 
667 	/**
668 	 * Sets the title image to be shown in the title area of this dialog.
669 	 *
670 	 * @param newTitleImage
671 	 *            the title image to be shown
672 	 */
setTitleImage(Image newTitleImage)673 	public void setTitleImage(Image newTitleImage) {
674 
675 		titleAreaImage = newTitleImage;
676 		if (titleImageLabel != null) {
677 			titleImageLabel.setImage(newTitleImage);
678 			determineTitleImageLargest();
679 			Control top;
680 			if (titleImageLargest)
681 				top = titleImageLabel;
682 			else
683 				top = messageLabel;
684 			resetWorkAreaAttachments(top);
685 		}
686 	}
687 
688 	/**
689 	 * Make the label used for displaying error images visible depending on
690 	 * boolean.
691 	 *
692 	 * @param visible
693 	 *            If <code>true</code> make the image visible, if not then
694 	 *            make it not visible.
695 	 */
setImageLabelVisible(boolean visible)696 	private void setImageLabelVisible(boolean visible) {
697 		messageImageLabel.setVisible(visible);
698 		bottomFillerLabel.setVisible(visible);
699 		leftFillerLabel.setVisible(visible);
700 	}
701 
702 	/**
703 	 * Reset the attachment of the workArea to now attach to top as the top
704 	 * control.
705 	 *
706 	 * @param top
707 	 */
resetWorkAreaAttachments(Control top)708 	private void resetWorkAreaAttachments(Control top) {
709 		FormData childData = new FormData();
710 		childData.top = new FormAttachment(top);
711 		childData.right = new FormAttachment(100, 0);
712 		childData.left = new FormAttachment(0, 0);
713 		childData.bottom = new FormAttachment(100, 0);
714 		workArea.setLayoutData(childData);
715 	}
716 
717 	/**
718 	 * Returns the current message text for this dialog.  This message is
719 	 * displayed in the message line of the dialog when the error message
720 	 * is <code>null</code>.  If there is a non-null error message, this
721 	 * message is not shown, but is stored so that it can be shown in
722 	 * the message line whenever {@link #setErrorMessage(String)} is called with
723 	 * a <code>null</code> parameter.
724 	 *
725 	 * @return the message text, which is never <code>null</code>.
726 	 *
727 	 * @see #setMessage(String)
728 	 * @see #setErrorMessage(String)
729 	 *
730 	 * @since 3.6
731 	 */
732 
getMessage()733 	public String getMessage() {
734 		return message;
735 	}
736 
737 	/**
738 	 * Returns the current error message being shown in the dialog, or
739 	 * <code>null</code> if there is no error message being shown.
740 	 *
741 	 * @return the error message, which may be <code>null</code>.
742 	 *
743 	 * @see #setErrorMessage(String)
744 	 * @see #setMessage(String)
745 	 *
746 	 * @since 3.6
747 	 */
748 
getErrorMessage()749 	public String getErrorMessage() {
750 		return errorMessage;
751 	}
752 }
753