KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > swt > widgets > TabFolder


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.swt.widgets;
12
13  
14 import org.eclipse.swt.internal.*;
15 import org.eclipse.swt.internal.win32.*;
16 import org.eclipse.swt.*;
17 import org.eclipse.swt.graphics.*;
18 import org.eclipse.swt.events.*;
19
20 /**
21  * Instances of this class implement the notebook user interface
22  * metaphor. It allows the user to select a notebook page from
23  * set of pages.
24  * <p>
25  * The item children that may be added to instances of this class
26  * must be of type <code>TabItem</code>.
27  * <code>Control</code> children are created and then set into a
28  * tab item using <code>TabItem#setControl</code>.
29  * </p><p>
30  * Note that although this class is a subclass of <code>Composite</code>,
31  * it does not make sense to set a layout on it.
32  * </p><p>
33  * <dl>
34  * <dt><b>Styles:</b></dt>
35  * <dd>TOP, BOTTOM</dd>
36  * <dt><b>Events:</b></dt>
37  * <dd>Selection</dd>
38  * </dl>
39  * <p>
40  * Note: Only one of the styles TOP and BOTTOM may be specified.
41  * </p><p>
42  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
43  * </p>
44  */

45 public class TabFolder extends Composite {
46     TabItem [] items;
47     ImageList imageList;
48     static final int TabFolderProc;
49     static final TCHAR TabFolderClass = new TCHAR (0, OS.WC_TABCONTROL, true);
50     
51     /*
52     * These are the undocumented control id's for the children of
53     * a tab control. Since there are no constants for these values,
54     * they may change with different versions of Windows.
55     */

56     static final int ID_UPDOWN = 1;
57     
58     static {
59         WNDCLASS lpWndClass = new WNDCLASS ();
60         OS.GetClassInfo (0, TabFolderClass, lpWndClass);
61         TabFolderProc = lpWndClass.lpfnWndProc;
62         /*
63         * Feature in Windows. The tab control window class
64         * uses the CS_HREDRAW and CS_VREDRAW style bits to
65         * force a full redraw of the control and all children
66         * when resized. This causes flashing. The fix is to
67         * register a new window class without these bits and
68         * implement special code that damages only the exposed
69         * area.
70         *
71         * NOTE: Screen readers look for the exact class name
72         * of the control in order to provide the correct kind
73         * of assistance. Therefore, it is critical that the
74         * new window class have the same name. It is possible
75         * to register a local window class with the same name
76         * as a global class. Since bits that affect the class
77         * are being changed, it is possible that other native
78         * code, other than SWT, could create a control with
79         * this class name, and fail unexpectedly.
80         */

81         int hInstance = OS.GetModuleHandle (null);
82         int hHeap = OS.GetProcessHeap ();
83         lpWndClass.hInstance = hInstance;
84         lpWndClass.style &= ~(OS.CS_HREDRAW | OS.CS_VREDRAW | OS.CS_GLOBALCLASS);
85         int byteCount = TabFolderClass.length () * TCHAR.sizeof;
86         int lpszClassName = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
87         OS.MoveMemory (lpszClassName, TabFolderClass, byteCount);
88         lpWndClass.lpszClassName = lpszClassName;
89         OS.RegisterClass (lpWndClass);
90         OS.HeapFree (hHeap, 0, lpszClassName);
91     }
92
93 /**
94  * Constructs a new instance of this class given its parent
95  * and a style value describing its behavior and appearance.
96  * <p>
97  * The style value is either one of the style constants defined in
98  * class <code>SWT</code> which is applicable to instances of this
99  * class, or must be built by <em>bitwise OR</em>'ing together
100  * (that is, using the <code>int</code> "|" operator) two or more
101  * of those <code>SWT</code> style constants. The class description
102  * lists the style constants that are applicable to the class.
103  * Style bits are also inherited from superclasses.
104  * </p>
105  *
106  * @param parent a composite control which will be the parent of the new instance (cannot be null)
107  * @param style the style of control to construct
108  *
109  * @exception IllegalArgumentException <ul>
110  * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
111  * </ul>
112  * @exception SWTException <ul>
113  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
114  * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
115  * </ul>
116  *
117  * @see SWT
118  * @see Widget#checkSubclass
119  * @see Widget#getStyle
120  */

121 public TabFolder (Composite parent, int style) {
122     super (parent, checkStyle (style));
123 }
124
125 /**
126  * Adds the listener to the collection of listeners who will
127  * be notified when the user changes the receiver's selection, by sending
128  * it one of the messages defined in the <code>SelectionListener</code>
129  * interface.
130  * <p>
131  * When <code>widgetSelected</code> is called, the item field of the event object is valid.
132  * <code>widgetDefaultSelected</code> is not called.
133  * </p>
134  *
135  * @param listener the listener which should be notified when the user changes the receiver's selection
136  *
137  * @exception IllegalArgumentException <ul>
138  * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
139  * </ul>
140  * @exception SWTException <ul>
141  * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
142  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
143  * </ul>
144  *
145  * @see SelectionListener
146  * @see #removeSelectionListener
147  * @see SelectionEvent
148  */

149 public void addSelectionListener(SelectionListener listener) {
150     checkWidget ();
151     if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
152     TypedListener typedListener = new TypedListener(listener);
153     addListener(SWT.Selection,typedListener);
154     addListener(SWT.DefaultSelection,typedListener);
155 }
156
157 int callWindowProc (int hwnd, int msg, int wParam, int lParam) {
158     if (handle == 0) return 0;
159     return OS.CallWindowProc (TabFolderProc, hwnd, msg, wParam, lParam);
160 }
161
162 static int checkStyle (int style) {
163     /*
164     * When the SWT.TOP style has not been set, force the
165     * tabs to be on the bottom for tab folders on PPC.
166     */

167     if (OS.IsPPC) {
168         if ((style & SWT.TOP) == 0) style |= SWT.BOTTOM;
169     }
170     style = checkBits (style, SWT.TOP, SWT.BOTTOM, 0, 0, 0, 0);
171     
172     /*
173     * Even though it is legal to create this widget
174     * with scroll bars, they serve no useful purpose
175     * because they do not automatically scroll the
176     * widget's client area. The fix is to clear
177     * the SWT style.
178     */

179     return style & ~(SWT.H_SCROLL | SWT.V_SCROLL);
180 }
181
182 protected void checkSubclass () {
183     if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
184 }
185
186 public Point computeSize (int wHint, int hHint, boolean changed) {
187     checkWidget ();
188     Point size = super.computeSize (wHint, hHint, changed);
189     RECT insetRect = new RECT (), itemRect = new RECT ();
190     OS.SendMessage (handle, OS.TCM_ADJUSTRECT, 0, insetRect);
191     int width = insetRect.left - insetRect.right;
192     int count = OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
193     if (count != 0) {
194         OS.SendMessage (handle, OS.TCM_GETITEMRECT, count - 1, itemRect);
195         width = Math.max (width, itemRect.right - insetRect.right);
196     }
197     RECT rect = new RECT ();
198     OS.SetRect (rect, 0, 0, width, size.y);
199     OS.SendMessage (handle, OS.TCM_ADJUSTRECT, 1, rect);
200     int border = getBorderWidth ();
201     rect.left -= border; rect.right += border;
202     width = rect.right - rect.left;
203     size.x = Math.max (width, size.x);
204     return size;
205 }
206
207 public Rectangle computeTrim (int x, int y, int width, int height) {
208     checkWidget ();
209     RECT rect = new RECT ();
210     OS.SetRect (rect, x, y, x + width, y + height);
211     OS.SendMessage (handle, OS.TCM_ADJUSTRECT, 1, rect);
212     int border = getBorderWidth ();
213     rect.left -= border; rect.right += border;
214     rect.top -= border; rect.bottom += border;
215     int newWidth = rect.right - rect.left;
216     int newHeight = rect.bottom - rect.top;
217     return new Rectangle (rect.left, rect.top, newWidth, newHeight);
218 }
219
220 void createItem (TabItem item, int index) {
221     int count = OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
222     if (!(0 <= index && index <= count)) error (SWT.ERROR_INVALID_RANGE);
223     if (count == items.length) {
224         TabItem [] newItems = new TabItem [items.length + 4];
225         System.arraycopy (items, 0, newItems, 0, items.length);
226         items = newItems;
227     }
228     TCITEM tcItem = new TCITEM ();
229     if (OS.SendMessage (handle, OS.TCM_INSERTITEM, index, tcItem) == -1) {
230         error (SWT.ERROR_ITEM_NOT_ADDED);
231     }
232     System.arraycopy (items, index, items, index + 1, count - index);
233     items [index] = item;
234     
235     /*
236     * Send a selection event when the item that is added becomes
237     * the new selection. This only happens when the first item
238     * is added.
239     */

240     if (count == 0) {
241         Event event = new Event ();
242         event.item = items [0];
243         sendEvent (SWT.Selection, event);
244         // the widget could be destroyed at this point
245
}
246 }
247
248 void createHandle () {
249     super.createHandle ();
250     state &= ~(CANVAS | THEME_BACKGROUND);
251     
252     /* Enable the flat look for tab folders on PPC */
253     if (OS.IsPPC) {
254         OS.SendMessage (handle, OS.CCM_SETVERSION, 0x020c /*COMCTL32_VERSION*/, 0);
255     }
256
257     /*
258     * Feature in Windows. Despite the fact that the
259     * tool tip text contains \r\n, the tooltip will
260     * not honour the new line unless TTM_SETMAXTIPWIDTH
261     * is set. The fix is to set TTM_SETMAXTIPWIDTH to
262     * a large value.
263     */

264     int hwndToolTip = OS.SendMessage (handle, OS.TCM_GETTOOLTIPS, 0, 0);
265     OS.SendMessage (hwndToolTip, OS.TTM_SETMAXTIPWIDTH, 0, 0x7FFF);
266 }
267
268 void createWidget () {
269     super.createWidget ();
270     items = new TabItem [4];
271 }
272
273 void destroyItem (TabItem item) {
274     int count = OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
275     int index = 0;
276     while (index < count) {
277         if (items [index] == item) break;
278         index++;
279     }
280     if (index == count) return;
281     int selectionIndex = OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0);
282     if (OS.SendMessage (handle, OS.TCM_DELETEITEM, index, 0) == 0) {
283         error (SWT.ERROR_ITEM_NOT_REMOVED);
284     }
285     System.arraycopy (items, index + 1, items, index, --count - index);
286     items [count] = null;
287     if (count == 0) {
288         if (imageList != null) {
289             OS.SendMessage (handle, OS.TCM_SETIMAGELIST, 0, 0);
290             display.releaseImageList (imageList);
291         }
292         imageList = null;
293         items = new TabItem [4];
294     }
295     if (count > 0 && index == selectionIndex) {
296         setSelection (Math.max (0, selectionIndex - 1), true);
297     }
298 }
299
300 void drawThemeBackground (int hDC, int hwnd, RECT rect) {
301     RECT rect2 = new RECT ();
302     OS.GetClientRect (handle, rect2);
303     OS.MapWindowPoints (handle, hwnd, rect2, 2);
304     if (OS.IntersectRect (new RECT (), rect2, rect)) {
305         OS.DrawThemeBackground (display.hTabTheme (), hDC, OS.TABP_BODY, 0, rect2, null);
306     }
307 }
308
309 Control findThemeControl () {
310     /* It is not possible to change the background of this control */
311     return this;
312 }
313
314 public Rectangle getClientArea () {
315     checkWidget ();
316     forceResize ();
317     RECT rect = new RECT ();
318     OS.GetClientRect (handle, rect);
319     OS.SendMessage (handle, OS.TCM_ADJUSTRECT, 0, rect);
320     int width = rect.right - rect.left;
321     int height = rect.bottom - rect.top;
322     return new Rectangle (rect.left, rect.top, width, height);
323 }
324
325 /**
326  * Returns the item at the given, zero-relative index in the
327  * receiver. Throws an exception if the index is out of range.
328  *
329  * @param index the index of the item to return
330  * @return the item at the given index
331  *
332  * @exception IllegalArgumentException <ul>
333  * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
334  * </ul>
335  * @exception SWTException <ul>
336  * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
337  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
338  * </ul>
339  */

