1 /*******************************************************************************
2 * Copyright (c) 2000, 2017 IBM Corporation and others.
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/
9 * SPDX-License-Identifier: EPL-2.0
12 * IBM Corporation - initial API and implementation
13 *******************************************************************************/
14 package org.eclipse.swt.widgets;
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.*;
24 * Instances of this class implement the notebook user interface
25 * metaphor. It allows the user to select a notebook page from
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>.
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.
37 * <dt><b>Styles:</b></dt>
38 * <dd>TOP, BOTTOM</dd>
39 * <dt><b>Events:</b></dt>
43 * Note: Only one of the styles TOP and BOTTOM may be specified.
45 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
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.
53 public class TabFolder extends Composite {
56 static final long TabFolderProc;
57 static final TCHAR TabFolderClass = new TCHAR (0, OS.WC_TABCONTROL, true);
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.
65 static final int ID_UPDOWN = 1;
68 WNDCLASS lpWndClass = new WNDCLASS ();
69 OS.GetClassInfo (0, TabFolderClass, lpWndClass);
70 TabFolderProc = lpWndClass.lpfnWndProc;
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
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.
90 lpWndClass.hInstance = OS.GetModuleHandle (null);
91 lpWndClass.style &= ~(OS.CS_HREDRAW | OS.CS_VREDRAW | OS.CS_GLOBALCLASS);
92 OS.RegisterClass (TabFolderClass, lpWndClass);
96 * Constructs a new instance of this class given its parent
97 * and a style value describing its behavior and appearance.
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.
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
111 * @exception IllegalArgumentException <ul>
112 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
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>
122 * @see Widget#checkSubclass
123 * @see Widget#getStyle
125 public TabFolder (Composite parent, int style) {
126 super (parent, checkStyle (style));
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>
135 * When <code>widgetSelected</code> is called, the item field of the event object is valid.
136 * <code>widgetDefaultSelected</code> is not called.
139 * @param listener the listener which should be notified when the user changes the receiver's selection
141 * @exception IllegalArgumentException <ul>
142 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
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>
149 * @see SelectionListener
150 * @see #removeSelectionListener
151 * @see SelectionEvent
153 public void addSelectionListener(SelectionListener listener) {
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);
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);
167 static int checkStyle (int style) {
168 style = checkBits (style, SWT.TOP, SWT.BOTTOM, 0, 0, 0, 0);
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
177 return style & ~(SWT.H_SCROLL | SWT.V_SCROLL);
181 protected void checkSubclass () {
182 if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
185 @Override Point computeSizeInPixels (int wHint, int hHint, boolean changed) {
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);
193 OS.SendMessage (handle, OS.TCM_GETITEMRECT, count - 1, itemRect);
194 width = Math.max (width, itemRect.right - insetRect.right);
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);
206 @Override Rectangle computeTrimInPixels (int x, int y, int width, int height) {
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);
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);
227 TCITEM tcItem = new TCITEM ();
228 if (OS.SendMessage (handle, OS.TCM_INSERTITEM, index, tcItem) == -1) {
229 error (SWT.ERROR_ITEM_NOT_ADDED);
231 System.arraycopy (items, index, items, index + 1, count - index);
232 items [index] = item;
235 * Send a selection event when the item that is added becomes
236 * the new selection. This only happens when the first item
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
248 void createHandle () {
249 super.createHandle ();
250 state &= ~(CANVAS | THEME_BACKGROUND);
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
259 long hwndToolTip = OS.SendMessage (handle, OS.TCM_GETTOOLTIPS, 0, 0);
260 OS.SendMessage (hwndToolTip, OS.TTM_SETMAXTIPWIDTH, 0, 0x7FFF);
262 createdAsRTL = (style & SWT.RIGHT_TO_LEFT) != 0;
266 void createWidget () {
267 super.createWidget ();
268 items = new TabItem [4];
271 void destroyItem (TabItem item) {
272 int count = (int)OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
274 while (index < count) {
275 if (items [index] == item) break;
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);
283 System.arraycopy (items, index + 1, items, index, --count - index);
284 items [count] = null;
286 if (imageList != null) {
287 OS.SendMessage (handle, OS.TCM_SETIMAGELIST, 0, 0);
288 display.releaseImageList (imageList);
291 items = new TabItem [4];
293 if (count > 0 && index == selectionIndex) {
294 setSelection (Math.max (0, selectionIndex - 1), true);
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);
309 Control findThemeControl () {
310 /* It is not possible to change the background of this control */
314 @Override Rectangle getClientAreaInPixels () {
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);
326 * Returns the item at the given, zero-relative index in the
327 * receiver. Throws an exception if the index is out of range.
329 * @param index the index of the item to return
330 * @return the item at the given index
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>
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>
340 public TabItem getItem (int index) {
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];
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.
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
355 * @exception IllegalArgumentException <ul>
356 * <li>ERROR_NULL_ARGUMENT - if the point is null</li>
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>
365 public TabItem getItem (Point point) {
367 if (point == null) error (SWT.ERROR_NULL_ARGUMENT);
368 TCHITTESTINFO pinfo = new TCHITTESTINFO ();
371 int index = (int)OS.SendMessage (handle, OS.TCM_HITTEST, 0, pinfo);
372 if (index == -1) return null;
373 return items [index];
377 * Returns the number of items contained in the receiver.
379 * @return the number of items
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>
386 public int getItemCount () {
388 return (int)OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
392 * Returns an array of <code>TabItem</code>s which are the items
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.
400 * @return the items in the receiver
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>
407 public TabItem [] getItems () {
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);
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.
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.
424 * @return an array representing the selection
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>
431 public TabItem [] getSelection () {
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]};
439 * Returns the zero-relative index of the item which is currently
440 * selected in the receiver, or -1 if no item is selected.
442 * @return the index of the selected item
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>
449 public int getSelectionIndex () {
451 return (int)OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0);
454 int imageIndex (Image image) {
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
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);
468 int index = imageList.indexOf (image);
470 index = imageList.add (image);
472 imageList.put (index, image);
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.
483 * @param item the search item
484 * @return the index of the item
486 * @exception IllegalArgumentException <ul>
487 * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
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>
494 public int indexOf (TabItem item) {
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;
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];
511 int count = (int)OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
512 while (index < count) {
513 if (items [index].control == child) break;
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);
522 * Since computeSize can be overridden by subclasses, we cannot
523 * call computeSizeInPixels directly.
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);
530 return new Point (width, height);
534 boolean mnemonicHit (char key) {
535 for (int i=0; i<items.length; i++) {
536 TabItem item = items [i];
538 char ch = findMnemonic (item.getText ());
539 if (Character.toUpperCase (key) == Character.toUpperCase (ch)) {
541 if (i != getSelectionIndex ()) setSelection (i, true);
551 boolean mnemonicMatch (char key) {
552 for (int i=0; i<items.length; i++) {
553 TabItem item = items [i];
555 char ch = findMnemonic (item.getText ());
556 if (Character.toUpperCase (key) == Character.toUpperCase (ch)) {
565 void releaseChildren (boolean destroy) {
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);
576 super.releaseChildren (destroy);
580 void releaseWidget () {
581 super.releaseWidget ();
582 if (imageList != null) {
583 OS.SendMessage (handle, OS.TCM_SETIMAGELIST, 0, 0);
584 display.releaseImageList (imageList);
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);
600 * Removes the listener from the collection of listeners who will
601 * be notified when the user changes the receiver's selection.
603 * @param listener the listener which should no longer be notified
605 * @exception IllegalArgumentException <ul>
606 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
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>
613 * @see SelectionListener
614 * @see #addSelectionListener
616 public void removeSelectionListener (SelectionListener listener) {
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);
626 void reskinChildren (int flags) {
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);
634 super.reskinChildren (flags);
638 * Sets the receiver's selection to the given item.
639 * The current selected is first cleared, then the new item is
642 * @param item the item to select
644 * @exception IllegalArgumentException <ul>
645 * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
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>
654 public void setSelection (TabItem item) {
656 if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
657 setSelection (new TabItem [] {item});
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
665 * @param items the array of items
667 * @exception IllegalArgumentException <ul>
668 * <li>ERROR_NULL_ARGUMENT - if the items array is null</li>
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>
675 public void setSelection (TabItem [] items) {
677 if (items == null) error (SWT.ERROR_NULL_ARGUMENT);
678 if (items.length == 0) {
679 setSelection (-1, false);
681 for (int i=items.length-1; i>=0; --i) {
682 int index = indexOf (items [i]);
683 if (index != -1) setSelection (index, false);
689 public void setFont (Font font) {
691 Rectangle oldRect = getClientAreaInPixels ();
692 super.setFont (font);
693 Rectangle newRect = getClientAreaInPixels ();
694 if (!oldRect.equals (newRect)) {
696 int index = (int)OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0);
698 TabItem item = items [index];
699 Control control = item.control;
700 if (control != null && !control.isDisposed ()) {
701 control.setBoundsInPixels (getClientAreaInPixels ());
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.
713 * @param index the index of the item to select
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>
720 public void setSelection (int index) {
722 int count = (int)OS.SendMessage (handle, OS.TCM_GETITEMCOUNT, 0, 0);
723 if (!(0 <= index && index < count)) return;
724 setSelection (index, false);
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);
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);
747 Event event = new Event ();
749 sendSelectionEvent (SWT.Selection, event, true);
755 boolean updateTextDirection(int textDirection) {
756 if (super.updateTextDirection(textDirection)) {
757 if (textDirection != AUTO_TEXT_DIRECTION) {
758 textDirection = style & SWT.FLIP_TEXT_DIRECTION;
760 for (int i = 0, n = items.length; i < n && items[i] != null; i++) {
761 items[i].updateTextDirection (textDirection);
769 String toolTipText (NMTTDISPINFO hdr) {
770 if ((hdr.uFlags & OS.TTF_IDISHWND) != 0) {
773 int index = (int)hdr.idFrom;
774 long hwndToolTip = OS.SendMessage (handle, OS.TCM_GETTOOLTIPS, 0, 0);
775 if (hwndToolTip == hdr.hwndFrom) {
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.
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;
787 hdr.uFlags &= ~OS.TTF_RTLREADING;
789 if (toolTipText != null) return "";
790 if (0 <= index && index < items.length) {
791 TabItem item = items [index];
792 if (item != null) return item.toolTipText;
795 return super.toolTipText (hdr);
799 boolean traversePage (boolean next) {
800 int count = getItemCount ();
801 if (count <= 1) return false;
802 int index = getSelectionIndex ();
806 int offset = (next) ? 1 : -1;
807 index = (index + offset + count) % count;
809 setSelection (index, true);
810 if (index == getSelectionIndex ()) {
811 OS.SendMessage (handle, OS.WM_CHANGEUISTATE, OS.UIS_INITIALIZE, 0);
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;
830 bits &= ~OS.WS_EX_LAYOUTRTL;
832 bits &= ~OS.WS_EX_RTLREADING;
833 OS.SetWindowLong (hwndChild, OS.GWL_EXSTYLE, bits);
834 OS.InvalidateRect (hwndChild, null, true);
837 hwndChild = OS.GetWindow (hwndChild, OS.GW_HWNDNEXT);
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;
857 tcItem.iImage = imageIndex (image);
858 OS.SendMessage (handle, OS.TCM_SETITEM, i, tcItem);
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.
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;
880 TCHAR windowClass () {
881 return TabFolderClass;
886 return TabFolderProc;
890 LRESULT WM_GETDLGCODE (long wParam, long lParam) {
891 LRESULT result = super.WM_GETDLGCODE (wParam, lParam);
893 * Return DLGC_BUTTON so that mnemonics will be
894 * processed without needing to press the ALT key
895 * when the widget has focus.
897 if (result != null) return result;
898 return new LRESULT (OS.DLGC_BUTTON | OS.DLGC_WANTARROWS);
902 LRESULT WM_GETOBJECT (long wParam, long lParam) {
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.
908 if (accessible == null) accessible = new_Accessible (this);
909 return super.WM_GETOBJECT (wParam, lParam);
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) {
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.
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);
937 LRESULT WM_MOUSELEAVE (long wParam, long lParam) {
938 LRESULT result = super.WM_MOUSELEAVE (wParam, lParam);
939 if (result != null) return result;
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.
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);
964 LRESULT WM_NCHITTEST (long wParam, long lParam) {
965 LRESULT result = super.WM_NCHITTEST (wParam, lParam);
966 if (result != null) return result;
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.
979 long hittest = OS.DefWindowProc (handle, OS.WM_NCHITTEST, wParam, lParam);
980 return new LRESULT (hittest);
984 LRESULT WM_NOTIFY (long wParam, long lParam) {
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
1003 LRESULT result = super.WM_NOTIFY (wParam, lParam);
1004 if (result != null) return result;
1005 return LRESULT.ZERO;
1009 LRESULT WM_PARENTNOTIFY (long wParam, long lParam) {
1010 LRESULT result = super.WM_PARENTNOTIFY (wParam, lParam);
1011 if (result != null) return result;
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.
1020 if ((style & SWT.RIGHT_TO_LEFT) != 0) {
1021 int code = OS.LOWORD (wParam);
1023 case OS.WM_CREATE: {
1024 int id = OS.HIWORD (wParam);
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);
1038 LRESULT WM_SIZE (long wParam, long lParam) {
1039 LRESULT result = super.WM_SIZE (wParam, lParam);
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
1047 if (isDisposed ()) return result;
1048 int index = (int)OS.SendMessage (handle, OS.TCM_GETCURSEL, 0, 0);
1050 TabItem item = items [index];
1051 Control control = item.control;
1052 if (control != null && !control.isDisposed ()) {
1053 control.setBoundsInPixels (getClientAreaInPixels ());
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) {
1070 // if (OS.IsAppThemed ()) {
1071 // OS.InvalidateRect (handle, null, true);
1074 int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
1075 if ((bits & OS.TCS_MULTILINE) != 0) {
1076 OS.InvalidateRect (handle, null, true);
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) {
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);
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);
1110 LRESULT wmNotifyChild (NMHDR hdr, long wParam, long lParam) {
1111 int code = hdr.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];
1119 Control control = item.control;
1120 if (control != null && !control.isDisposed ()) {
1121 if (code == OS.TCN_SELCHANGE) {
1122 control.setBoundsInPixels (getClientAreaInPixels ());
1124 control.setVisible (code == OS.TCN_SELCHANGE);
1127 if (code == OS.TCN_SELCHANGE) {
1128 Event event = new Event ();
1130 sendSelectionEvent (SWT.Selection, event, false);
1133 return super.wmNotifyChild (hdr, wParam, lParam);