001: /*******************************************************************************
002: * Copyright (c) 2000, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.internal.presentations.r21.widgets;
011:
012: import org.eclipse.swt.SWT;
013: import org.eclipse.swt.SWTError;
014: import org.eclipse.swt.SWTException;
015: import org.eclipse.swt.graphics.Color;
016: import org.eclipse.swt.graphics.GC;
017: import org.eclipse.swt.graphics.Image;
018: import org.eclipse.swt.graphics.Rectangle;
019: import org.eclipse.swt.widgets.Control;
020: import org.eclipse.swt.widgets.Display;
021: import org.eclipse.swt.widgets.Item;
022: import org.eclipse.swt.widgets.Widget;
023:
024: public class CTabItem extends Item {
025: CTabFolder parent;
026:
027: int x, y, width, height = 0;
028:
029: String toolTipText;
030:
031: Control control; // the tab page
032:
033: private Image disabledImage;
034:
035: // internal constants
036: static final int LEFT_MARGIN = 4;
037:
038: static final int RIGHT_MARGIN = 4;
039:
040: static final int TOP_MARGIN = 3;
041:
042: static final int BOTTOM_MARGIN = 3;
043:
044: private static final int INTERNAL_SPACING = 2;
045:
046: private static final String ellipsis = "..."; //$NON-NLS-1$
047:
048: String shortenedText;
049:
050: int shortenedTextWidth;
051:
052: /**
053: * Constructs a new instance of this class given its parent
054: * (which must be a <code>CTabFolder</code>) and a style value
055: * describing its behavior and appearance. The item is added
056: * to the end of the items maintained by its parent.
057: * <p>
058: * The style value is either one of the style constants defined in
059: * class <code>SWT</code> which is applicable to instances of this
060: * class, or must be built by <em>bitwise OR</em>'ing together
061: * (that is, using the <code>int</code> "|" operator) two or more
062: * of those <code>SWT</code> style constants. The class description
063: * lists the style constants that are applicable to the class.
064: * Style bits are also inherited from superclasses.
065: * </p>
066: *
067: * @param parent a CTabFolder which will be the parent of the new instance (cannot be null)
068: * @param style the style of control to construct
069: *
070: * @exception IllegalArgumentException <ul>
071: * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
072: * </ul>
073: * @exception SWTException <ul>
074: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
075: * </ul>
076: *
077: * @see SWT
078: * @see Widget#getStyle
079: */
080: public CTabItem(CTabFolder parent, int style) {
081: this (parent, style, parent.getItemCount());
082: }
083:
084: /**
085: * Constructs a new instance of this class given its parent
086: * (which must be a <code>CTabFolder</code>), a style value
087: * describing its behavior and appearance, and the index
088: * at which to place it in the items maintained by its parent.
089: * <p>
090: * The style value is either one of the style constants defined in
091: * class <code>SWT</code> which is applicable to instances of this
092: * class, or must be built by <em>bitwise OR</em>'ing together
093: * (that is, using the <code>int</code> "|" operator) two or more
094: * of those <code>SWT</code> style constants. The class description
095: * lists the style constants that are applicable to the class.
096: * Style bits are also inherited from superclasses.
097: * </p>
098: *
099: * @param parent a CTabFolder which will be the parent of the new instance (cannot be null)
100: * @param style the style of control to construct
101: * @param index the index to store the receiver in its parent
102: *
103: * @exception IllegalArgumentException <ul>
104: * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
105: * </ul>
106: * @exception SWTException <ul>
107: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
108: * </ul>
109: *
110: * @see SWT
111: * @see Widget#getStyle
112: */
113: public CTabItem(CTabFolder parent, int style, int index) {
114: super (parent, checkStyle(style));
115: parent.createItem(this , index);
116: }
117:
118: private static int checkStyle(int style) {
119: return SWT.NONE;
120: }
121:
122: public void dispose() {
123: if (isDisposed()) {
124: return;
125: }
126: parent.destroyItem(this );
127: super .dispose();
128: parent = null;
129: control = null;
130: toolTipText = null;
131: }
132:
133: /**
134: * Returns a rectangle describing the receiver's size and location
135: * relative to its parent.
136: *
137: * @return the receiver's bounding column rectangle
138: *
139: * @exception SWTException <ul>
140: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
141: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
142: * </ul>
143: */
144: public Rectangle getBounds() {
145: //checkWidget();
146: return new Rectangle(x, y, width, height);
147: }
148:
149: /**
150: * Gets the control that is displayed in the content are of the tab item.
151: *
152: * @return the control
153: *
154: * @exception SWTError(ERROR_THREAD_INVALID_ACCESS)
155: * when called from the wrong thread
156: * @exception SWTError(ERROR_WIDGET_DISPOSED)
157: * when the widget has been disposed
158: */
159: public Control getControl() {
160: checkWidget();
161: return control;
162: }
163:
164: public Display getDisplay() {
165: if (parent == null) {
166: SWT.error(SWT.ERROR_WIDGET_DISPOSED);
167: }
168: return parent.getDisplay();
169: }
170:
171: /**
172: * Get the image displayed in the tab if the tab is disabled.
173: *
174: * @return the disabled image or null
175: */
176: public Image getDisabledImage() {
177: //checkWidget();
178: return disabledImage;
179: }
180:
181: /**
182: * Returns the receiver's parent, which must be a <code>CTabFolder</code>.
183: *
184: * @return the receiver's parent
185: */
186: public CTabFolder getParent() {
187: //checkWidget();
188: return parent;
189: }
190:
191: /**
192: * Returns the receiver's tool tip text, or null if it has
193: * not been set.
194: *
195: * @return the receiver's tool tip text
196: *
197: * @exception SWTException <ul>
198: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
199: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
200: * </ul>
201: */
202: public String getToolTipText() {
203: checkWidget();
204: return toolTipText;
205: }
206:
207: /**
208: * Paint the receiver.
209: */
210: void onPaint(GC gc, boolean isSelected) {
211:
212: if (width == 0 || height == 0) {
213: return;
214: }
215:
216: Display display = getDisplay();
217: Color highlightShadow = display
218: .getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW);
219: Color normalShadow = display
220: .getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
221:
222: int index = parent.indexOf(this );
223:
224: if (isSelected) {
225:
226: Rectangle bounds = null;
227: if (!parent.onBottom) {
228: if (index == parent.topTabIndex) {
229: bounds = new Rectangle(x + 1, y + 1, width - 2,
230: height - 1);
231: } else {
232: bounds = new Rectangle(x + 2, y + 1, width - 3,
233: height - 1);
234: }
235: } else {
236: if (index == parent.topTabIndex) {
237: bounds = new Rectangle(x + 1, y + 1, width - 2,
238: height - 2);
239: } else {
240: bounds = new Rectangle(x + 2, y + 1, width - 3,
241: height - 2);
242: }
243: }
244: if (parent.backgroundImage != null) {
245: // draw a background image behind the text
246: Rectangle imageRect = parent.backgroundImage
247: .getBounds();
248: gc.drawImage(parent.backgroundImage, 0, 0,
249: imageRect.width, imageRect.height, bounds.x,
250: bounds.y, bounds.width, bounds.height);
251: } else if (parent.gradientColors != null) {
252: // draw a gradient behind the text
253: Color oldBackground = gc.getBackground();
254: if (parent.gradientColors.length == 1) {
255: if (parent.gradientColors[0] != null) {
256: gc.setBackground(parent.gradientColors[0]);
257: }
258: gc.fillRectangle(bounds.x, bounds.y, bounds.width,
259: bounds.height);
260: } else {
261: Color oldForeground = gc.getForeground();
262: Color lastColor = parent.gradientColors[0];
263: if (lastColor == null) {
264: lastColor = oldBackground;
265: }
266: for (int i = 0, pos = 0; i < parent.gradientPercents.length; ++i) {
267: gc.setForeground(lastColor);
268: lastColor = parent.gradientColors[i + 1];
269: if (lastColor == null) {
270: lastColor = oldBackground;
271: }
272: gc.setBackground(lastColor);
273: int gradientWidth = (parent.gradientPercents[i]
274: * bounds.width / 100)
275: - pos;
276: gc.fillGradientRectangle(bounds.x + pos,
277: bounds.y, gradientWidth, bounds.height,
278: false);
279: pos += gradientWidth;
280: }
281: gc.setForeground(oldForeground);
282: }
283: gc.setBackground(oldBackground);
284: }
285:
286: // draw tab lines
287: if (!parent.onBottom) {
288: gc.setForeground(normalShadow);
289: if (index != parent.topTabIndex) {
290: gc.drawLine(x + 1, y, x + 1, y);
291: gc.drawLine(x, y + 1, x, y + height - 2);
292: gc.drawLine(x, y + height - 1, x, y + height - 1);
293: }
294: gc.drawLine(x + width - 1, y, x + width - 1, y);
295: gc
296: .drawLine(x + width, y + 1, x + width, y
297: + height - 2);
298: gc.drawLine(x + width, y + height - 1, x + width, y
299: + height - 1);
300:
301: gc.setForeground(highlightShadow);
302: if (index != parent.topTabIndex) {
303: gc.drawLine(x + 2, y, x + 2, y);
304: gc.drawLine(x + 1, y + 1, x + 1, y + height - 2);
305: gc.drawLine(x + 1, y + height - 1, x + 1, y
306: + height - 1);
307: } else {
308: gc.drawLine(x, y, x, y + height - 1);
309: }
310:
311: gc.drawLine(x + width - 2, y, x + width - 2, y);
312: gc.drawLine(x + width - 1, y + 1, x + width - 1, y
313: + height - 2);
314: gc.drawLine(x + width - 1, y + height - 1, x + width
315: - 1, y + height - 1);
316:
317: // light line across top
318: if (index != parent.topTabIndex) {
319: gc.drawLine(x + 3, y, x + width - 3, y);
320: } else {
321: gc.drawLine(x + 1, y, x + width - 3, y);
322: }
323: } else {
324: gc.setForeground(normalShadow);
325: if (index != parent.topTabIndex) {
326: gc.drawLine(x, y, x, y);
327: gc.drawLine(x, y + 1, x, y + height - 2);
328: gc.drawLine(x + 1, y + height - 1, x + 1, y
329: + height - 1);
330: }
331: gc.drawLine(x + width, y, x + width, y);
332: gc
333: .drawLine(x + width, y + 1, x + width, y
334: + height - 2);
335: gc.drawLine(x + width - 1, y + height - 1, x + width
336: - 1, y + height - 1);
337:
338: gc.setForeground(highlightShadow);
339: if (index != parent.topTabIndex) {
340: gc.drawLine(x + 1, y, x + 1, y);
341: gc.drawLine(x + 1, y + 1, x + 1, y + height - 2);
342: gc.drawLine(x + 2, y + height - 1, x + 2, y
343: + height - 1);
344: } else {
345: gc.drawLine(x, y, x, y + height - 1);
346: }
347:
348: gc.drawLine(x + width - 1, y, x + width - 1, y);
349: gc.drawLine(x + width - 1, y + 1, x + width - 1, y
350: + height - 2);
351: gc.drawLine(x + width - 2, y + height - 1, x + width
352: - 2, y + height - 1);
353:
354: // light line across top and bottom
355: if (index != parent.topTabIndex) {
356: gc.drawLine(x + 1, y, x + width - 2, y);
357: gc.drawLine(x + 2, y + height - 1, x + width - 3, y
358: + height - 1);
359: } else {
360: gc.drawLine(x + 1, y, x + width - 2, y);
361: gc.drawLine(x + 1, y + height - 1, x + width - 3, y
362: + height - 1);
363: }
364: }
365: if (parent.isFocusControl()) {
366: // draw a focus rectangle
367: int x1, y1, width1, height1;
368: if (!parent.onBottom) {
369: if (index == parent.topTabIndex) {
370: x1 = x + 1;
371: y1 = y + 1;
372: width1 = width - 2;
373: height1 = height - 1;
374: } else {
375: x1 = x + 2;
376: y1 = y + 1;
377: width1 = width - 3;
378: height1 = height - 1;
379: }
380: } else {
381: if (index == parent.topTabIndex) {
382: x1 = x + 1;
383: y1 = y + 1;
384: width1 = width - 2;
385: height1 = height - 2;
386: } else {
387: x1 = x + 2;
388: y1 = y + 1;
389: width1 = width - 3;
390: height1 = height - 2;
391: }
392: }
393: gc.setBackground(display
394: .getSystemColor(SWT.COLOR_BLACK));
395: gc.setForeground(display
396: .getSystemColor(SWT.COLOR_WHITE));
397: gc.drawFocus(x1, y1, width1, height1);
398: }
399: } else {
400: // draw tab lines for unselected items
401: gc.setForeground(normalShadow);
402: if (!parent.onBottom) {
403: if (index != parent.topTabIndex
404: && index != parent.getSelectionIndex() + 1) {
405: gc.drawLine(x, y, x, y + (height / 2));
406: }
407: } else {
408: if (index != parent.topTabIndex
409: && index != parent.getSelectionIndex() + 1) {
410: gc.drawLine(x, y + (height / 2), x, y + height - 1);
411: }
412: }
413:
414: }
415:
416: // draw Image
417: int xDraw = x + LEFT_MARGIN;
418:
419: Image image = getImage();
420: if (!isSelected && image != null) {
421: Image temp = getDisabledImage();
422: if (temp != null) {
423: image = temp;
424: }
425: }
426: if (image != null) {
427: Rectangle imageBounds = image.getBounds();
428: int imageX = xDraw;
429: int imageHeight = Math.min(height - BOTTOM_MARGIN
430: - TOP_MARGIN, imageBounds.height);
431: int imageY = y + (height - imageHeight) / 2;
432: int imageWidth = imageBounds.width * imageHeight
433: / imageBounds.height;
434: gc.drawImage(image, imageBounds.x, imageBounds.y,
435: imageBounds.width, imageBounds.height, imageX,
436: imageY, imageWidth, imageHeight);
437: xDraw += imageWidth + INTERNAL_SPACING;
438: }
439:
440: // draw Text
441: int textWidth = x + width - xDraw - RIGHT_MARGIN;
442: if (isSelected && parent.showClose) {
443: textWidth = x + width - xDraw - parent.closeBar.getSize().x
444: - RIGHT_MARGIN;
445: }
446: if (shortenedText == null || shortenedTextWidth != textWidth) {
447: shortenedText = shortenText(gc, getText(), textWidth);
448: shortenedTextWidth = textWidth;
449: }
450: String text = shortenedText;
451:
452: if (isSelected && parent.selectionForeground != null) {
453: gc.setForeground(parent.selectionForeground);
454: } else {
455: gc.setForeground(parent.getForeground());
456: }
457: int textY = y
458: + (height - gc.textExtent(text, SWT.DRAW_MNEMONIC).y)
459: / 2;
460: gc.drawText(text, xDraw, textY, SWT.DRAW_TRANSPARENT
461: | SWT.DRAW_MNEMONIC);
462:
463: gc.setForeground(parent.getForeground());
464: }
465:
466: private static String shortenText(GC gc, String text, int width) {
467: if (gc.textExtent(text, SWT.DRAW_MNEMONIC).x <= width) {
468: return text;
469: }
470:
471: int ellipseWidth = gc.textExtent(ellipsis, SWT.DRAW_MNEMONIC).x;
472: int length = text.length();
473: int end = length - 1;
474: while (end > 0) {
475: text = text.substring(0, end);
476: int l1 = gc.textExtent(text, SWT.DRAW_MNEMONIC).x;
477: if (l1 + ellipseWidth <= width) {
478: return text + ellipsis;
479: }
480: end--;
481: }
482: return text + ellipsis;
483: }
484:
485: /**
486: * Answer the preferred height of the receiver for the GC.
487: */
488: int preferredHeight(GC gc) {
489: Image image = getImage();
490: int height = 0;
491: if (image != null) {
492: height = image.getBounds().height;
493: }
494: String text = getText();
495: height = Math.max(height, gc
496: .textExtent(text, SWT.DRAW_MNEMONIC).y);
497: return height + TOP_MARGIN + BOTTOM_MARGIN;
498: }
499:
500: /**
501: * Answer the preferred width of the receiver for the GC.
502: */
503: int preferredWidth(GC gc) {
504: int width = 0;
505: Image image = getImage();
506: if (image != null) {
507: width += image.getBounds().width;
508: }
509: String text = getText();
510: if (text != null) {
511: if (image != null) {
512: width += INTERNAL_SPACING;
513: }
514: width += gc.textExtent(text, SWT.DRAW_MNEMONIC).x;
515: }
516: if (parent.showClose) {
517: width += INTERNAL_SPACING + preferredHeight(gc); // closebar will be square and will fill preferred height
518: }
519: return width + LEFT_MARGIN + RIGHT_MARGIN;
520: }
521:
522: /**
523: * Sets the control that is used to fill the client area of
524: * the tab folder when the user selects the tab item.
525: * <p>
526: * @param control the new control (or null)
527: *
528: * @exception IllegalArgumentException <ul>
529: * <li>ERROR_INVALID_ARGUMENT - if the control has been disposed</li>
530: * <li>ERROR_INVALID_PARENT - if the control is not in the same widget tree</li>
531: * </ul>
532: * @exception SWTException <ul>
533: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
534: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
535: * </ul>
536: */
537: public void setControl(Control control) {
538: checkWidget();
539: if (control != null) {
540: if (control.isDisposed()) {
541: SWT.error(SWT.ERROR_INVALID_ARGUMENT);
542: }
543: if (control.getParent() != parent) {
544: SWT.error(SWT.ERROR_INVALID_PARENT);
545: }
546: }
547: if (this .control != null && !this .control.isDisposed()) {
548: this .control.setVisible(false);
549: }
550: this .control = control;
551: if (this .control != null) {
552: int index = parent.indexOf(this );
553: if (index == parent.getSelectionIndex()) {
554: this .control.setBounds(parent.getClientArea());
555: this .control.setVisible(true);
556: } else {
557: this .control.setVisible(false);
558: }
559: }
560: }
561:
562: public void setImage(Image image) {
563: checkWidget();
564: if (image != null && image.equals(getImage())) {
565: return;
566: }
567: super .setImage(image);
568: parent.resetTabSize(true);
569: }
570:
571: /**
572: * Sets the image that is displayed if the tab item is disabled.
573: * Null will clear the image.
574: *
575: * @param image the image to be displayed when the item is disabled or null
576: *
577: * @exception SWTException <ul>
578: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
579: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
580: * </ul>
581: */
582: public void setDisabledImage(Image image) {
583: checkWidget();
584: if (image != null && image.equals(getDisabledImage())) {
585: return;
586: }
587: disabledImage = image;
588: parent.redraw();
589: }
590:
591: /**
592: * Set the widget text.
593: * <p>
594: * This method sets the widget label. The label may include
595: * mnemonic characters but must not contain line delimiters.
596: *
597: * @param string the new label for the widget
598: *
599: * @exception IllegalArgumentException <ul>
600: * <li>ERROR_NULL_ARGUMENT - if the text is null</li>
601: * </ul>
602: * @exception SWTException <ul>
603: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
604: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
605: * </ul>
606: */
607: public void setText(String string) {
608: checkWidget();
609: if (string.equals(getText())) {
610: return;
611: }
612: super .setText(string);
613: shortenedText = null;
614: shortenedTextWidth = 0;
615: parent.resetTabSize(false);
616: }
617:
618: /**
619: * Sets the receiver's tool tip text to the argument, which
620: * may be null indicating that no tool tip text should be shown.
621: *
622: * @param string the new tool tip text (or null)
623: *
624: * @exception SWTException <ul>
625: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
626: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
627: * </ul>
628: */
629: public void setToolTipText(String string) {
630: checkWidget();
631: toolTipText = string;
632: }
633: }
|