340 public TabItem getItem (int index) {
341     checkWidget ();
342     int count = OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
343     if (!(0 <= index && index < count)) error (SWT.ERROR_INVALID_RANGE);
344     return items [index];
345 }
346
347 /**
348  * Returns the number of items contained in the receiver.
349  *
350  * @return the number of items
351  *
352  * @exception SWTException <ul>
353  * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
354  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
355  * </ul>
356  */

357 public int getItemCount () {
358     checkWidget ();
359     return OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
360 }
361
362 /**
363  * Returns an array of <code>TabItem</code>s which are the items
364  * in the receiver.
365  * <p>
366  * Note: This is not the actual structure used by the receiver
367  * to maintain its list of items, so modifying the array will
368  * not affect the receiver.
369  * </p>
370  *
371  * @return the items in the receiver
372  *
373  * @exception SWTException <ul>
374  * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
375  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
376  * </ul>
377  */

378 public TabItem [] getItems () {
379     checkWidget ();
380     int count = OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
381     TabItem [] result = new TabItem [count];
382     System.arraycopy (items, 0, result, 0, count);
383     return result;
384 }
385
386 /**
387  * Returns an array of <code>TabItem</code>s that are currently
388  * selected in the receiver. An empty array indicates that no
389  * items are selected.
390  * <p>
391  * Note: This is not the actual structure used by the receiver
392  * to maintain its selection, so modifying the array will
393  * not affect the receiver.
394  * </p>
395  * @return an array representing the selection
396  *
397  * @exception SWTException <ul>
398  * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
399  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
400  * </ul>
401  */

