### Eclipse Workspace Patch 1.0 #P org.eclipse.jface Index: src/org/eclipse/jface/dialogs/TitleAreaDialog.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jface/src/org/eclipse/jface/dialogs/TitleAreaDialog.java,v retrieving revision 1.41 diff -u -r1.41 TitleAreaDialog.java --- src/org/eclipse/jface/dialogs/TitleAreaDialog.java 6 Oct 2006 13:43:03 -0000 1.41 +++ src/org/eclipse/jface/dialogs/TitleAreaDialog.java 20 Dec 2006 20:32:09 -0000 @@ -44,7 +44,7 @@ *

* This dialog class may be subclassed. */ -public class TitleAreaDialog extends TrayDialog { +public class TitleAreaDialog extends TrayDialog implements IMessageContainer { /** * Image registry key for error message image. */ @@ -99,6 +99,8 @@ Color titleAreaColor; private String message = ""; //$NON-NLS-1$ + + private int messageType; private String errorMessage; @@ -475,6 +477,20 @@ public void setMessage(String newMessage) { setMessage(newMessage, IMessageProvider.NONE); } + + /* (non-Javadoc) + * @see org.eclipse.jface.dialogs.IMessageProvider#getMessage() + */ + public String getMessage() { + return message; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.dialogs.IMessageProvider#getMessageType() + */ + public int getMessageType() { + return messageType; + } /** * Sets the message for this dialog with an indication of what type of @@ -515,21 +531,23 @@ break; } } - showMessage(newMessage, newImage); + showMessage(newMessage, newType, newImage); } /** * Show the new message and image. * * @param newMessage + * @param newType * @param newImage */ - private void showMessage(String newMessage, Image newImage) { + private void showMessage(String newMessage, int newType, Image newImage) { // Any change? if (message.equals(newMessage) && messageImage == newImage) { return; } message = newMessage; + messageType = newType; if (message == null) message = "";//$NON-NLS-1$ // Message string to be shown - if there is an image then add in Index: src/org/eclipse/jface/dialogs/DialogPage.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jface/src/org/eclipse/jface/dialogs/DialogPage.java,v retrieving revision 1.13 diff -u -r1.13 DialogPage.java --- src/org/eclipse/jface/dialogs/DialogPage.java 8 May 2006 20:57:00 -0000 1.13 +++ src/org/eclipse/jface/dialogs/DialogPage.java 20 Dec 2006 20:32:09 -0000 @@ -27,7 +27,7 @@ * Abstract base implementation of a dialog page. All dialog pages are * subclasses of this one. */ -public abstract class DialogPage implements IDialogPage, IMessageProvider { +public abstract class DialogPage implements IDialogPage, IMessageContainer { /** * The control for this dialog page. */ @@ -270,6 +270,7 @@ /* * (non-Javadoc) Method declared on IDialogPage. + * @since 3.3 */ public String getMessage() { return message; @@ -277,6 +278,7 @@ /* * (non-Javadoc) Method declared on IMessageProvider. + * @since 3.3 */ public int getMessageType() { return messageType; Index: src/org/eclipse/jface/messages.properties =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jface/src/org/eclipse/jface/messages.properties,v retrieving revision 1.21 diff -u -r1.21 messages.properties --- src/org/eclipse/jface/messages.properties 7 Jul 2006 01:18:01 -0000 1.21 +++ src/org/eclipse/jface/messages.properties 20 Dec 2006 20:32:09 -0000 @@ -193,3 +193,11 @@ FieldDecorationRegistry.contentAssistMessage=Content Assist Available FieldDecorationRegistry.requiredFieldMessage=Required Field +MessageManager.sMessageSummary = 1 message detected +MessageManager.sWarningSummary = 1 warning detected +MessageManager.sErrorSummary = 1 error detected +MessageManager.pMessageSummary = {0} messages detected +MessageManager.pWarningSummary = {0} warnings detected +MessageManager.pErrorSummary = {0} errors detected + + Index: src/org/eclipse/jface/fieldassist/ControlDecoration.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jface/src/org/eclipse/jface/fieldassist/ControlDecoration.java,v retrieving revision 1.12 diff -u -r1.12 ControlDecoration.java --- src/org/eclipse/jface/fieldassist/ControlDecoration.java 10 Nov 2006 22:43:35 -0000 1.12 +++ src/org/eclipse/jface/fieldassist/ControlDecoration.java 20 Dec 2006 20:32:09 -0000 @@ -241,7 +241,7 @@ .getSystemColor(SWT.COLOR_INFO_FOREGROUND)); hoverShell.addPaintListener(new PaintListener() { public void paintControl(PaintEvent pe) { - pe.gc.drawString(text, hm, hm); + pe.gc.drawText(text, hm, hm); if (!CARBON) { pe.gc.drawPolygon(getPolygon(true)); } Index: src/org/eclipse/jface/dialogs/IMessageContainerWithDetails.java =================================================================== RCS file: src/org/eclipse/jface/dialogs/IMessageContainerWithDetails.java diff -N src/org/eclipse/jface/dialogs/IMessageContainerWithDetails.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/jface/dialogs/IMessageContainerWithDetails.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2006 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.jface.dialogs; + +/** + *

+ * This interface should be implemented by message containers that are capable + * of showing messages that have a summary and details. + *

+ *

+ * EXPERIMENTAL This class or interface has been added as part + * of a work in progress. This API may change at any given time. Please do not + * use this API without consulting with the Platform/UI team. + *

+ * + * @since 3.3 + */ +public interface IMessageContainerWithDetails extends IMessageContainer { + + /** + * Sets the message for this container with an indication of what type of + * message it is. + *

+ * The valid message types are one of NONE, + * INFORMATION,WARNING, or + * ERROR. + *

+ * + * @param newMessage + * the message, or null to clear the message + * @param detailedMessage + * the detailed message that provides more information. If used, + * newMessage should be used as a summary. + * @param newType + * the message type + */ + public void setMessage(String newMessage, String detailedMessage, + int newType); +} Index: src/org/eclipse/jface/fieldassist/MessageManager.java =================================================================== RCS file: src/org/eclipse/jface/fieldassist/MessageManager.java diff -N src/org/eclipse/jface/fieldassist/MessageManager.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/jface/fieldassist/MessageManager.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,489 @@ +/******************************************************************************* + * Copyright (c) 2006 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.jface.fieldassist; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Hashtable; + +import org.eclipse.jface.dialogs.IMessageContainer; +import org.eclipse.jface.dialogs.IMessageContainerWithDetails; +import org.eclipse.jface.dialogs.IMessageProvider; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; + +/** + * Use this class to work with message containers that contain decorated fields. + * The class provides for: + * + * + *

EXPERIMENTAL This class or interface has been added as part + * of a work in progress. This API may change at any given time. Please do not + * use this API without consulting with the Platform/UI team.

+ * + * @see IMessageContainer + * @see IMessageContainerWithDetails + * @since 3.3 + */ + +public class MessageManager { + private ArrayList messages = new ArrayList(); + private Hashtable decorators = new Hashtable(); + private boolean showOnlyOnFocus; + private IMessageContainer messageContainer; + private static FieldDecoration standardError = FieldDecorationRegistry + .getDefault().getFieldDecoration(FieldDecorationRegistry.DEC_ERROR); + private static FieldDecoration standardWarning = FieldDecorationRegistry + .getDefault().getFieldDecoration( + FieldDecorationRegistry.DEC_WARNING); + + private static final String[] SINGLE_MESSAGE_SUMMARY_KEYS = { + "MessageManager.sMessageSummary", //$NON-NLS-1$ + "MessageManager.sMessageSummary", //$NON-NLS-1$ + "MessageManager.sWarningSummary", //$NON-NLS-1$ + "MessageManager.sErrorSummary" //$NON-NLS-1$ + }; + + private static final String[] MULTIPLE_MESSAGE_SUMMARY_KEYS = { + "MessageManager.pMessageSummary", //$NON-NLS-1$ + "MessageManager.pMessageSummary", //$NON-NLS-1$ + "MessageManager.pWarningSummary", //$NON-NLS-1$ + "MessageManager.pErrorSummary" //$NON-NLS-1$ + }; + + class Message { + String prefix; + String id; + int type; + String message; + + Message(String prefix, String id, String message, int type) { + this.id = id; + this.message = message; + this.type = type; + this.prefix = prefix; + } + + String getFullMessage() { + if (prefix == null) + return message; + return prefix + message; + } + } + + class ControlDecorator implements IMessageContainerWithDetails { + private ControlDecoration decoration; + private ArrayList controlMessages = new ArrayList(); + private String message; + private int type; + private String prefix; + + ControlDecorator(Control control) { + this.decoration = new ControlDecoration(control, SWT.LEFT + | SWT.BOTTOM); + updateFocusSetting(); + } + + String getPrefix() { + if (prefix == null) + createPrefix(); + return prefix; + } + + void updateFocusSetting() { + if (getShowOnlyOnFocus() != decoration.getShowOnlyOnFocus()) + decoration.setShowOnlyOnFocus(getShowOnlyOnFocus()); + } + + private void createPrefix() { + Control c = decoration.getControl(); + Composite parent = c.getParent(); + Control[] siblings = parent.getChildren(); + for (int i = 0; i < siblings.length; i++) { + if (siblings[i] == c) { + // this is us - go backward until you hit + // a label + for (int j = i - 1; j >= 0; j--) { + Control label = siblings[j]; + if (label instanceof Label) { + prefix = ((Label) label).getText() + ": "; //$NON-NLS-1$ + return; + } + } + break; + } + } + // make a prefix anyway + prefix = ""; //$NON-NLS-1$ + } + + void addAll(ArrayList target) { + target.addAll(controlMessages); + } + + void addMessage(String id, String text, int type) { + MessageManager.this.addMessage(getPrefix(), id, text, type, + controlMessages); + updateMessageContainer(this, controlMessages, true); + } + + void removeMessage(String id) { + Message message = findMessage(id, controlMessages); + if (message != null) { + controlMessages.remove(message); + updateMessageContainer(this, controlMessages, true); + } + } + + void removeMessages() { + controlMessages.clear(); + updateMessageContainer(this, controlMessages, true); + } + + boolean isEmpty() { + return controlMessages.isEmpty(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.dialogs.IMessageContainer#setMessage(java.lang.String, + * int) + */ + public void setMessage(String newMessage, int newType) { + if (this.message != null && newMessage != null + && newMessage.equals(this.message) && newType == this.type) + return; + this.message = newMessage; + this.type = newType; + update(); + } + + public void setMessage(String newMessage, String details, int type) { + setMessage(newMessage, type); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.dialogs.IMessageProvider#getMessage() + */ + public String getMessage() { + return message; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.dialogs.IMessageProvider#getMessageType() + */ + public int getMessageType() { + return type; + } + + private void update() { + if (message == null) + decoration.hide(); + else { + if (type == IMessageProvider.ERROR) + decoration.setImage(standardError.getImage()); + else if (type == IMessageProvider.WARNING) + decoration.setImage(standardWarning.getImage()); + decoration.setDescriptionText(message); + decoration.show(); + } + } + } + + /** + * Creates a new instance of the message manager that will work with the + * provided message container. + * + * @param messageContainer + * the container to control + */ + public MessageManager(IMessageContainer messageContainer) { + this.messageContainer = messageContainer; + } + + /** + * Adds a general message that is not associated with any decorated field. + * + * @param id + * a unique message identifier that will be used to look the + * message up later + * + * @param messageText + * the message to add + * @param type + * the message type as defined in IMessageProvider. + */ + + public void addMessage(String id, String messageText, int type) { + addMessage(null, id, messageText, type, messages); + updateMessageContainer(); + } + + /** + * Adds a message that should be associated with the provided control. + * + * @param id + * the unique message identifier + * @param messageText + * the message to add + * @param type + * the message type + * @param control + * the control to associate the message with + */ + + public void addMessage(String id, String messageText, int type, + Control control) { + ControlDecorator dec = (ControlDecorator) decorators.get(control); + if (dec == null) { + dec = new ControlDecorator(control); + decorators.put(control, dec); + control.addDisposeListener(new DisposeListener() { + /* + * (non-Javadoc) + * + * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent) + */ + public void widgetDisposed(DisposeEvent e) { + decorators.remove(e.widget); + updateMessageContainer(); + } + }); + } + dec.addMessage(id, messageText, type); + updateMessageContainer(); + } + + /** + * Set the boolean that controls whether the control decorations are shown + * only when the controls have focus. The default value of this setting is + * false. + * + * @param showOnlyOnFocus + * true if the decoration should only be shown + * when the control has focus, and false if it + * should always be shown. + */ + public void setShowOnlyOnFocus(boolean showOnlyOnFocus) { + this.showOnlyOnFocus = showOnlyOnFocus; + updateMessageContainer(); + } + + /** + * Get the boolean that controls whether the control decorations are shown + * only when the controls have focus. The default value of this setting is + * false. + * + * @return true if the control decoration should only be + * shown when the control has focus, and false if it + * should always be shown. + */ + public boolean getShowOnlyOnFocus() { + return showOnlyOnFocus; + } + + /** + * Removes the provided general message. + * + * @param id + * the id of the message to remove + */ + + public void removeMessage(String id) { + Message message = findMessage(id, messages); + if (message != null) { + messages.remove(message); + updateMessageContainer(); + } + } + + /** + * Removes all the general messages. If there are local messages associated + * with controls, the replacement message may show up drawing user's + * attention to these local messages. Otherwise, the container will clear + * the message area. + */ + public void removeMessages() { + messages.clear(); + updateMessageContainer(); + } + + /** + * Removes the message associated with the provided control. + * + * @param id + * the id of the message to remove + * @param control + * the control the message is associated with + */ + + public void removeMessage(String id, Control control) { + ControlDecorator dec = (ControlDecorator) decorators.get(control); + if (dec == null) + return; + dec.removeMessage(id); + updateMessageContainer(); + } + + /** + * Removes all the messages associated with the provided control. + * + * @param control + * the control the messages are associated with + */ + + public void removeMessages(Control control) { + ControlDecorator dec = (ControlDecorator) decorators.get(control); + dec.removeMessages(); + updateMessageContainer(); + } + + /** + * Removes all the local field messages and all the general container + * messages. + */ + + public void removeAllMessages() { + for (Enumeration enm = decorators.elements(); enm.hasMoreElements();) { + ControlDecorator control = (ControlDecorator) enm.nextElement(); + control.removeMessages(); + } + messages.clear(); + updateMessageContainer(); + } + + /* + * Adds the message if it does not already exist in the provided list. + */ + + private void addMessage(String prefix, String id, String messageText, + int type, ArrayList list) { + Message message = findMessage(id, list); + if (message == null) { + message = new Message(prefix, id, messageText, type); + list.add(message); + } else { + message.message = messageText; + message.type = type; + } + } + + /* + * Finds the message with the provided id in the provided list. + */ + + private Message findMessage(String id, ArrayList list) { + for (int i = 0; i < list.size(); i++) { + Message message = (Message) list.get(i); + if (message.id.equals(id)) + return message; + } + return null; + } + + /* + * Updates the entire container by building up a merged list that contains + * messages from each decorated field plus messages from the container + * itself. + */ + + private void updateMessageContainer() { + ArrayList mergedList = new ArrayList(); + mergedList.addAll(messages); + for (Enumeration enm = decorators.elements(); enm.hasMoreElements();) { + ControlDecorator dec = (ControlDecorator) enm.nextElement(); + dec.addAll(mergedList); + dec.updateFocusSetting(); + } + updateMessageContainer(messageContainer, mergedList, false); + } + + /* + * This method works with a generic message container when a list of + * messages of various types need to be shown. The messages with the highest + * type are picked first. If there are more than one with this type, a + * multiple message is constructed; otherwise, the message is used as-is. + */ + + private void updateMessageContainer(IMessageContainer container, + ArrayList messages, boolean showAll) { + if (messages.isEmpty() || messages == null) { + container.setMessage(null, IMessageProvider.NONE); + return; + } + int maxType = 0; + // create a subset of messages with the highest type + ArrayList peers = new ArrayList(); + for (int i = 0; i < messages.size(); i++) { + Message message = (Message) messages.get(i); + if (message.type > maxType) { + peers.clear(); + maxType = message.type; + } + if (message.type == maxType) + peers.add(message); + } + String messageText; + String details = null; + if (peers.size() == 1 && ((Message) peers.get(0)).prefix == null) { + // a single message + messageText = ((Message) peers.get(0)).message; + } else { + StringWriter sw = new StringWriter(); + PrintWriter out = new PrintWriter(sw); + // StringBuffer sw = new StringBuffer(); + for (int i = 0; i < peers.size(); i++) { + if (i > 0) + out.println(); + Message m = (Message) peers.get(i); + out.print(showAll ? m.message : m.getFullMessage()); + } + out.flush(); + if (showAll) + messageText = sw.toString(); + else { + // show a summary message for the message + // and list of errors for the details + if (peers.size() > 1) + messageText = JFaceResources.format( + MULTIPLE_MESSAGE_SUMMARY_KEYS[maxType], + new String[] { peers.size() + "" }); //$NON-NLS-1$ + else + messageText = JFaceResources + .getString(SINGLE_MESSAGE_SUMMARY_KEYS[maxType]); + details = sw.toString(); + } + } + if (container instanceof IMessageContainerWithDetails) + ((IMessageContainerWithDetails) container).setMessage(messageText, + details, maxType); + else + container.setMessage(messageText, maxType); + } +} Index: src/org/eclipse/jface/dialogs/IMessageContainer.java =================================================================== RCS file: src/org/eclipse/jface/dialogs/IMessageContainer.java diff -N src/org/eclipse/jface/dialogs/IMessageContainer.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/jface/dialogs/IMessageContainer.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2006 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.jface.dialogs; + +/** + * A generic interface for containers capable of displaying messages of a set + * type (as defined in IMessageProvider). Although there is no + * firm contract on how these messages are displayed, a typical implementation + * will have some kind of a header area for this purpose. + * + *

EXPERIMENTAL This class or interface has been added as part + * of a work in progress. This API may change at any given time. Please do not + * use this API without consulting with the Platform/UI team.

+ * + * @since 3.3 + */ +public interface IMessageContainer extends IMessageProvider { + /** + * Sets the message for this container with an indication of what type of + * message it is. + *

+ * The valid message types are one of NONE, + * INFORMATION,WARNING, or + * ERROR. + *

+ * + * @param newMessage + * the message, or null to clear the message + * @param newType + * the message type + */ + public void setMessage(String newMessage, int newType); +}