]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.eclipse.swt.win32.win32.x86_64/src/org/eclipse/swt/widgets/TabFolder.java
Remove invalid SHA-256-Digests
[simantics/platform.git] / bundles / org.eclipse.swt.win32.win32.x86_64 / src / org / eclipse / swt / widgets / TabFolder.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2017 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.swt.widgets;
15
16
17 import org.eclipse.swt.*;
18 import org.eclipse.swt.events.*;
19 import org.eclipse.swt.graphics.*;
20 import org.eclipse.swt.internal.*;
21 import org.eclipse.swt.internal.win32.*;
22
23 /**
24  * Instances of this class implement the notebook user interface
25  * metaphor.  It allows the user to select a notebook page from
26  * set of pages.
27  * <p>
28  * The item children that may be added to instances of this class
29  * must be of type <code>TabItem</code>.
30  * <code>Control</code> children are created and then set into a
31  * tab item using <code>TabItem#setControl</code>.
32  * </p><p>
33  * Note that although this class is a subclass of <code>Composite</code>,
34  * it does not make sense to set a layout on it.
35  * </p>
36  * <dl>
37  * <dt><b>Styles:</b></dt>
38  * <dd>TOP, BOTTOM</dd>
39  * <dt><b>Events:</b></dt>
40  * <dd>Selection</dd>
41  * </dl>
42  * <p>
43  * Note: Only one of the styles TOP and BOTTOM may be specified.
44  * </p><p>
45  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
46  * </p>
47  *
48  * @see <a href="http://www.eclipse.org/swt/snippets/#tabfolder">TabFolder, TabItem snippets</a>
49  * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a>
50  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
51  * @noextend This class is not intended to be subclassed by clients.
52  */
53 public class TabFolder extends Composite {
54         TabItem [] items;
55         ImageList imageList;
56         static final long TabFolderProc;
57         static final TCHAR TabFolderClass = new TCHAR (0, OS.WC_TABCONTROL, true);
58         boolean createdAsRTL;
59
60         /*
61         * These are the undocumented control id's for the children of
62         * a tab control.  Since there are no constants for these values,
63         * they may change with different versions of Windows.
64         */
65         static final int ID_UPDOWN = 1;
66
67         static {
68                 WNDCLASS lpWndClass = new WNDCLASS ();
69                 OS.GetClassInfo (0, TabFolderClass, lpWndClass);
70                 TabFolderProc = lpWndClass.lpfnWndProc;
71                 /*
72                 * Feature in Windows.  The tab control window class
73                 * uses the CS_HREDRAW and CS_VREDRAW style bits to
74                 * force a full redraw of the control and all children
75                 * when resized.  This causes flashing.  The fix is to
76                 * register a new window class without these bits and
77                 * implement special code that damages only the exposed
78                 * area.
79                 *
80                 * NOTE:  Screen readers look for the exact class name
81                 * of the control in order to provide the correct kind
82                 * of assistance.  Therefore, it is critical that the
83                 * new window class have the same name.  It is possible
84                 * to register a local window class with the same name
85                 * as a global class.  Since bits that affect the class
86                 * are being changed, it is possible that other native
87                 * code, other than SWT, could create a control with
88                 * this class name, and fail unexpectedly.
89                 */
90                 lpWndClass.hInstance = OS.GetModuleHandle (null);
91                 lpWndClass.style &= ~(OS.CS_HREDRAW | OS.CS_VREDRAW | OS.CS_GLOBALCLASS);
92                 OS.RegisterClass (TabFolderClass, lpWndClass);
93         }
94
95 /**
96  * Constructs a new instance of this class given its parent
97  * and a style value describing its behavior and appearance.
98  * <p>
99  * The style value is either one of the style constants defined in
100  * class <code>SWT</code> which is applicable to instances of this
101  * class, or must be built by <em>bitwise OR</em>'ing together
102  * (that is, using the <code>int</code> "|" operator) two or more
103  * of those <code>SWT</code> style constants. The class description
104  * lists the style constants that are applicable to the class.
105  * Style bits are also inherited from superclasses.
106  * </p>
107  *
108  * @param parent a composite control which will be the parent of the new instance (cannot be null)
109  * @param style the style of control to construct
110  *
111  * @exception IllegalArgumentException <ul>
112  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
113  * </ul>
114  * @exception SWTException <ul>
115  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
116  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
117  * </ul>
118  *
119  * @see SWT
120  * @see SWT#TOP
121  * @see SWT#BOTTOM
122  * @see Widget#checkSubclass
123  * @see Widget#getStyle
124  */
125 public TabFolder (Composite parent, int style) {
126         super (parent, checkStyle (style));
127 }
128
129 /**
130  * Adds the listener to the collection of listeners who will
131  * be notified when the user changes the receiver's selection, by sending
132  * it one of the messages defined in the <code>SelectionListener</code>
133  * interface.
134  * <p>
135  * When <code>widgetSelected</code> is called, the item field of the event object is valid.
136  * <code>widgetDefaultSelected</code> is not called.
137  * </p>
138  *
139  * @param listener the listener which should be notified when the user changes the receiver's selection
140  *
141  * @exception IllegalArgumentException <ul>
142  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
143  * </ul>
144  * @exception SWTException <ul>
145  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
146  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
147  * </ul>
148  *
149  * @see SelectionListener
150  * @see #removeSelectionListener
151  * @see SelectionEvent
152  */
153 public void addSelectionListener(SelectionListener listener) {
154         checkWidget ();
155         if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
156         TypedListener typedListener = new TypedListener(listener);
157         addListener(SWT.Selection,typedListener);
158         addListener(SWT.DefaultSelection,typedListener);
159 }
160
161 @Override
162 long callWindowProc (long hwnd, int msg, long wParam, long lParam) {
163         if (handle == 0) return 0;
164         return OS.CallWindowProc (TabFolderProc, hwnd, msg, wParam, lParam);
165 }
166
167 static int checkStyle (int style) {
168         style = checkBits (style, SWT.TOP, SWT.BOTTOM, 0, 0, 0, 0);
169
170         /*
171         * Even though it is legal to create this widget
172         * with scroll bars, they serve no useful purpose
173         * because they do not automatically scroll the
174         * widget's client area.  The fix is to clear
175         * the SWT style.
176         */
177         return style & ~(SWT.H_SCROLL | SWT.V_SCROLL);
178 }
179
180 @Override
181 protected void checkSubclass () {
182         if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
183 }
184
185 @Override Point computeSizeInPixels (int wHint, int hHint, boolean changed) {
186         checkWidget ();
187         Point size = super.computeSizeInPixels (wHint, hHint, changed);
188         RECT insetRect = new RECT (), itemRect = new RECT ();
189         OS.SendMessage (handle, OS.TCM_ADJUSTRECT, 0, insetRect);
190         int width = insetRect.left - insetRect.right;
191         int count = (int)OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
192         if (count != 0) {
193                 OS.SendMessage (handle, OS.TCM_GETITEMRECT, count - 1, itemRect);
194                 width = Math.max (width, itemRect.right - insetRect.right);
195         }
196         RECT rect = new RECT ();
197         OS.SetRect (rect, 0, 0, width, size.y);
198         OS.SendMessage (handle, OS.TCM_ADJUSTRECT, 1, rect);
199         int border = getBorderWidthInPixels ();
200         rect.left -= border;  rect.right += border;
201         width = rect.right - rect.left;
202         size.x = Math.max (width, size.x);
203         return size;
204 }
205
206 @Override Rectangle computeTrimInPixels (int x, int y, int width, int height) {
207         checkWidget ();
208         RECT rect = new RECT ();
209         OS.SetRect (rect, x, y, x + width, y + height);
210         OS.SendMessage (handle, OS.TCM_ADJUSTRECT, 1, rect);
211         int border = getBorderWidthInPixels ();
212         rect.left -= border;  rect.right += border;
213         rect.top -= border;  rect.bottom += border;
214         int newWidth = rect.right - rect.left;
215         int newHeight = rect.bottom - rect.top;
216         return new Rectangle (rect.left, rect.top, newWidth, newHeight);
217 }
218
219 void createItem (TabItem item, int index) {
220         int count = (int)OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
221         if (!(0 <= index && index <= count)) error (SWT.ERROR_INVALID_RANGE);
222         if (count == items.length) {
223                 TabItem [] newItems = new TabItem [items.length + 4];
224                 System.arraycopy (items, 0, newItems, 0, items.length);
225                 items = newItems;
226         }
227         TCITEM tcItem = new TCITEM ();
228         if (OS.SendMessage (handle, OS.TCM_INSERTITEM, index, tcItem) == -1) {
229                 error (SWT.ERROR_ITEM_NOT_ADDED);
230         }
231         System.arraycopy (items, index, items, index + 1, count - index);
232         items [index] = item;
233
234         /*
235         * Send a selection event when the item that is added becomes
236         * the new selection.  This only happens when the first item
237         * is added.
238         */
239         if (count == 0) {
240                 Event event = new Event ();
241                 event.item = items [0];
242                 sendSelectionEvent (SWT.Selection, event, true);
243                 // the widget could be destroyed at this point
244         }
245 }
246
247 @Override
248 void createHandle () {
249         super.createHandle ();
250         state &= ~(CANVAS | THEME_BACKGROUND);
251
252         /*
253         * Feature in Windows.  Despite the fact that the
254         * tool tip text contains \r\n, the tooltip will
255         * not honour the new line unless TTM_SETMAXTIPWIDTH
256         * is set.  The fix is to set TTM_SETMAXTIPWIDTH to
257         * a large value.
258         */
259         long hwndToolTip = OS.SendMessage (handle, OS.TCM_GETTOOLTIPS, 0, 0);
260         OS.SendMessage (hwndToolTip, OS.TTM_SETMAXTIPWIDTH, 0, 0x7FFF);
261
262         createdAsRTL = (style & SWT.RIGHT_TO_LEFT) != 0;
263 }
264
265 @Override
266 void createWidget () {
267         super.createWidget ();
268         items = new TabItem [4];
269 }
270
271 void destroyItem (TabItem item) {
272         int count = (int)OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
273         int index = 0;
274         while (index < count) {
275                 if (items [index] == item) break;
276                 index++;
277         }
278         if (index == count) return;
279         int selectionIndex = (int)OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0);
280         if (OS.SendMessage (handle, OS.TCM_DELETEITEM, index, 0) == 0) {
281                 error (SWT.ERROR_ITEM_NOT_REMOVED);
282         }
283         System.arraycopy (items, index + 1, items, index, --count - index);
284         items [count] = null;
285         if (count == 0) {
286                 if (imageList != null) {
287                         OS.SendMessage (handle, OS.TCM_SETIMAGELIST, 0, 0);
288                         display.releaseImageList (imageList);
289                 }
290                 imageList = null;
291                 items = new TabItem [4];
292         }
293         if (count > 0 && index == selectionIndex) {
294                 setSelection (Math.max (0, selectionIndex - 1), true);
295         }
296 }
297
298 @Override
299 void drawThemeBackground (long hDC, long hwnd, RECT rect) {
300         RECT rect2 = new RECT ();
301         OS.GetClientRect (handle, rect2);
302         OS.MapWindowPoints (handle, hwnd, rect2, 2);
303         if (OS.IntersectRect (new RECT (), rect2, rect)) {
304                 OS.DrawThemeBackground (display.hTabTheme (), hDC, OS.TABP_BODY, 0, rect2, null);
305         }
306 }
307
308 @Override
309 Control findThemeControl () {
310         /* It is not possible to change the background of this control */
311         return this;
312 }
313
314 @Override Rectangle getClientAreaInPixels () {
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 = (int)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 tab item at the given point in the receiver
349  * or null if no such item exists. The point is in the
350  * coordinate system of the receiver.
351  *
352  * @param point the point used to locate the item
353  * @return the tab item at the given point, or null if the point is not in a tab item
354  *
355  * @exception IllegalArgumentException <ul>
356  *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
357  * </ul>
358  * @exception SWTException <ul>
359  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
360  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
361  * </ul>
362  *
363  * @since 3.4
364  */
365 public TabItem getItem (Point point) {
366         checkWidget ();
367         if (point == null) error (SWT.ERROR_NULL_ARGUMENT);
368         TCHITTESTINFO pinfo = new TCHITTESTINFO ();
369         pinfo.x = point.x;
370         pinfo.y = point.y;
371         int index = (int)OS.SendMessage (handle, OS.TCM_HITTEST, 0, pinfo);
372         if (index == -1) return null;
373         return items [index];
374 }
375
376 /**
377  * Returns the number of items contained in the receiver.
378  *
379  * @return the number of items
380  *
381  * @exception SWTException <ul>
382  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
383  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
384  * </ul>
385  */
386 public int getItemCount () {
387         checkWidget ();
388         return (int)OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
389 }
390
391 /**
392  * Returns an array of <code>TabItem</code>s which are the items
393  * in the receiver.
394  * <p>
395  * Note: This is not the actual structure used by the receiver
396  * to maintain its list of items, so modifying the array will
397  * not affect the receiver.
398  * </p>
399  *
400  * @return the items in the receiver
401  *
402  * @exception SWTException <ul>
403  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
404  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
405  * </ul>
406  */
407 public TabItem [] getItems () {
408         checkWidget ();
409         int count = (int)OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
410         TabItem [] result = new TabItem [count];
411         System.arraycopy (items, 0, result, 0, count);
412         return result;
413 }
414
415 /**
416  * Returns an array of <code>TabItem</code>s that are currently
417  * selected in the receiver. An empty array indicates that no
418  * items are selected.
419  * <p>
420  * Note: This is not the actual structure used by the receiver
421  * to maintain its selection, so modifying the array will
422  * not affect the receiver.
423  * </p>
424  * @return an array representing the selection
425  *
426  * @exception SWTException <ul>
427  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
428  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
429  * </ul>
430  */
431 public TabItem [] getSelection () {
432         checkWidget ();
433         int index = (int)OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0);
434         if (index == -1) return new TabItem [0];
435         return new TabItem [] {items [index]};
436 }
437
438 /**
439  * Returns the zero-relative index of the item which is currently
440  * selected in the receiver, or -1 if no item is selected.
441  *
442  * @return the index of the selected item
443  *
444  * @exception SWTException <ul>
445  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
446  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
447  * </ul>
448  */
449 public int getSelectionIndex () {
450         checkWidget ();
451         return (int)OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0);
452 }
453
454 int imageIndex (Image image) {
455         /*
456          * Bug 497387: Return -1 if there is no image for the tab, for more details
457          * refer: https://msdn.microsoft.com/pt-br/library/windows/hardware/bb760554
458          */
459         if (image == null) return -1;
460         if (imageList == null) {
461                 Rectangle bounds = image.getBoundsInPixels ();
462                 imageList = display.getImageList (style & SWT.RIGHT_TO_LEFT, bounds.width, bounds.height);
463                 int index = imageList.add (image);
464                 long hImageList = imageList.getHandle ();
465                 OS.SendMessage (handle, OS.TCM_SETIMAGELIST, 0, hImageList);
466                 return index;
467         }
468         int index = imageList.indexOf (image);
469         if (index == -1) {
470                 index = imageList.add (image);
471         } else {
472                 imageList.put (index, image);
473         }
474         return index;
475 }
476
477 /**
478  * Searches the receiver's list starting at the first item
479  * (index 0) until an item is found that is equal to the
480  * argument, and returns the index of that item. If no item
481  * is found, returns -1.
482  *
483  * @param item the search item
484  * @return the index of the item
485  *
486  * @exception IllegalArgumentException <ul>
487  *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
488  * </ul>
489  * @exception SWTException <ul>
490  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
491  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
492  * </ul>
493  */
494 public int indexOf (TabItem item) {
495         checkWidget ();
496         if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
497         int count = (int)OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
498         for (int i=0; i<count; i++) {
499                 if (items [i] == item) return i;
500         }
501         return -1;
502 }
503
504 @Override
505 Point minimumSize (int wHint, int hHint, boolean flushCache) {
506         Control [] children = _getChildren ();
507         int width = 0, height = 0;
508         for (int i=0; i<children.length; i++) {
509                 Control child = children [i];
510                 int index = 0;
511                 int count = (int)OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
512                 while (index < count) {
513                         if (items [index].control == child) break;
514                         index++;
515                 }
516                 if (index == count) {
517                         Rectangle rect = DPIUtil.autoScaleUp(child.getBounds ());
518                         width = Math.max (width, rect.x + rect.width);
519                         height = Math.max (height, rect.y + rect.height);
520                 } else {
521                         /*
522                          * Since computeSize can be overridden by subclasses, we cannot
523                          * call computeSizeInPixels directly.
524                          */
525                         Point size = DPIUtil.autoScaleUp(child.computeSize (DPIUtil.autoScaleDown(wHint), DPIUtil.autoScaleDown(hHint), flushCache));
526                         width = Math.max (width, size.x);
527                         height = Math.max (height, size.y);
528                 }
529         }
530         return new Point (width, height);
531 }
532
533 @Override
534 boolean mnemonicHit (char key) {
535         for (int i=0; i<items.length; i++) {
536                 TabItem item = items [i];
537                 if (item != null) {
538                         char ch = findMnemonic (item.getText ());
539                         if (Character.toUpperCase (key) == Character.toUpperCase (ch)) {
540                                 if (forceFocus ()) {
541                                         if (i != getSelectionIndex ()) setSelection (i, true);
542                                         return true;
543                                 }
544                         }
545                 }
546         }
547         return false;
548 }
549
550 @Override
551 boolean mnemonicMatch (char key) {
552         for (int i=0; i<items.length; i++) {
553                 TabItem item = items [i];
554                 if (item != null) {
555                         char ch = findMnemonic (item.getText ());
556                         if (Character.toUpperCase (key) == Character.toUpperCase (ch)) {
557                                 return true;
558                         }
559                 }
560         }
561         return false;
562 }
563
564 @Override
565 void releaseChildren (boolean destroy) {
566         if (items != null) {
567                 int count = (int)OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
568                 for (int i=0; i<count; i++) {
569                         TabItem item = items [i];
570                         if (item != null && !item.isDisposed ()) {
571                                 item.release (false);
572                         }
573                 }
574                 items = null;
575         }
576         super.releaseChildren (destroy);
577 }
578
579 @Override
580 void releaseWidget () {
581         super.releaseWidget ();
582         if (imageList != null) {
583                 OS.SendMessage (handle, OS.TCM_SETIMAGELIST, 0, 0);
584                 display.releaseImageList (imageList);
585         }
586         imageList = null;
587 }
588
589 @Override
590 void removeControl (Control control) {
591         super.removeControl (control);
592         int count = (int)OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
593         for (int i=0; i<count; i++) {
594                 TabItem item = items [i];
595                 if (item.control == control) item.setControl (null);
596         }
597 }
598
599 /**
600  * Removes the listener from the collection of listeners who will
601  * be notified when the user changes the receiver's selection.
602  *
603  * @param listener the listener which should no longer be notified
604  *
605  * @exception IllegalArgumentException <ul>
606  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
607  * </ul>
608  * @exception SWTException <ul>
609  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
610  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
611  * </ul>
612  *
613  * @see SelectionListener
614  * @see #addSelectionListener
615  */
616 public void removeSelectionListener (SelectionListener listener) {
617         checkWidget ();
618         if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
619         if (eventTable == null) return;
620         eventTable.unhook (SWT.Selection, listener);
621         eventTable.unhook (SWT.DefaultSelection,listener);
622 }
623
624
625 @Override
626 void reskinChildren (int flags) {
627         if (items != null) {
628                 int count = (int)OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
629                 for (int i=0; i<count; i++) {
630                         TabItem item = items [i];
631                         if (item != null) item.reskin (flags);
632                 }
633         }
634         super.reskinChildren (flags);
635 }
636
637 /**
638  * Sets the receiver's selection to the given item.
639  * The current selected is first cleared, then the new item is
640  * selected.
641  *
642  * @param item the item to select
643  *
644  * @exception IllegalArgumentException <ul>
645  *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
646  * </ul>
647  * @exception SWTException <ul>
648  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
649  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
650  * </ul>
651  *
652  * @since 3.2
653  */
654 public void setSelection (TabItem item) {
655         checkWidget ();
656         if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
657         setSelection (new TabItem [] {item});
658 }
659
660 /**
661  * Sets the receiver's selection to be the given array of items.
662  * The current selected is first cleared, then the new items are
663  * selected.
664  *
665  * @param items the array of items
666  *
667  * @exception IllegalArgumentException <ul>
668  *    <li>ERROR_NULL_ARGUMENT - if the items array is null</li>
669  * </ul>
670  * @exception SWTException <ul>
671  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
672  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
673  * </ul>
674  */
675 public void setSelection (TabItem [] items) {
676         checkWidget ();
677         if (items == null) error (SWT.ERROR_NULL_ARGUMENT);
678         if (items.length == 0) {
679                 setSelection (-1, false);
680         } else {
681                 for (int i=items.length-1; i>=0; --i) {
682                         int index = indexOf (items [i]);
683                         if (index != -1) setSelection (index, false);
684                 }
685         }
686 }
687
688 @Override
689 public void setFont (Font font) {
690         checkWidget ();
691         Rectangle oldRect = getClientAreaInPixels ();
692         super.setFont (font);
693         Rectangle newRect = getClientAreaInPixels ();
694         if (!oldRect.equals (newRect)) {
695                 sendResize ();
696                 int index = (int)OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0);
697                 if (index != -1) {
698                         TabItem item = items [index];
699                         Control control = item.control;
700                         if (control != null && !control.isDisposed ()) {
701                                 control.setBoundsInPixels (getClientAreaInPixels ());
702                         }
703                 }
704         }
705 }
706
707 /**
708  * Selects the item at the given zero-relative index in the receiver.
709  * If the item at the index was already selected, it remains selected.
710  * The current selection is first cleared, then the new items are
711  * selected. Indices that are out of range are ignored.
712  *
713  * @param index the index of the item to select
714  *
715  * @exception SWTException <ul>
716  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
717  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
718  * </ul>
719  */
720 public void setSelection (int index) {
721         checkWidget ();
722         int count = (int)OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
723         if (!(0 <= index && index < count)) return;
724         setSelection (index, false);
725 }
726
727 void setSelection (int index, boolean notify) {
728         int oldIndex = (int)OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0);
729         if (oldIndex == index) return;
730         if (oldIndex != -1) {
731                 TabItem item = items [oldIndex];
732                 Control control = item.control;
733                 if (control != null && !control.isDisposed ()) {
734                         control.setVisible (false);
735                 }
736         }
737         OS.SendMessage (handle, OS.TCM_SETCURSEL, index, 0);
738         int newIndex = (int)OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0);
739         if (newIndex != -1) {
740                 TabItem item = items [newIndex];
741                 Control control = item.control;
742                 if (control != null && !control.isDisposed ()) {
743                         control.setBoundsInPixels (getClientAreaInPixels ());
744                         control.setVisible (true);
745                 }
746                 if (notify) {
747                         Event event = new Event ();
748                         event.item = item;
749                         sendSelectionEvent (SWT.Selection, event, true);
750                 }
751         }
752 }
753
754 @Override
755 boolean updateTextDirection(int textDirection) {
756         if (super.updateTextDirection(textDirection)) {
757                 if (textDirection != AUTO_TEXT_DIRECTION) {
758                         textDirection = style & SWT.FLIP_TEXT_DIRECTION;
759                 }
760                 for (int i = 0, n = items.length; i < n && items[i] != null; i++) {
761                         items[i].updateTextDirection (textDirection);
762                 }
763                 return true;
764         }
765         return false;
766 }
767
768 @Override
769 String toolTipText (NMTTDISPINFO hdr) {
770         if ((hdr.uFlags & OS.TTF_IDISHWND) != 0) {
771                 return null;
772         }
773         int index = (int)hdr.idFrom;
774         long hwndToolTip = OS.SendMessage (handle, OS.TCM_GETTOOLTIPS, 0, 0);
775         if (hwndToolTip == hdr.hwndFrom) {
776                 /*
777                 * Bug in Windows. For some reason the reading order
778                 * in NMTTDISPINFO is sometimes set incorrectly.  The
779                 * reading order seems to change every time the mouse
780                 * enters the control from the top edge.  The fix is
781                 * to explicitly set TTF_RTLREADING.
782                 */
783                 int flags = SWT.RIGHT_TO_LEFT | SWT.FLIP_TEXT_DIRECTION;
784                 if ((style & flags) != 0 && (style & flags) != flags) {
785                         hdr.uFlags |= OS.TTF_RTLREADING;
786                 } else {
787                         hdr.uFlags &= ~OS.TTF_RTLREADING;
788                 }
789                 if (toolTipText != null) return "";
790                 if (0 <= index && index < items.length) {
791                         TabItem item = items [index];
792                         if (item != null) return item.toolTipText;
793                 }
794         }
795         return super.toolTipText (hdr);
796 }
797
798 @Override
799 boolean traversePage (boolean next) {
800         int count = getItemCount ();
801         if (count <= 1) return false;
802         int index = getSelectionIndex ();
803         if (index == -1) {
804                 index = 0;
805         } else {
806                 int offset = (next) ? 1 : -1;
807                 index = (index + offset + count) % count;
808         }
809         setSelection (index, true);
810         if (index == getSelectionIndex ()) {
811                 OS.SendMessage (handle, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0);
812                 return true;
813         }
814         return false;
815 }
816
817 @Override
818 void updateOrientation () {
819         super.updateOrientation ();
820         long hwndChild = OS.GetWindow (handle, OS.GW_CHILD);
821         while (hwndChild != 0) {
822                 char [] buffer = new char [128];
823                 int length = OS.GetClassName (hwndChild, buffer, buffer.length);
824                 String className = new String (buffer, 0, length);
825                 if (className.equals ("msctls_updown32")) { //$NON-NLS-1$
826                         int bits = OS.GetWindowLong (hwndChild, OS.GWL_EXSTYLE);
827                         if ((style & SWT.RIGHT_TO_LEFT) != 0) {
828                                 bits |= OS.WS_EX_LAYOUTRTL;
829                         } else {
830                                 bits &= ~OS.WS_EX_LAYOUTRTL;
831                         }
832                         bits &= ~OS.WS_EX_RTLREADING;
833                         OS.SetWindowLong (hwndChild, OS.GWL_EXSTYLE, bits);
834                         OS.InvalidateRect (hwndChild, null, true);
835                         break;
836                 }
837                 hwndChild = OS.GetWindow (hwndChild, OS.GW_HWNDNEXT);
838         }
839         RECT rect = new RECT ();
840         OS.GetWindowRect (handle, rect);
841         int width = rect.right - rect.left, height = rect.bottom - rect.top;
842         OS.SetWindowPos (handle, 0, 0, 0, width - 1, height - 1, OS.SWP_NOMOVE | OS.SWP_NOZORDER);
843         OS.SetWindowPos (handle, 0, 0, 0, width, height, OS.SWP_NOMOVE | OS.SWP_NOZORDER);
844         if (imageList != null) {
845                 Point size = imageList.getImageSize ();
846                 display.releaseImageList (imageList);
847                 imageList = display.getImageList (style & SWT.RIGHT_TO_LEFT, size.x, size.y);
848                 long hImageList = imageList.getHandle ();
849                 OS.SendMessage (handle, OS.TCM_SETIMAGELIST, 0, hImageList);
850                 TCITEM tcItem = new TCITEM ();
851                 tcItem.mask = OS.TCIF_IMAGE;
852                 for (int i = 0; i < items.length; i++) {
853                         TabItem item = items [i];
854                         if (item == null) break;
855                         Image image = item.image;
856                         if (image != null) {
857                                 tcItem.iImage = imageIndex (image);
858                                 OS.SendMessage (handle, OS.TCM_SETITEM, i, tcItem);
859                         }
860                 }
861         }
862 }
863
864 @Override
865 int widgetStyle () {
866         /*
867         * Bug in Windows.  Under certain circumstances,
868         * when TCM_SETITEM is used to change the text
869         * in a tab item, the tab folder draws on top
870         * of the client area.  The fix is ensure that
871         * this cannot happen by setting WS_CLIPCHILDREN.
872         */
873         int bits = super.widgetStyle () | OS.WS_CLIPCHILDREN;
874         if ((style & SWT.NO_FOCUS) != 0) bits |= OS.TCS_FOCUSNEVER;
875         if ((style & SWT.BOTTOM) != 0) bits |= OS.TCS_BOTTOM;
876         return bits | OS.TCS_TABS | OS.TCS_TOOLTIPS;
877 }
878
879 @Override
880 TCHAR windowClass () {
881         return TabFolderClass;
882 }
883
884 @Override
885 long windowProc () {
886         return TabFolderProc;
887 }
888
889 @Override
890 LRESULT WM_GETDLGCODE (long wParam, long lParam) {
891         LRESULT result = super.WM_GETDLGCODE (wParam, lParam);
892         /*
893         * Return DLGC_BUTTON so that mnemonics will be
894         * processed without needing to press the ALT key
895         * when the widget has focus.
896         */
897         if (result != null) return result;
898         return new LRESULT (OS.DLGC_BUTTON | OS.DLGC_WANTARROWS);
899 }
900
901 @Override
902 LRESULT WM_GETOBJECT (long wParam, long lParam) {
903         /*
904         * Ensure that there is an accessible object created for this
905         * control because support for publishing the keyboard shortcut
906         * for page switching is implemented in the accessibility package.
907         */
908         if (accessible == null) accessible = new_Accessible (this);
909         return super.WM_GETOBJECT (wParam, lParam);
910 }
911
912 @Override
913 LRESULT WM_KEYDOWN (long wParam, long lParam) {
914         LRESULT result = super.WM_KEYDOWN (wParam, lParam);
915         if (result != null) return result;
916         switch ((int)wParam) {
917                 case OS.VK_LEFT:
918                 case OS.VK_RIGHT:
919                         /*
920                         * Bug in Windows. The behavior for the left and right keys is not
921                         * changed if the orientation changes after the control was created.
922                         * The fix is to replace VK_LEFT by VK_RIGHT and VK_RIGHT by VK_LEFT
923                         * when the current orientation differs from the orientation used to
924                         * create the control.
925                         */
926                         boolean isRTL = (style & SWT.RIGHT_TO_LEFT) != 0;
927                         if (isRTL != createdAsRTL) {
928                                 long code = callWindowProc (handle, OS.WM_KEYDOWN, wParam == OS.VK_RIGHT ? OS.VK_LEFT : OS.VK_RIGHT, lParam);
929                                 return new LRESULT (code);
930                         }
931                         break;
932         }
933         return result;
934 }
935
936 @Override
937 LRESULT WM_MOUSELEAVE (long wParam, long lParam) {
938         LRESULT result = super.WM_MOUSELEAVE (wParam, lParam);
939         if (result != null) return result;
940         /*
941         * Bug in Windows.  On XP, when a tooltip is
942         * hidden due to a time out or mouse press,
943         * the tooltip remains active although no
944         * longer visible and won't show again until
945         * another tooltip becomes active.  If there
946         * is only one tooltip in the window,  it will
947         * never show again.  The fix is to remove the
948         * current tooltip and add it again every time
949         * the mouse leaves the control.
950         */
951         TOOLINFO lpti = new TOOLINFO ();
952         lpti.cbSize = TOOLINFO.sizeof;
953         long hwndToolTip = OS.SendMessage (handle, OS.TCM_GETTOOLTIPS, 0, 0);
954         if (OS.SendMessage (hwndToolTip, OS.TTM_GETCURRENTTOOL, 0, lpti) != 0) {
955                 if ((lpti.uFlags & OS.TTF_IDISHWND) == 0) {
956                         OS.SendMessage (hwndToolTip, OS.TTM_DELTOOL, 0, lpti);
957                         OS.SendMessage (hwndToolTip, OS.TTM_ADDTOOL, 0, lpti);
958                 }
959         }
960         return result;
961 }
962
963 @Override
964 LRESULT WM_NCHITTEST (long wParam, long lParam) {
965         LRESULT result = super.WM_NCHITTEST (wParam, lParam);
966         if (result != null) return result;
967         /*
968         * Feature in Windows.  The tab control implements
969         * WM_NCHITTEST to return HTCLIENT when the cursor
970         * is inside the tab buttons.  This causes mouse
971         * events like WM_MOUSEMOVE to be delivered to the
972         * parent.  Also, tool tips for the tab control are
973         * never invoked because tool tips rely on mouse
974         * events to be delivered to the window that wants
975         * to display the tool tip.  The fix is to call the
976         * default window proc that returns HTCLIENT when
977         * the mouse is in the client area.
978         */
979         long hittest = OS.DefWindowProc (handle, OS.WM_NCHITTEST, wParam, lParam);
980         return new LRESULT (hittest);
981 }
982
983 @Override
984 LRESULT WM_NOTIFY (long wParam, long lParam) {
985         /*
986         * Feature in Windows.  When the tab folder window
987         * proc processes WM_NOTIFY, it forwards this
988         * message to its parent.  This is done so that
989         * children of this control that send this message
990         * type to their parent will notify not only
991         * this control but also the parent of this control,
992         * which is typically the application window and
993         * the window that is looking for the message.
994         * If the control did not forward the message,
995         * applications would have to subclass the control
996         * window to see the message. Because the control
997         * window is subclassed by SWT, the message
998         * is delivered twice, once by SWT and once when
999         * the message is forwarded by the window proc.
1000         * The fix is to avoid calling the window proc
1001         * for this control.
1002         */
1003         LRESULT result = super.WM_NOTIFY (wParam, lParam);
1004         if (result != null) return result;
1005         return LRESULT.ZERO;
1006 }
1007
1008 @Override
1009 LRESULT WM_PARENTNOTIFY (long wParam, long lParam) {
1010         LRESULT result = super.WM_PARENTNOTIFY (wParam, lParam);
1011         if (result != null) return result;
1012         /*
1013         * Feature in Windows.  Windows does not explicitly set the orientation of
1014         * the buddy control.  Instead, the orientation is inherited when WS_EX_LAYOUTRTL
1015         * is specified for the tab folder.  This means that when both WS_EX_LAYOUTRTL
1016         * and WS_EX_NOINHERITLAYOUT are specified for the tab folder, the buddy control
1017         * will not be oriented correctly.  The fix is to explicitly set the orientation
1018         * for the buddy control.
1019         */
1020         if ((style & SWT.RIGHT_TO_LEFT) != 0) {
1021                 int code = OS.LOWORD (wParam);
1022                 switch (code) {
1023                         case OS.WM_CREATE: {
1024                                 int id = OS.HIWORD (wParam);
1025                                 long hwnd = lParam;
1026                                 if (id == ID_UPDOWN) {
1027                                         int bits = OS.GetWindowLong (hwnd, OS.GWL_EXSTYLE);
1028                                         OS.SetWindowLong (hwnd, OS.GWL_EXSTYLE, bits | OS.WS_EX_LAYOUTRTL);
1029                                 }
1030                                 break;
1031                         }
1032                 }
1033         }
1034         return result;
1035 }
1036
1037 @Override
1038 LRESULT WM_SIZE (long wParam, long lParam) {
1039         LRESULT result = super.WM_SIZE (wParam, lParam);
1040         /*
1041         * It is possible (but unlikely), that application
1042         * code could have disposed the widget in the resize
1043         * event.  If this happens, end the processing of the
1044         * Windows message by returning the result of the
1045         * WM_SIZE message.
1046         */
1047         if (isDisposed ()) return result;
1048         int index = (int)OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0);
1049         if (index != -1) {
1050                 TabItem item = items [index];
1051                 Control control = item.control;
1052                 if (control != null && !control.isDisposed ()) {
1053                         control.setBoundsInPixels (getClientAreaInPixels ());
1054                 }
1055         }
1056         return result;
1057 }
1058
1059 @Override
1060 LRESULT WM_WINDOWPOSCHANGING (long wParam, long lParam) {
1061         LRESULT result = super.WM_WINDOWPOSCHANGING (wParam, lParam);
1062         if (result != null) return result;
1063         if (!OS.IsWindowVisible (handle)) return result;
1064         WINDOWPOS lpwp = new WINDOWPOS ();
1065         OS.MoveMemory (lpwp, lParam, WINDOWPOS.sizeof);
1066         if ((lpwp.flags & (OS.SWP_NOSIZE | OS.SWP_NOREDRAW)) != 0) {
1067                 return result;
1068         }
1069         // TEMPORARY CODE
1070 //      if (OS.IsAppThemed ()) {
1071 //              OS.InvalidateRect (handle, null, true);
1072 //              return result;
1073 //      }
1074         int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
1075         if ((bits & OS.TCS_MULTILINE) != 0) {
1076                 OS.InvalidateRect (handle, null, true);
1077                 return result;
1078         }
1079         RECT rect = new RECT ();
1080         OS.SetRect (rect, 0, 0, lpwp.cx, lpwp.cy);
1081         OS.SendMessage (handle, OS.WM_NCCALCSIZE, 0, rect);
1082         int newWidth = rect.right - rect.left;
1083         int newHeight = rect.bottom - rect.top;
1084         OS.GetClientRect (handle, rect);
1085         int oldWidth = rect.right - rect.left;
1086         int oldHeight = rect.bottom - rect.top;
1087         if (newWidth == oldWidth && newHeight == oldHeight) {
1088                 return result;
1089         }
1090         RECT inset = new RECT ();
1091         OS.SendMessage (handle, OS.TCM_ADJUSTRECT, 0, inset);
1092         int marginX = -inset.right, marginY = -inset.bottom;
1093         if (newWidth != oldWidth) {
1094                 int left = oldWidth;
1095                 if (newWidth < oldWidth) left = newWidth;
1096                 OS.SetRect (rect, left - marginX, 0, newWidth, newHeight);
1097                 OS.InvalidateRect (handle, rect, true);
1098         }
1099         if (newHeight != oldHeight) {
1100                 int bottom = oldHeight;
1101                 if (newHeight < oldHeight) bottom = newHeight;
1102                 if (newWidth < oldWidth) oldWidth -= marginX;
1103                 OS.SetRect (rect, 0, bottom - marginY, oldWidth, newHeight);
1104                 OS.InvalidateRect (handle, rect, true);
1105         }
1106         return result;
1107 }
1108
1109 @Override
1110 LRESULT wmNotifyChild (NMHDR hdr, long wParam, long lParam) {
1111         int code = hdr.code;
1112         switch (code) {
1113                 case OS.TCN_SELCHANGE:
1114                 case OS.TCN_SELCHANGING:
1115                         TabItem item = null;
1116                         int index = (int)OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0);
1117                         if (index != -1) item = items [index];
1118                         if (item != null) {
1119                                 Control control = item.control;
1120                                 if (control != null && !control.isDisposed ()) {
1121                                         if (code == OS.TCN_SELCHANGE) {
1122                                                 control.setBoundsInPixels (getClientAreaInPixels ());
1123                                         }
1124                                         control.setVisible (code == OS.TCN_SELCHANGE);
1125                                 }
1126                         }
1127                         if (code == OS.TCN_SELCHANGE) {
1128                                 Event event = new Event ();
1129                                 event.item = item;
1130                                 sendSelectionEvent (SWT.Selection, event, false);
1131                         }
1132         }
1133         return super.wmNotifyChild (hdr, wParam, lParam);
1134 }
1135
1136 }