comparison dwt/custom/CTabItem.d @ 41:6337764516f1

Sync dwt/custom with dwt-linux (took copy of complete folder)
author Frank Benoit <benoit@tionex.de>
date Tue, 07 Oct 2008 16:29:55 +0200
parents 5b53d338c709
children
comparison
equal deleted inserted replaced
40:fbe68c33eeee 41:6337764516f1
1 /******************************************************************************* 1 /*******************************************************************************
2 * Copyright (c) 2000, 2007 IBM Corporation and others. 2 * Copyright (c) 2000, 2008 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials 3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0 4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at 5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html 6 * http://www.eclipse.org/legal/epl-v10.html
7 * 7 *
8 * Contributors: 8 * Contributors:
9 * IBM Corporation - initial API and implementation 9 * IBM Corporation - initial API and implementation
10 *
11 * Port to the D programming language: 10 * Port to the D programming language:
12 * Jacob Carlborg <jacob.carlborg@gmail.com> 11 * Frank Benoit <benoit@tionex.de>
13 *******************************************************************************/ 12 *******************************************************************************/
14 module dwt.custom; 13 module dwt.custom.CTabItem;
14
15 import dwt.dwthelper.utils;
16
17
15 18
16 import dwt.DWT; 19 import dwt.DWT;
17 import dwt.DWTException; 20 import dwt.DWTException;
18 import dwt.custom.CTabFolder;
19 import dwt.graphics.Color; 21 import dwt.graphics.Color;
20 import dwt.graphics.Font; 22 import dwt.graphics.Font;
21 import dwt.graphics.GC; 23 import dwt.graphics.GC;
22 import dwt.graphics.Image; 24 import dwt.graphics.Image;
23 import dwt.graphics.Point; 25 import dwt.graphics.Point;
26 import dwt.graphics.TextLayout; 28 import dwt.graphics.TextLayout;
27 import dwt.widgets.Control; 29 import dwt.widgets.Control;
28 import dwt.widgets.Display; 30 import dwt.widgets.Display;
29 import dwt.widgets.Item; 31 import dwt.widgets.Item;
30 import dwt.widgets.Widget; 32 import dwt.widgets.Widget;
31 33 import dwt.custom.CTabFolder;
32 import dwt.dwthelper.utils;
33 34
34 /** 35 /**
35 * Instances of this class represent a selectable user interface object 36 * Instances of this class represent a selectable user interface object
36 * that represent a page in a notebook widget. 37 * that represent a page in a notebook widget.
37 * 38 *
38 * <dl> 39 * <dl>
39 * <dt><b>Styles:</b></dt> 40 * <dt><b>Styles:</b></dt>
40 * <dd>DWT.CLOSE</dd> 41 * <dd>DWT.CLOSE</dd>
41 * <dt><b>Events:</b></dt> 42 * <dt><b>Events:</b></dt>
42 * <dd>(none)</dd> 43 * <dd>(none)</dd>
43 * </dl> 44 * </dl>
44 * <p> 45 * <p>
45 * IMPORTANT: This class is <em>not</em> intended to be subclassed. 46 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
46 * </p> 47 * </p>
48 *
49 * @see <a href="http://www.eclipse.org/swt/snippets/#ctabfolder">CTabFolder, CTabItem snippets</a>
50 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
47 */ 51 */
48 public class CTabItem : Item { 52 public class CTabItem : Item {
49 CTabFolder parent; 53 CTabFolder parent;
50 int x, y, width, height = 0; 54 int x,y,width,height = 0;
51 Control control; // the tab page 55 Control control; // the tab page
52 56
53 String toolTipText; 57 String toolTipText;
54 String shortenedText; 58 String shortenedText;
55 int shortenedTextWidth; 59 int shortenedTextWidth;
56 60
57 // Appearance 61 // Appearance
58 Font font; 62 Font font;
59 Image disabledImage; 63 Image disabledImage;
60 64
61 Rectangle closeRect = new Rectangle(0, 0, 0, 0); 65 Rectangle closeRect;
62 int closeImageState = CTabFolder.NONE; 66 int closeImageState = CTabFolder.NONE;
63 bool showClose = false; 67 bool showClose = false;
64 bool showing = false; 68 bool showing = false;
65 69
66 // internal constants 70 // internal constants
70 static final int RIGHT_MARGIN = 4; 74 static final int RIGHT_MARGIN = 4;
71 static final int INTERNAL_SPACING = 4; 75 static final int INTERNAL_SPACING = 4;
72 static final int FLAGS = DWT.DRAW_TRANSPARENT | DWT.DRAW_MNEMONIC; 76 static final int FLAGS = DWT.DRAW_TRANSPARENT | DWT.DRAW_MNEMONIC;
73 static final String ELLIPSIS = "..."; //$NON-NLS-1$ // could use the ellipsis glyph on some platforms "\u2026" 77 static final String ELLIPSIS = "..."; //$NON-NLS-1$ // could use the ellipsis glyph on some platforms "\u2026"
74 78
75 /** 79 /**
76 * Constructs a new instance of this class given its parent 80 * Constructs a new instance of this class given its parent
77 * (which must be a <code>CTabFolder</code>) and a style value 81 * (which must be a <code>CTabFolder</code>) and a style value
78 * describing its behavior and appearance. The item is added 82 * describing its behavior and appearance. The item is added
79 * to the end of the items maintained by its parent. 83 * to the end of the items maintained by its parent.
80 * <p> 84 * <p>
81 * The style value is either one of the style constants defined in 85 * The style value is either one of the style constants defined in
82 * class <code>DWT</code> which is applicable to instances of this 86 * class <code>DWT</code> which is applicable to instances of this
83 * class, or must be built by <em>bitwise OR</em>'ing together 87 * class, or must be built by <em>bitwise OR</em>'ing together
84 * (that is, using the <code>int</code> "|" operator) two or more 88 * (that is, using the <code>int</code> "|" operator) two or more
85 * of those <code>DWT</code> style constants. The class description 89 * of those <code>DWT</code> style constants. The class description
86 * lists the style constants that are applicable to the class. 90 * lists the style constants that are applicable to the class.
87 * Style bits are also inherited from superclasses. 91 * Style bits are also inherited from superclasses.
88 * </p> 92 * </p>
89 * 93 *
90 * @param parent a CTabFolder which will be the parent of the new instance (cannot be null) 94 * @param parent a CTabFolder which will be the parent of the new instance (cannot be null)
91 * @param style the style of control to construct 95 * @param style the style of control to construct
92 * 96 *
93 * @exception IllegalArgumentException <ul> 97 * @exception IllegalArgumentException <ul>
94 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> 98 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
95 * </ul> 99 * </ul>
96 * @exception DWTException <ul> 100 * @exception DWTException <ul>
97 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> 101 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
98 * </ul> 102 * </ul>
99 * 103 *
100 * @see DWT 104 * @see DWT
101 * @see Widget#getStyle() 105 * @see Widget#getStyle()
102 */ 106 */
103 public this (CTabFolder parent, int style) { 107 public this (CTabFolder parent, int style) {
104 this(parent, style, parent.getItemCount()); 108 this(parent, style, parent.getItemCount());
105 } 109 }
106 110 /**
107 /** 111 * Constructs a new instance of this class given its parent
108 * Constructs a new instance of this class given its parent 112 * (which must be a <code>CTabFolder</code>), a style value
109 * (which must be a <code>CTabFolder</code>), a style value 113 * describing its behavior and appearance, and the index
110 * describing its behavior and appearance, and the index 114 * at which to place it in the items maintained by its parent.
111 * at which to place it in the items maintained by its parent. 115 * <p>
112 * <p> 116 * The style value is either one of the style constants defined in
113 * The style value is either one of the style constants defined in 117 * class <code>DWT</code> which is applicable to instances of this
114 * class <code>DWT</code> which is applicable to instances of this 118 * class, or must be built by <em>bitwise OR</em>'ing together
115 * class, or must be built by <em>bitwise OR</em>'ing together 119 * (that is, using the <code>int</code> "|" operator) two or more
116 * (that is, using the <code>int</code> "|" operator) two or more 120 * of those <code>DWT</code> style constants. The class description
117 * of those <code>DWT</code> style constants. The class description 121 * lists the style constants that are applicable to the class.
118 * lists the style constants that are applicable to the class. 122 * Style bits are also inherited from superclasses.
119 * Style bits are also inherited from superclasses. 123 * </p>
120 * </p> 124 *
121 * 125 * @param parent a CTabFolder which will be the parent of the new instance (cannot be null)
122 * @param parent a CTabFolder which will be the parent of the new instance (cannot be null) 126 * @param style the style of control to construct
123 * @param style the style of control to construct 127 * @param index the zero-relative index to store the receiver in its parent
124 * @param index the zero-relative index to store the receiver in its parent 128 *
125 * 129 * @exception IllegalArgumentException <ul>
126 * @exception IllegalArgumentException <ul> 130 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
127 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> 131 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li>
128 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</li> 132 * </ul>
129 * </ul> 133 * @exception DWTException <ul>
130 * @exception DWTException <ul> 134 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
131 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> 135 * </ul>
132 * </ul> 136 *
133 * 137 * @see DWT
134 * @see DWT 138 * @see Widget#getStyle()
135 * @see Widget#getStyle() 139 */
136 */ 140 public this (CTabFolder parent, int style, int index) {
137 public this (CTabFolder parent, int style, int index) { 141 closeRect = new Rectangle(0, 0, 0, 0);
138 super(parent, style); 142 super (parent, style);
139 showClose = (style & DWT.CLOSE) !is 0; 143 showClose = (style & DWT.CLOSE) !is 0;
140 parent.createItem(this, index); 144 parent.createItem (this, index);
141 } 145 }
142 146
143 /* 147 /*
144 * Return whether to use ellipses or just truncate labels 148 * Return whether to use ellipses or just truncate labels
145 */ 149 */
146 bool useEllipses () { 150 bool useEllipses() {
147 return parent.simple; 151 return parent.simple;
148 } 152 }
149 153
150 String shortenText (GC gc, String text, int width) { 154 String shortenText(GC gc, String text, int width) {
151 return useEllipses() ? shortenText(gc, text, width, ELLIPSIS) : shortenText(gc, text, width, ""); //$NON-NLS-1$ 155 return useEllipses()
152 } 156 ? shortenText(gc, text, width, ELLIPSIS)
153 157 : shortenText(gc, text, width, ""); //$NON-NLS-1$
154 String shortenText (GC gc, String text, int width, String ellipses) { 158 }
155 if (gc.textExtent(text, FLAGS).x <= width) 159
156 return text; 160 String shortenText(GC gc, String text, int width, String ellipses) {
157 int ellipseWidth = gc.textExtent(ellipses, FLAGS).x; 161 if (gc.textExtent(text, FLAGS).x <= width) return text;
158 int length = text.length; 162 int ellipseWidth = gc.textExtent(ellipses, FLAGS).x;
159 TextLayout layout = new TextLayout(getDisplay()); 163 int length = text.length;
160 layout.setText(text); 164 TextLayout layout = new TextLayout(getDisplay());
161 int end = layout.getPreviousOffset(length, DWT.MOVEMENT_CLUSTER); 165 layout.setText(text);
162 while (end > 0) { 166 int end = layout.getPreviousOffset(length, DWT.MOVEMENT_CLUSTER);
163 text = text.substring(0, end); 167 while (end > 0) {
164 int l = gc.textExtent(text, FLAGS).x; 168 text = text[ 0 .. end ];
165 if (l + ellipseWidth <= width) { 169 int l = gc.textExtent(text, FLAGS).x;
166 break; 170 if (l + ellipseWidth <= width) {
171 break;
172 }
173 end = layout.getPreviousOffset(end, DWT.MOVEMENT_CLUSTER);
174 }
175 layout.dispose();
176 return end is 0 ? text.substring(0, 1) : text ~ ellipses;
177 }
178
179 public override void dispose() {
180 if (isDisposed ()) return;
181 //if (!isValidThread ()) error (DWT.ERROR_THREAD_INVALID_ACCESS);
182 parent.destroyItem(this);
183 super.dispose();
184 parent = null;
185 control = null;
186 toolTipText = null;
187 shortenedText = null;
188 font = null;
189 }
190 void drawClose(GC gc) {
191 if (closeRect.width is 0 || closeRect.height is 0) return;
192 Display display = getDisplay();
193
194 // draw X 9x9
195 int indent = Math.max(1, (CTabFolder.BUTTON_SIZE-9)/2);
196 int x = closeRect.x + indent;
197 int y = closeRect.y + indent;
198 y += parent.onBottom ? -1 : 1;
199
200 Color closeBorder = display.getSystemColor(CTabFolder.BUTTON_BORDER);
201 switch (closeImageState) {
202 case CTabFolder.NORMAL: {
203 int[] shape = [x,y, x+2,y, x+4,y+2, x+5,y+2, x+7,y, x+9,y,
204 x+9,y+2, x+7,y+4, x+7,y+5, x+9,y+7, x+9,y+9,
205 x+7,y+9, x+5,y+7, x+4,y+7, x+2,y+9, x,y+9,
206 x,y+7, x+2,y+5, x+2,y+4, x,y+2];
207 gc.setBackground(display.getSystemColor(CTabFolder.BUTTON_FILL));
208 gc.fillPolygon(shape);
209 gc.setForeground(closeBorder);
210 gc.drawPolygon(shape);
211 break;
212 }
213 case CTabFolder.HOT: {
214 int[] shape = [x,y, x+2,y, x+4,y+2, x+5,y+2, x+7,y, x+9,y,
215 x+9,y+2, x+7,y+4, x+7,y+5, x+9,y+7, x+9,y+9,
216 x+7,y+9, x+5,y+7, x+4,y+7, x+2,y+9, x,y+9,
217 x,y+7, x+2,y+5, x+2,y+4, x,y+2];
218 Color fill = new Color(display, CTabFolder.CLOSE_FILL);
219 gc.setBackground(fill);
220 gc.fillPolygon(shape);
221 fill.dispose();
222 gc.setForeground(closeBorder);
223 gc.drawPolygon(shape);
224 break;
225 }
226 case CTabFolder.SELECTED: {
227 int[] shape = [x+1,y+1, x+3,y+1, x+5,y+3, x+6,y+3, x+8,y+1, x+10,y+1,
228 x+10,y+3, x+8,y+5, x+8,y+6, x+10,y+8, x+10,y+10,
229 x+8,y+10, x+6,y+8, x+5,y+8, x+3,y+10, x+1,y+10,
230 x+1,y+8, x+3,y+6, x+3,y+5, x+1,y+3];
231 Color fill = new Color(display, CTabFolder.CLOSE_FILL);
232 gc.setBackground(fill);
233 gc.fillPolygon(shape);
234 fill.dispose();
235 gc.setForeground(closeBorder);
236 gc.drawPolygon(shape);
237 break;
238 }
239 case CTabFolder.NONE: {
240 int[] shape = [x,y, x+10,y, x+10,y+10, x,y+10];
241 if (parent.gradientColors !is null && !parent.gradientVertical) {
242 parent.drawBackground(gc, shape, false);
243 } else {
244 Color defaultBackground = parent.getBackground();
245 Image image = parent.bgImage;
246 Color[] colors = parent.gradientColors;
247 int[] percents = parent.gradientPercents;
248 bool vertical = parent.gradientVertical;
249 parent.drawBackground(gc, shape, x, y, 10, 10, defaultBackground, image, colors, percents, vertical);
167 } 250 }
168 end = layout.getPreviousOffset(end, DWT.MOVEMENT_CLUSTER); 251 break;
169 } 252 }
170 layout.dispose(); 253 default:
171 return end is 0 ? text.substring(0, 1) : text + ellipses; 254 }
172 } 255 }
173 256 void drawSelected(GC gc ) {
174 public void dispose () { 257 Point size = parent.getSize();
175 if (isDisposed()) 258 int rightEdge = Math.min (x + width, parent.getRightItemEdge());
259
260 // Draw selection border across all tabs
261 int xx = parent.borderLeft;
262 int yy = parent.onBottom ? size.y - parent.borderBottom - parent.tabHeight - parent.highlight_header : parent.borderTop + parent.tabHeight + 1;
263 int ww = size.x - parent.borderLeft - parent.borderRight;
264 int hh = parent.highlight_header - 1;
265 int[] shape = [xx,yy, xx+ww,yy, xx+ww,yy+hh, xx,yy+hh];
266 if (parent.selectionGradientColors !is null && !parent.selectionGradientVertical) {
267 parent.drawBackground(gc, shape, true);
268 } else {
269 gc.setBackground(parent.selectionBackground);
270 gc.fillRectangle(xx, yy, ww, hh);
271 }
272
273 if (parent.single) {
274 if (!showing) return;
275 } else {
276 // if selected tab scrolled out of view or partially out of view
277 // just draw bottom line
278 if (!showing){
279 int x1 = Math.max(0, parent.borderLeft - 1);
280 int y1 = (parent.onBottom) ? y - 1 : y + height;
281 int x2 = size.x - parent.borderRight;
282 gc.setForeground(CTabFolder.borderColor);
283 gc.drawLine(x1, y1, x2, y1);
176 return; 284 return;
177 //if (!isValidThread ()) error (DWT.ERROR_THREAD_INVALID_ACCESS); 285 }
178 parent.destroyItem(this); 286
179 super.dispose(); 287 // draw selected tab background and outline
180 parent = null; 288 shape = null;
181 control = null; 289 if (this.parent.onBottom) {
182 toolTipText = null; 290 int[] left = parent.simple ? CTabFolder.SIMPLE_BOTTOM_LEFT_CORNER : CTabFolder.BOTTOM_LEFT_CORNER;
183 shortenedText = null; 291 int[] right = parent.simple ? CTabFolder.SIMPLE_BOTTOM_RIGHT_CORNER : parent.curve;
184 font = null; 292 if (parent.borderLeft is 0 && parent.indexOf(this) is parent.firstIndex) {
185 } 293 left = [x, y+height];
186
187 void drawClose (GC gc) {
188 if (closeRect.width is 0 || closeRect.height is 0)
189 return;
190 Display display = getDisplay();
191
192 // draw X 9x9
193 int indent = Math.max(1, (CTabFolder.BUTTON_SIZE - 9) / 2);
194 int x = closeRect.x + indent;
195 int y = closeRect.y + indent;
196 y += parent.onBottom ? -1 : 1;
197
198 Color closeBorder = display.getSystemColor(CTabFolder.BUTTON_BORDER);
199 switch (closeImageState) {
200 case CTabFolder.NORMAL: {
201 int[] shape = new int[]
202 {
203 x , y , x + 2 , y , x + 4 , y + 2 , x + 5 , y + 2 , x + 7 , y , x + 9 , y , x + 9 , y + 2 , x + 7 , y + 4 , x + 7 , y + 5 , x + 9 , y + 7 , x + 9 , y + 9 , x + 7 , y + 9 , x + 5 , y + 7 , x + 4 , y + 7 , x + 2 , y + 9 , x , y + 9 , x , y + 7 , x + 2 , y + 5 , x + 2 , y + 4 , x , y + 2
204 };
205 gc.setBackground(display.getSystemColor(CTabFolder.BUTTON_FILL));
206 gc.fillPolygon(shape);
207 gc.setForeground(closeBorder);
208 gc.drawPolygon(shape);
209 break;
210 } 294 }
211 case CTabFolder.HOT: { 295 shape = new int[left.length+right.length+8];
212 int[] shape = new int[] 296 int index = 0;
213 { 297 shape[index++] = x; // first point repeated here because below we reuse shape to draw outline
214 x , y , x + 2 , y , x + 4 , y + 2 , x + 5 , y + 2 , x + 7 , y , x + 9 , y , x + 9 , y + 2 , x + 7 , y + 4 , x + 7 , y + 5 , x + 9 , y + 7 , x + 9 , y + 9 , x + 7 , y + 9 , x + 5 , y + 7 , x + 4 , y + 7 , x + 2 , y + 9 , x , y + 9 , x , y + 7 , x + 2 , y + 5 , x + 2 , y + 4 , x , y + 2 298 shape[index++] = y - 1;
215 }; 299 shape[index++] = x;
216 Color fill = new Color(display, CTabFolder.CLOSE_FILL); 300 shape[index++] = y - 1;
217 gc.setBackground(fill); 301 for (int i = 0; i < left.length/2; i++) {
218 gc.fillPolygon(shape); 302 shape[index++] = x + left[2*i];
219 fill.dispose(); 303 shape[index++] = y + height + left[2*i+1] - 1;
220 gc.setForeground(closeBorder);
221 gc.drawPolygon(shape);
222 break;
223 } 304 }
224 case CTabFolder.SELECTED: { 305 for (int i = 0; i < right.length/2; i++) {
225 int[] shape = new int[] 306 shape[index++] = parent.simple ? rightEdge - 1 + right[2*i] : rightEdge - parent.curveIndent + right[2*i];
226 { 307 shape[index++] = parent.simple ? y + height + right[2*i+1] - 1 : y + right[2*i+1] - 2;
227 x + 1 , y + 1 , x + 3 , y + 1 , x + 5 , y + 3 , x + 6 , y + 3 , x + 8 , y + 1 , x + 10 , y + 1 , x + 10 , y + 3 , x + 8 , y + 5 , x + 8 , y + 6 , x + 10 , y + 8 , x + 10 , y + 10 , x + 8 , y + 10 , x + 6 , y + 8 , x + 5 , y + 8 , x + 3 , y + 10 , x + 1 , y + 10 , x + 1 , y + 8 , x + 3 , y + 6 , x + 3 , y + 5 , x + 1 , y + 3
228 };
229 Color fill = new Color(display, CTabFolder.CLOSE_FILL);
230 gc.setBackground(fill);
231 gc.fillPolygon(shape);
232 fill.dispose();
233 gc.setForeground(closeBorder);
234 gc.drawPolygon(shape);
235 break;
236 } 308 }
237 case CTabFolder.NONE: { 309 shape[index++] = parent.simple ? rightEdge - 1 : rightEdge + parent.curveWidth - parent.curveIndent;
238 int[] shape = new int[] 310 shape[index++] = y - 1;
239 { 311 shape[index++] = parent.simple ? rightEdge - 1 : rightEdge + parent.curveWidth - parent.curveIndent;
240 x , y , x + 10 , y , x + 10 , y + 10 , x , y + 10 312 shape[index++] = y - 1;
241 }; 313 } else {
242 if (parent.gradientColors !is null && !parent.gradientVertical) { 314 int[] left = parent.simple ? CTabFolder.SIMPLE_TOP_LEFT_CORNER : CTabFolder.TOP_LEFT_CORNER;
243 parent.drawBackground(gc, shape, false); 315 int[] right = parent.simple ? CTabFolder.SIMPLE_TOP_RIGHT_CORNER : parent.curve;
316 if (parent.borderLeft is 0 && parent.indexOf(this) is parent.firstIndex) {
317 left = [x, y];
318 }
319 shape = new int[left.length+right.length+8];
320 int index = 0;
321 shape[index++] = x; // first point repeated here because below we reuse shape to draw outline
322 shape[index++] = y + height + 1;
323 shape[index++] = x;
324 shape[index++] = y + height + 1;
325 for (int i = 0; i < left.length/2; i++) {
326 shape[index++] = x + left[2*i];
327 shape[index++] = y + left[2*i+1];
328 }
329 for (int i = 0; i < right.length/2; i++) {
330 shape[index++] = parent.simple ? rightEdge - 1 + right[2*i] : rightEdge - parent.curveIndent + right[2*i];
331 shape[index++] = y + right[2*i+1];
332 }
333 shape[index++] = parent.simple ? rightEdge - 1 : rightEdge + parent.curveWidth - parent.curveIndent;
334 shape[index++] = y + height + 1;
335 shape[index++] = parent.simple ? rightEdge - 1 : rightEdge + parent.curveWidth - parent.curveIndent;
336 shape[index++] = y + height + 1;
337 }
338
339 Rectangle clipping = gc.getClipping();
340 Rectangle bounds = getBounds();
341 bounds.height += 1;
342 if (parent.onBottom) bounds.y -= 1;
343 bool tabInPaint = clipping.intersects(bounds);
344
345 if (tabInPaint) {
346 // fill in tab background
347 if (parent.selectionGradientColors !is null && !parent.selectionGradientVertical) {
348 parent.drawBackground(gc, shape, true);
349 } else {
350 Color defaultBackground = parent.selectionBackground;
351 Image image = parent.selectionBgImage;
352 Color[] colors = parent.selectionGradientColors;
353 int[] percents = parent.selectionGradientPercents;
354 bool vertical = parent.selectionGradientVertical;
355 xx = x;
356 yy = parent.onBottom ? y -1 : y + 1;
357 ww = width;
358 hh = height;
359 if (!parent.single && !parent.simple) ww += parent.curveWidth - parent.curveIndent;
360 parent.drawBackground(gc, shape, xx, yy, ww, hh, defaultBackground, image, colors, percents, vertical);
361 }
362 }
363
364 //Highlight MUST be drawn before the outline so that outline can cover it in the right spots (start of swoop)
365 //otherwise the curve looks jagged
366 drawHighlight(gc, rightEdge);
367
368 // draw outline
369 shape[0] = Math.max(0, parent.borderLeft - 1);
370 if (parent.borderLeft is 0 && parent.indexOf(this) is parent.firstIndex) {
371 shape[1] = parent.onBottom ? y + height - 1 : y;
372 shape[5] = shape[3] = shape[1];
373 }
374 shape[shape.length - 2] = size.x - parent.borderRight + 1;
375 for (int i = 0; i < shape.length/2; i++) {
376 if (shape[2*i + 1] is y + height + 1) shape[2*i + 1] -= 1;
377 }
378 RGB inside = parent.selectionBackground.getRGB();
379 if (parent.selectionBgImage !is null ||
380 (parent.selectionGradientColors !is null && parent.selectionGradientColors.length > 1)) {
381 inside = null;
382 }
383 RGB outside = parent.getBackground().getRGB();
384 if (parent.bgImage !is null ||
385 (parent.gradientColors !is null && parent.gradientColors.length > 1)) {
386 outside = null;
387 }
388 parent.antialias(shape, CTabFolder.borderColor.getRGB(), inside, outside, gc);
389 gc.setForeground(CTabFolder.borderColor);
390 gc.drawPolyline(shape);
391
392 if (!tabInPaint) return;
393 }
394
395 // draw Image
396 int xDraw = x + LEFT_MARGIN;
397 if (parent.single && (parent.showClose || showClose)) xDraw += CTabFolder.BUTTON_SIZE;
398 Image image = getImage();
399 if (image !is null) {
400 Rectangle imageBounds = image.getBounds();
401 // only draw image if it won't overlap with close button
402 int maxImageWidth = rightEdge - xDraw - RIGHT_MARGIN;
403 if (!parent.single && closeRect.width > 0) maxImageWidth -= closeRect.width + INTERNAL_SPACING;
404 if (imageBounds.width < maxImageWidth) {
405 int imageX = xDraw;
406 int imageY = y + (height - imageBounds.height) / 2;
407 imageY += parent.onBottom ? -1 : 1;
408 gc.drawImage(image, imageX, imageY);
409 xDraw += imageBounds.width + INTERNAL_SPACING;
410 }
411 }
412
413 // draw Text
414 int textWidth = rightEdge - xDraw - RIGHT_MARGIN;
415 if (!parent.single && closeRect.width > 0) textWidth -= closeRect.width + INTERNAL_SPACING;
416 if (textWidth > 0) {
417 Font gcFont = gc.getFont();
418 gc.setFont(font is null ? parent.getFont() : font);
419
420 if (shortenedText is null || shortenedTextWidth !is textWidth) {
421 shortenedText = shortenText(gc, getText(), textWidth);
422 shortenedTextWidth = textWidth;
423 }
424 Point extent = gc.textExtent(shortenedText, FLAGS);
425 int textY = y + (height - extent.y) / 2;
426 textY += parent.onBottom ? -1 : 1;
427
428 gc.setForeground(parent.selectionForeground);
429 gc.drawText(shortenedText, xDraw, textY, FLAGS);
430 gc.setFont(gcFont);
431
432 // draw a Focus rectangle
433 if (parent.isFocusControl()) {
434 Display display = getDisplay();
435 if (parent.simple || parent.single) {
436 gc.setBackground(display.getSystemColor(DWT.COLOR_BLACK));
437 gc.setForeground(display.getSystemColor(DWT.COLOR_WHITE));
438 gc.drawFocus(xDraw-1, textY-1, extent.x+2, extent.y+2);
439 } else {
440 gc.setForeground(display.getSystemColor(CTabFolder.BUTTON_BORDER));
441 gc.drawLine(xDraw, textY+extent.y+1, xDraw+extent.x+1, textY+extent.y+1);
442 }
443 }
444 }
445 if (parent.showClose || showClose) drawClose(gc);
446 }
447
448 /*
449 * Draw a highlight effect along the left, top, and right edges of the tab.
450 * Only for curved tabs, on top.
451 * Do not draw if insufficient colors.
452 */
453 void drawHighlight(GC gc, int rightEdge) {
454 //only draw for curvy tabs and only draw for top tabs
455 if(parent.simple || this.parent.onBottom)
456 return;
457
458 if(parent.selectionHighlightGradientBegin is null)
459 return;
460
461 Color[] gradients = parent.selectionHighlightGradientColorsCache;
462 if(gradients is null)
463 return;
464 int gradientsSize = gradients.length;
465 if(gradientsSize is 0)
466 return; //shouldn't happen but just to be tidy
467
468 gc.setForeground(gradients[0]);
469
470 //draw top horizontal line
471 gc.drawLine(
472 CTabFolder.TOP_LEFT_CORNER_HILITE[0] + x + 1, //rely on fact that first pair is top/right of curve
473 1 + y,
474 rightEdge - parent.curveIndent,
475 1 + y);
476
477 int[] leftHighlightCurve = CTabFolder.TOP_LEFT_CORNER_HILITE;
478
479 int d = parent.tabHeight - parent.topCurveHighlightEnd.length /2;
480
481 int lastX = 0;
482 int lastY = 0;
483 int lastColorIndex = 0;
484
485 //draw upper left curve highlight
486 for (int i = 0; i < leftHighlightCurve.length /2; i++) {
487 int rawX = leftHighlightCurve[i * 2];
488 int rawY = leftHighlightCurve[i * 2 + 1];
489 lastX = rawX + x;
490 lastY = rawY + y;
491 lastColorIndex = rawY - 1;
492 gc.setForeground(gradients[lastColorIndex]);
493 gc.drawPoint(lastX, lastY);
494 }
495 //draw left vertical line highlight
496 for(int i = lastColorIndex; i < gradientsSize; i++) {
497 gc.setForeground(gradients[i]);
498 gc.drawPoint(lastX, 1 + lastY++);
499 }
500
501 int rightEdgeOffset = rightEdge - parent.curveIndent;
502
503 //draw right swoop highlight up to diagonal portion
504 for (int i = 0; i < parent.topCurveHighlightStart.length /2; i++) {
505 int rawX = parent.topCurveHighlightStart[i * 2];
506 int rawY = parent.topCurveHighlightStart[i * 2 + 1];
507 lastX = rawX + rightEdgeOffset;
508 lastY = rawY + y;
509 lastColorIndex = rawY - 1;
510 if(lastColorIndex >= gradientsSize)
511 break; //can happen if tabs are unusually short and cut off the curve
512 gc.setForeground(gradients[lastColorIndex]);
513 gc.drawPoint(lastX, lastY);
514 }
515 //draw right diagonal line highlight
516 for(int i = lastColorIndex; i < lastColorIndex + d; i++) {
517 if(i >= gradientsSize)
518 break; //can happen if tabs are unusually short and cut off the curve
519 gc.setForeground(gradients[i]);
520 gc.drawPoint(1 + lastX++, 1 + lastY++);
521 }
522
523 //draw right swoop highlight from diagonal portion to end
524 for (int i = 0; i < parent.topCurveHighlightEnd.length /2; i++) {
525 int rawX = parent.topCurveHighlightEnd[i * 2]; //d is already encoded in this value
526 int rawY = parent.topCurveHighlightEnd[i * 2 + 1]; //d already encoded
527 lastX = rawX + rightEdgeOffset;
528 lastY = rawY + y;
529 lastColorIndex = rawY - 1;
530 if(lastColorIndex >= gradientsSize)
531 break; //can happen if tabs are unusually short and cut off the curve
532 gc.setForeground(gradients[lastColorIndex]);
533 gc.drawPoint(lastX, lastY);
534 }
535 }
536
537 /*
538 * Draw the unselected border for the receiver on the right.
539 *
540 * @param gc
541 */
542 void drawRightUnselectedBorder(GC gc) {
543
544 int[] shape = null;
545 int startX = x + width - 1;
546
547 if (this.parent.onBottom) {
548 int[] right = parent.simple
549 ? CTabFolder.SIMPLE_UNSELECTED_INNER_CORNER
550 : CTabFolder.BOTTOM_RIGHT_CORNER;
551
552 shape = new int[right.length + 2];
553 int index = 0;
554
555 for (int i = 0; i < right.length / 2; i++) {
556 shape[index++] = startX + right[2 * i];
557 shape[index++] = y + height + right[2 * i + 1] - 1;
558 }
559 shape[index++] = startX;
560 shape[index++] = y - 1;
561 } else {
562 int[] right = parent.simple
563 ? CTabFolder.SIMPLE_UNSELECTED_INNER_CORNER
564 : CTabFolder.TOP_RIGHT_CORNER;
565
566 shape = new int[right.length + 2];
567 int index = 0;
568
569 for (int i = 0; i < right.length / 2; i++) {
570 shape[index++] = startX + right[2 * i];
571 shape[index++] = y + right[2 * i + 1];
572 }
573
574 shape[index++] = startX;
575 shape[index++] = y + height;
576
577 }
578
579 drawBorder(gc, shape);
580
581 }
582
583 /*
584 * Draw the border of the tab
585 *
586 * @param gc
587 * @param shape
588 */
589 void drawBorder(GC gc, int[] shape) {
590
591 gc.setForeground(CTabFolder.borderColor);
592 gc.drawPolyline(shape);
593 }
594
595 /*
596 * Draw the unselected border for the receiver on the left.
597 *
598 * @param gc
599 */
600 void drawLeftUnselectedBorder(GC gc) {
601
602 int[] shape = null;
603 if (this.parent.onBottom) {
604 int[] left = parent.simple
605 ? CTabFolder.SIMPLE_UNSELECTED_INNER_CORNER
606 : CTabFolder.BOTTOM_LEFT_CORNER;
607
608 shape = new int[left.length + 2];
609 int index = 0;
610 shape[index++] = x;
611 shape[index++] = y - 1;
612 for (int i = 0; i < left.length / 2; i++) {
613 shape[index++] = x + left[2 * i];
614 shape[index++] = y + height + left[2 * i + 1] - 1;
615 }
616 } else {
617 int[] left = parent.simple
618 ? CTabFolder.SIMPLE_UNSELECTED_INNER_CORNER
619 : CTabFolder.TOP_LEFT_CORNER;
620
621 shape = new int[left.length + 2];
622 int index = 0;
623 shape[index++] = x;
624 shape[index++] = y + height;
625 for (int i = 0; i < left.length / 2; i++) {
626 shape[index++] = x + left[2 * i];
627 shape[index++] = y + left[2 * i + 1];
628 }
629
630 }
631
632 drawBorder(gc, shape);
633 }
634
635 void drawUnselected(GC gc) {
636 // Do not draw partial items
637 if (!showing) return;
638
639 Rectangle clipping = gc.getClipping();
640 Rectangle bounds = getBounds();
641 if (!clipping.intersects(bounds)) return;
642
643 // draw border
644 int index = parent.indexOf(this);
645
646 if (index > 0 && index < parent.selectedIndex)
647 drawLeftUnselectedBorder(gc);
648 // If it is the last one then draw a line
649 if (index > parent.selectedIndex)
650 drawRightUnselectedBorder(gc);
651
652 // draw Image
653 int xDraw = x + LEFT_MARGIN;
654 Image image = getImage();
655 if (image !is null && parent.showUnselectedImage) {
656 Rectangle imageBounds = image.getBounds();
657 // only draw image if it won't overlap with close button
658 int maxImageWidth = x + width - xDraw - RIGHT_MARGIN;
659 if (parent.showUnselectedClose && (parent.showClose || showClose)) {
660 maxImageWidth -= closeRect.width + INTERNAL_SPACING;
661 }
662 if (imageBounds.width < maxImageWidth) {
663 int imageX = xDraw;
664 int imageHeight = imageBounds.height;
665 int imageY = y + (height - imageHeight) / 2;
666 imageY += parent.onBottom ? -1 : 1;
667 int imageWidth = imageBounds.width * imageHeight / imageBounds.height;
668 gc.drawImage(image,
669 imageBounds.x, imageBounds.y, imageBounds.width, imageBounds.height,
670 imageX, imageY, imageWidth, imageHeight);
671 xDraw += imageWidth + INTERNAL_SPACING;
672 }
673 }
674 // draw Text
675 int textWidth = x + width - xDraw - RIGHT_MARGIN;
676 if (parent.showUnselectedClose && (parent.showClose || showClose)) {
677 textWidth -= closeRect.width + INTERNAL_SPACING;
678 }
679 if (textWidth > 0) {
680 Font gcFont = gc.getFont();
681 gc.setFont(font is null ? parent.getFont() : font);
682 if (shortenedText is null || shortenedTextWidth !is textWidth) {
683 shortenedText = shortenText(gc, getText(), textWidth);
684 shortenedTextWidth = textWidth;
685 }
686 Point extent = gc.textExtent(shortenedText, FLAGS);
687 int textY = y + (height - extent.y) / 2;
688 textY += parent.onBottom ? -1 : 1;
689 gc.setForeground(parent.getForeground());
690 gc.drawText(shortenedText, xDraw, textY, FLAGS);
691 gc.setFont(gcFont);
692 }
693 // draw close
694 if (parent.showUnselectedClose && (parent.showClose || showClose)) drawClose(gc);
695 }
696 /**
697 * Returns a rectangle describing the receiver's size and location
698 * relative to its parent.
699 *
700 * @return the receiver's bounding column rectangle
701 *
702 * @exception DWTException <ul>
703 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
704 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
705 * </ul>
706 */
707 public Rectangle getBounds () {
708 //checkWidget();
709 int w = width;
710 if (!parent.simple && !parent.single && parent.indexOf(this) is parent.selectedIndex) w += parent.curveWidth - parent.curveIndent;
711 return new Rectangle(x, y, w, height);
712 }
713 /**
714 * Gets the control that is displayed in the content area of the tab item.
715 *
716 * @return the control
717 *
718 * @exception DWTException <ul>
719 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
720 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
721 * </ul>
722 */
723 public Control getControl () {
724 checkWidget();
725 return control;
726 }
727 /**
728 * Get the image displayed in the tab if the tab is disabled.
729 *
730 * @return the disabled image or null
731 *
732 * @exception DWTException <ul>
733 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
734 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
735 * </ul>
736 *
737 * @deprecated the disabled image is not used
738 */
739 public Image getDisabledImage(){
740 checkWidget();
741 return disabledImage;
742 }
743 /**
744 * Returns the font that the receiver will use to paint textual information.
745 *
746 * @return the receiver's font
747 *
748 * @exception DWTException <ul>
749 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
750 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
751 * </ul>
752 *
753 * @since 3.0
754 */
755 public Font getFont() {
756 checkWidget();
757 if (font !is null) return font;
758 return parent.getFont();
759 }
760 /**
761 * Returns the receiver's parent, which must be a <code>CTabFolder</code>.
762 *
763 * @return the receiver's parent
764 *
765 * @exception DWTException <ul>
766 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
767 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
768 * </ul>
769 */
770 public CTabFolder getParent () {
771 //checkWidget();
772 return parent;
773 }
774 /**
775 * Returns <code>true</code> to indicate that the receiver's close button should be shown.
776 * Otherwise return <code>false</code>. The initial value is defined by the style (DWT.CLOSE)
777 * that was used to create the receiver.
778 *
779 * @return <code>true</code> if the close button should be shown
780 *
781 * @exception DWTException <ul>
782 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
783 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
784 * </ul>
785 *
786 * @since 3.4
787 */
788 public bool getShowClose() {
789 checkWidget();
790 return showClose;
791 }
792 /**
793 * Returns the receiver's tool tip text, or null if it has
794 * not been set.
795 *
796 * @return the receiver's tool tip text
797 *
798 * @exception DWTException <ul>
799 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
800 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
801 * </ul>
802 */
803 public String getToolTipText () {
804 checkWidget();
805 if (toolTipText is null && shortenedText !is null) {
806 String text = getText();
807 if (shortenedText!=text) return text;
808 }
809 return toolTipText;
810 }
811 /**
812 * Returns <code>true</code> if the item will be rendered in the visible area of the CTabFolder. Returns false otherwise.
813 *
814 * @return <code>true</code> if the item will be rendered in the visible area of the CTabFolder. Returns false otherwise.
815 *
816 * @exception DWTException <ul>
817 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
818 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
819 * </ul>
820 *
821 * @since 3.0
822 */
823 public bool isShowing () {
824 checkWidget();
825 return showing;
826 }
827 void onPaint(GC gc, bool isSelected) {
828 if (width is 0 || height is 0) return;
829 if (isSelected) {
830 drawSelected(gc);
831 } else {
832 drawUnselected(gc);
833 }
834 }
835 int preferredHeight(GC gc) {
836 Image image = getImage();
837 int h = (image is null) ? 0 : image.getBounds().height;
838 String text = getText();
839 if (font is null) {
840 h = Math.max(h, gc.textExtent(text, FLAGS).y);
841 } else {
842 Font gcFont = gc.getFont();
843 gc.setFont(font);
844 h = Math.max(h, gc.textExtent(text, FLAGS).y);
845 gc.setFont(gcFont);
846 }
847 return h + TOP_MARGIN + BOTTOM_MARGIN;
848 }
849 int preferredWidth(GC gc, bool isSelected, bool minimum) {
850 // NOTE: preferred width does not include the "dead space" caused
851 // by the curve.
852 if (isDisposed()) return 0;
853 int w = 0;
854 Image image = getImage();
855 if (image !is null && (isSelected || parent.showUnselectedImage)) {
856 w += image.getBounds().width;
857 }
858 String text = null;
859 if (minimum) {
860 int minChars = parent.minChars;
861 text = minChars is 0 ? null : getText();
862 if (text !is null && text.length > minChars) {
863 if (useEllipses()) {
864 int end = minChars < ELLIPSIS.length + 1 ? minChars : minChars - ELLIPSIS.length;
865 text = text[ 0 .. end ];
866 if (minChars > ELLIPSIS.length + 1) text ~= ELLIPSIS;
867 } else {
868 int end = minChars;
869 text = text[ 0 .. end ];
870 }
871 }
872 } else {
873 text = getText();
874 }
875 if (text !is null) {
876 if (w > 0) w += INTERNAL_SPACING;
877 if (font is null) {
878 w += gc.textExtent(text, FLAGS).x;
879 } else {
880 Font gcFont = gc.getFont();
881 gc.setFont(font);
882 w += gc.textExtent(text, FLAGS).x;
883 gc.setFont(gcFont);
884 }
885 }
886 if (parent.showClose || showClose) {
887 if (isSelected || parent.showUnselectedClose) {
888 if (w > 0) w += INTERNAL_SPACING;
889 w += CTabFolder.BUTTON_SIZE;
890 }
891 }
892 return w + LEFT_MARGIN + RIGHT_MARGIN;
893 }
894 /**
895 * Sets the control that is used to fill the client area of
896 * the tab folder when the user selects the tab item.
897 *
898 * @param control the new control (or null)
899 *
900 * @exception IllegalArgumentException <ul>
901 * <li>ERROR_INVALID_ARGUMENT - if the control has been disposed</li>
902 * <li>ERROR_INVALID_PARENT - if the control is not in the same widget tree</li>
903 * </ul>
904 * @exception DWTException <ul>
905 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
906 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
907 * </ul>
908 */
909 public void setControl (Control control) {
910 checkWidget();
911 if (control !is null) {
912 if (control.isDisposed()) DWT.error (DWT.ERROR_INVALID_ARGUMENT);
913 if (control.getParent() !is parent) DWT.error (DWT.ERROR_INVALID_PARENT);
914 }
915 if (this.control !is null && !this.control.isDisposed()) {
916 this.control.setVisible(false);
917 }
918 this.control = control;
919 if (this.control !is null) {
920 int index = parent.indexOf (this);
921 if (index is parent.getSelectionIndex ()){
922 this.control.setBounds(parent.getClientArea ());
923 this.control.setVisible(true);
924 } else {
925 this.control.setVisible(false);
926 }
927 }
928 }
929 /**
930 * Sets the image that is displayed if the tab item is disabled.
931 * Null will clear the image.
932 *
933 * @param image the image to be displayed when the item is disabled or null
934 *
935 * @exception DWTException <ul>
936 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
937 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
938 * </ul>
939 *
940 * @deprecated This image is not used
941 */
942 public void setDisabledImage (Image image) {
943 checkWidget();
944 if (image !is null && image.isDisposed ()) {
945 DWT.error(DWT.ERROR_INVALID_ARGUMENT);
946 }
947 this.disabledImage = image;
948 }
949 /**
950 * Sets the font that the receiver will use to paint textual information
951 * for this item to the font specified by the argument, or to the default font
952 * for that kind of control if the argument is null.
953 *
954 * @param font the new font (or null)
955 *
956 * @exception IllegalArgumentException <ul>
957 * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
958 * </ul>
959 * @exception DWTException <ul>
960 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
961 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
962 * </ul>
963 *
964 * @since 3.0
965 */
966 public void setFont (Font font){
967 checkWidget();
968 if (font !is null && font.isDisposed ()) {
969 DWT.error(DWT.ERROR_INVALID_ARGUMENT);
970 }
971 if (font is null && this.font is null) return;
972 if (font !is null && font==this.font) return;
973 this.font = font;
974 if (!parent.updateTabHeight(false)) {
975 parent.updateItems();
976 parent.redrawTabs();
977 }
978 }
979 public override void setImage (Image image) {
980 checkWidget();
981 if (image !is null && image.isDisposed ()) {
982 DWT.error(DWT.ERROR_INVALID_ARGUMENT);
983 }
984 Image oldImage = getImage();
985 if (image is null && oldImage is null) return;
986 if (image !is null && image==oldImage) return;
987 super.setImage(image);
988 if (!parent.updateTabHeight(false)) {
989 // If image is the same size as before,
990 // redraw only the image
991 if (oldImage !is null && image !is null) {
992 Rectangle oldBounds = oldImage.getBounds();
993 Rectangle bounds = image.getBounds();
994 if (bounds.width is oldBounds.width && bounds.height is oldBounds.height) {
995 if (showing) {
996 bool selected = parent.indexOf(this) is parent.selectedIndex;
997 if (selected || parent.showUnselectedImage) {
998 int imageX = x + LEFT_MARGIN, maxImageWidth;
999 if (selected) {
1000 if (parent.single && (parent.showClose || showClose)) imageX += CTabFolder.BUTTON_SIZE;
1001 int rightEdge = Math.min (x + width, parent.getRightItemEdge());
1002 maxImageWidth = rightEdge - imageX - RIGHT_MARGIN;
1003 if (!parent.single && closeRect.width > 0) maxImageWidth -= closeRect.width + INTERNAL_SPACING;
1004 } else {
1005 maxImageWidth = x + width - imageX - RIGHT_MARGIN;
1006 if (parent.showUnselectedClose && (parent.showClose || showClose)) {
1007 maxImageWidth -= closeRect.width + INTERNAL_SPACING;
1008 }
1009 }
1010 if (bounds.width < maxImageWidth) {
1011 int imageY = y + (height - bounds.height) / 2 + (parent.onBottom ? -1 : 1);
1012 parent.redraw(imageX, imageY, bounds.width, bounds.height, false);
1013 }
1014 }
244 } 1015 }
245 else {
246 Color defaultBackground = parent.getBackground();
247 Image image = parent.bgImage;
248 Color[] colors = parent.gradientColors;
249 int[] percents = parent.gradientPercents;
250 bool vertical = parent.gradientVertical;
251 parent.drawBackground(gc, shape, x, y, 10, 10, defaultBackground, image, colors, percents, vertical);
252 }
253 break;
254 }
255 }
256 }
257
258 void drawSelected (GC gc) {
259 Point size = parent.getSize();
260 int rightEdge = Math.min(x + width, parent.getRightItemEdge());
261
262 // Draw selection border across all tabs
263 int xx = parent.borderLeft;
264 int
265 yy = parent.onBottom ? size.y - parent.borderBottom - parent.tabHeight - parent.highlight_header : parent.borderTop + parent.tabHeight + 1;
266 int ww = size.x - parent.borderLeft - parent.borderRight;
267 int hh = parent.highlight_header - 1;
268 int[] shape = new int[]
269 {
270 xx , yy , xx + ww , yy , xx + ww , yy + hh , xx , yy + hh
271 };
272 if (parent.selectionGradientColors !is null && !parent.selectionGradientVertical) {
273 parent.drawBackground(gc, shape, true);
274 }
275 else {
276 gc.setBackground(parent.selectionBackground);
277 gc.fillRectangle(xx, yy, ww, hh);
278 }
279
280 if (parent.single) {
281 if (!showing)
282 return;
283 }
284 else {
285 // if selected tab scrolled out of view or partially out of view
286 // just draw bottom line
287 if (!showing) {
288 int x1 = Math.max(0, parent.borderLeft - 1);
289 int y1 = (parent.onBottom) ? y - 1 : y + height;
290 int x2 = size.x - parent.borderRight;
291 gc.setForeground(CTabFolder.borderColor);
292 gc.drawLine(x1, y1, x2, y1);
293 return; 1016 return;
294 } 1017 }
295 1018 }
296 // draw selected tab background and outline
297 shape = null;
298 if (this.parent.onBottom) {
299 int[] left = parent.simple ? CTabFolder.SIMPLE_BOTTOM_LEFT_CORNER : CTabFolder.BOTTOM_LEFT_CORNER;
300 int[] right = parent.simple ? CTabFolder.SIMPLE_BOTTOM_RIGHT_CORNER : parent.curve;
301 if (parent.borderLeft is 0 && parent.indexOf(this) is parent.firstIndex) {
302 left = new int[]
303 {
304 x , y + height
305 };
306 }
307 shape = new int[left.length + right.length + 8];
308 int index = 0;
309 shape[index++] = x; // first point repeated here because below we reuse shape to draw outline
310 shape[index++] = y - 1;
311 shape[index++] = x;
312 shape[index++] = y - 1;
313 for (int i = 0; i < left.length / 2; i++) {
314 shape[index++] = x + left[2 * i];
315 shape[index++] = y + height + left[2 * i + 1] - 1;
316 }
317 for (int i = 0; i < right.length / 2; i++) {
318 shape[index++] = parent.simple ? rightEdge - 1 + right[2 * i] : rightEdge - parent.curveIndent + right[2 * i];
319 shape[index++] = parent.simple ? y + height + right[2 * i + 1] - 1 : y + right[2 * i + 1] - 2;
320 }
321 shape[index++] = parent.simple ? rightEdge - 1 : rightEdge + parent.curveWidth - parent.curveIndent;
322 shape[index++] = y - 1;
323 shape[index++] = parent.simple ? rightEdge - 1 : rightEdge + parent.curveWidth - parent.curveIndent;
324 shape[index++] = y - 1;
325 }
326 else {
327 int[] left = parent.simple ? CTabFolder.SIMPLE_TOP_LEFT_CORNER : CTabFolder.TOP_LEFT_CORNER;
328 int[] right = parent.simple ? CTabFolder.SIMPLE_TOP_RIGHT_CORNER : parent.curve;
329 if (parent.borderLeft is 0 && parent.indexOf(this) is parent.firstIndex) {
330 left = new int[]
331 {
332 x , y
333 };
334 }
335 shape = new int[left.length + right.length + 8];
336 int index = 0;
337 shape[index++] = x; // first point repeated here because below we reuse shape to draw outline
338 shape[index++] = y + height + 1;
339 shape[index++] = x;
340 shape[index++] = y + height + 1;
341 for (int i = 0; i < left.length / 2; i++) {
342 shape[index++] = x + left[2 * i];
343 shape[index++] = y + left[2 * i + 1];
344 }
345 for (int i = 0; i < right.length / 2; i++) {
346 shape[index++] = parent.simple ? rightEdge - 1 + right[2 * i] : rightEdge - parent.curveIndent + right[2 * i];
347 shape[index++] = y + right[2 * i + 1];
348 }
349 shape[index++] = parent.simple ? rightEdge - 1 : rightEdge + parent.curveWidth - parent.curveIndent;
350 shape[index++] = y + height + 1;
351 shape[index++] = parent.simple ? rightEdge - 1 : rightEdge + parent.curveWidth - parent.curveIndent;
352 shape[index++] = y + height + 1;
353 }
354
355 Rectangle clipping = gc.getClipping();
356 Rectangle bounds = getBounds();
357 bounds.height += 1;
358 if (parent.onBottom)
359 bounds.y -= 1;
360 bool tabInPaint = clipping.intersects(bounds);
361
362 if (tabInPaint) {
363 // fill in tab background
364 if (parent.selectionGradientColors !is null && !parent.selectionGradientVertical) {
365 parent.drawBackground(gc, shape, true);
366 }
367 else {
368 Color defaultBackground = parent.selectionBackground;
369 Image image = parent.selectionBgImage;
370 Color[] colors = parent.selectionGradientColors;
371 int[] percents = parent.selectionGradientPercents;
372 bool vertical = parent.selectionGradientVertical;
373 xx = x;
374 yy = parent.onBottom ? y - 1 : y + 1;
375 ww = width;
376 hh = height;
377 if (!parent.single && !parent.simple)
378 ww += parent.curveWidth - parent.curveIndent;
379 parent.drawBackground(gc, shape, xx, yy, ww, hh, defaultBackground, image, colors, percents, vertical);
380 }
381 }
382
383 //Highlight MUST be drawn before the outline so that outline can cover it in the right spots (start of swoop)
384 //otherwise the curve looks jagged
385 drawHighlight(gc, rightEdge);
386
387 // draw outline
388 shape[0] = Math.max(0, parent.borderLeft - 1);
389 if (parent.borderLeft is 0 && parent.indexOf(this) is parent.firstIndex) {
390 shape[1] = parent.onBottom ? y + height - 1 : y;
391 shape[5] = shape[3] = shape[1];
392 }
393 shape[shape.length - 2] = size.x - parent.borderRight + 1;
394 for (int i = 0; i < shape.length / 2; i++) {
395 if (shape[2 * i + 1] is y + height + 1)
396 shape[2 * i + 1] -= 1;
397 }
398 RGB inside = parent.selectionBackground.getRGB();
399 if (parent.selectionBgImage !is null || (parent.selectionGradientColors !is null && parent.selectionGradientColors.length > 1)) {
400 inside = null;
401 }
402 RGB outside = parent.getBackground().getRGB();
403 if (parent.bgImage !is null || (parent.gradientColors !is null && parent.gradientColors.length > 1)) {
404 outside = null;
405 }
406 parent.antialias(shape, CTabFolder.borderColor.getRGB(), inside, outside, gc);
407 gc.setForeground(CTabFolder.borderColor);
408 gc.drawPolyline(shape);
409
410 if (!tabInPaint)
411 return;
412 }
413
414 // draw Image
415 int xDraw = x + LEFT_MARGIN;
416 if (parent.single && (parent.showClose || showClose))
417 xDraw += CTabFolder.BUTTON_SIZE;
418 Image image = getImage();
419 if (image !is null) {
420 Rectangle imageBounds = image.getBounds();
421 // only draw image if it won't overlap with close button
422 int maxImageWidth = rightEdge - xDraw - RIGHT_MARGIN;
423 if (!parent.single && closeRect.width > 0)
424 maxImageWidth -= closeRect.width + INTERNAL_SPACING;
425 if (imageBounds.width < maxImageWidth) {
426 int imageX = xDraw;
427 int imageY = y + (height - imageBounds.height) / 2;
428 imageY += parent.onBottom ? -1 : 1;
429 gc.drawImage(image, imageX, imageY);
430 xDraw += imageBounds.width + INTERNAL_SPACING;
431 }
432 }
433
434 // draw Text
435 int textWidth = rightEdge - xDraw - RIGHT_MARGIN;
436 if (!parent.single && closeRect.width > 0)
437 textWidth -= closeRect.width + INTERNAL_SPACING;
438 if (textWidth > 0) {
439 Font gcFont = gc.getFont();
440 gc.setFont(font is null ? parent.getFont() : font);
441
442 if (shortenedText is null || shortenedTextWidth !is textWidth) {
443 shortenedText = shortenText(gc, getText(), textWidth);
444 shortenedTextWidth = textWidth;
445 }
446 Point extent = gc.textExtent(shortenedText, FLAGS);
447 int textY = y + (height - extent.y) / 2;
448 textY += parent.onBottom ? -1 : 1;
449
450 gc.setForeground(parent.selectionForeground);
451 gc.drawText(shortenedText, xDraw, textY, FLAGS);
452 gc.setFont(gcFont);
453
454 // draw a Focus rectangle
455 if (parent.isFocusControl()) {
456 Display display = getDisplay();
457 if (parent.simple || parent.single) {
458 gc.setBackground(display.getSystemColor(DWT.COLOR_BLACK));
459 gc.setForeground(display.getSystemColor(DWT.COLOR_WHITE));
460 gc.drawFocus(xDraw - 1, textY - 1, extent.x + 2, extent.y + 2);
461 }
462 else {
463 gc.setForeground(display.getSystemColor(CTabFolder.BUTTON_BORDER));
464 gc.drawLine(xDraw, textY + extent.y + 1, xDraw + extent.x + 1, textY + extent.y + 1);
465 }
466 }
467 }
468 if (parent.showClose || showClose)
469 drawClose(gc);
470 }
471
472 /*
473 * Draw a highlight effect along the left, top, and right edges of the tab.
474 * Only for curved tabs, on top.
475 * Do not draw if insufficient colors.
476 */
477 void drawHighlight (GC gc, int rightEdge) {
478 //only draw for curvy tabs and only draw for top tabs
479 if (parent.simple || this.parent.onBottom)
480 return;
481
482 if (parent.selectionHighlightGradientBegin is null)
483 return;
484
485 Color[] gradients = parent.selectionHighlightGradientColorsCache;
486 if (gradients is null)
487 return;
488 int gradientsSize = gradients.length;
489 if (gradientsSize is 0)
490 return; //shouldn't happen but just to be tidy
491
492 gc.setForeground(gradients[0]);
493
494 //draw top horizontal line
495 gc.drawLine(CTabFolder.TOP_LEFT_CORNER_HILITE[0] + x + 1, //rely on fact that first pair is top/right of curve
496 1 + y, rightEdge - parent.curveIndent, 1 + y);
497
498 int[] leftHighlightCurve = CTabFolder.TOP_LEFT_CORNER_HILITE;
499
500 int d = parent.tabHeight - parent.topCurveHighlightEnd.length / 2;
501
502 int lastX = 0;
503 int lastY = 0;
504 int lastColorIndex = 0;
505
506 //draw upper left curve highlight
507 for (int i = 0; i < leftHighlightCurve.length / 2; i++) {
508 int rawX = leftHighlightCurve[i * 2];
509 int rawY = leftHighlightCurve[i * 2 + 1];
510 lastX = rawX + x;
511 lastY = rawY + y;
512 lastColorIndex = rawY - 1;
513 gc.setForeground(gradients[lastColorIndex]);
514 gc.drawPoint(lastX, lastY);
515 }
516 //draw left vertical line highlight
517 for (int i = lastColorIndex; i < gradientsSize; i++) {
518 gc.setForeground(gradients[i]);
519 gc.drawPoint(lastX, 1 + lastY++);
520 }
521
522 int rightEdgeOffset = rightEdge - parent.curveIndent;
523
524 //draw right swoop highlight up to diagonal portion
525 for (int i = 0; i < parent.topCurveHighlightStart.length / 2; i++) {
526 int rawX = parent.topCurveHighlightStart[i * 2];
527 int rawY = parent.topCurveHighlightStart[i * 2 + 1];
528 lastX = rawX + rightEdgeOffset;
529 lastY = rawY + y;
530 lastColorIndex = rawY - 1;
531 if (lastColorIndex >= gradientsSize)
532 break; //can happen if tabs are unusually short and cut off the curve
533 gc.setForeground(gradients[lastColorIndex]);
534 gc.drawPoint(lastX, lastY);
535 }
536 //draw right diagonal line highlight
537 for (int i = lastColorIndex; i < lastColorIndex + d; i++) {
538 if (i >= gradientsSize)
539 break; //can happen if tabs are unusually short and cut off the curve
540 gc.setForeground(gradients[i]);
541 gc.drawPoint(1 + lastX++, 1 + lastY++);
542 }
543
544 //draw right swoop highlight from diagonal portion to end
545 for (int i = 0; i < parent.topCurveHighlightEnd.length / 2; i++) {
546 int rawX = parent.topCurveHighlightEnd[i * 2]; //d is already encoded in this value
547 int rawY = parent.topCurveHighlightEnd[i * 2 + 1]; //d already encoded
548 lastX = rawX + rightEdgeOffset;
549 lastY = rawY + y;
550 lastColorIndex = rawY - 1;
551 if (lastColorIndex >= gradientsSize)
552 break; //can happen if tabs are unusually short and cut off the curve
553 gc.setForeground(gradients[lastColorIndex]);
554 gc.drawPoint(lastX, lastY);
555 }
556 }
557
558 /*
559 * Draw the unselected border for the receiver on the right.
560 *
561 * @param gc
562 */
563 void drawRightUnselectedBorder (GC gc) {
564
565 int[] shape = null;
566 int startX = x + width - 1;
567
568 if (this.parent.onBottom) {
569 int[] right = parent.simple ? CTabFolder.SIMPLE_UNSELECTED_INNER_CORNER : CTabFolder.BOTTOM_RIGHT_CORNER;
570
571 shape = new int[right.length + 2];
572 int index = 0;
573
574 for (int i = 0; i < right.length / 2; i++) {
575 shape[index++] = startX + right[2 * i];
576 shape[index++] = y + height + right[2 * i + 1] - 1;
577 }
578 shape[index++] = startX;
579 shape[index++] = y - 1;
580 }
581 else {
582 int[] right = parent.simple ? CTabFolder.SIMPLE_UNSELECTED_INNER_CORNER : CTabFolder.TOP_RIGHT_CORNER;
583
584 shape = new int[right.length + 2];
585 int index = 0;
586
587 for (int i = 0; i < right.length / 2; i++) {
588 shape[index++] = startX + right[2 * i];
589 shape[index++] = y + right[2 * i + 1];
590 }
591
592 shape[index++] = startX;
593 shape[index++] = y + height;
594
595 }
596
597 drawBorder(gc, shape);
598
599 }
600
601 /*
602 * Draw the border of the tab
603 *
604 * @param gc
605 * @param shape
606 */
607 void drawBorder (GC gc, int[] shape) {
608
609 gc.setForeground(CTabFolder.borderColor);
610 gc.drawPolyline(shape);
611 }
612
613 /*
614 * Draw the unselected border for the receiver on the left.
615 *
616 * @param gc
617 */
618 void drawLeftUnselectedBorder (GC gc) {
619
620 int[] shape = null;
621 if (this.parent.onBottom) {
622 int[] left = parent.simple ? CTabFolder.SIMPLE_UNSELECTED_INNER_CORNER : CTabFolder.BOTTOM_LEFT_CORNER;
623
624 shape = new int[left.length + 2];
625 int index = 0;
626 shape[index++] = x;
627 shape[index++] = y - 1;
628 for (int i = 0; i < left.length / 2; i++) {
629 shape[index++] = x + left[2 * i];
630 shape[index++] = y + height + left[2 * i + 1] - 1;
631 }
632 }
633 else {
634 int[] left = parent.simple ? CTabFolder.SIMPLE_UNSELECTED_INNER_CORNER : CTabFolder.TOP_LEFT_CORNER;
635
636 shape = new int[left.length + 2];
637 int index = 0;
638 shape[index++] = x;
639 shape[index++] = y + height;
640 for (int i = 0; i < left.length / 2; i++) {
641 shape[index++] = x + left[2 * i];
642 shape[index++] = y + left[2 * i + 1];
643 }
644
645 }
646
647 drawBorder(gc, shape);
648 }
649
650 void drawUnselected (GC gc) {
651 // Do not draw partial items
652 if (!showing)
653 return;
654
655 Rectangle clipping = gc.getClipping();
656 Rectangle bounds = getBounds();
657 if (!clipping.intersects(bounds))
658 return;
659
660 // draw border
661 int index = parent.indexOf(this);
662
663 if (index > 0 && index < parent.selectedIndex)
664 drawLeftUnselectedBorder(gc);
665 // If it is the last one then draw a line
666 if (index > parent.selectedIndex)
667 drawRightUnselectedBorder(gc);
668
669 // draw Image
670 int xDraw = x + LEFT_MARGIN;
671 Image image = getImage();
672 if (image !is null && parent.showUnselectedImage) {
673 Rectangle imageBounds = image.getBounds();
674 // only draw image if it won't overlap with close button
675 int maxImageWidth = x + width - xDraw - RIGHT_MARGIN;
676 if (parent.showUnselectedClose && (parent.showClose || showClose)) {
677 maxImageWidth -= closeRect.width + INTERNAL_SPACING;
678 }
679 if (imageBounds.width < maxImageWidth) {
680 int imageX = xDraw;
681 int imageHeight = imageBounds.height;
682 int imageY = y + (height - imageHeight) / 2;
683 imageY += parent.onBottom ? -1 : 1;
684 int imageWidth = imageBounds.width * imageHeight / imageBounds.height;
685 gc.drawImage(image, imageBounds.x, imageBounds.y, imageBounds.width, imageBounds.height, imageX, imageY, imageWidth, imageHeight);
686 xDraw += imageWidth + INTERNAL_SPACING;
687 }
688 }
689 // draw Text
690 int textWidth = x + width - xDraw - RIGHT_MARGIN;
691 if (parent.showUnselectedClose && (parent.showClose || showClose)) {
692 textWidth -= closeRect.width + INTERNAL_SPACING;
693 }
694 if (textWidth > 0) {
695 Font gcFont = gc.getFont();
696 gc.setFont(font is null ? parent.getFont() : font);
697 if (shortenedText is null || shortenedTextWidth !is textWidth) {
698 shortenedText = shortenText(gc, getText(), textWidth);
699 shortenedTextWidth = textWidth;
700 }
701 Point extent = gc.textExtent(shortenedText, FLAGS);
702 int textY = y + (height - extent.y) / 2;
703 textY += parent.onBottom ? -1 : 1;
704 gc.setForeground(parent.getForeground());
705 gc.drawText(shortenedText, xDraw, textY, FLAGS);
706 gc.setFont(gcFont);
707 }
708 // draw close
709 if (parent.showUnselectedClose && (parent.showClose || showClose))
710 drawClose(gc);
711 }
712
713 /**
714 * Returns a rectangle describing the receiver's size and location
715 * relative to its parent.
716 *
717 * @return the receiver's bounding column rectangle
718 *
719 * @exception DWTException <ul>
720 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
721 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
722 * </ul>
723 */
724 public Rectangle getBounds () {
725 //checkWidget();
726 int w = width;
727 if (!parent.simple && !parent.single && parent.indexOf(this) is parent.selectedIndex)
728 w += parent.curveWidth - parent.curveIndent;
729 return new Rectangle(x, y, w, height);
730 }
731
732 /**
733 * Gets the control that is displayed in the content area of the tab item.
734 *
735 * @return the control
736 *
737 * @exception DWTException <ul>
738 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
739 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
740 * </ul>
741 */
742 public Control getControl () {
743 checkWidget();
744 return control;
745 }
746
747 /**
748 * Get the image displayed in the tab if the tab is disabled.
749 *
750 * @return the disabled image or null
751 *
752 * @exception DWTException <ul>
753 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
754 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
755 * </ul>
756 *
757 * @deprecated the disabled image is not used
758 */
759 public Image getDisabledImage () {
760 checkWidget();
761 return disabledImage;
762 }
763
764 /**
765 * Returns the font that the receiver will use to paint textual information.
766 *
767 * @return the receiver's font
768 *
769 * @exception DWTException <ul>
770 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
771 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
772 * </ul>
773 *
774 * @since 3.0
775 */
776 public Font getFont () {
777 checkWidget();
778 if (font !is null)
779 return font;
780 return parent.getFont();
781 }
782
783 /**
784 * Returns the receiver's parent, which must be a <code>CTabFolder</code>.
785 *
786 * @return the receiver's parent
787 *
788 * @exception DWTException <ul>
789 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
790 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
791 * </ul>
792 */
793 public CTabFolder getParent () {
794 //checkWidget();
795 return parent;
796 }
797
798 /**
799 * Returns <code>true</code> to indicate that the receiver's close button should be shown.
800 * Otherwise return <code>false</code>. The initial value is defined by the style (DWT.CLOSE)
801 * that was used to create the receiver.
802 *
803 * @return <code>true</code> if the close button should be shown
804 *
805 * @exception DWTException <ul>
806 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
807 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
808 * </ul>
809 *
810 * @since 3.4
811 */
812 public bool getShowClose () {
813 checkWidget();
814 return showClose;
815 }
816
817 /**
818 * Returns the receiver's tool tip text, or null if it has
819 * not been set.
820 *
821 * @return the receiver's tool tip text
822 *
823 * @exception DWTException <ul>
824 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
825 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
826 * </ul>
827 */
828 public String getToolTipText () {
829 checkWidget();
830 if (toolTipText is null && shortenedText !is null) {
831 String text = getText();
832 if (!shortenedText.opEquals(text))
833 return text;
834 }
835 return toolTipText;
836 }
837
838 /**
839 * Returns <code>true</code> if the item will be rendered in the visible area of the CTabFolder. Returns false otherwise.
840 *
841 * @return <code>true</code> if the item will be rendered in the visible area of the CTabFolder. Returns false otherwise.
842 *
843 * @exception DWTException <ul>
844 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
845 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
846 * </ul>
847 *
848 * @since 3.0
849 */
850 public bool isShowing () {
851 checkWidget();
852 return showing;
853 }
854
855 void onPaint (GC gc, bool isSelected) {
856 if (width is 0 || height is 0)
857 return;
858 if (isSelected) {
859 drawSelected(gc);
860 }
861 else {
862 drawUnselected(gc);
863 }
864 }
865
866 int preferredHeight (GC gc) {
867 Image image = getImage();
868 int h = (image is null) ? 0 : image.getBounds().height;
869 String text = getText();
870 if (font is null) {
871 h = Math.max(h, gc.textExtent(text, FLAGS).y);
872 }
873 else {
874 Font gcFont = gc.getFont();
875 gc.setFont(font);
876 h = Math.max(h, gc.textExtent(text, FLAGS).y);
877 gc.setFont(gcFont);
878 }
879 return h + TOP_MARGIN + BOTTOM_MARGIN;
880 }
881
882 int preferredWidth (GC gc, bool isSelected, bool minimum) {
883 // NOTE: preferred width does not include the "dead space" caused
884 // by the curve.
885 if (isDisposed())
886 return 0;
887 int w = 0;
888 Image image = getImage();
889 if (image !is null && (isSelected || parent.showUnselectedImage)) {
890 w += image.getBounds().width;
891 }
892 String text = null;
893 if (minimum) {
894 int minChars = parent.minChars;
895 text = minChars is 0 ? null : getText();
896 if (text !is null && text.length() > minChars) {
897 if (useEllipses()) {
898 int end = minChars < ELLIPSIS.length() + 1 ? minChars : minChars - ELLIPSIS.length();
899 text = text.substring(0, end);
900 if (minChars > ELLIPSIS.length() + 1)
901 text += ELLIPSIS;
902 }
903 else {
904 int end = minChars;
905 text = text.substring(0, end);
906 }
907 }
908 }
909 else {
910 text = getText();
911 }
912 if (text !is null) {
913 if (w > 0)
914 w += INTERNAL_SPACING;
915 if (font is null) {
916 w += gc.textExtent(text, FLAGS).x;
917 }
918 else {
919 Font gcFont = gc.getFont();
920 gc.setFont(font);
921 w += gc.textExtent(text, FLAGS).x;
922 gc.setFont(gcFont);
923 }
924 }
925 if (parent.showClose || showClose) {
926 if (isSelected || parent.showUnselectedClose) {
927 if (w > 0)
928 w += INTERNAL_SPACING;
929 w += CTabFolder.BUTTON_SIZE;
930 }
931 }
932 return w + LEFT_MARGIN + RIGHT_MARGIN;
933 }
934
935 /**
936 * Sets the control that is used to fill the client area of
937 * the tab folder when the user selects the tab item.
938 *
939 * @param control the new control (or null)
940 *
941 * @exception IllegalArgumentException <ul>
942 * <li>ERROR_INVALID_ARGUMENT - if the control has been disposed</li>
943 * <li>ERROR_INVALID_PARENT - if the control is not in the same widget tree</li>
944 * </ul>
945 * @exception DWTException <ul>
946 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
947 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
948 * </ul>
949 */
950 public void setControl (Control control) {
951 checkWidget();
952 if (control !is null) {
953 if (control.isDisposed())
954 DWT.error(DWT.ERROR_INVALID_ARGUMENT);
955 if (control.getParent() !is parent)
956 DWT.error(DWT.ERROR_INVALID_PARENT);
957 }
958 if (this.control !is null && !this.control.isDisposed()) {
959 this.control.setVisible(false);
960 }
961 this.control = control;
962 if (this.control !is null) {
963 int index = parent.indexOf(this);
964 if (index is parent.getSelectionIndex()) {
965 this.control.setBounds(parent.getClientArea());
966 this.control.setVisible(true);
967 }
968 else {
969 this.control.setVisible(false);
970 }
971 }
972 }
973
974 /**
975 * Sets the image that is displayed if the tab item is disabled.
976 * Null will clear the image.
977 *
978 * @param image the image to be displayed when the item is disabled or null
979 *
980 * @exception DWTException <ul>
981 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
982 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
983 * </ul>
984 *
985 * @deprecated This image is not used
986 */
987 public void setDisabledImage (Image image) {
988 checkWidget();
989 if (image !is null && image.isDisposed()) {
990 DWT.error(DWT.ERROR_INVALID_ARGUMENT);
991 }
992 this.disabledImage = image;
993 }
994
995 /**
996 * Sets the font that the receiver will use to paint textual information
997 * for this item to the font specified by the argument, or to the default font
998 * for that kind of control if the argument is null.
999 *
1000 * @param font the new font (or null)
1001 *
1002 * @exception IllegalArgumentException <ul>
1003 * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
1004 * </ul>
1005 * @exception DWTException <ul>
1006 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1007 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1008 * </ul>
1009 *
1010 * @since 3.0
1011 */
1012 public void setFont (Font font) {
1013 checkWidget();
1014 if (font !is null && font.isDisposed()) {
1015 DWT.error(DWT.ERROR_INVALID_ARGUMENT);
1016 }
1017 if (font is null && this.font is null)
1018 return;
1019 if (font !is null && font.opEquals(this.font))
1020 return;
1021 this.font = font;
1022 if (!parent.updateTabHeight(false)) {
1023 parent.updateItems();
1024 parent.redrawTabs();
1025 }
1026 }
1027
1028 public void setImage (Image image) {
1029 checkWidget();
1030 if (image !is null && image.isDisposed()) {
1031 DWT.error(DWT.ERROR_INVALID_ARGUMENT);
1032 }
1033 Image oldImage = getImage();
1034 if (image is null && oldImage is null)
1035 return;
1036 if (image !is null && image.opEquals(oldImage))
1037 return;
1038 super.setImage(image);
1039 if (!parent.updateTabHeight(false)) {
1040 // If image is the same size as before,
1041 // redraw only the image
1042 if (oldImage !is null && image !is null) {
1043 Rectangle oldBounds = oldImage.getBounds();
1044 Rectangle bounds = image.getBounds();
1045 if (bounds.width is oldBounds.width && bounds.height is oldBounds.height) {
1046 if (showing) {
1047 bool selected = parent.indexOf(this) is parent.selectedIndex;
1048 if (selected || parent.showUnselectedImage) {
1049 int imageX = x + LEFT_MARGIN, maxImageWidth;
1050 if (selected) {
1051 if (parent.single && (parent.showClose || showClose))
1052 imageX += CTabFolder.BUTTON_SIZE;
1053 int rightEdge = Math.min(x + width, parent.getRightItemEdge());
1054 maxImageWidth = rightEdge - imageX - RIGHT_MARGIN;
1055 if (!parent.single && closeRect.width > 0)
1056 maxImageWidth -= closeRect.width + INTERNAL_SPACING;
1057 }
1058 else {
1059 maxImageWidth = x + width - imageX - RIGHT_MARGIN;
1060 if (parent.showUnselectedClose && (parent.showClose || showClose)) {
1061 maxImageWidth -= closeRect.width + INTERNAL_SPACING;
1062 }
1063 }
1064 if (bounds.width < maxImageWidth) {
1065 int imageY = y + (height - bounds.height) / 2 + (parent.onBottom ? -1 : 1);
1066 parent.redraw(imageX, imageY, bounds.width, bounds.height, false);
1067 }
1068 }
1069 }
1070 return;
1071 }
1072 }
1073 parent.updateItems();
1074 parent.redrawTabs();
1075 }
1076 }
1077
1078 /**
1079 * Sets to <code>true</code> to indicate that the receiver's close button should be shown.
1080 * If the parent cast(CTabFolder) was created with DWT.CLOSE style, changing this value has
1081 * no effect.
1082 *
1083 * @param close the new state of the close button
1084 *
1085 * @exception DWTException <ul>
1086 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1087 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1088 * </ul>
1089 *
1090 * @since 3.4
1091 */
1092 public void setShowClose (bool close) {
1093 checkWidget();
1094 if (showClose is close)
1095 return;
1096 showClose = close;
1097 parent.updateItems(); 1019 parent.updateItems();
1098 parent.redrawTabs(); 1020 parent.redrawTabs();
1099 } 1021 }
1100 1022 }
1101 public void setText (String String) { 1023 /**
1102 checkWidget(); 1024 * Sets to <code>true</code> to indicate that the receiver's close button should be shown.
1103 if (String is null) 1025 * If the parent (CTabFolder) was created with DWT.CLOSE style, changing this value has
1104 DWT.error(DWT.ERROR_NULL_ARGUMENT); 1026 * no effect.
1105 if (String.opEquals(getText())) 1027 *
1106 return; 1028 * @param close the new state of the close button
1107 super.setText(String); 1029 *
1108 shortenedText = null; 1030 * @exception DWTException <ul>
1109 shortenedTextWidth = 0; 1031 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1110 if (!parent.updateTabHeight(false)) { 1032 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1111 parent.updateItems(); 1033 * </ul>
1112 parent.redrawTabs(); 1034 *
1113 } 1035 * @since 3.4
1114 } 1036 */
1115 1037 public void setShowClose(bool close) {
1116 /** 1038 checkWidget();
1117 * Sets the receiver's tool tip text to the argument, which 1039 if (showClose is close) return;
1118 * may be null indicating that no tool tip text should be shown. 1040 showClose = close;
1119 * 1041 parent.updateItems();
1120 * @param String the new tool tip text (or null) 1042 parent.redrawTabs();
1121 * 1043 }
1122 * @exception DWTException <ul> 1044 public override void setText (String string) {
1123 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1045 checkWidget();
1124 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1046 // DWT extension: allow null for zero length string
1125 * </ul> 1047 //if (string is null) DWT.error (DWT.ERROR_NULL_ARGUMENT);
1126 */ 1048 if (string.equals (getText())) return;
1127 public void setToolTipText (String String) { 1049 super.setText(string);
1128 checkWidget(); 1050 shortenedText = null;
1129 toolTipText = String; 1051 shortenedTextWidth = 0;
1130 } 1052 if (!parent.updateTabHeight(false)) {
1131 1053 parent.updateItems();
1132 } 1054 parent.redrawTabs();
1055 }
1056 }
1057 /**
1058 * Sets the receiver's tool tip text to the argument, which
1059 * may be null indicating that no tool tip text should be shown.
1060 *
1061 * @param string the new tool tip text (or null)
1062 *
1063 * @exception DWTException <ul>
1064 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1065 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1066 * </ul>
1067 */
1068 public void setToolTipText (String string) {
1069 checkWidget();
1070 toolTipText = string;
1071 }
1072
1073 }