root/dwt/custom/CTabItem.d

Revision 315:349b8c12e243, 38.4 kB (checked in by Frank Benoit <benoit@tionex.de>, 3 months ago)

Sync dwt/custom with dwt-linux

Line 
1 /*******************************************************************************
2  * Copyright (c) 2000, 2008 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  * Port to the D programming language:
11  *     Frank Benoit <benoit@tionex.de>
12  *******************************************************************************/
13 module dwt.custom.CTabItem;
14
15 import dwt.dwthelper.utils;
16
17
18
19 import dwt.DWT;
20 import dwt.DWTException;
21 import dwt.graphics.Color;
22 import dwt.graphics.Font;
23 import dwt.graphics.GC;
24 import dwt.graphics.Image;
25 import dwt.graphics.Point;
26 import dwt.graphics.RGB;
27 import dwt.graphics.Rectangle;
28 import dwt.graphics.TextLayout;
29 import dwt.widgets.Control;
30 import dwt.widgets.Display;
31 import dwt.widgets.Item;
32 import dwt.widgets.Widget;
33 import dwt.custom.CTabFolder;
34
35 /**
36  * Instances of this class represent a selectable user interface object
37  * that represent a page in a notebook widget.
38  *
39  * <dl>
40  * <dt><b>Styles:</b></dt>
41  * <dd>DWT.CLOSE</dd>
42  * <dt><b>Events:</b></dt>
43  * <dd>(none)</dd>
44  * </dl>
45  * <p>
46  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
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>
51  */
52 public class CTabItem : Item {
53     CTabFolder parent;
54     int x,y,width,height = 0;
55     Control control; // the tab page
56
57     String toolTipText;
58     String shortenedText;
59     int shortenedTextWidth;
60
61     // Appearance
62     Font font;
63     Image disabledImage;
64
65     Rectangle closeRect;
66     int closeImageState = CTabFolder.NONE;
67     bool showClose = false;
68     bool showing = false;
69
70     // internal constants
71     static final int TOP_MARGIN = 2;
72     static final int BOTTOM_MARGIN = 2;
73     static final int LEFT_MARGIN = 4;
74     static final int RIGHT_MARGIN = 4;
75     static final int INTERNAL_SPACING = 4;
76     static final int FLAGS = DWT.DRAW_TRANSPARENT | DWT.DRAW_MNEMONIC;
77     static final String ELLIPSIS = "..."; //$NON-NLS-1$ // could use the ellipsis glyph on some platforms "\u2026"
78
79 /**
80  * Constructs a new instance of this class given its parent
81  * (which must be a <code>CTabFolder</code>) and a style value
82  * describing its behavior and appearance. The item is added
83  * to the end of the items maintained by its parent.
84  * <p>
85  * The style value is either one of the style constants defined in
86  * class <code>DWT</code> which is applicable to instances of this
87  * class, or must be built by <em>bitwise OR</em>'ing together
88  * (that is, using the <code>int</code> "|" operator) two or more
89  * of those <code>DWT</code> style constants. The class description
90  * lists the style constants that are applicable to the class.
91  * Style bits are also inherited from superclasses.
92  * </p>
93  *
94  * @param parent a CTabFolder which will be the parent of the new instance (cannot be null)
95  * @param style the style of control to construct
96  *
97  * @exception IllegalArgumentException <ul>
98  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
99  * </ul>
100  * @exception DWTException <ul>
101  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
102  * </ul>
103  *
104  * @see DWT
105  * @see Widget#getStyle()
106  */
107 public this (CTabFolder parent, int style) {
108     this(parent, style, parent.getItemCount());
109 }
110 /**
111  * Constructs a new instance of this class given its parent
112  * (which must be a <code>CTabFolder</code>), a style value
113  * describing its behavior and appearance, and the index
114  * at which to place it in the items maintained by its parent.
115  * <p>
116  * The style value is either one of the style constants defined in
117  * class <code>DWT</code> which is applicable to instances of this
118  * class, or must be built by <em>bitwise OR</em>'ing together
119  * (that is, using the <code>int</code> "|" operator) two or more
120  * of those <code>DWT</code> style constants. The class description
121  * lists the style constants that are applicable to the class.
122  * Style bits are also inherited from superclasses.
123  * </p>
124  *
125  * @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
127  * @param index the zero-relative index to store the receiver in its parent
128  *
129  * @exception IllegalArgumentException <ul>
130  *    <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>
132  * </ul>
133  * @exception DWTException <ul>
134  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
135  * </ul>
136  *
137  * @see DWT
138  * @see Widget#getStyle()
139  */
140 public this (CTabFolder parent, int style, int index) {
141     closeRect = new Rectangle(0, 0, 0, 0);
142     super (parent, style);
143     showClose = (style & DWT.CLOSE) !is 0;
144     parent.createItem (this, index);
145 }
146
147 /*
148  * Return whether to use ellipses or just truncate labels
149  */
150 bool useEllipses() {
151     return parent.simple;
152 }
153
154 String shortenText(GC gc, String text, int width) {
155     return useEllipses()
156         ? shortenText(gc, text, width, ELLIPSIS)
157         : shortenText(gc, text, width, ""); //$NON-NLS-1$
158 }
159
160 String shortenText(GC gc, String text, int width, String ellipses) {
161     if (gc.textExtent(text, FLAGS).x <= width) return text;
162     int ellipseWidth = gc.textExtent(ellipses, FLAGS).x;
163     int length = text.length;
164     TextLayout layout = new TextLayout(getDisplay());
165     layout.setText(text);
166     int end = layout.getPreviousOffset(length, DWT.MOVEMENT_CLUSTER);
167     while (end > 0) {
168         text = text[ 0 .. end ];
169         int l = gc.textExtent(text, FLAGS).x;
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);
250             }
251             break;
252         }
253         default:
254     }
255 }
256 void drawSelected(GC gc ) {
257     Point size = parent.getSize();
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);
284             return;
285         }
286
287         // draw selected tab background and outline
288         shape = null;
289         if (this.parent.onBottom) {
290             int[] left = parent.simple ? CTabFolder.SIMPLE_BOTTOM_LEFT_CORNER : CTabFolder.BOTTOM_LEFT_CORNER;
291             int[] right = parent.simple ? CTabFolder.SIMPLE_BOTTOM_RIGHT_CORNER : parent.curve;
292             if (parent.borderLeft is 0 && parent.indexOf(this) is parent.firstIndex) {
293                 left = [x, y+height];
294             }
295             shape = new int[left.length+right.length+8];
296             int index = 0;
297             shape[index++] = x; // first point repeated here because below we reuse shape to draw outline
298             shape[index++] = y - 1;
299             shape[index++] = x;
300             shape[index++] = y - 1;
301             for (int i = 0; i < left.length/2; i++) {
302                 shape[index++] = x + left[2*i];
303                 shape[index++] = y + height + left[2*i+1] - 1;
304             }
305             for (int i = 0; i < right.length/2; i++) {
306                 shape[index++] = parent.simple ? rightEdge - 1 + right[2*i] : rightEdge - parent.curveIndent + right[2*i];
307                 shape[index++] = parent.simple ? y + height + right[2*i+1] - 1 : y + right[2*i+1] - 2;
308             }
309             shape[index++] = parent.simple ? rightEdge - 1 : rightEdge + parent.curveWidth - parent.curveIndent;
310             shape[index++] = y - 1;
311             shape[index++] = parent.simple ? rightEdge - 1 : rightEdge + parent.curveWidth - parent.curveIndent;
312             shape[index++] = y - 1;
313         } else {
314             int[] left = parent.simple ? CTabFolder.SIMPLE_TOP_LEFT_CORNER : CTabFolder.TOP_LEFT_CORNER;
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.getSystemC