402 public TabItem [] getSelection () {
403     checkWidget ();
404     int index = OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0);
405     if (index == -1) return new TabItem [0];
406     return new TabItem [] {items [index]};
407 }
408
409 /**
410  * Returns the zero-relative index of the item which is currently
411  * selected in the receiver, or -1 if no item is selected.
412  *
413  * @return the index of the selected item
414  *
415  * @exception SWTException <ul>
416  * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
417  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
418  * </ul>
419  */

420 public int getSelectionIndex () {
421     checkWidget ();
422     return OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0);
423 }
424
425 int imageIndex (Image image) {
426     if (image == null) return OS.I_IMAGENONE;
427     if (imageList == null) {
428         Rectangle bounds = image.getBounds ();
429         imageList = display.getImageList (style & SWT.RIGHT_TO_LEFT, bounds.width, bounds.height);
430         int index = imageList.add (image);
431         int hImageList = imageList.getHandle ();
432         OS.SendMessage (handle, OS.TCM_SETIMAGELIST, 0, hImageList);
433         return index;
434     }
435     int index = imageList.indexOf (image);
436     if (index == -1) {
437         index = imageList.add (image);
438     } else {
439         imageList.put (index, image);
440     }
441     return index;
442 }
443
444 /**
445  * Searches the receiver's list starting at the first item
446  * (index 0) until an item is found that is equal to the
447  * argument, and returns the index of that item. If no item
448  * is found, returns -1.
449  *
450  * @param item the search item
451  * @return the index of the item
452  *
453  * @exception IllegalArgumentException <ul>
454  * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
455  * </ul>
456  * @exception SWTException <ul>
457  * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
458  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
459  * </ul>
460  */

