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