001: /*******************************************************************************
002: * Copyright (c) 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: ******************************************************************************/package org.eclipse.ui.internal.forms;
011:
012: import java.io.PrintWriter;
013: import java.io.StringWriter;
014: import java.util.ArrayList;
015: import java.util.Enumeration;
016: import java.util.Hashtable;
017: import java.util.Iterator;
018:
019: import org.eclipse.jface.dialogs.IMessageProvider;
020: import org.eclipse.jface.fieldassist.ControlDecoration;
021: import org.eclipse.jface.fieldassist.FieldDecoration;
022: import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
023: import org.eclipse.swt.SWT;
024: import org.eclipse.swt.custom.CLabel;
025: import org.eclipse.swt.widgets.Composite;
026: import org.eclipse.swt.widgets.Control;
027: import org.eclipse.swt.widgets.Label;
028: import org.eclipse.ui.forms.IMessage;
029: import org.eclipse.ui.forms.IMessageManager;
030: import org.eclipse.ui.forms.IMessagePrefixProvider;
031: import org.eclipse.ui.forms.widgets.Hyperlink;
032: import org.eclipse.ui.forms.widgets.ScrolledForm;
033:
034: /**
035: * @see IMessageManager
036: */
037:
038: public class MessageManager implements IMessageManager {
039: private static final DefaultPrefixProvider DEFAULT_PREFIX_PROVIDER = new DefaultPrefixProvider();
040: private ArrayList messages = new ArrayList();
041: private Hashtable decorators = new Hashtable();
042: private boolean autoUpdate = true;
043: private ScrolledForm scrolledForm;
044: private IMessagePrefixProvider prefixProvider = DEFAULT_PREFIX_PROVIDER;
045: private int decorationPosition = SWT.LEFT | SWT.BOTTOM;
046: private static FieldDecoration standardError = FieldDecorationRegistry
047: .getDefault().getFieldDecoration(
048: FieldDecorationRegistry.DEC_ERROR);
049: private static FieldDecoration standardWarning = FieldDecorationRegistry
050: .getDefault().getFieldDecoration(
051: FieldDecorationRegistry.DEC_WARNING);
052:
053: private static final String[] SINGLE_MESSAGE_SUMMARY_KEYS = {
054: Messages.MessageManager_sMessageSummary,
055: Messages.MessageManager_sMessageSummary,
056: Messages.MessageManager_sWarningSummary,
057: Messages.MessageManager_sErrorSummary };
058:
059: private static final String[] MULTIPLE_MESSAGE_SUMMARY_KEYS = {
060: Messages.MessageManager_pMessageSummary,
061: Messages.MessageManager_pMessageSummary,
062: Messages.MessageManager_pWarningSummary,
063: Messages.MessageManager_pErrorSummary };
064:
065: static class Message implements IMessage {
066: Control control;
067: Object data;
068: Object key;
069: String message;
070: int type;
071: String prefix;
072:
073: Message(Object key, String message, int type, Object data) {
074: this .key = key;
075: this .message = message;
076: this .type = type;
077: this .data = data;
078: }
079:
080: /*
081: * (non-Javadoc)
082: *
083: * @see org.eclipse.jface.dialogs.IMessage#getKey()
084: */
085: public Object getKey() {
086: return key;
087: }
088:
089: /*
090: * (non-Javadoc)
091: *
092: * @see org.eclipse.jface.dialogs.IMessageProvider#getMessage()
093: */
094: public String getMessage() {
095: return message;
096: }
097:
098: /*
099: * (non-Javadoc)
100: *
101: * @see org.eclipse.jface.dialogs.IMessageProvider#getMessageType()
102: */
103: public int getMessageType() {
104: return type;
105: }
106:
107: /*
108: * (non-Javadoc)
109: *
110: * @see org.eclipse.ui.forms.messages.IMessage#getControl()
111: */
112: public Control getControl() {
113: return control;
114: }
115:
116: /*
117: * (non-Javadoc)
118: *
119: * @see org.eclipse.ui.forms.messages.IMessage#getData()
120: */
121: public Object getData() {
122: return data;
123: }
124:
125: /*
126: * (non-Javadoc)
127: *
128: * @see org.eclipse.ui.forms.messages.IMessage#getPrefix()
129: */
130: public String getPrefix() {
131: return prefix;
132: }
133: }
134:
135: static class DefaultPrefixProvider implements
136: IMessagePrefixProvider {
137:
138: public String getPrefix(Control c) {
139: Composite parent = c.getParent();
140: Control[] siblings = parent.getChildren();
141: for (int i = 0; i < siblings.length; i++) {
142: if (siblings[i] == c) {
143: // this is us - go backward until you hit
144: // a label-like widget
145: for (int j = i - 1; j >= 0; j--) {
146: Control label = siblings[j];
147: String ltext = null;
148: if (label instanceof Label) {
149: ltext = ((Label) label).getText();
150: } else if (label instanceof Hyperlink) {
151: ltext = ((Hyperlink) label).getText();
152: } else if (label instanceof CLabel) {
153: ltext = ((CLabel) label).getText();
154: }
155: if (ltext != null) {
156: if (!ltext.endsWith(":")) //$NON-NLS-1$
157: return ltext + ": "; //$NON-NLS-1$
158: return ltext + " "; //$NON-NLS-1$
159: }
160: }
161: break;
162: }
163: }
164: return null;
165: }
166: }
167:
168: class ControlDecorator {
169: private ControlDecoration decoration;
170: private ArrayList controlMessages = new ArrayList();
171: private String prefix;
172:
173: ControlDecorator(Control control) {
174: this .decoration = new ControlDecoration(control,
175: decorationPosition, scrolledForm.getBody());
176: }
177:
178: public boolean isDisposed() {
179: return decoration.getControl() == null;
180: }
181:
182: void updatePrefix() {
183: prefix = null;
184: }
185:
186: void updatePosition() {
187: Control control = decoration.getControl();
188: decoration.dispose();
189: this .decoration = new ControlDecoration(control,
190: decorationPosition, scrolledForm.getBody());
191: update();
192: }
193:
194: String getPrefix() {
195: if (prefix == null)
196: createPrefix();
197: return prefix;
198: }
199:
200: private void createPrefix() {
201: if (prefixProvider == null) {
202: prefix = ""; //$NON-NLS-1$
203: return;
204: }
205: prefix = prefixProvider.getPrefix(decoration.getControl());
206: if (prefix == null)
207: // make a prefix anyway
208: prefix = ""; //$NON-NLS-1$
209: }
210:
211: void addAll(ArrayList target) {
212: target.addAll(controlMessages);
213: }
214:
215: void addMessage(Object key, String text, Object data, int type) {
216: Message message = MessageManager.this
217: .addMessage(getPrefix(), key, text, data, type,
218: controlMessages);
219: message.control = decoration.getControl();
220: if (isAutoUpdate())
221: update();
222: }
223:
224: boolean removeMessage(Object key) {
225: Message message = findMessage(key, controlMessages);
226: if (message != null) {
227: controlMessages.remove(message);
228: if (isAutoUpdate())
229: update();
230: }
231: return message != null;
232: }
233:
234: boolean removeMessages() {
235: if (controlMessages.isEmpty())
236: return false;
237: controlMessages.clear();
238: if (isAutoUpdate())
239: update();
240: return true;
241: }
242:
243: public void update() {
244: if (controlMessages.isEmpty()) {
245: decoration.setDescriptionText(null);
246: decoration.hide();
247: } else {
248: ArrayList peers = createPeers(controlMessages);
249: int type = ((IMessage) peers.get(0)).getMessageType();
250: String description = createDetails(createPeers(peers),
251: true);
252: if (type == IMessageProvider.ERROR)
253: decoration.setImage(standardError.getImage());
254: else if (type == IMessageProvider.WARNING)
255: decoration.setImage(standardWarning.getImage());
256: decoration.setDescriptionText(description);
257: decoration.show();
258: }
259: }
260: }
261:
262: /**
263: * Creates a new instance of the message manager that will work with the
264: * provided form.
265: *
266: * @param scrolledForm
267: * the form to control
268: */
269: public MessageManager(ScrolledForm scrolledForm) {
270: this .scrolledForm = scrolledForm;
271: }
272:
273: /*
274: * (non-Javadoc)
275: *
276: * @see org.eclipse.ui.forms.IMessageManager#addMessage(java.lang.Object,
277: * java.lang.String, int)
278: */
279: public void addMessage(Object key, String messageText, Object data,
280: int type) {
281: addMessage(null, key, messageText, data, type, messages);
282: if (isAutoUpdate())
283: updateForm();
284: }
285:
286: /*
287: * (non-Javadoc)
288: *
289: * @see org.eclipse.ui.forms.IMessageManager#addMessage(java.lang.Object,
290: * java.lang.String, int, org.eclipse.swt.widgets.Control)
291: */
292: public void addMessage(Object key, String messageText, Object data,
293: int type, Control control) {
294: ControlDecorator dec = (ControlDecorator) decorators
295: .get(control);
296:
297: if (dec == null) {
298: dec = new ControlDecorator(control);
299: decorators.put(control, dec);
300: }
301: dec.addMessage(key, messageText, data, type);
302: if (isAutoUpdate())
303: updateForm();
304: }
305:
306: /*
307: * (non-Javadoc)
308: *
309: * @see org.eclipse.ui.forms.IMessageManager#removeMessage(java.lang.Object)
310: */
311: public void removeMessage(Object key) {
312: Message message = findMessage(key, messages);
313: if (message != null) {
314: messages.remove(message);
315: if (isAutoUpdate())
316: updateForm();
317: }
318: }
319:
320: /*
321: * (non-Javadoc)
322: *
323: * @see org.eclipse.ui.forms.IMessageManager#removeMessages()
324: */
325: public void removeMessages() {
326: if (!messages.isEmpty()) {
327: messages.clear();
328: if (isAutoUpdate())
329: updateForm();
330: }
331: }
332:
333: /*
334: * (non-Javadoc)
335: *
336: * @see org.eclipse.ui.forms.IMessageManager#removeMessage(java.lang.Object,
337: * org.eclipse.swt.widgets.Control)
338: */
339: public void removeMessage(Object key, Control control) {
340: ControlDecorator dec = (ControlDecorator) decorators
341: .get(control);
342: if (dec == null)
343: return;
344: if (dec.removeMessage(key))
345: if (isAutoUpdate())
346: updateForm();
347: }
348:
349: /*
350: * (non-Javadoc)
351: *
352: * @see org.eclipse.ui.forms.IMessageManager#removeMessages(org.eclipse.swt.widgets.Control)
353: */
354: public void removeMessages(Control control) {
355: ControlDecorator dec = (ControlDecorator) decorators
356: .get(control);
357: if (dec != null) {
358: if (dec.removeMessages()) {
359: if (isAutoUpdate())
360: updateForm();
361: }
362: }
363: }
364:
365: /*
366: * (non-Javadoc)
367: *
368: * @see org.eclipse.ui.forms.IMessageManager#removeAllMessages()
369: */
370: public void removeAllMessages() {
371: boolean needsUpdate = false;
372: for (Enumeration enm = decorators.elements(); enm
373: .hasMoreElements();) {
374: ControlDecorator control = (ControlDecorator) enm
375: .nextElement();
376: if (control.removeMessages())
377: needsUpdate = true;
378: }
379: if (!messages.isEmpty()) {
380: messages.clear();
381: needsUpdate = true;
382: }
383: if (needsUpdate && isAutoUpdate())
384: updateForm();
385: }
386:
387: /*
388: * Adds the message if it does not already exist in the provided list.
389: */
390:
391: private Message addMessage(String prefix, Object key,
392: String messageText, Object data, int type, ArrayList list) {
393: Message message = findMessage(key, list);
394: if (message == null) {
395: message = new Message(key, messageText, type, data);
396: message.prefix = prefix;
397: list.add(message);
398: } else {
399: message.message = messageText;
400: message.type = type;
401: message.data = data;
402: }
403: return message;
404: }
405:
406: /*
407: * Finds the message with the provided key in the provided list.
408: */
409:
410: private Message findMessage(Object key, ArrayList list) {
411: for (int i = 0; i < list.size(); i++) {
412: Message message = (Message) list.get(i);
413: if (message.getKey().equals(key))
414: return message;
415: }
416: return null;
417: }
418:
419: /*
420: * (non-Javadoc)
421: *
422: * @see org.eclipse.ui.forms.IMessageManager#update()
423: */
424: public void update() {
425: // Update decorations
426: for (Iterator iter = decorators.values().iterator(); iter
427: .hasNext();) {
428: ControlDecorator dec = (ControlDecorator) iter.next();
429: dec.update();
430: }
431: // Update the form
432: updateForm();
433: }
434:
435: /*
436: * Updates the container by rolling the messages up from the controls.
437: */
438:
439: private void updateForm() {
440: ArrayList mergedList = new ArrayList();
441: mergedList.addAll(messages);
442: for (Enumeration enm = decorators.elements(); enm
443: .hasMoreElements();) {
444: ControlDecorator dec = (ControlDecorator) enm.nextElement();
445: dec.addAll(mergedList);
446: }
447: update(mergedList);
448: }
449:
450: private void update(ArrayList mergedList) {
451: pruneControlDecorators();
452: if (mergedList.isEmpty() || mergedList == null) {
453: scrolledForm.setMessage(null, IMessageProvider.NONE);
454: return;
455: }
456: ArrayList peers = createPeers(mergedList);
457: int maxType = ((IMessage) peers.get(0)).getMessageType();
458: String messageText;
459: IMessage[] array = (IMessage[]) peers
460: .toArray(new IMessage[peers.size()]);
461: if (peers.size() == 1
462: && ((Message) peers.get(0)).prefix == null) {
463: // a single message
464: IMessage message = (IMessage) peers.get(0);
465: messageText = message.getMessage();
466: scrolledForm.setMessage(messageText, maxType, array);
467: } else {
468: // show a summary message for the message
469: // and list of errors for the details
470: if (peers.size() > 1)
471: messageText = Messages.bind(
472: MULTIPLE_MESSAGE_SUMMARY_KEYS[maxType],
473: new String[] { peers.size() + "" }); //$NON-NLS-1$
474: else
475: messageText = SINGLE_MESSAGE_SUMMARY_KEYS[maxType];
476: scrolledForm.setMessage(messageText, maxType, array);
477: }
478: }
479:
480: private static String getFullMessage(IMessage message) {
481: if (message.getPrefix() == null)
482: return message.getMessage();
483: return message.getPrefix() + message.getMessage();
484: }
485:
486: private ArrayList createPeers(ArrayList messages) {
487: ArrayList peers = new ArrayList();
488: int maxType = 0;
489: for (int i = 0; i < messages.size(); i++) {
490: Message message = (Message) messages.get(i);
491: if (message.type > maxType) {
492: peers.clear();
493: maxType = message.type;
494: }
495: if (message.type == maxType)
496: peers.add(message);
497: }
498: return peers;
499: }
500:
501: private String createDetails(ArrayList messages,
502: boolean excludePrefix) {
503: StringWriter sw = new StringWriter();
504: PrintWriter out = new PrintWriter(sw);
505:
506: for (int i = 0; i < messages.size(); i++) {
507: if (i > 0)
508: out.println();
509: IMessage m = (IMessage) messages.get(i);
510: out.print(excludePrefix ? m.getMessage()
511: : getFullMessage(m));
512: }
513: out.flush();
514: return sw.toString();
515: }
516:
517: public static String createDetails(IMessage[] messages) {
518: if (messages == null || messages.length == 0)
519: return null;
520: StringWriter sw = new StringWriter();
521: PrintWriter out = new PrintWriter(sw);
522:
523: for (int i = 0; i < messages.length; i++) {
524: if (i > 0)
525: out.println();
526: out.print(getFullMessage(messages[i]));
527: }
528: out.flush();
529: return sw.toString();
530: }
531:
532: /*
533: * (non-Javadoc)
534: *
535: * @see org.eclipse.ui.forms.IMessageManager#createSummary(org.eclipse.ui.forms.IMessage[])
536: */
537: public String createSummary(IMessage[] messages) {
538: return createDetails(messages);
539: }
540:
541: private void pruneControlDecorators() {
542: for (Iterator iter = decorators.values().iterator(); iter
543: .hasNext();) {
544: ControlDecorator dec = (ControlDecorator) iter.next();
545: if (dec.isDisposed())
546: iter.remove();
547: }
548: }
549:
550: /*
551: * (non-Javadoc)
552: *
553: * @see org.eclipse.ui.forms.IMessageManager#getMessagePrefixProvider()
554: */
555: public IMessagePrefixProvider getMessagePrefixProvider() {
556: return prefixProvider;
557: }
558:
559: /*
560: * (non-Javadoc)
561: *
562: * @see org.eclipse.ui.forms.IMessageManager#setMessagePrefixProvider(org.eclipse.ui.forms.IMessagePrefixProvider)
563: */
564: public void setMessagePrefixProvider(IMessagePrefixProvider provider) {
565: this .prefixProvider = provider;
566: for (Iterator iter = decorators.values().iterator(); iter
567: .hasNext();) {
568: ControlDecorator dec = (ControlDecorator) iter.next();
569: dec.updatePrefix();
570: }
571: }
572:
573: /*
574: * (non-Javadoc)
575: *
576: * @see org.eclipse.ui.forms.IMessageManager#getDecorationPosition()
577: */
578: public int getDecorationPosition() {
579: return decorationPosition;
580: }
581:
582: /*
583: * (non-Javadoc)
584: *
585: * @see org.eclipse.ui.forms.IMessageManager#setDecorationPosition(int)
586: */
587: public void setDecorationPosition(int position) {
588: this .decorationPosition = position;
589: for (Iterator iter = decorators.values().iterator(); iter
590: .hasNext();) {
591: ControlDecorator dec = (ControlDecorator) iter.next();
592: dec.updatePosition();
593: }
594: }
595:
596: /*
597: * (non-Javadoc)
598: *
599: * @see org.eclipse.ui.forms.IMessageManager#isAutoUpdate()
600: */
601: public boolean isAutoUpdate() {
602: return autoUpdate;
603: }
604:
605: /*
606: * (non-Javadoc)
607: *
608: * @see org.eclipse.ui.forms.IMessageManager#setAutoUpdate(boolean)
609: */
610: public void setAutoUpdate(boolean autoUpdate) {
611: boolean needsUpdate = !this.autoUpdate && autoUpdate;
612: this.autoUpdate = autoUpdate;
613: if (needsUpdate)
614: update();
615: }
616: }
|