461 public int indexOf (TabItem item) {
462     checkWidget ();
463     if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
464     int count = OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
465     for (int i=0; i<count; i++) {
466         if (items [i] == item) return i;
467     }
468     return -1;
469 }
470
471 Point minimumSize (int wHint, int hHint, boolean flushCache) {
472     Control [] children = _getChildren ();
473     int width = 0, height = 0;
474     for (int i=0; i<children.length; i++) {
475         Control child = children [i];
476         int index = 0;
477         int count = OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
478         while (index < count) {
479             if (items [index].control == child) break;
480             index++;
481         }
482         if (index == count) {
483             Rectangle rect = child.getBounds ();
484             width = Math.max (width, rect.x + rect.width);
485             height = Math.max (height, rect.y + rect.height);
486         } else {
487             Point size = child.computeSize (wHint, hHint, flushCache);
488             width = Math.max (width, size.x);
489             height = Math.max (height, size.y);
490         }
491     }
492     return new Point (width, height);
493 }
494
495 boolean mnemonicHit (char key) {
496     for (int i=0; i<items.length; i++) {
497         TabItem item = items [i];
498         if (item != null) {
499             char ch = findMnemonic (item.getText ());
500             if (Character.toUpperCase (key) == Character.toUpperCase (ch)) {
501                 if (forceFocus ()) {
502                     if (i != getSelectionIndex ()) setSelection (i, true);
503                     return true;
504                 }
505             }
506         }
507     }
508     return false;
509 }
510
511 boolean mnemonicMatch (char key) {
512     for (int i=0; i<items.length; i++) {
513         TabItem item = items [i];
514         if (item != null) {
515             char ch = findMnemonic (item.getText ());
516             if (Character.toUpperCase (key) == Character.toUpperCase (ch)) {
517                 return true;
518             }
519         }
520     }
521     return false;
522 }
523
524 void releaseChildren (boolean destroy) {
525     if (items != null) {
526         int count = OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
527         for (int i=0; i<count; i++) {
528             TabItem item = items [i];
529             if (item != null && !item.isDisposed ()) {
530                 item.release (false);
531             }
532         }
533         items = null;
534     }
535     super.releaseChildren (destroy);
536 }
537
538 void releaseWidget () {
539     super.releaseWidget ();
540     if (imageList != null) {
541         OS.SendMessage (handle, OS.TCM_SETIMAGELIST, 0, 0);
542         display.releaseImageList (imageList);
543     }
544     imageList = null;
545 }
546
547 void removeControl (Control control) {
548     super.removeControl (control);
549     int count = OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
550     for (int i=0; i<count; i++) {
551         TabItem item = items [i];
552         if (item.control == control) item.setControl (null);
553     }
554 }
555
556 /**
557  * Removes the listener from the collection of listeners who will
558  * be notified when the user changes the receiver's selection.
559  *
560  * @param listener the listener which should no longer be notified
561  *
562  * @exception IllegalArgumentException <ul>
563  * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
564  * </ul>
565  * @exception SWTException <ul>
566  * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
567  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
568  * </ul>
569  *
570  * @see SelectionListener
571  * @see #addSelectionListener
572  */

