comparison dwt/custom/CLabel.d @ 0:380af2bdd8e5

Upload of whole dwt tree
author Jacob Carlborg <doob@me.com> <jacob.carlborg@gmail.com>
date Sat, 09 Aug 2008 17:00:02 +0200
parents
children f565d3a95c0a
comparison
equal deleted inserted replaced
-1:000000000000 0:380af2bdd8e5
1 /*******************************************************************************
2 * Copyright (c) 2000, 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 * Port to the D Programming language:
12 * Jacob Carlborg <jacob.carlborg@gmail.com>
13 *******************************************************************************/
14 module dwt.custom.CLabel;
15
16 import dwt.DWT;
17 import dwt.DWTException;
18 import dwt.accessibility.ACC;
19 import dwt.accessibility.Accessible;
20 import dwt.accessibility.AccessibleAdapter;
21 import dwt.accessibility.AccessibleControlAdapter;
22 import dwt.accessibility.AccessibleControlEvent;
23 import dwt.accessibility.AccessibleEvent;
24 import dwt.events.DisposeEvent;
25 import dwt.events.DisposeListener;
26 import dwt.events.PaintEvent;
27 import dwt.events.PaintListener;
28 import dwt.events.TraverseEvent;
29 import dwt.events.TraverseListener;
30 import dwt.graphics.Color;
31 import dwt.graphics.Font;
32 import dwt.graphics.GC;
33 import dwt.graphics.Image;
34 import dwt.graphics.Point;
35 import dwt.graphics.Rectangle;
36 import dwt.graphics.TextLayout;
37 import dwt.widgets.Canvas;
38 import dwt.widgets.Composite;
39 import dwt.widgets.Control;
40 import dwt.widgets.Display;
41
42 import dwt.dwthelper.utils;
43
44 /**
45 * A Label which supports aligned text and/or an image and different border styles.
46 * <p>
47 * If there is not enough space a CLabel uses the following strategy to fit the
48 * information into the available space:
49 * <pre>
50 * ignores the indent in left align mode
51 * ignores the image and the gap
52 * shortens the text by replacing the center portion of the label with an ellipsis
53 * shortens the text by removing the center portion of the label
54 * </pre>
55 * <p>
56 * <dl>
57 * <dt><b>Styles:</b>
58 * <dd>LEFT, RIGHT, CENTER, SHADOW_IN, SHADOW_OUT, SHADOW_NONE</dd>
59 * <dt><b>Events:</b>
60 * <dd></dd>
61 * </dl>
62 *
63 * </p><p>
64 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
65 * </p>
66 */
67 public class CLabel : Canvas
68 {
69
70 /** Gap between icon and text */
71 private static const int GAP = 5;
72 /** Left and right margins */
73 private static const int INDENT = 3;
74 /** a String inserted in the middle of text that has been shortened */
75 private static const String ELLIPSIS = "..."; //$NON-NLS-1$ // could use the ellipsis glyph on some platforms "\u2026"
76 /** the alignnment. Either CENTER, RIGHT, LEFT. Default is LEFT*/
77 private int alignn = DWT.LEFT;
78 private int hIndent = INDENT;
79 private int vIndent = INDENT;
80 /** the current text */
81 private String text;
82 /** the current icon */
83 private Image image;
84 // The tooltip is used for two purposes - the application can set
85 // a tooltip or the tooltip can be used to display the full text when the
86 // the text has been truncated due to the label being too short.
87 // The appToolTip stores the tooltip set by the application. Control.tooltiptext
88 // contains whatever tooltip is currently being displayed.
89 private String appToolTipText;
90
91 private Image backgroundImage;
92 private Color[] gradientColors;
93 private int[] gradientPercents;
94 private bool gradientVertical;
95 private Color background;
96
97 private static int
98 DRAW_FLAGS = DWT.DRAW_MNEMONIC | DWT.DRAW_TAB | DWT.DRAW_TRANSPARENT | DWT.DRAW_DELIMITER;
99
100 /**
101 * Constructs a new instance of this class given its parent
102 * and a style value describing its behavior and appearance.
103 * <p>
104 * The style value is either one of the style constants defined in
105 * class <code>DWT</code> which is applicable to instances of this
106 * class, or must be built by <em>bitwise OR</em>'ing together
107 * (that is, using the <code>int</code> "|" operator) two or more
108 * of those <code>DWT</code> style constants. The class description
109 * lists the style constants that are applicable to the class.
110 * Style bits are also inherited from superclasses.
111 * </p>
112 *
113 * @param parent a widget which will be the parent of the new instance (cannot be null)
114 * @param style the style of widget to construct
115 *
116 * @exception IllegalArgumentException <ul>
117 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
118 * </ul>
119 * @exception DWTException <ul>
120 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
121 * </ul>
122 *
123 * @see DWT#LEFT
124 * @see DWT#RIGHT
125 * @see DWT#CENTER
126 * @see DWT#SHADOW_IN
127 * @see DWT#SHADOW_OUT
128 * @see DWT#SHADOW_NONE
129 * @see #getStyle()
130 */
131 public this (Composite parent, int style)
132 {
133 super(parent, checkStyle(style));
134 if ((style & (DWT.CENTER | DWT.RIGHT)) is 0)
135 style |= DWT.LEFT;
136 if ((style & DWT.CENTER) !is 0)
137 alignn = DWT.CENTER;
138 if ((style & DWT.RIGHT) !is 0)
139 alignn = DWT.RIGHT;
140 if ((style & DWT.LEFT) !is 0)
141 alignn = DWT.LEFT;
142
143 addPaintListener(new class PaintListener
144 {
145 public void paintControl (PaintEvent event)
146 {
147 onPaint(event);
148 }
149 });
150
151 addDisposeListener(new class DisposeListener
152 {
153 public void widgetDisposed (DisposeEvent event)
154 {
155 onDispose(event);
156 }
157 });
158
159 addTraverseListener(new class TraverseListener
160 {
161 public void keyTraversed (TraverseEvent event)
162 {
163 if (event.detail is DWT.TRAVERSE_MNEMONIC)
164 {
165 onMnemonic(event);
166 }
167 }
168 });
169
170 initAccessible();
171
172 }
173
174 /**
175 * Check the style bits to ensure that no invalid styles are applied.
176 */
177 private static int checkStyle (int style)
178 {
179 if ((style & DWT.BORDER) !is 0)
180 style |= DWT.SHADOW_IN;
181 int
182 mask = DWT.SHADOW_IN | DWT.SHADOW_OUT | DWT.SHADOW_NONE | DWT.LEFT_TO_RIGHT | DWT.RIGHT_TO_LEFT;
183 style = style & mask;
184 return style |= DWT.NO_FOCUS | DWT.DOUBLE_BUFFERED;
185 }
186
187 //protected void checkSubclass () {
188 // String name = getClass().getName ();
189 // String validName = CLabel.class.getName();
190 // if (!validName.opEquals(name)) {
191 // DWT.error (DWT.ERROR_INVALID_SUBCLASS);
192 // }
193 //}
194
195 public Point computeSize (int wHint, int hHint, bool changed)
196 {
197 checkWidget();
198 Point e = getTotalSize(image, text);
199 if (wHint is DWT.DEFAULT)
200 {
201 e.x += 2 * hIndent;
202 }
203 else
204 {
205 e.x = wHint;
206 }
207 if (hHint is DWT.DEFAULT)
208 {
209 e.y += 2 * vIndent;
210 }
211 else
212 {
213 e.y = hHint;
214 }
215 return e;
216 }
217
218 /**
219 * Draw a rectangle in the given colors.
220 */
221 private void drawBevelRect (GC gc, int x, int y, int w, int h,
222 Color topleft, Color bottomright)
223 {
224 gc.setForeground(bottomright);
225 gc.drawLine(x + w, y, x + w, y + h);
226 gc.drawLine(x, y + h, x + w, y + h);
227
228 gc.setForeground(topleft);
229 gc.drawLine(x, y, x + w - 1, y);
230 gc.drawLine(x, y, x, y + h - 1);
231 }
232
233 /*
234 * Return the lowercase of the first non-'&' character following
235 * an '&' character in the given String. If there are no '&'
236 * characters in the given String, return '\0'.
237 */
238 char _findMnemonic (String str)
239 {
240 if (str is null)
241 return '\0';
242 int index = 0;
243 int length = str.length();
244 do
245 {
246 while (index < length && str.charAt(index) !is '&')
247 index++;
248 if (++index >= length)
249 return '\0';
250 if (str.charAt(index) !is '&')
251 return CharacterToLower(str.charAt(index));
252 index++;
253 } while (index < length);
254 return '\0';
255 }
256
257 /**
258 * Returns the alignnment.
259 * The alignnment style (LEFT, CENTER or RIGHT) is returned.
260 *
261 * @return DWT.LEFT, DWT.RIGHT or DWT.CENTER
262 */
263 public int getAlignment ()
264 {
265 //checkWidget();
266 return alignn;
267 }
268
269 /**
270 * Return the CLabel's image or <code>null</code>.
271 *
272 * @return the image of the label or null
273 */
274 public Image getImage ()
275 {
276 //checkWidget();
277 return image;
278 }
279
280 /**
281 * Compute the minimum size.
282 */
283 private Point getTotalSize (Image image, String text)
284 {
285 Point size = new Point(0, 0);
286
287 if (image !is null)
288 {
289 Rectangle r = image.getBounds();
290 size.x += r.width;
291 size.y += r.height;
292 }
293
294 GC gc = new GC(this);
295 if (text !is null && text.length() > 0)
296 {
297 Point e = gc.textExtent(text, DRAW_FLAGS);
298 size.x += e.x;
299 size.y = Math.max(size.y, e.y);
300 if (image !is null)
301 size.x += GAP;
302 }
303 else
304 {
305 size.y = Math.max(size.y, gc.getFontMetrics().getHeight());
306 }
307 gc.dispose();
308
309 return size;
310 }
311
312 public int getStyle ()
313 {
314 int style = super.getStyle();
315 switch (alignn)
316 {
317 case DWT.RIGHT:
318 style |= DWT.RIGHT;
319 break;
320 case DWT.CENTER:
321 style |= DWT.CENTER;
322 break;
323 case DWT.LEFT:
324 style |= DWT.LEFT;
325 break;
326 }
327 return style;
328 }
329
330 /**
331 * Return the Label's text.
332 *
333 * @return the text of the label or null
334 */
335 public String getText ()
336 {
337 //checkWidget();
338 return text;
339 }
340
341 public String getToolTipText ()
342 {
343 checkWidget();
344 return appToolTipText;
345 }
346
347 private void initAccessible ()
348 {
349 Accessible accessible = getAccessible();
350 accessible.addAccessibleListener(new class AccessibleAdapter
351 {
352 public void getName (AccessibleEvent e)
353 {
354 e.result = getText();
355 }
356
357 public void getHelp (AccessibleEvent e)
358 {
359 e.result = getToolTipText();
360 }
361
362 public void getKeyboardShortcut (AccessibleEvent e)
363 {
364 char mnemonic = _findMnemonic(this.text);
365 if (mnemonic !is '\0')
366 {
367 e.result = "Alt+" + mnemonic; //$NON-NLS-1$
368 }
369 }
370 } );
371
372 accessible.addAccessibleControlListener(new class
373 AccessibleControlAdapter
374 {
375 public void getChildAtPoint (AccessibleControlEvent e)
376 {
377 e.childID = ACC.CHILDID_SELF;
378 }
379
380 public void getLocation (AccessibleControlEvent e)
381 {
382 Rectangle rect = getDisplay().map(getParent(), null,
383 getBounds());
384 e.x = rect.x;
385 e.y = rect.y;
386 e.width = rect.width;
387 e.height = rect.height;
388 }
389
390 public void getChildCount (AccessibleControlEvent e)
391 {
392 e.detail = 0;
393 }
394
395 public void getRole (AccessibleControlEvent e)
396 {
397 e.detail = ACC.ROLE_LABEL;
398 }
399
400 public void getState (AccessibleControlEvent e)
401 {
402 e.detail = ACC.STATE_READONLY;
403 }
404 });
405 }
406
407 void onDispose (DisposeEvent event)
408 {
409 gradientColors = null;
410 gradientPercents = null;
411 backgroundImage = null;
412 text = null;
413 image = null;
414 appToolTipText = null;
415 }
416
417 void onMnemonic (TraverseEvent event)
418 {
419 char mnemonic = _findMnemonic(text);
420 if (mnemonic is '\0')
421 return;
422 if (CharacterToLower(event.character) !is mnemonic)
423 return;
424 Composite control = this.getParent();
425 while (control !is null)
426 {
427 Control[] children = control.getChildren();
428 int index = 0;
429 while (index < children.length)
430 {
431 if (children[index] is this)
432 break;
433 index++;
434 }
435 index++;
436 if (index < children.length)
437 {
438 if (children[index].setFocus())
439 {
440 event.doit = true;
441 event.detail = DWT.TRAVERSE_NONE;
442 }
443 }
444 control = control.getParent();
445 }
446 }
447
448 void onPaint (PaintEvent event)
449 {
450 Rectangle rect = getClientArea();
451 if (rect.width is 0 || rect.height is 0)
452 return;
453
454 bool shortenText = false;
455 String t = text;
456 Image img = image;
457 int availableWidth = Math.max(0, rect.width - 2 * hIndent);
458 Point extent = getTotalSize(img, t);
459 if (extent.x > availableWidth)
460 {
461 img = null;
462 extent = getTotalSize(img, t);
463 if (extent.x > availableWidth)
464 {
465 shortenText = true;
466 }
467 }
468
469 GC gc = event.gc;
470 String[] lines = text is null ? null : splitString(text);
471
472 // shorten the text
473 if (shortenText)
474 {
475 extent.x = 0;
476 for (int i = 0; i < lines.length; i++)
477 {
478 Point e = gc.textExtent(lines[i], DRAW_FLAGS);
479 if (e.x > availableWidth)
480 {
481 lines[i] = shortenText(gc, lines[i], availableWidth);
482 extent.x = Math.max(extent.x,
483 getTotalSize(null, lines[i]).x);
484 }
485 else
486 {
487 extent.x = Math.max(extent.x, e.x);
488 }
489 }
490 if (appToolTipText is null)
491 {
492 super.setToolTipText(text);
493 }
494 }
495 else
496 {
497 super.setToolTipText(appToolTipText);
498 }
499
500 // determine horizontal position
501 int x = rect.x + hIndent;
502 if (alignn is DWT.CENTER)
503 {
504 x = (rect.width - extent.x) / 2;
505 }
506 if (alignn is DWT.RIGHT)
507 {
508 x = rect.width - hIndent - extent.x;
509 }
510
511 // draw a background image behind the text
512 try
513 {
514 if (backgroundImage !is null)
515 {
516 // draw a background image behind the text
517 Rectangle imageRect = backgroundImage.getBounds();
518 // tile image to fill space
519 gc.setBackground(getBackground());
520 gc.fillRectangle(rect);
521 int xPos = 0;
522 while (xPos < rect.width)
523 {
524 int yPos = 0;
525 while (yPos < rect.height)
526 {
527 gc.drawImage(backgroundImage, xPos, yPos);
528 yPos += imageRect.height;
529 }
530 xPos += imageRect.width;
531 }
532 }
533 else if (gradientColors !is null)
534 {
535 // draw a gradient behind the text
536 const Color oldBackground = gc.getBackground();
537 if (gradientColors.length is 1)
538 {
539 if (gradientColors[0] !is null)
540 gc.setBackground(gradientColors[0]);
541 gc.fillRectangle(0, 0, rect.width, rect.height);
542 }
543 else
544 {
545 const Color oldForeground = gc.getForeground();
546 Color lastColor = gradientColors[0];
547 if (lastColor is null)
548 lastColor = oldBackground;
549 int pos = 0;
550 for (int i = 0; i < gradientPercents.length; ++i)
551 {
552 gc.setForeground(lastColor);
553 lastColor = gradientColors[i + 1];
554 if (lastColor is null)
555 lastColor = oldBackground;
556 gc.setBackground(lastColor);
557 if (gradientVertical)
558 {
559 const int
560 gradientHeight = (gradientPercents[i] * rect.height / 100) - pos;
561 gc.fillGradientRectangle(0, pos, rect.width,
562 gradientHeight, true);
563 pos += gradientHeight;
564 }
565 else
566 {
567 const int
568 gradientWidth = (gradientPercents[i] * rect.width / 100) - pos;
569 gc.fillGradientRectangle(pos, 0, gradientWidth,
570 rect.height, false);
571 pos += gradientWidth;
572 }
573 }
574 if (gradientVertical && pos < rect.height)
575 {
576 gc.setBackground(getBackground());
577 gc.fillRectangle(0, pos, rect.width, rect.height - pos);
578 }
579 if (!gradientVertical && pos < rect.width)
580 {
581 gc.setBackground(getBackground());
582 gc.fillRectangle(pos, 0, rect.width - pos, rect.height);
583 }
584 gc.setForeground(oldForeground);
585 }
586 gc.setBackground(oldBackground);
587 }
588 else
589 {
590 if (background !is null || (getStyle() & DWT.DOUBLE_BUFFERED) is 0)
591 {
592 gc.setBackground(getBackground());
593 gc.fillRectangle(rect);
594 }
595 }
596 }
597 catch (DWTException e)
598 {
599 if ((getStyle() & DWT.DOUBLE_BUFFERED) is 0)
600 {
601 gc.setBackground(getBackground());
602 gc.fillRectangle(rect);
603 }
604 }
605
606 // draw border
607 int style = getStyle();
608 if ((style & DWT.SHADOW_IN) !is 0 || (style & DWT.SHADOW_OUT) !is 0)
609 {
610 paintBorder(gc, rect);
611 }
612
613 // draw the image
614 if (img !is null)
615 {
616 Rectangle imageRect = img.getBounds();
617 gc.drawImage(img, 0, 0, imageRect.width, imageRect.height, x,
618 (rect.height - imageRect.height) / 2, imageRect.width,
619 imageRect.height);
620 x += imageRect.width + GAP;
621 extent.x -= imageRect.width + GAP;
622 }
623 // draw the text
624 if (lines !is null)
625 {
626 int lineHeight = gc.getFontMetrics().getHeight();
627 int textHeight = lines.length * lineHeight;
628 int lineY = Math.max(vIndent,
629 rect.y + (rect.height - textHeight) / 2);
630 gc.setForeground(getForeground());
631 for (int i = 0; i < lines.length; i++)
632 {
633 int lineX = x;
634 if (lines.length > 1)
635 {
636 if (alignn is DWT.CENTER)
637 {
638 int lineWidth = gc.textExtent(lines[i], DRAW_FLAGS).x;
639 lineX = x + Math.max(0, (extent.x - lineWidth) / 2);
640 }
641 if (alignn is DWT.RIGHT)
642 {
643 int lineWidth = gc.textExtent(lines[i], DRAW_FLAGS).x;
644 lineX = Math.max(x,
645 rect.x + rect.width - hIndent - lineWidth);
646 }
647 }
648 gc.drawText(lines[i], lineX, lineY, DRAW_FLAGS);
649 lineY += lineHeight;
650 }
651 }
652 }
653
654 /**
655 * Paint the Label's border.
656 */
657 private void paintBorder (GC gc, Rectangle r)
658 {
659 Display disp = getDisplay();
660
661 Color c1 = null;
662 Color c2 = null;
663
664 int style = getStyle();
665 if ((style & DWT.SHADOW_IN) !is 0)
666 {
667 c1 = disp.getSystemColor(DWT.COLOR_WIDGET_NORMAL_SHADOW);
668 c2 = disp.getSystemColor(DWT.COLOR_WIDGET_HIGHLIGHT_SHADOW);
669 }
670 if ((style & DWT.SHADOW_OUT) !is 0)
671 {
672 c1 = disp.getSystemColor(DWT.COLOR_WIDGET_LIGHT_SHADOW);
673 c2 = disp.getSystemColor(DWT.COLOR_WIDGET_NORMAL_SHADOW);
674 }
675
676 if (c1 !is null && c2 !is null)
677 {
678 gc.setLineWidth(1);
679 drawBevelRect(gc, r.x, r.y, r.width - 1, r.height - 1, c1, c2);
680 }
681 }
682
683 /**
684 * Set the alignnment of the CLabel.
685 * Use the values LEFT, CENTER and RIGHT to alignn image and text within the available space.
686 *
687 * @param alignn the alignnment style of LEFT, RIGHT or CENTER
688 *
689 * @exception DWTException <ul>
690 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
691 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
692 * <li>ERROR_INVALID_ARGUMENT - if the value of alignn is not one of DWT.LEFT, DWT.RIGHT or DWT.CENTER</li>
693 * </ul>
694 */
695 public void setAlignment (int alignn)
696 {
697 checkWidget();
698 if (alignn !is DWT.LEFT && alignn !is DWT.RIGHT && alignn !is DWT.CENTER)
699 {
700 DWT.error(DWT.ERROR_INVALID_ARGUMENT);
701 }
702 if (this.alignn !is alignn)
703 {
704 this.alignn = alignn;
705 redraw();
706 }
707 }
708
709 public void setBackground (Color color)
710 {
711 super.setBackground(color);
712 // Are these settings the same as before?
713 if (backgroundImage is null && gradientColors is null && gradientPercents is null)
714 {
715 if (color is null)
716 {
717 if (background is null)
718 return;
719 }
720 else
721 {
722 if (color.opEquals(background))
723 return;
724 }
725 }
726 background = color;
727 backgroundImage = null;
728 gradientColors = null;
729 gradientPercents = null;
730 redraw();
731 }
732
733 /**
734 * Specify a gradient of colours to be drawn in the background of the CLabel.
735 * <p>For example, to draw a gradient that varies from dark blue to blue and then to
736 * white and stays white for the right half of the label, use the following call
737 * to setBackground:</p>
738 * <pre>
739 * clabel.setBackground(new Color[]{display.getSystemColor(DWT.COLOR_DARK_BLUE),
740 * display.getSystemColor(DWT.COLOR_BLUE),
741 * display.getSystemColor(DWT.COLOR_WHITE),
742 * display.getSystemColor(DWT.COLOR_WHITE)},
743 * new int[] {25, 50, 100});
744 * </pre>
745 *
746 * @param colors an array of Color that specifies the colors to appear in the gradient
747 * in order of appearance from left to right; The value <code>null</code>
748 * clears the background gradient; the value <code>null</code> can be used
749 * inside the array of Color to specify the background color.
750 * @param percents an array of integers between 0 and 100 specifying the percent of the width
751 * of the widget at which the color should change; the size of the percents
752 * array must be one less than the size of the colors array.
753 *
754 * @exception DWTException <ul>
755 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
756 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
757 * <li>ERROR_INVALID_ARGUMENT - if the values of colors and percents are not consistent</li>
758 * </ul>
759 */
760 public void setBackground (Color[] colors, int[] percents)
761 {
762 setBackground(colors, percents, false);
763 }
764
765 /**
766 * Specify a gradient of colours to be drawn in the background of the CLabel.
767 * <p>For example, to draw a gradient that varies from dark blue to white in the vertical,
768 * direction use the following call
769 * to setBackground:</p>
770 * <pre>
771 * clabel.setBackground(new Color[]{display.getSystemColor(DWT.COLOR_DARK_BLUE),
772 * display.getSystemColor(DWT.COLOR_WHITE)},
773 * new int[] {100}, true);
774 * </pre>
775 *
776 * @param colors an array of Color that specifies the colors to appear in the gradient
777 * in order of appearance from left/top to right/bottom; The value <code>null</code>
778 * clears the background gradient; the value <code>null</code> can be used
779 * inside the array of Color to specify the background color.
780 * @param percents an array of integers between 0 and 100 specifying the percent of the width/height
781 * of the widget at which the color should change; the size of the percents
782 * array must be one less than the size of the colors array.
783 * @param vertical indicate the direction of the gradient. True is vertical and false is horizontal.
784 *
785 * @exception DWTException <ul>
786 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
787 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
788 * <li>ERROR_INVALID_ARGUMENT - if the values of colors and percents are not consistent</li>
789 * </ul>
790 *
791 * @since 3.0
792 */
793 public void setBackground (Color[] colors, int[] percents, bool vertical)
794 {
795 checkWidget();
796 if (colors !is null)
797 {
798 if (percents is null || percents.length !is colors.length - 1)
799 {
800 DWT.error(DWT.ERROR_INVALID_ARGUMENT);
801 }
802 if (getDisplay().getDepth() < 15)
803 {
804 // Don't use gradients on low color displays
805 colors = new Color[][colors[colors.length - 1]];
806 percents = new int[][];
807 }
808 for (int i = 0; i < percents.length; i++)
809 {
810 if (percents[i] < 0 || percents[i] > 100)
811 {
812 DWT.error(DWT.ERROR_INVALID_ARGUMENT);
813 }
814 if (i > 0 && percents[i] < percents[i - 1])
815 {
816 DWT.error(DWT.ERROR_INVALID_ARGUMENT);
817 }
818 }
819 }
820
821 // Are these settings the same as before?
822 const Color background = getBackground();
823 if (backgroundImage is null)
824 {
825 if ((gradientColors !is null) && (colors !is null) && (gradientColors.length is colors.length))
826 {
827 bool same = false;
828 for (int i = 0; i < gradientColors.length; i++)
829 {
830 same = (gradientColors[i] is colors[i]) || ((gradientColors[i] is null) && (colors[i] is background)) || ((gradientColors[i] is background) && (colors[i] is null));
831 if (!same)
832 break;
833 }
834 if (same)
835 {
836 for (int i = 0; i < gradientPercents.length; i++)
837 {
838 same = gradientPercents[i] is percents[i];
839 if (!same)
840 break;
841 }
842 }
843 if (same && this.gradientVertical is vertical)
844 return;
845 }
846 }
847 else
848 {
849 backgroundImage = null;
850 }
851 // Store the new settings
852 if (colors is null)
853 {
854 gradientColors = null;
855 gradientPercents = null;
856 gradientVertical = false;
857 }
858 else
859 {
860 gradientColors = new Color[colors.length];
861 for (int i = 0; i < colors.length; ++i)
862 gradientColors[i] = (colors[i] !is null) ? colors[i] : background;
863 gradientPercents = new int[percents.length];
864 for (int i = 0; i < percents.length; ++i)
865 gradientPercents[i] = percents[i];
866 gradientVertical = vertical;
867 }
868 // Refresh with the new settings
869 redraw();
870 }
871
872 /**
873 * Set the image to be drawn in the background of the label.
874 *
875 * @param image the image to be drawn in the background
876 *
877 * @exception DWTException <ul>
878 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
879 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
880 * </ul>
881 */
882 public void setBackground (Image image)
883 {
884 checkWidget();
885 if (image is backgroundImage)
886 return;
887 if (image !is null)
888 {
889 gradientColors = null;
890 gradientPercents = null;
891 }
892 backgroundImage = image;
893 redraw();
894
895 }
896
897 public void setFont (Font font)
898 {
899 super.setFont(font);
900 redraw();
901 }
902
903 /**
904 * Set the label's Image.
905 * The value <code>null</code> clears it.
906 *
907 * @param image the image to be displayed in the label or null
908 *
909 * @exception DWTException <ul>
910 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
911 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
912 * </ul>
913 */
914 public void setImage (Image image)
915 {
916 checkWidget();
917 if (image !is this.image)
918 {
919 this.image = image;
920 redraw();
921 }
922 }
923
924 /**
925 * Set the label's text.
926 * The value <code>null</code> clears it.
927 *
928 * @param text the text to be displayed in the label or null
929 *
930 * @exception DWTException <ul>
931 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
932 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
933 * </ul>
934 */
935 public void setText (String text)
936 {
937 checkWidget();
938 if (text is null)
939 text = ""; //$NON-NLS-1$
940 if (!text.opEquals(this.text))
941 {
942 this.text = text;
943 redraw();
944 }
945 }
946
947 public void setToolTipText (String String)
948 {
949 super.setToolTipText(String);
950 appToolTipText = super.getToolTipText();
951 }
952
953 /**
954 * Shorten the given text <code>t</code> so that its length doesn't exceed
955 * the given width. The default implementation replaces characters in the
956 * center of the original String with an ellipsis ("...").
957 * Override if you need a different strategy.
958 *
959 * @param gc the gc to use for text measurement
960 * @param t the text to shorten
961 * @param width the width to shorten the text to, in pixels
962 * @return the shortened text
963 */
964 protected String shortenText (GC gc, String t, int width)
965 {
966 if (t is null)
967 return null;
968 int w = gc.textExtent(ELLIPSIS, DRAW_FLAGS).x;
969 if (width <= w)
970 return t;
971 int l = t.length();
972 int max = l / 2;
973 int min = 0;
974 int mid = (max + min) / 2 - 1;
975 if (mid <= 0)
976 return t;
977 TextLayout layout = new TextLayout(getDisplay());
978 layout.setText(t);
979 mid = validateOffset(layout, mid);
980 while (min < mid && mid < max)
981 {
982 String s1 = t.substring(0, mid);
983 String s2 = t.substring(validateOffset(layout, l - mid), l);
984 int l1 = gc.textExtent(s1, DRAW_FLAGS).x;
985 int l2 = gc.textExtent(s2, DRAW_FLAGS).x;
986 if (l1 + w + l2 > width)
987 {
988 max = mid;
989 mid = validateOffset(layout, (max + min) / 2);
990 }
991 else if (l1 + w + l2 < width)
992 {
993 min = mid;
994 mid = validateOffset(layout, (max + min) / 2);
995 }
996 else
997 {
998 min = max;
999 }
1000 }
1001 String
1002 result = mid is 0 ? t : t.substring(0, mid) + ELLIPSIS + t.substring(
1003 validateOffset(layout, l - mid), l);
1004 layout.dispose();
1005 return result;
1006 }
1007
1008 int validateOffset (TextLayout layout, int offset)
1009 {
1010 int nextOffset = layout.getNextOffset(offset, DWT.MOVEMENT_CLUSTER);
1011 if (nextOffset !is offset)
1012 return layout.getPreviousOffset(nextOffset, DWT.MOVEMENT_CLUSTER);
1013 return offset;
1014 }
1015
1016 private String[] splitString (String text)
1017 {
1018 String[] lines = new String[1];
1019 int start = 0, pos;
1020 do
1021 {
1022 pos = text.indexOf('\n', start);
1023 if (pos is -1)
1024 {
1025 lines[lines.length - 1] = text.substring(start);
1026 }
1027 else
1028 {
1029 bool crlf = (pos > 0) && (text.charAt(pos - 1) is '\r');
1030 lines[lines.length - 1] = text.substring(start,
1031 pos - (crlf ? 1 : 0));
1032 start = pos + 1;
1033 String[] newLines = new String[lines.length + 1];
1034 System.arraycopy(lines, 0, newLines, 0, lines.length);
1035 lines = newLines;
1036 }
1037 } while (pos !is -1);
1038 return lines;
1039 }
1040 }