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