573 public void removeSelectionListener (SelectionListener listener) {
574     checkWidget ();
575     if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
576     if (eventTable == null) return;
577     eventTable.unhook (SWT.Selection, listener);
578     eventTable.unhook (SWT.DefaultSelection,listener);
579 }
580
581 /**
582  * Sets the receiver's selection to the given item.
583  * The current selected is first cleared, then the new item is
584  * selected.
585  *
586  * @param item the item to select
587  *
588  * @exception IllegalArgumentException <ul>
589  * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
590  * </ul>
591  * @exception SWTException <ul>
592  * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
593  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
594  * </ul>
595  *
596  * @since 3.2
597  */

598 public void setSelection (TabItem item) {
599     checkWidget ();
600     if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
601     setSelection (new TabItem [] {item});
602 }
603
604 /**
605  * Sets the receiver's selection to be the given array of items.
606  * The current selected is first cleared, then the new items are
607  * selected.
608  *
609  * @param items the array of items
610  *
611  * @exception IllegalArgumentException <ul>
612  * <li>ERROR_NULL_ARGUMENT - if the items array is null</li>
613  * </ul>
614  * @exception SWTException <ul>
615  * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
616  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
617  * </ul>
618  */

619 public void setSelection (TabItem [] items) {
620     checkWidget ();
621     if (items == null) error (SWT.ERROR_NULL_ARGUMENT);
622     if (items.length == 0) {
623         setSelection (-1, false);
624     } else {
625         for (int i=items.length-1; i>=0; --i) {
626             int index = indexOf (items [i]);
627             if (index != -1) setSelection (index, false);
628         }
629     }
630 }
631
632 public void setFont (Font font) {
633     checkWidget ();
634     Rectangle oldRect = getClientArea ();
635     super.setFont (font);
636     Rectangle newRect = getClientArea ();
637     if (!oldRect.equals (newRect)) {
638         sendResize ();
639         int index = OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0);
640         if (index != -1) {
641             TabItem item = items [index];
642             Control control = item.control;
643             if (control != null && !control.isDisposed ()) {
644                 control.setBounds (getClientArea ());
645             }
646         }
647     }
648 }
649
650 /**
651  * Selects the item at the given zero-relative index in the receiver.
652  * If the item at the index was already selected, it remains selected.
653  * The current selection is first cleared, then the new items are
654  * selected. Indices that are out of range are ignored.
655  *
656  * @param index the index of the item to select
657  *
658  * @exception SWTException <ul>
659  * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
660  * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
661  * </ul>
662  */

