Added
Link Here
|
1 |
/******************************************************************************* |
2 |
* Copyright (c) 2006 IBM Corporation and others. |
3 |
* All rights reserved. This program and the accompanying materials |
4 |
* are made available under the terms of the Eclipse Public License v1.0 |
5 |
* which accompanies this distribution, and is available at |
6 |
* http://www.eclipse.org/legal/epl-v10.html |
7 |
* |
8 |
* Contributors: |
9 |
* IBM Corporation - initial API and implementation |
10 |
******************************************************************************/ |
11 |
|
12 |
package org.eclipse.jface.fieldassist; |
13 |
|
14 |
import java.io.PrintWriter; |
15 |
import java.io.StringWriter; |
16 |
import java.util.ArrayList; |
17 |
import java.util.Enumeration; |
18 |
import java.util.Hashtable; |
19 |
|
20 |
import org.eclipse.jface.dialogs.IMessageContainer; |
21 |
import org.eclipse.jface.dialogs.IMessageContainerWithDetails; |
22 |
import org.eclipse.jface.dialogs.IMessageProvider; |
23 |
import org.eclipse.jface.resource.JFaceResources; |
24 |
import org.eclipse.swt.SWT; |
25 |
import org.eclipse.swt.events.DisposeEvent; |
26 |
import org.eclipse.swt.events.DisposeListener; |
27 |
import org.eclipse.swt.widgets.Composite; |
28 |
import org.eclipse.swt.widgets.Control; |
29 |
import org.eclipse.swt.widgets.Label; |
30 |
|
31 |
/** |
32 |
* Use this class to work with message containers that contain decorated fields. |
33 |
* The class provides for: |
34 |
* <ul> |
35 |
* <li>Bridging the concept of messages and field decorations</li> |
36 |
* <li>Adding multiple messages per field</li> |
37 |
* <li>Rolling up local messages to the message container</li> |
38 |
* <li>Adding multiple general messages to the message container</li> |
39 |
* </ul> |
40 |
* |
41 |
* <p><strong>EXPERIMENTAL</strong> This class or interface has been added as part |
42 |
* of a work in progress. This API may change at any given time. Please do not |
43 |
* use this API without consulting with the Platform/UI team.</p> |
44 |
* |
45 |
* @see IMessageContainer |
46 |
* @see IMessageContainerWithDetails |
47 |
* @since 3.3 |
48 |
*/ |
49 |
|
50 |
public class MessageManager { |
51 |
private ArrayList messages = new ArrayList(); |
52 |
private Hashtable decorators = new Hashtable(); |
53 |
private boolean showOnlyOnFocus; |
54 |
private IMessageContainer messageContainer; |
55 |
private static FieldDecoration standardError = FieldDecorationRegistry |
56 |
.getDefault().getFieldDecoration(FieldDecorationRegistry.DEC_ERROR); |
57 |
private static FieldDecoration standardWarning = FieldDecorationRegistry |
58 |
.getDefault().getFieldDecoration( |
59 |
FieldDecorationRegistry.DEC_WARNING); |
60 |
|
61 |
private static final String[] SINGLE_MESSAGE_SUMMARY_KEYS = { |
62 |
"MessageManager.sMessageSummary", //$NON-NLS-1$ |
63 |
"MessageManager.sMessageSummary", //$NON-NLS-1$ |
64 |
"MessageManager.sWarningSummary", //$NON-NLS-1$ |
65 |
"MessageManager.sErrorSummary" //$NON-NLS-1$ |
66 |
}; |
67 |
|
68 |
private static final String[] MULTIPLE_MESSAGE_SUMMARY_KEYS = { |
69 |
"MessageManager.pMessageSummary", //$NON-NLS-1$ |
70 |
"MessageManager.pMessageSummary", //$NON-NLS-1$ |
71 |
"MessageManager.pWarningSummary", //$NON-NLS-1$ |
72 |
"MessageManager.pErrorSummary" //$NON-NLS-1$ |
73 |
}; |
74 |
|
75 |
class Message { |
76 |
String prefix; |
77 |
String id; |
78 |
int type; |
79 |
String message; |
80 |
|
81 |
Message(String prefix, String id, String message, int type) { |
82 |
this.id = id; |
83 |
this.message = message; |
84 |
this.type = type; |
85 |
this.prefix = prefix; |
86 |
} |
87 |
|
88 |
String getFullMessage() { |
89 |
if (prefix == null) |
90 |
return message; |
91 |
return prefix + message; |
92 |
} |
93 |
} |
94 |
|
95 |
class ControlDecorator implements IMessageContainerWithDetails { |
96 |
private ControlDecoration decoration; |
97 |
private ArrayList controlMessages = new ArrayList(); |
98 |
private String message; |
99 |
private int type; |
100 |
private String prefix; |
101 |
|
102 |
ControlDecorator(Control control) { |
103 |
this.decoration = new ControlDecoration(control, SWT.LEFT |
104 |
| SWT.BOTTOM); |
105 |
updateFocusSetting(); |
106 |
} |
107 |
|
108 |
String getPrefix() { |
109 |
if (prefix == null) |
110 |
createPrefix(); |
111 |
return prefix; |
112 |
} |
113 |
|
114 |
void updateFocusSetting() { |
115 |
if (getShowOnlyOnFocus() != decoration.getShowOnlyOnFocus()) |
116 |
decoration.setShowOnlyOnFocus(getShowOnlyOnFocus()); |
117 |
} |
118 |
|
119 |
private void createPrefix() { |
120 |
Control c = decoration.getControl(); |
121 |
Composite parent = c.getParent(); |
122 |
Control[] siblings = parent.getChildren(); |
123 |
for (int i = 0; i < siblings.length; i++) { |
124 |
if (siblings[i] == c) { |
125 |
// this is us - go backward until you hit |
126 |
// a label |
127 |
for (int j = i - 1; j >= 0; j--) { |
128 |
Control label = siblings[j]; |
129 |
if (label instanceof Label) { |
130 |
prefix = ((Label) label).getText() + ": "; //$NON-NLS-1$ |
131 |
return; |
132 |
} |
133 |
} |
134 |
break; |
135 |
} |
136 |
} |
137 |
// make a prefix anyway |
138 |
prefix = ""; //$NON-NLS-1$ |
139 |
} |
140 |
|
141 |
void addAll(ArrayList target) { |
142 |
target.addAll(controlMessages); |
143 |
} |
144 |
|
145 |
void addMessage(String id, String text, int type) { |
146 |
MessageManager.this.addMessage(getPrefix(), id, text, type, |
147 |
controlMessages); |
148 |
updateMessageContainer(this, controlMessages, true); |
149 |
} |
150 |
|
151 |
void removeMessage(String id) { |
152 |
Message message = findMessage(id, controlMessages); |
153 |
if (message != null) { |
154 |
controlMessages.remove(message); |
155 |
updateMessageContainer(this, controlMessages, true); |
156 |
} |
157 |
} |
158 |
|
159 |
void removeMessages() { |
160 |
controlMessages.clear(); |
161 |
updateMessageContainer(this, controlMessages, true); |
162 |
} |
163 |
|
164 |
boolean isEmpty() { |
165 |
return controlMessages.isEmpty(); |
166 |
} |
167 |
|
168 |
/* |
169 |
* (non-Javadoc) |
170 |
* |
171 |
* @see org.eclipse.jface.dialogs.IMessageContainer#setMessage(java.lang.String, |
172 |
* int) |
173 |
*/ |
174 |
public void setMessage(String newMessage, int newType) { |
175 |
if (this.message != null && newMessage != null |
176 |
&& newMessage.equals(this.message) && newType == this.type) |
177 |
return; |
178 |
this.message = newMessage; |
179 |
this.type = newType; |
180 |
update(); |
181 |
} |
182 |
|
183 |
public void setMessage(String newMessage, String details, int type) { |
184 |
setMessage(newMessage, type); |
185 |
} |
186 |
|
187 |
/* |
188 |
* (non-Javadoc) |
189 |
* |
190 |
* @see org.eclipse.jface.dialogs.IMessageProvider#getMessage() |
191 |
*/ |
192 |
public String getMessage() { |
193 |
return message; |
194 |
} |
195 |
|
196 |
/* |
197 |
* (non-Javadoc) |
198 |
* |
199 |
* @see org.eclipse.jface.dialogs.IMessageProvider#getMessageType() |
200 |
*/ |
201 |
public int getMessageType() { |
202 |
return type; |
203 |
} |
204 |
|
205 |
private void update() { |
206 |
if (message == null) |
207 |
decoration.hide(); |
208 |
else { |
209 |
if (type == IMessageProvider.ERROR) |
210 |
decoration.setImage(standardError.getImage()); |
211 |
else if (type == IMessageProvider.WARNING) |
212 |
decoration.setImage(standardWarning.getImage()); |
213 |
decoration.setDescriptionText(message); |
214 |
decoration.show(); |
215 |
} |
216 |
} |
217 |
} |
218 |
|
219 |
/** |
220 |
* Creates a new instance of the message manager that will work with the |
221 |
* provided message container. |
222 |
* |
223 |
* @param messageContainer |
224 |
* the container to control |
225 |
*/ |
226 |
public MessageManager(IMessageContainer messageContainer) { |
227 |
this.messageContainer = messageContainer; |
228 |
} |
229 |
|
230 |
/** |
231 |
* Adds a general message that is not associated with any decorated field. |
232 |
* |
233 |
* @param id |
234 |
* a unique message identifier that will be used to look the |
235 |
* message up later |
236 |
* |
237 |
* @param messageText |
238 |
* the message to add |
239 |
* @param type |
240 |
* the message type as defined in <code>IMessageProvider</code>. |
241 |
*/ |
242 |
|
243 |
public void addMessage(String id, String messageText, int type) { |
244 |
addMessage(null, id, messageText, type, messages); |
245 |
updateMessageContainer(); |
246 |
} |
247 |
|
248 |
/** |
249 |
* Adds a message that should be associated with the provided control. |
250 |
* |
251 |
* @param id |
252 |
* the unique message identifier |
253 |
* @param messageText |
254 |
* the message to add |
255 |
* @param type |
256 |
* the message type |
257 |
* @param control |
258 |
* the control to associate the message with |
259 |
*/ |
260 |
|
261 |
public void addMessage(String id, String messageText, int type, |
262 |
Control control) { |
263 |
ControlDecorator dec = (ControlDecorator) decorators.get(control); |
264 |
if (dec == null) { |
265 |
dec = new ControlDecorator(control); |
266 |
decorators.put(control, dec); |
267 |
control.addDisposeListener(new DisposeListener() { |
268 |
/* |
269 |
* (non-Javadoc) |
270 |
* |
271 |
* @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent) |
272 |
*/ |
273 |
public void widgetDisposed(DisposeEvent e) { |
274 |
decorators.remove(e.widget); |
275 |
updateMessageContainer(); |
276 |
} |
277 |
}); |
278 |
} |
279 |
dec.addMessage(id, messageText, type); |
280 |
updateMessageContainer(); |
281 |
} |
282 |
|
283 |
/** |
284 |
* Set the boolean that controls whether the control decorations are shown |
285 |
* only when the controls have focus. The default value of this setting is |
286 |
* <code>false</code>. |
287 |
* |
288 |
* @param showOnlyOnFocus |
289 |
* <code>true</code> if the decoration should only be shown |
290 |
* when the control has focus, and <code>false</code> if it |
291 |
* should always be shown. |
292 |
*/ |
293 |
public void setShowOnlyOnFocus(boolean showOnlyOnFocus) { |
294 |
this.showOnlyOnFocus = showOnlyOnFocus; |
295 |
updateMessageContainer(); |
296 |
} |
297 |
|
298 |
/** |
299 |
* Get the boolean that controls whether the control decorations are shown |
300 |
* only when the controls have focus. The default value of this setting is |
301 |
* <code>false</code>. |
302 |
* |
303 |
* @return <code>true</code> if the control decoration should only be |
304 |
* shown when the control has focus, and <code>false</code> if it |
305 |
* should always be shown. |
306 |
*/ |
307 |
public boolean getShowOnlyOnFocus() { |
308 |
return showOnlyOnFocus; |
309 |
} |
310 |
|
311 |
/** |
312 |
* Removes the provided general message. |
313 |
* |
314 |
* @param id |
315 |
* the id of the message to remove |
316 |
*/ |
317 |
|
318 |
public void removeMessage(String id) { |
319 |
Message message = findMessage(id, messages); |
320 |
if (message != null) { |
321 |
messages.remove(message); |
322 |
updateMessageContainer(); |
323 |
} |
324 |
} |
325 |
|
326 |
/** |
327 |
* Removes all the general messages. If there are local messages associated |
328 |
* with controls, the replacement message may show up drawing user's |
329 |
* attention to these local messages. Otherwise, the container will clear |
330 |
* the message area. |
331 |
*/ |
332 |
public void removeMessages() { |
333 |
messages.clear(); |
334 |
updateMessageContainer(); |
335 |
} |
336 |
|
337 |
/** |
338 |
* Removes the message associated with the provided control. |
339 |
* |
340 |
* @param id |
341 |
* the id of the message to remove |
342 |
* @param control |
343 |
* the control the message is associated with |
344 |
*/ |
345 |
|
346 |
public void removeMessage(String id, Control control) { |
347 |
ControlDecorator dec = (ControlDecorator) decorators.get(control); |
348 |
if (dec == null) |
349 |
return; |
350 |
dec.removeMessage(id); |
351 |
updateMessageContainer(); |
352 |
} |
353 |
|
354 |
/** |
355 |
* Removes all the messages associated with the provided control. |
356 |
* |
357 |
* @param control |
358 |
* the control the messages are associated with |
359 |
*/ |
360 |
|
361 |
public void removeMessages(Control control) { |
362 |
ControlDecorator dec = (ControlDecorator) decorators.get(control); |
363 |
dec.removeMessages(); |
364 |
updateMessageContainer(); |
365 |
} |
366 |
|
367 |
/** |
368 |
* Removes all the local field messages and all the general container |
369 |
* messages. |
370 |
*/ |
371 |
|
372 |
public void removeAllMessages() { |
373 |
for (Enumeration enm = decorators.elements(); enm.hasMoreElements();) { |
374 |
ControlDecorator control = (ControlDecorator) enm.nextElement(); |
375 |
control.removeMessages(); |
376 |
} |
377 |
messages.clear(); |
378 |
updateMessageContainer(); |
379 |
} |
380 |
|
381 |
/* |
382 |
* Adds the message if it does not already exist in the provided list. |
383 |
*/ |
384 |
|
385 |
private void addMessage(String prefix, String id, String messageText, |
386 |
int type, ArrayList list) { |
387 |
Message message = findMessage(id, list); |
388 |
if (message == null) { |
389 |
message = new Message(prefix, id, messageText, type); |
390 |
list.add(message); |
391 |
} else { |
392 |
message.message = messageText; |
393 |
message.type = type; |
394 |
} |
395 |
} |
396 |
|
397 |
/* |
398 |
* Finds the message with the provided id in the provided list. |
399 |
*/ |
400 |
|
401 |
private Message findMessage(String id, ArrayList list) { |
402 |
for (int i = 0; i < list.size(); i++) { |
403 |
Message message = (Message) list.get(i); |
404 |
if (message.id.equals(id)) |
405 |
return message; |
406 |
} |
407 |
return null; |
408 |
} |
409 |
|
410 |
/* |
411 |
* Updates the entire container by building up a merged list that contains |
412 |
* messages from each decorated field plus messages from the container |
413 |
* itself. |
414 |
*/ |
415 |
|
416 |
private void updateMessageContainer() { |
417 |
ArrayList mergedList = new ArrayList(); |
418 |
mergedList.addAll(messages); |
419 |
for (Enumeration enm = decorators.elements(); enm.hasMoreElements();) { |
420 |
ControlDecorator dec = (ControlDecorator) enm.nextElement(); |
421 |
dec.addAll(mergedList); |
422 |
dec.updateFocusSetting(); |
423 |
} |
424 |
updateMessageContainer(messageContainer, mergedList, false); |
425 |
} |
426 |
|
427 |
/* |
428 |
* This method works with a generic message container when a list of |
429 |
* messages of various types need to be shown. The messages with the highest |
430 |
* type are picked first. If there are more than one with this type, a |
431 |
* multiple message is constructed; otherwise, the message is used as-is. |
432 |
*/ |
433 |
|
434 |
private void updateMessageContainer(IMessageContainer container, |
435 |
ArrayList messages, boolean showAll) { |
436 |
if (messages.isEmpty() || messages == null) { |
437 |
container.setMessage(null, IMessageProvider.NONE); |
438 |
return; |
439 |
} |
440 |
int maxType = 0; |
441 |
// create a subset of messages with the highest type |
442 |
ArrayList peers = new ArrayList(); |
443 |
for (int i = 0; i < messages.size(); i++) { |
444 |
Message message = (Message) messages.get(i); |
445 |
if (message.type > maxType) { |
446 |
peers.clear(); |
447 |
maxType = message.type; |
448 |
} |
449 |
if (message.type == maxType) |
450 |
peers.add(message); |
451 |
} |
452 |
String messageText; |
453 |
String details = null; |
454 |
if (peers.size() == 1 && ((Message) peers.get(0)).prefix == null) { |
455 |
// a single message |
456 |
messageText = ((Message) peers.get(0)).message; |
457 |
} else { |
458 |
StringWriter sw = new StringWriter(); |
459 |
PrintWriter out = new PrintWriter(sw); |
460 |
// StringBuffer sw = new StringBuffer(); |
461 |
for (int i = 0; i < peers.size(); i++) { |
462 |
if (i > 0) |
463 |
out.println(); |
464 |
Message m = (Message) peers.get(i); |
465 |
out.print(showAll ? m.message : m.getFullMessage()); |
466 |
} |
467 |
out.flush(); |
468 |
if (showAll) |
469 |
messageText = sw.toString(); |
470 |
else { |
471 |
// show a summary message for the message |
472 |
// and list of errors for the details |
473 |
if (peers.size() > 1) |
474 |
messageText = JFaceResources.format( |
475 |
MULTIPLE_MESSAGE_SUMMARY_KEYS[maxType], |
476 |
new String[] { peers.size() + "" }); //$NON-NLS-1$ |
477 |
else |
478 |
messageText = JFaceResources |
479 |
.getString(SINGLE_MESSAGE_SUMMARY_KEYS[maxType]); |
480 |
details = sw.toString(); |
481 |
} |
482 |
} |
483 |
if (container instanceof IMessageContainerWithDetails) |
484 |
((IMessageContainerWithDetails) container).setMessage(messageText, |
485 |
details, maxType); |
486 |
else |
487 |
container.setMessage(messageText, maxType); |
488 |
} |
489 |
} |