Mercurial > projects > dwt-mac
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 } |