663 public void setSelection (int index) {
664     checkWidget ();
665     int count = OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
666     if (!(0 <= index && index < count)) return;
667     setSelection (index, false);
668 }
669
670 void setSelection (int index, boolean notify) {
671     int oldIndex = OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0);
672     if (oldIndex == index) return;
673     if (oldIndex != -1) {
674         TabItem item = items [oldIndex];
675         Control control = item.control;
676         if (control != null && !control.isDisposed ()) {
677             control.setVisible (false);
678         }
679     }
680     OS.SendMessage (handle, OS.TCM_SETCURSEL, index, 0);
681     int newIndex = OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0);
682     if (newIndex != -1) {
683         TabItem item = items [newIndex];
684         Control control = item.control;
685         if (control != null && !control.isDisposed ()) {
686             control.setBounds (getClientArea ());
687             control.setVisible (true);
688         }
689         if (notify) {
690             Event event = new Event ();
691             event.item = item;
692             sendEvent (SWT.Selection, event);
693         }
694     }
695 }
696
697 String JavaDoc toolTipText (NMTTDISPINFO hdr) {
698     if ((hdr.uFlags & OS.TTF_IDISHWND) != 0) {
699         return null;
700     }
701     int index = hdr.idFrom;
702     int hwndToolTip = OS.SendMessage (handle, OS.TCM_GETTOOLTIPS, 0, 0);
703     if (hwndToolTip == hdr.hwndFrom) {
704         if (toolTipText != null) return "";
705         if (0 <= index && index < items.length) {
706             TabItem item = items [index];
707             if (item != null) return item.toolTipText;
708         }
709     }
710     return super.toolTipText (hdr);
711 }
712
713 boolean traversePage (boolean next) {
714     int count = getItemCount ();
715     if (count <= 1) return false;
716     int index = getSelectionIndex ();
717     if (index == -1) {
718         index = 0;
719     } else {
720         int offset = (next) ? 1 : -1;
721         index = (index + offset + count) % count;
722     }
723     setSelection (index, true);
724     if (index == getSelectionIndex ()) {
725         OS.SendMessage (handle, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0);
726         return true;
727     }
728     return false;
729 }
730
731 int widgetStyle () {
732     /*
733     * Bug in Windows. Under certain circumstances,
734     * when TCM_SETITEM is used to change the text
735     * in a tab item, the tab folder draws on top
736     * of the client area. The fix is ensure that
737     * this cannot happen by setting WS_CLIPCHILDREN.
738     */

739     int bits = super.widgetStyle () | OS.WS_CLIPCHILDREN;
740     if ((style & SWT.NO_FOCUS) != 0) bits |= OS.TCS_FOCUSNEVER;
741     if ((style & SWT.BOTTOM) != 0) bits |= OS.TCS_BOTTOM;
742     return bits | OS.TCS_TABS | OS.TCS_TOOLTIPS;
743 }
744
745 TCHAR windowClass () {
746     return TabFolderClass;
747 }
748
749 int windowProc () {
750     return TabFolderProc;
751 }
752
753 LRESULT WM_GETDLGCODE (int wParam, int lParam) {
754     LRESULT result = super.WM_GETDLGCODE (wParam, lParam);
755     /*
756     * Return DLGC_BUTTON so that mnemonics will be
757     * processed without needing to press the ALT key
758     * when the widget has focus.
759     */

760     if (result != null) return result;
761     return new LRESULT (OS.DLGC_BUTTON);
762 }
763
764 LRESULT WM_MOUSELEAVE (int wParam, int lParam) {
765     LRESULT result = super.WM_MOUSELEAVE (wParam, lParam);
766     if (result != null) return result;
767     /*
768     * Bug in Windows. On XP, when a tooltip is
769     * hidden due to a time out or mouse press,
770     * the tooltip remains active although no
771     * longer visible and won't show again until
772     * another tooltip becomes active. If there
773     * is only one tooltip in the window, it will
774     * never show again. The fix is to remove the
775     * current tooltip and add it again every time
776     * the mouse leaves the control.
777     */

778     if (OS.COMCTL32_MAJOR >= 6) {
779         TOOLINFO lpti = new TOOLINFO ();
780         lpti.cbSize = TOOLINFO.sizeof;
781         int hwndToolTip = OS.SendMessage (handle, OS.TCM_GETTOOLTIPS, 0, 0);
782         if (OS.SendMessage (hwndToolTip, OS.TTM_GETCURRENTTOOL, 0, lpti) != 0) {
783             if ((lpti.uFlags & OS.TTF_IDISHWND) == 0) {
784                 OS.SendMessage (hwndToolTip, OS.TTM_DELTOOL, 0, lpti);
785                 OS.SendMessage (hwndToolTip, OS.TTM_ADDTOOL, 0, lpti);
786             }
787         }
788     }
789     return result;
790 }
791
792 LRESULT WM_NCHITTEST (int wParam, int lParam) {
793     LRESULT result = super.WM_NCHITTEST (wParam, lParam);
794     if (result != null) return result;
795     /*
796     * Feature in Windows. The tab control implements
797     * WM_NCHITTEST to return HTCLIENT when the cursor
798     * is inside the tab buttons. This causes mouse
799     * events like WM_MOUSEMOVE to be delivered to the
800     * parent. Also, tool tips for the tab control are
801     * never invoked because tool tips rely on mouse
802     * events to be delivered to the window that wants
803     * to display the tool tip. The fix is to call the
804     * default window proc that returns HTCLIENT when
805     * the mouse is in the client area.
806     */

807     int hittest = OS.DefWindowProc (handle, OS.WM_NCHITTEST, wParam, lParam);
808     return new LRESULT (hittest);
809 }
810
811 LRESULT WM_NOTIFY (int wParam, int lParam) {
812     /*
813     * Feature in Windows. When the tab folder window
814     * proc processes WM_NOTIFY, it forwards this
815     * message to its parent. This is done so that
816     * children of this control that send this message
817     * type to their parent will notify not only
818     * this control but also the parent of this control,
819     * which is typically the application window and
820     * the window that is looking for the message.
821     * If the control did not forward the message,
822     * applications would have to subclass the control
823     * window to see the message. Because the control
824     * window is subclassed by SWT, the message
825     * is delivered twice, once by SWT and once when
826     * the message is forwarded by the window proc.
827     * The fix is to avoid calling the window proc
828     * for this control.
829     */

830     LRESULT result = super.WM_NOTIFY (wParam, lParam);
831     if (result != null) return result;
832     return LRESULT.ZERO;
833 }
834
835 LRESULT WM_PARENTNOTIFY (int wParam, int lParam) {
836     LRESULT result = super.WM_PARENTNOTIFY (wParam, lParam);
837     if (result != null) return result;
838     /*
839     * Feature in Windows. Windows does not explicitly set the orientation of
840     * the buddy control. Instead, the orientation is inherited when WS_EX_LAYOUTRTL
841     * is specified for the tab folder. This means that when both WS_EX_LAYOUTRTL
842     * and WS_EX_NOINHERITLAYOUT are specified for the tab folder, the buddy control
843     * will not be oriented correctly. The fix is to explicitly set the orientation
844     * for the buddy control.
845     *
846     * NOTE: WS_EX_LAYOUTRTL is not supported on Windows NT.
847     */

848     if (OS.WIN32_VERSION < OS.VERSION (4, 10)) return result;
849     if ((style & SWT.RIGHT_TO_LEFT) != 0) {
850         int code = wParam & 0xFFFF;
851         switch (code) {
852             case OS.WM_CREATE: {
853                 int id = (wParam >> 16), hwnd = lParam;
854                 if (id == ID_UPDOWN) {
855                     int bits = OS.GetWindowLong (hwnd, OS.GWL_EXSTYLE);
856                     OS.SetWindowLong (hwnd, OS.GWL_EXSTYLE, bits | OS.WS_EX_LAYOUTRTL);
857                 }
858                 break;
859             }
860         }
861     }
862     return result;
863 }
864
865 LRESULT WM_SIZE (int wParam, int lParam) {
866     LRESULT result = super.WM_SIZE (wParam, lParam);
867     /*
868     * It is possible (but unlikely), that application
869     * code could have disposed the widget in the resize
870     * event. If this happens, end the processing of the
871     * Windows message by returning the result of the
872     * WM_SIZE message.
873     */

874     if (isDisposed ()) return result;
875     int index = OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0);
876     if (index != -1) {
877         TabItem item = items [index];
878         Control control = item.control;
879         if (control != null && !control.isDisposed ()) {
880             control.setBounds (getClientArea ());
881         }
882     }
883     return result;
884 }
885
886 LRESULT WM_WINDOWPOSCHANGING (int wParam, int lParam) {
887     LRESULT result = super.WM_WINDOWPOSCHANGING (wParam, lParam);
888     if (result != null) return result;
889     if (!OS.IsWindowVisible (handle)) return result;
890     WINDOWPOS lpwp = new WINDOWPOS ();
891     OS.MoveMemory (lpwp, lParam, WINDOWPOS.sizeof);
892     if ((lpwp.flags & (OS.SWP_NOSIZE | OS.SWP_NOREDRAW)) != 0) {
893         return result;
894     }
895     // TEMPORARY CODE
896
// if (OS.COMCTL32_MAJOR >= 6 && OS.IsAppThemed ()) {
897
// OS.InvalidateRect (handle, null, true);
898
// return result;
899
// }
900
int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
901     if ((bits & OS.TCS_MULTILINE) != 0) {
902         OS.InvalidateRect (handle, null, true);
903         return result;
904     }
905     RECT rect = new RECT ();
906     OS.SetRect (rect, 0, 0, lpwp.cx, lpwp.cy);
907     OS.SendMessage (handle, OS.WM_NCCALCSIZE, 0, rect);
908     int newWidth = rect.right - rect.left;
909     int newHeight = rect.bottom - rect.top;
910     OS.GetClientRect (handle, rect);
911     int oldWidth = rect.right - rect.left;
912     int oldHeight = rect.bottom - rect.top;
913     if (newWidth == oldWidth && newHeight == oldHeight) {
914         return result;
915     }
916     RECT inset = new RECT ();
917     OS.SendMessage (handle, OS.TCM_ADJUSTRECT, 0, inset);
918     int marginX = -inset.right, marginY = -inset.bottom;
919     if (newWidth != oldWidth) {
920         int left = oldWidth;
921         if (newWidth < oldWidth) left = newWidth;
922         OS.SetRect (rect, left - marginX, 0, newWidth, newHeight);
923         OS.InvalidateRect (handle, rect, true);
924     }
925     if (newHeight != oldHeight) {
926         int bottom = oldHeight;
927         if (newHeight < oldHeight) bottom = newHeight;
928         if (newWidth < oldWidth) oldWidth -= marginX;
929         OS.SetRect (rect, 0, bottom - marginY, oldWidth, newHeight);
930         OS.InvalidateRect (handle, rect, true);
931     }
932     return result;
933 }
934
935 LRESULT wmNotifyChild (NMHDR hdr, int wParam, int lParam) {
936     int code = hdr.code;
937     switch (code) {
938         case OS.TCN_SELCHANGE:
939         case OS.TCN_SELCHANGING:
940             TabItem item = null;
941             int index = OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0);
942             if (index != -1) item = items [index];
943             if (item != null) {
944                 Control control = item.control;
945                 if (control != null && !control.isDisposed ()) {
946                     if (code == OS.TCN_SELCHANGE) {
947                         control.setBounds (getClientArea ());
948                     }
949                     control.setVisible (code == OS.TCN_SELCHANGE);
950                 }
951             }
952             if (code == OS.TCN_SELCHANGE) {
953                 Event event = new Event ();
954                 event.item = item;
955                 postEvent (SWT.Selection, event);
956             }
957     }
958     return super.wmNotifyChild (hdr, wParam, lParam);
959 }
960
961 }
962
Popular Tags