]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.eclipse.swt.win32.win32.x86_64/src/org/eclipse/swt/widgets/ToolBar.java
Work around SWT 4.13 - 4.18 Win32 DnD bug 567422
[simantics/platform.git] / bundles / org.eclipse.swt.win32.win32.x86_64 / src / org / eclipse / swt / widgets / ToolBar.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2014 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.graphics.*;
19 import org.eclipse.swt.internal.*;
20 import org.eclipse.swt.internal.win32.*;
21
22 /**
23  * Instances of this class support the layout of selectable
24  * tool bar items.
25  * <p>
26  * The item children that may be added to instances of this class
27  * must be of type <code>ToolItem</code>.
28  * </p><p>
29  * Note that although this class is a subclass of <code>Composite</code>,
30  * it does not make sense to add <code>Control</code> children to it,
31  * or set a layout on it.
32  * </p>
33  * <dl>
34  * <dt><b>Styles:</b></dt>
35  * <dd>FLAT, WRAP, RIGHT, HORIZONTAL, VERTICAL, SHADOW_OUT</dd>
36  * <dt><b>Events:</b></dt>
37  * <dd>(none)</dd>
38  * </dl>
39  * <p>
40  * Note: Only one of the styles HORIZONTAL and VERTICAL may be specified.
41  * </p><p>
42  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
43  * </p>
44  *
45  * @see <a href="http://www.eclipse.org/swt/snippets/#toolbar">ToolBar, ToolItem snippets</a>
46  * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a>
47  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
48  * @noextend This class is not intended to be subclassed by clients.
49  */
50 public class ToolBar extends Composite {
51         int lastFocusId, lastArrowId, lastHotId;
52         ToolItem [] items;
53         ToolItem [] tabItemList;
54         boolean ignoreResize, ignoreMouse;
55         ImageList imageList, disabledImageList, hotImageList;
56         static final long ToolBarProc;
57         static final TCHAR ToolBarClass = new TCHAR (0, OS.TOOLBARCLASSNAME, true);
58         static {
59                 WNDCLASS lpWndClass = new WNDCLASS ();
60                 OS.GetClassInfo (0, ToolBarClass, lpWndClass);
61                 ToolBarProc = lpWndClass.lpfnWndProc;
62         }
63
64         /*
65         * From the Windows SDK for TB_SETBUTTONSIZE:
66         *
67         *   "If an application does not explicitly
68         *       set the button size, the size defaults
69         *       to 24 by 22 pixels".
70         */
71         static final int DEFAULT_WIDTH = 24;
72         static final int DEFAULT_HEIGHT = 22;
73
74 /**
75  * Constructs a new instance of this class given its parent
76  * and a style value describing its behavior and appearance.
77  * <p>
78  * The style value is either one of the style constants defined in
79  * class <code>SWT</code> which is applicable to instances of this
80  * class, or must be built by <em>bitwise OR</em>'ing together
81  * (that is, using the <code>int</code> "|" operator) two or more
82  * of those <code>SWT</code> style constants. The class description
83  * lists the style constants that are applicable to the class.
84  * Style bits are also inherited from superclasses.
85  * </p>
86  *
87  * @param parent a composite control which will be the parent of the new instance (cannot be null)
88  * @param style the style of control to construct
89  *
90  * @exception IllegalArgumentException <ul>
91  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
92  * </ul>
93  * @exception SWTException <ul>
94  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
95  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
96  * </ul>
97  *
98  * @see SWT#FLAT
99  * @see SWT#WRAP
100  * @see SWT#RIGHT
101  * @see SWT#HORIZONTAL
102  * @see SWT#SHADOW_OUT
103  * @see SWT#VERTICAL
104  * @see Widget#checkSubclass()
105  * @see Widget#getStyle()
106  */
107 public ToolBar (Composite parent, int style) {
108         super (parent, checkStyle (style));
109         /*
110         * Ensure that either of HORIZONTAL or VERTICAL is set.
111         * NOTE: HORIZONTAL and VERTICAL have the same values
112         * as H_SCROLL and V_SCROLL so it is necessary to first
113         * clear these bits to avoid scroll bars and then reset
114         * the bits using the original style supplied by the
115         * programmer.
116         *
117         * NOTE: The CCS_VERT style cannot be applied when the
118         * widget is created because of this conflict.
119         */
120         if ((style & SWT.VERTICAL) != 0) {
121                 this.style |= SWT.VERTICAL;
122                 int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
123                 /*
124                 * Feature in Windows.  When a tool bar has the style
125                 * TBSTYLE_LIST and has a drop down item, Window leaves
126                 * too much padding around the button.  This affects
127                 * every button in the tool bar and makes the preferred
128                 * height too big.  The fix is to set the TBSTYLE_LIST
129                 * when the tool bar contains both text and images.
130                 *
131                 * NOTE: Tool bars with CCS_VERT must have TBSTYLE_LIST
132                 * set before any item is added or the tool bar does
133                 * not lay out properly.  The work around does not run
134                 * in this case.
135                 */
136                 if (OS.IsAppThemed ()) {
137                         if ((style & SWT.RIGHT) != 0) bits |= OS.TBSTYLE_LIST;
138                 }
139                 OS.SetWindowLong (handle, OS.GWL_STYLE, bits | OS.CCS_VERT);
140         } else {
141                 this.style |= SWT.HORIZONTAL;
142         }
143 }
144
145 @Override
146 long callWindowProc (long hwnd, int msg, long wParam, long lParam) {
147         if (handle == 0) return 0;
148         /*
149         * Bug in Windows.  For some reason, during the processing
150         * of WM_SYSCHAR, the tool bar window proc does not call the
151         * default window proc causing mnemonics for the menu bar
152         * to be ignored.  The fix is to always call the default
153         * window proc for WM_SYSCHAR.
154         */
155         if (msg == OS.WM_SYSCHAR) {
156                 return OS.DefWindowProc (hwnd, msg, wParam, lParam);
157         }
158         return OS.CallWindowProc (ToolBarProc, hwnd, msg, wParam, lParam);
159 }
160
161 static int checkStyle (int style) {
162         /*
163         * On Windows, only flat tool bars can be traversed.
164         */
165         if ((style & SWT.FLAT) == 0) style |= SWT.NO_FOCUS;
166
167         /*
168         * A vertical tool bar cannot wrap because TB_SETROWS
169         * fails when the toolbar has TBSTYLE_WRAPABLE.
170         */
171         if ((style & SWT.VERTICAL) != 0) style &= ~SWT.WRAP;
172
173         /*
174         * Even though it is legal to create this widget
175         * with scroll bars, they serve no useful purpose
176         * because they do not automatically scroll the
177         * widget's client area.  The fix is to clear
178         * the SWT style.
179         */
180         return style & ~(SWT.H_SCROLL | SWT.V_SCROLL);
181 }
182
183 @Override
184 void checkBuffered () {
185         super.checkBuffered ();
186         style |= SWT.DOUBLE_BUFFERED;
187 }
188
189 @Override
190 protected void checkSubclass () {
191         if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
192 }
193
194 @Override Point computeSizeInPixels (int wHint, int hHint, boolean changed) {
195         int width = 0, height = 0;
196         if ((style & SWT.VERTICAL) != 0) {
197                 RECT rect = new RECT ();
198                 TBBUTTON lpButton = new TBBUTTON ();
199                 int count = (int)OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0);
200                 for (int i=0; i<count; i++) {
201                         OS.SendMessage (handle, OS.TB_GETITEMRECT, i, rect);
202                         height = Math.max (height, rect.bottom);
203                         OS.SendMessage (handle, OS.TB_GETBUTTON, i, lpButton);
204                         if ((lpButton.fsStyle & OS.BTNS_SEP) != 0) {
205                                 TBBUTTONINFO info = new TBBUTTONINFO ();
206                                 info.cbSize = TBBUTTONINFO.sizeof;
207                                 info.dwMask = OS.TBIF_SIZE;
208                                 OS.SendMessage (handle, OS.TB_GETBUTTONINFO, lpButton.idCommand, info);
209                                 width = Math.max (width, info.cx);
210                         } else {
211                                 width = Math.max (width, rect.right);
212                         }
213                 }
214         } else {
215                 RECT oldRect = new RECT ();
216                 OS.GetWindowRect (handle, oldRect);
217                 int oldWidth = oldRect.right - oldRect.left;
218                 int oldHeight = oldRect.bottom - oldRect.top;
219                 int border = getBorderWidthInPixels ();
220                 int newWidth = wHint == SWT.DEFAULT ? 0x3FFF : wHint + border * 2;
221                 int newHeight = hHint == SWT.DEFAULT ? 0x3FFF : hHint + border * 2;
222                 boolean redraw = getDrawing () && OS.IsWindowVisible (handle);
223                 ignoreResize = true;
224                 if (redraw) OS.UpdateWindow (handle);
225                 int flags = OS.SWP_NOACTIVATE | OS.SWP_NOMOVE | OS.SWP_NOREDRAW | OS.SWP_NOZORDER;
226                 OS.SetWindowPos (handle, 0, 0, 0, newWidth, newHeight, flags);
227                 int count = (int)OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0);
228                 if (count != 0) {
229                         RECT rect = new RECT ();
230                         OS.SendMessage (handle, OS.TB_GETITEMRECT, count - 1, rect);
231                         width = Math.max (width, rect.right);
232                         height = Math.max (height, rect.bottom);
233                 }
234                 OS.SetWindowPos (handle, 0, 0, 0, oldWidth, oldHeight, flags);
235                 if (redraw) OS.ValidateRect (handle, null);
236                 ignoreResize = false;
237         }
238
239         /*
240         * From the Windows SDK for TB_SETBUTTONSIZE:
241         *
242         *   "If an application does not explicitly
243         *       set the button size, the size defaults
244         *       to 24 by 22 pixels".
245         */
246         if (width == 0) width = DEFAULT_WIDTH;
247         if (height == 0) height = DEFAULT_HEIGHT;
248         if (wHint != SWT.DEFAULT) width = wHint;
249         if (hHint != SWT.DEFAULT) height = hHint;
250         Rectangle trim = computeTrimInPixels (0, 0, width, height);
251         width = trim.width;  height = trim.height;
252         return new Point (width, height);
253 }
254
255 @Override Rectangle computeTrimInPixels (int x, int y, int width, int height) {
256         Rectangle trim = super.computeTrimInPixels (x, y, width, height);
257         int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
258         if ((bits & OS.CCS_NODIVIDER) == 0) trim.height += 2;
259         return trim;
260 }
261
262 @Override
263 Widget computeTabGroup () {
264         ToolItem [] items = _getItems ();
265         if (tabItemList == null) {
266                 int i = 0;
267                 while (i < items.length && items [i].control == null) i++;
268                 if (i == items.length) return super.computeTabGroup ();
269         }
270         int index = (int)OS.SendMessage (handle, OS.TB_GETHOTITEM, 0, 0);
271         if (index == -1) index = lastHotId;
272         while (index >= 0) {
273                 ToolItem item = items [index];
274                 if (item.isTabGroup ()) return item;
275                 index--;
276         }
277         return super.computeTabGroup ();
278 }
279
280 @Override
281 Widget [] computeTabList () {
282         ToolItem [] items = _getItems ();
283         if (tabItemList == null) {
284                 int i = 0;
285                 while (i < items.length && items [i].control == null) i++;
286                 if (i == items.length) return super.computeTabList ();
287         }
288         Widget result [] = {};
289         if (!isTabGroup () || !isEnabled () || !isVisible ()) return result;
290         ToolItem [] list = tabList != null ? _getTabItemList () : items;
291         for (int i=0; i<list.length; i++) {
292                 ToolItem child = list [i];
293                 Widget  [] childList = child.computeTabList ();
294                 if (childList.length != 0) {
295                         Widget [] newResult = new Widget [result.length + childList.length];
296                         System.arraycopy (result, 0, newResult, 0, result.length);
297                         System.arraycopy (childList, 0, newResult, result.length, childList.length);
298                         result = newResult;
299                 }
300         }
301         if (result.length == 0) result = new Widget [] {this};
302         return result;
303 }
304
305 @Override
306 void createHandle () {
307         super.createHandle ();
308         state &= ~CANVAS;
309
310         /*
311         * Feature in Windows.  When TBSTYLE_FLAT is used to create
312         * a flat toolbar, for some reason TBSTYLE_TRANSPARENT is
313         * also set.  This causes the toolbar to flicker when it is
314         * moved or resized.  The fix is to clear TBSTYLE_TRANSPARENT.
315         *
316         * NOTE:  This work around is unnecessary on XP.  There is no
317         * flickering and clearing the TBSTYLE_TRANSPARENT interferes
318         * with the XP theme.
319         */
320         if ((style & SWT.FLAT) != 0) {
321                 if (!OS.IsAppThemed ()) {
322                         int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
323                         bits &= ~OS.TBSTYLE_TRANSPARENT;
324                         OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
325                 }
326         }
327
328         /*
329         * Feature in Windows.  Despite the fact that the
330         * tool tip text contains \r\n, the tooltip will
331         * not honour the new line unless TTM_SETMAXTIPWIDTH
332         * is set.  The fix is to set TTM_SETMAXTIPWIDTH to
333         * a large value.
334         */
335         /*
336         * These lines are intentionally commented.  The tool
337         * bar currently sets this value to 300 so it is not
338         * necessary to set TTM_SETMAXTIPWIDTH.
339         */
340 //      long hwndToolTip = OS.SendMessage (handle, OS.TB_GETTOOLTIPS, 0, 0);
341 //      OS.SendMessage (hwndToolTip, OS.TTM_SETMAXTIPWIDTH, 0, 0x7FFF);
342
343         /*
344         * Feature in Windows.  When the control is created,
345         * it does not use the default system font.  A new HFONT
346         * is created and destroyed when the control is destroyed.
347         * This means that a program that queries the font from
348         * this control, uses the font in another control and then
349         * destroys this control will have the font unexpectedly
350         * destroyed in the other control.  The fix is to assign
351         * the font ourselves each time the control is created.
352         * The control will not destroy a font that it did not
353         * create.
354         */
355         long hFont = OS.GetStockObject (OS.SYSTEM_FONT);
356         OS.SendMessage (handle, OS.WM_SETFONT, hFont, 0);
357
358         /* Set the button struct, bitmap and button sizes */
359         OS.SendMessage (handle, OS.TB_BUTTONSTRUCTSIZE, TBBUTTON.sizeof, 0);
360         OS.SendMessage (handle, OS.TB_SETBITMAPSIZE, 0, 0);
361         OS.SendMessage (handle, OS.TB_SETBUTTONSIZE, 0, 0);
362
363         /* Set the extended style bits */
364         int bits = OS.TBSTYLE_EX_DRAWDDARROWS | OS.TBSTYLE_EX_MIXEDBUTTONS | OS.TBSTYLE_EX_HIDECLIPPEDBUTTONS | OS.TBSTYLE_EX_DOUBLEBUFFER;
365         OS.SendMessage (handle, OS.TB_SETEXTENDEDSTYLE, 0, bits);
366 }
367
368 void createItem (ToolItem item, int index) {
369         int count = (int)OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0);
370         if (!(0 <= index && index <= count)) error (SWT.ERROR_INVALID_RANGE);
371         int id = 0;
372         while (id < items.length && items [id] != null) id++;
373         if (id == items.length) {
374                 ToolItem [] newItems = new ToolItem [items.length + 4];
375                 System.arraycopy (items, 0, newItems, 0, items.length);
376                 items = newItems;
377         }
378         int bits = item.widgetStyle ();
379         TBBUTTON lpButton = new TBBUTTON ();
380         lpButton.idCommand = id;
381         lpButton.fsStyle = (byte) bits;
382         lpButton.fsState = (byte) OS.TBSTATE_ENABLED;
383
384         /*
385         * Bug in Windows.  Despite the fact that the image list
386         * index has never been set for the item, Windows always
387         * assumes that the image index for the item is valid.
388         * When an item is inserted, the image index is zero.
389         * Therefore, when the first image is inserted and is
390         * assigned image index zero, every item draws with this
391         * image.  The fix is to set the image index to none
392         * when the item is created.  This is not necessary in
393         * the case when the item has the BTNS_SEP style because
394         * separators cannot show images.
395         */
396         if ((bits & OS.BTNS_SEP) == 0) lpButton.iBitmap = OS.I_IMAGENONE;
397         if (OS.SendMessage (handle, OS.TB_INSERTBUTTON, index, lpButton) == 0) {
398                 error (SWT.ERROR_ITEM_NOT_ADDED);
399         }
400         items [item.id = id] = item;
401         if ((style & SWT.VERTICAL) != 0) setRowCount (count + 1);
402         layoutItems ();
403 }
404
405 @Override
406 void createWidget () {
407         super.createWidget ();
408         items = new ToolItem [4];
409         lastFocusId = lastArrowId = lastHotId = -1;
410 }
411
412 @Override
413 int applyThemeBackground () {
414         return -1; /* No Change */
415 }
416
417 void destroyItem (ToolItem item) {
418         TBBUTTONINFO info = new TBBUTTONINFO ();
419         info.cbSize = TBBUTTONINFO.sizeof;
420         info.dwMask = OS.TBIF_IMAGE | OS.TBIF_STYLE;
421         int index = (int)OS.SendMessage (handle, OS.TB_GETBUTTONINFO, item.id, info);
422         /*
423         * Feature in Windows.  For some reason, a tool item that has
424         * the style BTNS_SEP does not return I_IMAGENONE when queried
425         * for an image index, despite the fact that no attempt has been
426         * made to assign an image to the item.  As a result, operations
427         * on an image list that use the wrong index cause random results.
428         * The fix is to ensure that the tool item is not a separator
429         * before using the image index.  Since separators cannot have
430         * an image and one is never assigned, this is not a problem.
431         */
432         if ((info.fsStyle & OS.BTNS_SEP) == 0 && info.iImage != OS.I_IMAGENONE) {
433                 if (imageList != null) imageList.put (info.iImage, null);
434                 if (hotImageList != null) hotImageList.put (info.iImage, null);
435                 if (disabledImageList != null) disabledImageList.put (info.iImage, null);
436         }
437         OS.SendMessage (handle, OS.TB_DELETEBUTTON, index, 0);
438         if (item.id == lastFocusId) lastFocusId = -1;
439         if (item.id == lastArrowId) lastArrowId = -1;
440         if (item.id == lastHotId) lastHotId = -1;
441         items [item.id] = null;
442         item.id = -1;
443         int count = (int)OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0);
444         if (count == 0) {
445                 if (imageList != null) {
446                         OS.SendMessage (handle, OS.TB_SETIMAGELIST, 0, 0);
447                         display.releaseToolImageList (imageList);
448                 }
449                 if (hotImageList != null) {
450                         OS.SendMessage (handle, OS.TB_SETHOTIMAGELIST, 0, 0);
451                         display.releaseToolHotImageList (hotImageList);
452                 }
453                 if (disabledImageList != null) {
454                         OS.SendMessage (handle, OS.TB_SETDISABLEDIMAGELIST, 0, 0);
455                         display.releaseToolDisabledImageList (disabledImageList);
456                 }
457                 imageList = hotImageList = disabledImageList = null;
458                 items = new ToolItem [4];
459         }
460         if ((style & SWT.VERTICAL) != 0) setRowCount (count - 1);
461         layoutItems ();
462 }
463
464 @Override
465 void enableWidget (boolean enabled) {
466         super.enableWidget (enabled);
467         /*
468         * Bug in Windows.  When a tool item with the style
469         * BTNS_CHECK or BTNS_CHECKGROUP is selected and then
470         * disabled, the item does not draw using the disabled
471         * image.  The fix is to use the disabled image in all
472         * image lists for the item.
473         *
474         * Feature in Windows.  When a tool bar is disabled,
475         * the text draws disabled but the images do not.
476         * The fix is to use the disabled image in all image
477         * lists for all items.
478         */
479         for (int i=0; i<items.length; i++) {
480                 ToolItem item = items [i];
481                 if (item != null) {
482                         if ((item.style & SWT.SEPARATOR) == 0) {
483                                 item.updateImages (enabled && item.getEnabled ());
484                         }
485                 }
486         }
487 }
488
489 ImageList getDisabledImageList () {
490         return disabledImageList;
491 }
492
493 ImageList getHotImageList () {
494         return hotImageList;
495 }
496
497 ImageList getImageList () {
498         return imageList;
499 }
500
501 /**
502  * Returns the item at the given, zero-relative index in the
503  * receiver. Throws an exception if the index is out of range.
504  *
505  * @param index the index of the item to return
506  * @return the item at the given index
507  *
508  * @exception IllegalArgumentException <ul>
509  *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
510  * </ul>
511  * @exception SWTException <ul>
512  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
513  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
514  * </ul>
515  */
516 public ToolItem getItem (int index) {
517         checkWidget ();
518         int count = (int)OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0);
519         if (!(0 <= index && index < count)) error (SWT.ERROR_INVALID_RANGE);
520         TBBUTTON lpButton = new TBBUTTON ();
521         long result = OS.SendMessage (handle, OS.TB_GETBUTTON, index, lpButton);
522         if (result == 0) error (SWT.ERROR_CANNOT_GET_ITEM);
523         return items [lpButton.idCommand];
524 }
525
526 /**
527  * Returns the item at the given point in the receiver
528  * or null if no such item exists. The point is in the
529  * coordinate system of the receiver.
530  *
531  * @param point the point used to locate the item
532  * @return the item at the given point
533  *
534  * @exception IllegalArgumentException <ul>
535  *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
536  * </ul>
537  * @exception SWTException <ul>
538  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
539  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
540  * </ul>
541  */
542 public ToolItem getItem (Point point) {
543         checkWidget ();
544         if (point == null) error (SWT.ERROR_NULL_ARGUMENT);
545         return getItemInPixels(DPIUtil.autoScaleUp(point));
546 }
547
548 ToolItem getItemInPixels (Point point) {
549         ToolItem [] items = getItems ();
550         for (int i=0; i<items.length; i++) {
551                 Rectangle rect = items [i].getBoundsInPixels ();
552                 if (rect.contains (point)) return items [i];
553         }
554         return null;
555 }
556
557 /**
558  * Returns the number of items contained in the receiver.
559  *
560  * @return the number of items
561  *
562  * @exception SWTException <ul>
563  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
564  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
565  * </ul>
566  */
567 public int getItemCount () {
568         checkWidget ();
569         return (int)OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0);
570 }
571
572 /**
573  * Returns an array of <code>ToolItem</code>s which are the items
574  * in the receiver.
575  * <p>
576  * Note: This is not the actual structure used by the receiver
577  * to maintain its list of items, so modifying the array will
578  * not affect the receiver.
579  * </p>
580  *
581  * @return the items in the receiver
582  *
583  * @exception SWTException <ul>
584  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
585  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
586  * </ul>
587  */
588 public ToolItem [] getItems () {
589         checkWidget ();
590         return _getItems ();
591 }
592
593 ToolItem [] _getItems () {
594         int count = (int)OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0);
595         TBBUTTON lpButton = new TBBUTTON ();
596         ToolItem [] result = new ToolItem [count];
597         for (int i=0; i<count; i++) {
598                 OS.SendMessage (handle, OS.TB_GETBUTTON, i, lpButton);
599                 result [i] = items [lpButton.idCommand];
600         }
601         return result;
602 }
603
604 /**
605  * Returns the number of rows in the receiver. When
606  * the receiver has the <code>WRAP</code> style, the
607  * number of rows can be greater than one.  Otherwise,
608  * the number of rows is always one.
609  *
610  * @return the number of items
611  *
612  * @exception SWTException <ul>
613  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
614  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
615  * </ul>
616  */
617 public int getRowCount () {
618         checkWidget ();
619         if ((style & SWT.VERTICAL) != 0) {
620                 return (int)OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0);
621         }
622         return (int)OS.SendMessage (handle, OS.TB_GETROWS, 0, 0);
623 }
624
625 ToolItem [] _getTabItemList () {
626         if (tabItemList == null) return tabItemList;
627         int count = 0;
628         for (int i=0; i<tabItemList.length; i++) {
629                 if (!tabItemList [i].isDisposed ()) count++;
630         }
631         if (count == tabItemList.length) return tabItemList;
632         ToolItem [] newList = new ToolItem [count];
633         int index = 0;
634         for (int i=0; i<tabItemList.length; i++) {
635                 if (!tabItemList [i].isDisposed ()) {
636                         newList [index++] = tabItemList [i];
637                 }
638         }
639         tabItemList = newList;
640         return tabItemList;
641 }
642
643 /**
644  * Searches the receiver's list starting at the first item
645  * (index 0) until an item is found that is equal to the
646  * argument, and returns the index of that item. If no item
647  * is found, returns -1.
648  *
649  * @param item the search item
650  * @return the index of the item
651  *
652  * @exception IllegalArgumentException <ul>
653  *    <li>ERROR_NULL_ARGUMENT - if the tool item is null</li>
654  *    <li>ERROR_INVALID_ARGUMENT - if the tool item has been disposed</li>
655  * </ul>
656  * @exception SWTException <ul>
657  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
658  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
659  * </ul>
660  */
661 public int indexOf (ToolItem item) {
662         checkWidget ();
663         if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
664         if (item.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
665         return (int)OS.SendMessage (handle, OS.TB_COMMANDTOINDEX, item.id, 0);
666 }
667
668 void layoutItems () {
669         /*
670         * Feature in Windows.  When a tool bar has the style
671         * TBSTYLE_LIST and has a drop down item, Window leaves
672         * too much padding around the button.  This affects
673         * every button in the tool bar and makes the preferred
674         * height too big.  The fix is to set the TBSTYLE_LIST
675         * when the tool bar contains both text and images.
676         *
677         * NOTE: Tool bars with CCS_VERT must have TBSTYLE_LIST
678         * set before any item is added or the tool bar does
679         * not lay out properly.  The work around does not run
680         * in this case.
681         */
682         if (OS.IsAppThemed ()) {
683                 if ((style & SWT.RIGHT) != 0 && (style & SWT.VERTICAL) == 0) {
684                         boolean hasText = false, hasImage = false;
685                         for (int i=0; i<items.length; i++) {
686                                 ToolItem item = items [i];
687                                 if (item != null) {
688                                         if (!hasText) hasText = item.text.length () != 0;
689                                         if (!hasImage) hasImage = item.image != null;
690                                         if (hasText && hasImage) break;
691                                 }
692                         }
693                         int oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE), newBits = oldBits;
694                         if (hasText && hasImage) {
695                                 newBits |= OS.TBSTYLE_LIST;
696                         } else {
697                                 newBits &= ~OS.TBSTYLE_LIST;
698                         }
699                         if (newBits != oldBits) {
700                                 setDropDownItems (false);
701                                 OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
702                                 /*
703                                 * Feature in Windows.  For some reason, when the style
704                                 * is changed to TBSTYLE_LIST, Windows does not lay out
705                                 * the tool items.  The fix is to use WM_SETFONT to force
706                                 * the tool bar to redraw and lay out.
707                                 */
708                                 long hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
709                                 OS.SendMessage (handle, OS.WM_SETFONT, hFont, 0);
710                                 setDropDownItems (true);
711                         }
712                 }
713         }
714
715         if ((style & SWT.WRAP) != 0) {
716                 OS.SendMessage (handle, OS.TB_AUTOSIZE, 0, 0);
717         }
718         /*
719         *  When the tool bar is vertical, make the width of each button
720         *  be the width of the widest button in the tool bar.  Note that
721         *  when the tool bar contains a drop down item, it needs to take
722         *  into account extra padding.
723         */
724         if ((style & SWT.VERTICAL) != 0) {
725                 int itemCount = (int)OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0);
726                 if (itemCount > 1) {
727                         TBBUTTONINFO info = new TBBUTTONINFO ();
728                         info.cbSize = TBBUTTONINFO.sizeof;
729                         info.dwMask = OS.TBIF_SIZE;
730                         long size = OS.SendMessage (handle, OS.TB_GETBUTTONSIZE, 0, 0);
731                         info.cx = (short) OS.LOWORD (size);
732                         int index = 0, extraPadding = 0;
733                         while (index < items.length) {
734                                 ToolItem item = items [index];
735                                 if (item != null && (item.style & SWT.DROP_DOWN) != 0) {
736                                         /*
737                                          * Specifying 1 pixel extra padding to avoid truncation
738                                          * of widest item in the tool-bar when a tool-bar has
739                                          * SWT.VERTICAL style and any of the items in the
740                                          * tool-bar has SWT.DROP_DOWN style, Refer bug#437206
741                                          */
742                                         extraPadding = 1;
743                                         break;
744                                 }
745                                 index++;
746                         }
747                         if (index < items.length) {
748                                 long padding = OS.SendMessage (handle, OS.TB_GETPADDING, 0, 0);
749                                 info.cx += OS.LOWORD (padding + extraPadding) * 2;
750                         }
751                         for (int i=0; i<items.length; i++) {
752                                 ToolItem item = items [i];
753                                 if (item != null && (item.style & SWT.SEPARATOR) == 0) {
754                                         OS.SendMessage (handle, OS.TB_SETBUTTONINFO, item.id, info);
755                                 }
756                         }
757                 }
758         }
759
760         /*
761         * Feature on Windows. When SWT.WRAP or SWT.VERTICAL are set
762         * the separator items with control are implemented using BTNS_BUTTON
763         * instead of BTNS_SEP. When that is the case and TBSTYLE_LIST is
764         * set, the layout of the ToolBar recalculates the width for all
765         * BTNS_BUTTON based on the text and bitmap of the item.
766         * This is not strictly wrong, but the user defined width for the
767         * separators has to be respected if set.
768         * The fix is to detect this case and reset the cx width for the item.
769         */
770         if ((style & (SWT.WRAP | SWT.VERTICAL)) != 0) {
771                 int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
772                 if ((bits & OS.TBSTYLE_LIST) != 0) {
773                         TBBUTTONINFO info = new TBBUTTONINFO ();
774                         info.cbSize = TBBUTTONINFO.sizeof;
775                         info.dwMask = OS.TBIF_SIZE;
776                         for (int i=0; i<items.length; i++) {
777                                 ToolItem item = items [i];
778                                 if (item != null && item.cx > 0) {
779                                         info.cx = item.cx;
780                                         OS.SendMessage (handle, OS.TB_SETBUTTONINFO, item.id, info);
781                                 }
782                         }
783                 }
784         }
785
786         for (int i=0; i<items.length; i++) {
787                 ToolItem item = items [i];
788                 if (item != null) item.resizeControl ();
789         }
790 }
791
792 @Override
793 boolean mnemonicHit (char ch) {
794         int [] id = new int [1];
795         if (OS.SendMessage (handle, OS.TB_MAPACCELERATOR, ch, id) == 0) {
796                 return false;
797         }
798         if ((style & SWT.FLAT) != 0 && !setTabGroupFocus ()) return false;
799         int index = (int)OS.SendMessage (handle, OS.TB_COMMANDTOINDEX, id [0], 0);
800         if (index == -1) return false;
801         OS.SendMessage (handle, OS.TB_SETHOTITEM, index, 0);
802         items [id [0]].click (false);
803         return true;
804 }
805
806 @Override
807 boolean mnemonicMatch (char ch) {
808         int [] id = new int [1];
809         if (OS.SendMessage (handle, OS.TB_MAPACCELERATOR, ch, id) == 0) {
810                 return false;
811         }
812         /*
813         * Feature in Windows.  TB_MAPACCELERATOR matches either the mnemonic
814         * character or the first character in a tool item.  This behavior is
815         * undocumented and unwanted.  The fix is to ensure that the tool item
816         * contains a mnemonic when TB_MAPACCELERATOR returns true.
817         */
818         int index = (int)OS.SendMessage (handle, OS.TB_COMMANDTOINDEX, id [0], 0);
819         if (index == -1) return false;
820         return findMnemonic (items [id [0]].text) != '\0';
821 }
822
823 @Override
824 void releaseChildren (boolean destroy) {
825         if (items != null) {
826                 for (int i=0; i<items.length; i++) {
827                         ToolItem item = items [i];
828                         if (item != null && !item.isDisposed ()) {
829                                 item.release (false);
830                         }
831                 }
832                 items = null;
833         }
834         super.releaseChildren (destroy);
835 }
836
837 @Override
838 void releaseWidget () {
839         super.releaseWidget ();
840         if (imageList != null) {
841                 OS.SendMessage (handle, OS.TB_SETIMAGELIST, 0, 0);
842                 display.releaseToolImageList (imageList);
843         }
844         if (hotImageList != null) {
845                 OS.SendMessage (handle, OS.TB_SETHOTIMAGELIST, 0, 0);
846                 display.releaseToolHotImageList (hotImageList);
847         }
848         if (disabledImageList != null) {
849                 OS.SendMessage (handle, OS.TB_SETDISABLEDIMAGELIST, 0, 0);
850                 display.releaseToolDisabledImageList (disabledImageList);
851         }
852         imageList = hotImageList = disabledImageList = null;
853 }
854
855 @Override
856 void removeControl (Control control) {
857         super.removeControl (control);
858         for (int i=0; i<items.length; i++) {
859                 ToolItem item = items [i];
860                 if (item != null && item.control == control) {
861                         item.setControl (null);
862                 }
863         }
864 }
865
866 @Override
867 void reskinChildren (int flags) {
868         if (items != null) {
869                 for (int i=0; i<items.length; i++) {
870                         ToolItem item = items [i];
871                         if (item != null) item.reskin (flags);
872                 }
873         }
874         super.reskinChildren (flags);
875 }
876
877 @Override
878 void setBackgroundImage (long hBitmap) {
879         super.setBackgroundImage (hBitmap);
880         setBackgroundTransparent (hBitmap != 0);
881 }
882
883 @Override
884 void setBackgroundPixel (int pixel) {
885         super.setBackgroundPixel (pixel);
886         setBackgroundTransparent (pixel != -1);
887 }
888
889 void setBackgroundTransparent (boolean transparent) {
890         /*
891         * Feature in Windows.  When TBSTYLE_TRANSPARENT is set
892         * in a tool bar that is drawing a background, images in
893         * the image list that include transparency information
894         * do not draw correctly.  The fix is to clear and set
895         * TBSTYLE_TRANSPARENT depending on the background color.
896         *
897         * NOTE:  This work around is unnecessary on XP.  The
898         * TBSTYLE_TRANSPARENT style is never cleared on that
899         * platform.
900         */
901         if ((style & SWT.FLAT) != 0) {
902                 if (!OS.IsAppThemed ()) {
903                         int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
904                         if (!transparent && findBackgroundControl () == null) {
905                                 bits &= ~OS.TBSTYLE_TRANSPARENT;
906                         } else {
907                                 bits |= OS.TBSTYLE_TRANSPARENT;
908                         }
909                         OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
910                 }
911         }
912 }
913
914 @Override
915 void setBoundsInPixels (int x, int y, int width, int height, int flags) {
916         /*
917         * Feature in Windows.  For some reason, when a tool bar is
918         * repositioned more than once using DeferWindowPos () into
919         * the same HDWP, the toolbar redraws more than once, defeating
920         * the purpose of DeferWindowPos ().  The fix is to end the
921         * deferred positioning before the next tool bar is added,
922         * ensuring that only one tool bar position is deferred at
923         * any given time.
924         */
925         if (parent.lpwp != null) {
926                 if (getDrawing () && OS.IsWindowVisible (handle)) {
927                         parent.setResizeChildren (false);
928                         parent.setResizeChildren (true);
929                 }
930         }
931         super.setBoundsInPixels (x, y, width, height, flags);
932 }
933
934 @Override
935 void setDefaultFont () {
936         super.setDefaultFont ();
937         OS.SendMessage (handle, OS.TB_SETBITMAPSIZE, 0, 0);
938         OS.SendMessage (handle, OS.TB_SETBUTTONSIZE, 0, 0);
939 }
940
941 void setDropDownItems (boolean set) {
942         /*
943         * Feature in Windows.  When the first button in a tool bar
944         * is a drop down item, Window leaves too much padding around
945         * the button.  This affects every button in the tool bar and
946         * makes the preferred height too big.  The fix is clear the
947         * BTNS_DROPDOWN before Windows lays out the tool bar and set
948         * the bit afterwards.
949         *
950         * NOTE:  This work around only runs when the tool bar contains
951         * only images.
952         */
953         if (OS.IsAppThemed ()) {
954                 boolean hasText = false, hasImage = false;
955                 for (int i=0; i<items.length; i++) {
956                         ToolItem item = items [i];
957                         if (item != null) {
958                                 if (!hasText) hasText = item.text.length () != 0;
959                                 if (!hasImage) hasImage = item.image != null;
960                                 if (hasText && hasImage) break;
961                         }
962                 }
963                 if (hasImage && !hasText) {
964                         for (int i=0; i<items.length; i++) {
965                                 ToolItem item = items [i];
966                                 if (item != null && (item.style & SWT.DROP_DOWN) != 0) {
967                                         TBBUTTONINFO info = new TBBUTTONINFO ();
968                                         info.cbSize = TBBUTTONINFO.sizeof;
969                                         info.dwMask = OS.TBIF_STYLE;
970                                         OS.SendMessage (handle, OS.TB_GETBUTTONINFO, item.id, info);
971                                         if (set) {
972                                                 info.fsStyle |= OS.BTNS_DROPDOWN;
973                                         } else {
974                                                 info.fsStyle &= ~OS.BTNS_DROPDOWN;
975                                         }
976                                         OS.SendMessage (handle, OS.TB_SETBUTTONINFO, item.id, info);
977                                 }
978                         }
979                 }
980         }
981 }
982
983 void setDisabledImageList (ImageList imageList) {
984         if (disabledImageList == imageList) return;
985         long hImageList = 0;
986         if ((disabledImageList = imageList) != null) {
987                 hImageList = disabledImageList.getHandle ();
988         }
989         setDropDownItems (false);
990         OS.SendMessage (handle, OS.TB_SETDISABLEDIMAGELIST, 0, hImageList);
991         setDropDownItems (true);
992 }
993
994 @Override
995 public void setFont (Font font) {
996         checkWidget ();
997         setDropDownItems (false);
998         super.setFont (font);
999         setDropDownItems (true);
1000         /*
1001         * Bug in Windows.  When WM_SETFONT is sent to a tool bar
1002         * that contains only separators, causes the bitmap and button
1003         * sizes to be set.  The fix is to reset these sizes after the font
1004         * has been changed when the tool bar contains only separators.
1005         */
1006         int index = 0;
1007         int mask = SWT.PUSH | SWT.CHECK | SWT.RADIO | SWT.DROP_DOWN;
1008         while (index < items.length) {
1009                 ToolItem item = items [index];
1010                 if (item != null && (item.style & mask) != 0) break;
1011                 index++;
1012         }
1013         if (index == items.length) {
1014                 OS.SendMessage (handle, OS.TB_SETBITMAPSIZE, 0, 0);
1015                 OS.SendMessage (handle, OS.TB_SETBUTTONSIZE, 0, 0);
1016         }
1017         layoutItems ();
1018 }
1019
1020 void setHotImageList (ImageList imageList) {
1021         if (hotImageList == imageList) return;
1022         long hImageList = 0;
1023         if ((hotImageList = imageList) != null) {
1024                 hImageList = hotImageList.getHandle ();
1025         }
1026         setDropDownItems (false);
1027         OS.SendMessage (handle, OS.TB_SETHOTIMAGELIST, 0, hImageList);
1028         setDropDownItems (true);
1029 }
1030
1031 void setImageList (ImageList imageList) {
1032         if (this.imageList == imageList) return;
1033         long hImageList = 0;
1034         if ((this.imageList = imageList) != null) {
1035                 hImageList = imageList.getHandle ();
1036         }
1037         setDropDownItems (false);
1038         OS.SendMessage (handle, OS.TB_SETIMAGELIST, 0, hImageList);
1039         setDropDownItems (true);
1040 }
1041
1042 @Override
1043 public boolean setParent (Composite parent) {
1044         checkWidget ();
1045         if (!super.setParent (parent)) return false;
1046         long hwndParent = parent.handle;
1047         OS.SendMessage (handle, OS.TB_SETPARENT, hwndParent, 0);
1048         /*
1049         * Bug in Windows.  When a tool bar is reparented, the tooltip
1050         * control that is automatically created for the item is not
1051         * reparented to the new shell.  The fix is to move the tooltip
1052         * over using SetWindowLongPtr().  Note that for some reason,
1053         * SetParent() does not work.
1054         */
1055         long hwndShell = parent.getShell ().handle;
1056         long hwndToolTip = OS.SendMessage (handle, OS.TB_GETTOOLTIPS, 0, 0);
1057         OS.SetWindowLongPtr (hwndToolTip, OS.GWLP_HWNDPARENT, hwndShell);
1058         return true;
1059 }
1060
1061 @Override
1062 public void setRedraw (boolean redraw) {
1063         checkWidget ();
1064         setDropDownItems (false);
1065         super.setRedraw (redraw);
1066         setDropDownItems (true);
1067 }
1068
1069 void setRowCount (int count) {
1070         if ((style & SWT.VERTICAL) != 0) {
1071                 /*
1072                 * Feature in Windows.  When the TB_SETROWS is used to set the
1073                 * number of rows in a tool bar, the tool bar is resized to show
1074                 * the items.  This is unexpected.  The fix is to save and restore
1075                 * the current size of the tool bar.
1076                 */
1077                 RECT rect = new RECT ();
1078                 OS.GetWindowRect (handle, rect);
1079                 OS.MapWindowPoints (0, parent.handle, rect, 2);
1080                 ignoreResize = true;
1081                 /*
1082                 * Feature in Windows.  When the last button in a tool bar has the
1083                 * style BTNS_SEP and TB_SETROWS is used to set the number of rows
1084                 * in the tool bar, depending on the number of buttons, the toolbar
1085                 * will wrap items with the style BTNS_CHECK, even when the fLarger
1086                 * flags is used to force the number of rows to be larger than the
1087                 * number of items.  The fix is to set the number of rows to be two
1088                 * larger than the actual number of rows in the tool bar.  When items
1089                 * are being added, as long as the number of rows is at least one
1090                 * item larger than the count, the tool bar is laid out properly.
1091                 * When items are being removed, setting the number of rows to be
1092                 * one more than the item count has no effect.  The number of rows
1093                 * is already one more causing TB_SETROWS to do nothing.  Therefore,
1094                 * choosing two instead of one as the row increment fixes both cases.
1095                 */
1096                 count += 2;
1097                 OS.SendMessage (handle, OS.TB_SETROWS, OS.MAKEWPARAM (count, 1), 0);
1098                 int flags = OS.SWP_NOACTIVATE | OS.SWP_NOMOVE | OS.SWP_NOZORDER;
1099                 OS.SetWindowPos (handle, 0, 0, 0, rect.right - rect.left, rect.bottom - rect.top, flags);
1100                 ignoreResize = false;
1101         }
1102 }
1103
1104 /*public*/ void setTabItemList (ToolItem [] tabList) {
1105         checkWidget ();
1106         if (tabList != null) {
1107                 for (int i=0; i<tabList.length; i++) {
1108                         ToolItem item = tabList [i];
1109                         if (item == null) error (SWT.ERROR_INVALID_ARGUMENT);
1110                         if (item.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
1111                         if (item.parent != this) error (SWT.ERROR_INVALID_PARENT);
1112                 }
1113                 ToolItem [] newList = new ToolItem [tabList.length];
1114                 System.arraycopy (tabList, 0, newList, 0, tabList.length);
1115                 tabList = newList;
1116         }
1117         this.tabItemList = tabList;
1118 }
1119
1120 @Override
1121 boolean setTabItemFocus () {
1122         int index = 0;
1123         while (index < items.length) {
1124                 ToolItem item = items [index];
1125                 if (item != null && (item.style & SWT.SEPARATOR) == 0) {
1126                         if (item.getEnabled ()) break;
1127                 }
1128                 index++;
1129         }
1130         if (index == items.length) return false;
1131         return super.setTabItemFocus ();
1132 }
1133
1134 @Override
1135 boolean updateTextDirection(int textDirection) {
1136         if (super.updateTextDirection(textDirection)) {
1137                 ToolItem [] items = _getItems ();
1138                 int i = items.length;
1139                 while (i-- > 0) {
1140                         items[i].updateTextDirection(style & SWT.FLIP_TEXT_DIRECTION);
1141                 }
1142                 return true;
1143         }
1144         return false;
1145 }
1146
1147 @Override
1148 String toolTipText (NMTTDISPINFO hdr) {
1149         if ((hdr.uFlags & OS.TTF_IDISHWND) != 0) {
1150                 return null;
1151         }
1152         /*
1153         * Bug in Windows.  On Windows XP, when TB_SETHOTITEM is
1154         * used to set the hot item, the tool bar control attempts
1155         * to display the tool tip, even when the cursor is not in
1156         * the hot item.  The fix is to detect this case and fail to
1157         * provide the string, causing no tool tip to be displayed.
1158         */
1159         if (!hasCursor ()) return ""; //$NON-NLS-1$
1160         int index = (int)hdr.idFrom;
1161         long hwndToolTip = OS.SendMessage (handle, OS.TB_GETTOOLTIPS, 0, 0);
1162         if (hwndToolTip == hdr.hwndFrom) {
1163                 /*
1164                 * Bug in Windows. For some reason the reading order
1165                 * in NMTTDISPINFO is sometimes set incorrectly.  The
1166                 * reading order seems to change every time the mouse
1167                 * enters the control from the top edge.  The fix is
1168                 * to explicitly set TTF_RTLREADING.
1169                 */
1170                 int flags = SWT.RIGHT_TO_LEFT | SWT.FLIP_TEXT_DIRECTION;
1171                 if ((style & flags) != 0 && (style & flags) != flags) {
1172                         hdr.uFlags |= OS.TTF_RTLREADING;
1173                 } else {
1174                         hdr.uFlags &= ~OS.TTF_RTLREADING;
1175                 }
1176                 if (toolTipText != null) return ""; //$NON-NLS-1$
1177                 if (0 <= index && index < items.length) {
1178                         ToolItem item = items [index];
1179                         if (item != null) {
1180                                 /*
1181                                 * Bug in Windows.  When the  arrow keys are used to change
1182                                 * the hot item, for some reason, Windows displays the tool
1183                                 * tip for the hot item in at (0, 0) on the screen rather
1184                                 * than next to the current hot item.  This fix is to disallow
1185                                 * tool tips while the user is traversing with the arrow keys.
1186                                 */
1187                                 if (lastArrowId != -1) return "";
1188                                 return item.toolTipText;
1189                         }
1190                 }
1191         }
1192         return super.toolTipText (hdr);
1193 }
1194
1195 @Override
1196 void updateOrientation () {
1197         super.updateOrientation ();
1198         if (imageList != null) {
1199                 Point size = imageList.getImageSize ();
1200                 ImageList newImageList = display.getImageListToolBar (style & SWT.RIGHT_TO_LEFT, size.x, size.y);
1201                 ImageList newHotImageList = display.getImageListToolBarHot (style & SWT.RIGHT_TO_LEFT, size.x, size.y);
1202                 ImageList newDisabledImageList = display.getImageListToolBarDisabled (style & SWT.RIGHT_TO_LEFT, size.x, size.y);
1203                 TBBUTTONINFO info = new TBBUTTONINFO ();
1204                 info.cbSize = TBBUTTONINFO.sizeof;
1205                 info.dwMask = OS.TBIF_IMAGE;
1206                 int count = (int)OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0);
1207                 for (int i=0; i<count; i++) {
1208                         ToolItem item = items [i];
1209                         if ((item.style & SWT.SEPARATOR) != 0) continue;
1210                         if (item.image == null) continue;
1211                         OS.SendMessage (handle, OS.TB_GETBUTTONINFO, item.id, info);
1212                         if (info.iImage != OS.I_IMAGENONE) {
1213                                 Image image = imageList.get(info.iImage);
1214                                 Image hot = hotImageList.get(info.iImage);
1215                                 Image disabled = disabledImageList.get(info.iImage);
1216                                 imageList.put(info.iImage, null);
1217                                 hotImageList.put(info.iImage, null);
1218                                 disabledImageList.put(info.iImage, null);
1219                                 info.iImage = newImageList.add(image);
1220                                 newHotImageList.add(hot);
1221                                 newDisabledImageList.add(disabled);
1222                                 OS.SendMessage (handle, OS.TB_SETBUTTONINFO, item.id, info);
1223                         }
1224                 }
1225                 display.releaseToolImageList (imageList);
1226                 display.releaseToolHotImageList (hotImageList);
1227                 display.releaseToolDisabledImageList (disabledImageList);
1228                 OS.SendMessage (handle, OS.TB_SETIMAGELIST, 0, newImageList.getHandle ());
1229                 OS.SendMessage (handle, OS.TB_SETHOTIMAGELIST, 0, newHotImageList.getHandle ());
1230                 OS.SendMessage (handle, OS.TB_SETDISABLEDIMAGELIST, 0, newDisabledImageList.getHandle ());
1231                 imageList = newImageList;
1232                 hotImageList = newHotImageList;
1233                 disabledImageList = newDisabledImageList;
1234                 OS.InvalidateRect (handle, null, true);
1235         }
1236 }
1237
1238 @Override
1239 int widgetStyle () {
1240         int bits = super.widgetStyle () | OS.CCS_NORESIZE | OS.TBSTYLE_TOOLTIPS | OS.TBSTYLE_CUSTOMERASE;
1241         if (OS.IsAppThemed ()) bits |= OS.TBSTYLE_TRANSPARENT;
1242         if ((style & SWT.SHADOW_OUT) == 0) bits |= OS.CCS_NODIVIDER;
1243         if ((style & SWT.WRAP) != 0) bits |= OS.TBSTYLE_WRAPABLE;
1244         if ((style & SWT.FLAT) != 0) bits |= OS.TBSTYLE_FLAT;
1245         /*
1246         * Feature in Windows.  When a tool bar has the style
1247         * TBSTYLE_LIST and has a drop down item, Window leaves
1248         * too much padding around the button.  This affects
1249         * every button in the tool bar and makes the preferred
1250         * height too big.  The fix is to set the TBSTYLE_LIST
1251         * when the tool bar contains both text and images.
1252         *
1253         * NOTE: Tool bars with CCS_VERT must have TBSTYLE_LIST
1254         * set before any item is added or the tool bar does
1255         * not lay out properly.  The work around does not run
1256         * in this case.
1257         */
1258         if (!OS.IsAppThemed ()) {
1259                 if ((style & SWT.RIGHT) != 0) bits |= OS.TBSTYLE_LIST;
1260         }
1261         return bits;
1262 }
1263
1264 @Override
1265 TCHAR windowClass () {
1266         return ToolBarClass;
1267 }
1268
1269 @Override
1270 long windowProc () {
1271         return ToolBarProc;
1272 }
1273
1274 @Override
1275 LRESULT WM_CAPTURECHANGED (long wParam, long lParam) {
1276         LRESULT result = super.WM_CAPTURECHANGED (wParam, lParam);
1277         if (result != null) return result;
1278         /*
1279         * Bug in Windows.  When the tool bar loses capture while an
1280         * item is pressed, the item remains pressed.  The fix is
1281         * unpress all items using TB_SETSTATE and TBSTATE_PRESSED.
1282         */
1283         for (int i=0; i<items.length; i++) {
1284                 ToolItem item = items [i];
1285                 if (item != null) {
1286                         int fsState = (int)OS.SendMessage (handle, OS.TB_GETSTATE, item.id, 0);
1287                         if ((fsState & OS.TBSTATE_PRESSED) != 0) {
1288                                 fsState &= ~OS.TBSTATE_PRESSED;
1289                                 OS.SendMessage (handle, OS.TB_SETSTATE, item.id, fsState);
1290                         }
1291                 }
1292         }
1293         return result;
1294 }
1295
1296 @Override
1297 LRESULT WM_CHAR (long wParam, long lParam) {
1298         LRESULT result = super.WM_CHAR (wParam, lParam);
1299         if (result != null) return result;
1300         switch ((int)wParam) {
1301                 case ' ':
1302                         int index = (int)OS.SendMessage (handle, OS.TB_GETHOTITEM, 0, 0);
1303                         if (index != -1) {
1304                                 TBBUTTON lpButton = new TBBUTTON ();
1305                                 long code = OS.SendMessage (handle, OS.TB_GETBUTTON, index, lpButton);
1306                                 if (code != 0) {
1307                                         items [lpButton.idCommand].click (false);
1308                                         return LRESULT.ZERO;
1309                                 }
1310                         }
1311         }
1312         return result;
1313 }
1314
1315 @Override
1316 LRESULT WM_COMMAND (long wParam, long lParam) {
1317         /*
1318         * Feature in Windows.  When the toolbar window
1319         * proc processes WM_COMMAND, it forwards this
1320         * message to its parent.  This is done so that
1321         * children of this control that send this message
1322         * type to their parent will notify not only
1323         * this control but also the parent of this control,
1324         * which is typically the application window and
1325         * the window that is looking for the message.
1326         * If the control did not forward the message,
1327         * applications would have to subclass the control
1328         * window to see the message. Because the control
1329         * window is subclassed by SWT, the message
1330         * is delivered twice, once by SWT and once when
1331         * the message is forwarded by the window proc.
1332         * The fix is to avoid calling the window proc
1333         * for this control.
1334         */
1335         LRESULT result = super.WM_COMMAND (wParam, lParam);
1336         if (result != null) return result;
1337         return LRESULT.ZERO;
1338 }
1339
1340 @Override
1341 LRESULT WM_GETDLGCODE (long wParam, long lParam) {
1342         LRESULT result = super.WM_GETDLGCODE (wParam, lParam);
1343         /*
1344         * Return DLGC_BUTTON so that mnemonics will be
1345         * processed without needing to press the ALT key
1346         * when the widget has focus.
1347         */
1348         if (result != null) return result;
1349         return new LRESULT (OS.DLGC_BUTTON | OS.DLGC_WANTARROWS);
1350 }
1351
1352 @Override
1353 LRESULT WM_KEYDOWN (long wParam, long lParam) {
1354         LRESULT result = super.WM_KEYDOWN (wParam, lParam);
1355         if (result != null) return result;
1356         switch ((int)wParam) {
1357                 case OS.VK_SPACE:
1358                         /*
1359                         * Ensure that the window proc does not process VK_SPACE
1360                         * so that it can be handled in WM_CHAR.  This allows the
1361                         * application the opportunity to cancel the operation.
1362                         */
1363                         return LRESULT.ZERO;
1364         }
1365         return result;
1366 }
1367
1368 @Override
1369 LRESULT WM_KILLFOCUS (long wParam, long lParam) {
1370         int index = (int)OS.SendMessage (handle, OS.TB_GETHOTITEM, 0, 0);
1371         TBBUTTON lpButton = new TBBUTTON ();
1372         long code = OS.SendMessage (handle, OS.TB_GETBUTTON, index, lpButton);
1373         if (code != 0) lastFocusId = lpButton.idCommand;
1374         return super.WM_KILLFOCUS (wParam, lParam);
1375 }
1376
1377 @Override
1378 LRESULT WM_LBUTTONDOWN (long wParam, long lParam) {
1379         if (ignoreMouse) return null;
1380         return super.WM_LBUTTONDOWN (wParam, lParam);
1381 }
1382
1383 @Override
1384 LRESULT WM_LBUTTONUP (long wParam, long lParam) {
1385         if (ignoreMouse) return null;
1386         return super.WM_LBUTTONUP (wParam, lParam);
1387 }
1388
1389 @Override
1390 LRESULT WM_MOUSELEAVE (long wParam, long lParam) {
1391         LRESULT result = super.WM_MOUSELEAVE (wParam, lParam);
1392         if (result != null) return result;
1393         /*
1394         * Bug in Windows.  On XP, when a tooltip is
1395         * hidden due to a time out or mouse press,
1396         * the tooltip remains active although no
1397         * longer visible and won't show again until
1398         * another tooltip becomes active.  If there
1399         * is only one tooltip in the window,  it will
1400         * never show again.  The fix is to remove the
1401         * current tooltip and add it again every time
1402         * the mouse leaves the control.
1403         */
1404         TOOLINFO lpti = new TOOLINFO ();
1405         lpti.cbSize = TOOLINFO.sizeof;
1406         long hwndToolTip = OS.SendMessage (handle, OS.TB_GETTOOLTIPS, 0, 0);
1407         if (OS.SendMessage (hwndToolTip, OS.TTM_GETCURRENTTOOL, 0, lpti) != 0) {
1408                 if ((lpti.uFlags & OS.TTF_IDISHWND) == 0) {
1409                         OS.SendMessage (hwndToolTip, OS.TTM_DELTOOL, 0, lpti);
1410                         OS.SendMessage (hwndToolTip, OS.TTM_ADDTOOL, 0, lpti);
1411                 }
1412         }
1413         return result;
1414 }
1415
1416 @Override
1417 LRESULT WM_MOUSEMOVE (long wParam, long lParam) {
1418         if (OS.GetMessagePos () != display.lastMouse) lastArrowId = -1;
1419         return super.WM_MOUSEMOVE (wParam, lParam);
1420 }
1421
1422 @Override
1423 LRESULT WM_NOTIFY (long wParam, long lParam) {
1424         /*
1425         * Feature in Windows.  When the toolbar window
1426         * proc processes WM_NOTIFY, it forwards this
1427         * message to its parent.  This is done so that
1428         * children of this control that send this message
1429         * type to their parent will notify not only
1430         * this control but also the parent of this control,
1431         * which is typically the application window and
1432         * the window that is looking for the message.
1433         * If the control did not forward the message,
1434         * applications would have to subclass the control
1435         * window to see the message. Because the control
1436         * window is subclassed by SWT, the message
1437         * is delivered twice, once by SWT and once when
1438         * the message is forwarded by the window proc.
1439         * The fix is to avoid calling the window proc
1440         * for this control.
1441         */
1442         LRESULT result = super.WM_NOTIFY (wParam, lParam);
1443         if (result != null) return result;
1444         return LRESULT.ZERO;
1445 }
1446
1447 @Override
1448 LRESULT WM_SETFOCUS (long wParam, long lParam) {
1449         LRESULT result = super.WM_SETFOCUS (wParam, lParam);
1450         if (lastFocusId != -1 && handle == OS.GetFocus ()) {
1451                 int index = (int)OS.SendMessage (handle, OS.TB_COMMANDTOINDEX, lastFocusId, 0);
1452                 OS.SendMessage (handle, OS.TB_SETHOTITEM, index, 0);
1453         }
1454         return result;
1455 }
1456
1457 @Override
1458 LRESULT WM_SIZE (long wParam, long lParam) {
1459         if (ignoreResize) {
1460                 long code = callWindowProc (handle, OS.WM_SIZE, wParam, lParam);
1461                 if (code == 0) return LRESULT.ZERO;
1462                 return new LRESULT (code);
1463         }
1464         LRESULT result = super.WM_SIZE (wParam, lParam);
1465         if (isDisposed ()) return result;
1466         /*
1467         * Bug in Windows.  The code in Windows that determines
1468         * when tool items should wrap seems to use the window
1469         * bounds rather than the client area.  Unfortunately,
1470         * tool bars with the style TBSTYLE_EX_HIDECLIPPEDBUTTONS
1471         * use the client area.  This means that buttons which
1472         * overlap the border are hidden before they are wrapped.
1473         * The fix is to compute TBSTYLE_EX_HIDECLIPPEDBUTTONS
1474         * and set it each time the tool bar is resized.
1475         */
1476         if ((style & SWT.BORDER) != 0 && (style & SWT.WRAP) != 0) {
1477                 RECT windowRect = new RECT ();
1478                 OS.GetWindowRect (handle, windowRect);
1479                 int index = 0, border = getBorderWidthInPixels () * 2;
1480                 RECT rect = new RECT ();
1481                 int count = (int)OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0);
1482                 while (index < count) {
1483                         OS.SendMessage (handle, OS.TB_GETITEMRECT, index, rect);
1484                         OS.MapWindowPoints (handle, 0, rect, 2);
1485                         if (rect.right > windowRect.right - border * 2) break;
1486                         index++;
1487                 }
1488                 int bits = (int)OS.SendMessage (handle, OS.TB_GETEXTENDEDSTYLE, 0, 0);
1489                 if (index == count) {
1490                         bits |= OS.TBSTYLE_EX_HIDECLIPPEDBUTTONS;
1491                 } else {
1492                         bits &= ~OS.TBSTYLE_EX_HIDECLIPPEDBUTTONS;
1493                 }
1494                 OS.SendMessage (handle, OS.TB_SETEXTENDEDSTYLE, 0, bits);
1495         }
1496         layoutItems ();
1497         return result;
1498 }
1499
1500 @Override
1501 LRESULT WM_WINDOWPOSCHANGING (long wParam, long lParam) {
1502         LRESULT result = super.WM_WINDOWPOSCHANGING (wParam, lParam);
1503         if (result != null) return result;
1504         if (ignoreResize) return result;
1505         /*
1506         * Bug in Windows.  When a flat tool bar is wrapped,
1507         * Windows draws a horizontal separator between the
1508         * rows.  The tool bar does not draw the first or
1509         * the last two pixels of this separator.  When the
1510         * toolbar is resized to be bigger, only the new
1511         * area is drawn and the last two pixels, which are
1512         * blank are drawn over by separator.  This leaves
1513         * garbage on the screen.  The fix is to damage the
1514         * pixels.
1515         */
1516         if (!getDrawing ()) return result;
1517         if ((style & SWT.WRAP) == 0) return result;
1518         if (!OS.IsWindowVisible (handle)) return result;
1519         if (OS.SendMessage (handle, OS.TB_GETROWS, 0, 0) == 1) {
1520                 return result;
1521         }
1522         WINDOWPOS lpwp = new WINDOWPOS ();
1523         OS.MoveMemory (lpwp, lParam, WINDOWPOS.sizeof);
1524         if ((lpwp.flags & (OS.SWP_NOSIZE | OS.SWP_NOREDRAW)) != 0) {
1525                 return result;
1526         }
1527         RECT oldRect = new RECT ();
1528         OS.GetClientRect (handle, oldRect);
1529         RECT newRect = new RECT ();
1530         OS.SetRect (newRect, 0, 0, lpwp.cx, lpwp.cy);
1531         OS.SendMessage (handle, OS.WM_NCCALCSIZE, 0, newRect);
1532         int oldWidth = oldRect.right - oldRect.left;
1533         int newWidth = newRect.right - newRect.left;
1534         if (newWidth > oldWidth) {
1535                 RECT rect = new RECT ();
1536                 int newHeight = newRect.bottom - newRect.top;
1537                 OS.SetRect (rect, oldWidth - 2, 0, oldWidth, newHeight);
1538                 OS.InvalidateRect (handle, rect, false);
1539         }
1540         return result;
1541 }
1542
1543 @Override
1544 LRESULT wmCommandChild (long wParam, long lParam) {
1545         ToolItem child = items [OS.LOWORD (wParam)];
1546         if (child == null) return null;
1547         return child.wmCommandChild (wParam, lParam);
1548 }
1549
1550 private boolean customDrawing() {
1551         return hasCustomBackground() || (hasCustomForeground() && OS.IsWindowEnabled(handle));
1552 }
1553
1554 @Override
1555 LRESULT wmNotifyChild (NMHDR hdr, long wParam, long lParam) {
1556         switch (hdr.code) {
1557                 case OS.TBN_DROPDOWN:
1558                         NMTOOLBAR lpnmtb = new NMTOOLBAR ();
1559                         OS.MoveMemory (lpnmtb, lParam, NMTOOLBAR.sizeof);
1560                         ToolItem child = items [lpnmtb.iItem];
1561                         if (child != null) {
1562                                 Event event = new Event ();
1563                                 event.detail = SWT.ARROW;
1564                                 int index = (int)OS.SendMessage (handle, OS.TB_COMMANDTOINDEX, lpnmtb.iItem, 0);
1565                                 RECT rect = new RECT ();
1566                                 OS.SendMessage (handle, OS.TB_GETITEMRECT, index, rect);
1567                                 event.setLocationInPixels(rect.left, rect.bottom);
1568                                 child.sendSelectionEvent (SWT.Selection, event, false);
1569                         }
1570                         break;
1571                 case OS.NM_CUSTOMDRAW:
1572                         /*
1573                         * Bug in Windows.  For some reason, under the XP Silver
1574                         * theme, tool bars continue to draw using the gray color
1575                         * from the default Blue theme.  The fix is to draw the
1576                         * background.
1577                         */
1578                         NMTBCUSTOMDRAW nmcd = new NMTBCUSTOMDRAW ();
1579                         OS.MoveMemory (nmcd, lParam, NMTBCUSTOMDRAW.sizeof);
1580 //                      if (drawCount != 0 || !OS.IsWindowVisible (handle)) {
1581 //                              if (OS.WindowFromDC (nmcd.hdc) == handle) break;
1582 //                      }
1583                         switch (nmcd.dwDrawStage) {
1584                                 case OS.CDDS_PREERASE: {
1585                                         /*
1586                                         * Bug in Windows.  When a tool bar does not have the style
1587                                         * TBSTYLE_FLAT, the rectangle to be erased in CDDS_PREERASE
1588                                         * is empty.  The fix is to draw the whole client area.
1589                                         */
1590                                         int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
1591                                         if ((bits & OS.TBSTYLE_FLAT) == 0) {
1592                                                 drawBackground (nmcd.hdc);
1593                                         } else {
1594                                                 RECT rect = new RECT ();
1595                                                 OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
1596                                                 drawBackground (nmcd.hdc, rect);
1597                                         }
1598                                         return new LRESULT (OS.CDRF_SKIPDEFAULT);
1599                                 }
1600                                 case OS.CDDS_PREPAINT: {
1601                                         return new LRESULT (customDrawing() ? OS.CDRF_NOTIFYITEMDRAW : OS.CDRF_DODEFAULT);
1602                                 }
1603                                 case OS.CDDS_ITEMPREPAINT: {
1604                                         if (customDrawing()) {
1605                                                 nmcd.clrBtnFace = getBackgroundPixel();
1606                                                 nmcd.clrText = getForegroundPixel();
1607                                                 OS.MoveMemory(lParam, nmcd, NMTBCUSTOMDRAW.sizeof);
1608                                                 return new LRESULT(OS.TBCDRF_USECDCOLORS);
1609                                         }
1610                                         return new LRESULT (OS.CDRF_DODEFAULT);
1611                                 }
1612                         }
1613                         break;
1614                 case OS.TBN_HOTITEMCHANGE:
1615                         NMTBHOTITEM lpnmhi = new NMTBHOTITEM ();
1616                         OS.MoveMemory (lpnmhi, lParam, NMTBHOTITEM.sizeof);
1617                         switch (lpnmhi.dwFlags) {
1618                                 case OS.HICF_MOUSE: {
1619                                         /*
1620                                         * Bug in Windows.  When the tool bar has focus, a mouse is
1621                                         * in an item and hover help for that item is displayed and
1622                                         * then the arrow keys are used to change the hot item,
1623                                         * for some reason, Windows snaps the hot item back to the
1624                                         * one that is under the mouse.  The fix is to disallow
1625                                         * hot item changes when the user is traversing using the
1626                                         * arrow keys.
1627                                         */
1628                                         if (lastArrowId != -1) return LRESULT.ONE;
1629                                         break;
1630                                 }
1631                                 case OS.HICF_ARROWKEYS: {
1632                                         RECT client = new RECT ();
1633                                         OS.GetClientRect (handle, client);
1634                                         int index = (int)OS.SendMessage (handle, OS.TB_COMMANDTOINDEX, lpnmhi.idNew, 0);
1635                                         RECT rect = new RECT ();
1636                                         OS.SendMessage (handle, OS.TB_GETITEMRECT, index, rect);
1637                                         if (rect.right > client.right || rect.bottom > client.bottom) {
1638                                                 return LRESULT.ONE;
1639                                         }
1640                                         lastArrowId = lpnmhi.idNew;
1641                                         break;
1642                                 }
1643                                 default:
1644                                         lastArrowId = -1;
1645                         }
1646                         if ((lpnmhi.dwFlags & OS.HICF_LEAVING) == 0) {
1647                                 lastHotId = lpnmhi.idNew;
1648                         }
1649                         break;
1650         }
1651         return super.wmNotifyChild (hdr, wParam, lParam);
1652 }
1653
1654 }