1 /*******************************************************************************
2 * Copyright (c) 2000, 2014 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.graphics.*;
19 import org.eclipse.swt.internal.*;
20 import org.eclipse.swt.internal.win32.*;
23 * Instances of this class support the layout of selectable
26 * The item children that may be added to instances of this class
27 * must be of type <code>ToolItem</code>.
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.
34 * <dt><b>Styles:</b></dt>
35 * <dd>FLAT, WRAP, RIGHT, HORIZONTAL, VERTICAL, SHADOW_OUT</dd>
36 * <dt><b>Events:</b></dt>
40 * Note: Only one of the styles HORIZONTAL and VERTICAL may be specified.
42 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
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.
50 public class ToolBar extends Composite {
51 int lastFocusId, lastArrowId, lastHotId;
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);
59 WNDCLASS lpWndClass = new WNDCLASS ();
60 OS.GetClassInfo (0, ToolBarClass, lpWndClass);
61 ToolBarProc = lpWndClass.lpfnWndProc;
65 * From the Windows SDK for TB_SETBUTTONSIZE:
67 * "If an application does not explicitly
68 * set the button size, the size defaults
69 * to 24 by 22 pixels".
71 static final int DEFAULT_WIDTH = 24;
72 static final int DEFAULT_HEIGHT = 22;
75 * Constructs a new instance of this class given its parent
76 * and a style value describing its behavior and appearance.
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.
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
90 * @exception IllegalArgumentException <ul>
91 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
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>
101 * @see SWT#HORIZONTAL
102 * @see SWT#SHADOW_OUT
104 * @see Widget#checkSubclass()
105 * @see Widget#getStyle()
107 public ToolBar (Composite parent, int style) {
108 super (parent, checkStyle (style));
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
117 * NOTE: The CCS_VERT style cannot be applied when the
118 * widget is created because of this conflict.
120 if ((style & SWT.VERTICAL) != 0) {
121 this.style |= SWT.VERTICAL;
122 int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
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.
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
136 if (OS.IsAppThemed ()) {
137 if ((style & SWT.RIGHT) != 0) bits |= OS.TBSTYLE_LIST;
139 OS.SetWindowLong (handle, OS.GWL_STYLE, bits | OS.CCS_VERT);
141 this.style |= SWT.HORIZONTAL;
146 long callWindowProc (long hwnd, int msg, long wParam, long lParam) {
147 if (handle == 0) return 0;
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.
155 if (msg == OS.WM_SYSCHAR) {
156 return OS.DefWindowProc (hwnd, msg, wParam, lParam);
158 return OS.CallWindowProc (ToolBarProc, hwnd, msg, wParam, lParam);
161 static int checkStyle (int style) {
163 * On Windows, only flat tool bars can be traversed.
165 if ((style & SWT.FLAT) == 0) style |= SWT.NO_FOCUS;
168 * A vertical tool bar cannot wrap because TB_SETROWS
169 * fails when the toolbar has TBSTYLE_WRAPABLE.
171 if ((style & SWT.VERTICAL) != 0) style &= ~SWT.WRAP;
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
180 return style & ~(SWT.H_SCROLL | SWT.V_SCROLL);
184 void checkBuffered () {
185 super.checkBuffered ();
186 style |= SWT.DOUBLE_BUFFERED;
190 protected void checkSubclass () {
191 if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
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);
211 width = Math.max (width, rect.right);
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);
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);
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);
234 OS.SetWindowPos (handle, 0, 0, 0, oldWidth, oldHeight, flags);
235 if (redraw) OS.ValidateRect (handle, null);
236 ignoreResize = false;
240 * From the Windows SDK for TB_SETBUTTONSIZE:
242 * "If an application does not explicitly
243 * set the button size, the size defaults
244 * to 24 by 22 pixels".
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);
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;
263 Widget computeTabGroup () {
264 ToolItem [] items = _getItems ();
265 if (tabItemList == null) {
267 while (i < items.length && items [i].control == null) i++;
268 if (i == items.length) return super.computeTabGroup ();
270 int index = (int)OS.SendMessage (handle, OS.TB_GETHOTITEM, 0, 0);
271 if (index == -1) index = lastHotId;
273 ToolItem item = items [index];
274 if (item.isTabGroup ()) return item;
277 return super.computeTabGroup ();
281 Widget [] computeTabList () {
282 ToolItem [] items = _getItems ();
283 if (tabItemList == null) {
285 while (i < items.length && items [i].control == null) i++;
286 if (i == items.length) return super.computeTabList ();
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);
301 if (result.length == 0) result = new Widget [] {this};
306 void createHandle () {
307 super.createHandle ();
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.
316 * NOTE: This work around is unnecessary on XP. There is no
317 * flickering and clearing the TBSTYLE_TRANSPARENT interferes
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);
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
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.
340 // long hwndToolTip = OS.SendMessage (handle, OS.TB_GETTOOLTIPS, 0, 0);
341 // OS.SendMessage (hwndToolTip, OS.TTM_SETMAXTIPWIDTH, 0, 0x7FFF);
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
355 long hFont = OS.GetStockObject (OS.SYSTEM_FONT);
356 OS.SendMessage (handle, OS.WM_SETFONT, hFont, 0);
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);
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);
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);
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);
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;
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.
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);
400 items [item.id = id] = item;
401 if ((style & SWT.VERTICAL) != 0) setRowCount (count + 1);
406 void createWidget () {
407 super.createWidget ();
408 items = new ToolItem [4];
409 lastFocusId = lastArrowId = lastHotId = -1;
413 int applyThemeBackground () {
414 return -1; /* No Change */
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);
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.
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);
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;
443 int count = (int)OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0);
445 if (imageList != null) {
446 OS.SendMessage (handle, OS.TB_SETIMAGELIST, 0, 0);
447 display.releaseToolImageList (imageList);
449 if (hotImageList != null) {
450 OS.SendMessage (handle, OS.TB_SETHOTIMAGELIST, 0, 0);
451 display.releaseToolHotImageList (hotImageList);
453 if (disabledImageList != null) {
454 OS.SendMessage (handle, OS.TB_SETDISABLEDIMAGELIST, 0, 0);
455 display.releaseToolDisabledImageList (disabledImageList);
457 imageList = hotImageList = disabledImageList = null;
458 items = new ToolItem [4];
460 if ((style & SWT.VERTICAL) != 0) setRowCount (count - 1);
465 void enableWidget (boolean enabled) {
466 super.enableWidget (enabled);
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.
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.
479 for (int i=0; i<items.length; i++) {
480 ToolItem item = items [i];
482 if ((item.style & SWT.SEPARATOR) == 0) {
483 item.updateImages (enabled && item.getEnabled ());
489 ImageList getDisabledImageList () {
490 return disabledImageList;
493 ImageList getHotImageList () {
497 ImageList getImageList () {
502 * Returns the item at the given, zero-relative index in the
503 * receiver. Throws an exception if the index is out of range.
505 * @param index the index of the item to return
506 * @return the item at the given index
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>
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>
516 public ToolItem getItem (int index) {
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];
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.
531 * @param point the point used to locate the item
532 * @return the item at the given point
534 * @exception IllegalArgumentException <ul>
535 * <li>ERROR_NULL_ARGUMENT - if the point is null</li>
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>
542 public ToolItem getItem (Point point) {
544 if (point == null) error (SWT.ERROR_NULL_ARGUMENT);
545 return getItemInPixels(DPIUtil.autoScaleUp(point));
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];
558 * Returns the number of items contained in the receiver.
560 * @return the number of items
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>
567 public int getItemCount () {
569 return (int)OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0);
573 * Returns an array of <code>ToolItem</code>s which are the items
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.
581 * @return the items in the receiver
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>
588 public ToolItem [] getItems () {
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];
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.
610 * @return the number of items
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>
617 public int getRowCount () {
619 if ((style & SWT.VERTICAL) != 0) {
620 return (int)OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0);
622 return (int)OS.SendMessage (handle, OS.TB_GETROWS, 0, 0);
625 ToolItem [] _getTabItemList () {
626 if (tabItemList == null) return tabItemList;
628 for (int i=0; i<tabItemList.length; i++) {
629 if (!tabItemList [i].isDisposed ()) count++;
631 if (count == tabItemList.length) return tabItemList;
632 ToolItem [] newList = new ToolItem [count];
634 for (int i=0; i<tabItemList.length; i++) {
635 if (!tabItemList [i].isDisposed ()) {
636 newList [index++] = tabItemList [i];
639 tabItemList = newList;
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.
649 * @param item the search item
650 * @return the index of the item
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>
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>
661 public int indexOf (ToolItem item) {
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);
668 void layoutItems () {
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.
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
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];
688 if (!hasText) hasText = item.text.length () != 0;
689 if (!hasImage) hasImage = item.image != null;
690 if (hasText && hasImage) break;
693 int oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE), newBits = oldBits;
694 if (hasText && hasImage) {
695 newBits |= OS.TBSTYLE_LIST;
697 newBits &= ~OS.TBSTYLE_LIST;
699 if (newBits != oldBits) {
700 setDropDownItems (false);
701 OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
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.
708 long hFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
709 OS.SendMessage (handle, OS.WM_SETFONT, hFont, 0);
710 setDropDownItems (true);
715 if ((style & SWT.WRAP) != 0) {
716 OS.SendMessage (handle, OS.TB_AUTOSIZE, 0, 0);
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.
724 if ((style & SWT.VERTICAL) != 0) {
725 int itemCount = (int)OS.SendMessage (handle, OS.TB_BUTTONCOUNT, 0, 0);
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) {
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
747 if (index < items.length) {
748 long padding = OS.SendMessage (handle, OS.TB_GETPADDING, 0, 0);
749 info.cx += OS.LOWORD (padding + extraPadding) * 2;
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);
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.
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) {
780 OS.SendMessage (handle, OS.TB_SETBUTTONINFO, item.id, info);
786 for (int i=0; i<items.length; i++) {
787 ToolItem item = items [i];
788 if (item != null) item.resizeControl ();
793 boolean mnemonicHit (char ch) {
794 int [] id = new int [1];
795 if (OS.SendMessage (handle, OS.TB_MAPACCELERATOR, ch, id) == 0) {
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);
807 boolean mnemonicMatch (char ch) {
808 int [] id = new int [1];
809 if (OS.SendMessage (handle, OS.TB_MAPACCELERATOR, ch, id) == 0) {
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.
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';
824 void releaseChildren (boolean destroy) {
826 for (int i=0; i<items.length; i++) {
827 ToolItem item = items [i];
828 if (item != null && !item.isDisposed ()) {
829 item.release (false);
834 super.releaseChildren (destroy);
838 void releaseWidget () {
839 super.releaseWidget ();
840 if (imageList != null) {
841 OS.SendMessage (handle, OS.TB_SETIMAGELIST, 0, 0);
842 display.releaseToolImageList (imageList);
844 if (hotImageList != null) {
845 OS.SendMessage (handle, OS.TB_SETHOTIMAGELIST, 0, 0);
846 display.releaseToolHotImageList (hotImageList);
848 if (disabledImageList != null) {
849 OS.SendMessage (handle, OS.TB_SETDISABLEDIMAGELIST, 0, 0);
850 display.releaseToolDisabledImageList (disabledImageList);
852 imageList = hotImageList = disabledImageList = null;
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);
867 void reskinChildren (int flags) {
869 for (int i=0; i<items.length; i++) {
870 ToolItem item = items [i];
871 if (item != null) item.reskin (flags);
874 super.reskinChildren (flags);
878 void setBackgroundImage (long hBitmap) {
879 super.setBackgroundImage (hBitmap);
880 setBackgroundTransparent (hBitmap != 0);
884 void setBackgroundPixel (int pixel) {
885 super.setBackgroundPixel (pixel);
886 setBackgroundTransparent (pixel != -1);
889 void setBackgroundTransparent (boolean transparent) {
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.
897 * NOTE: This work around is unnecessary on XP. The
898 * TBSTYLE_TRANSPARENT style is never cleared on that
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;
907 bits |= OS.TBSTYLE_TRANSPARENT;
909 OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
915 void setBoundsInPixels (int x, int y, int width, int height, int flags) {
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
925 if (parent.lpwp != null) {
926 if (getDrawing () && OS.IsWindowVisible (handle)) {
927 parent.setResizeChildren (false);
928 parent.setResizeChildren (true);
931 super.setBoundsInPixels (x, y, width, height, flags);
935 void setDefaultFont () {
936 super.setDefaultFont ();
937 OS.SendMessage (handle, OS.TB_SETBITMAPSIZE, 0, 0);
938 OS.SendMessage (handle, OS.TB_SETBUTTONSIZE, 0, 0);
941 void setDropDownItems (boolean set) {
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.
950 * NOTE: This work around only runs when the tool bar contains
953 if (OS.IsAppThemed ()) {
954 boolean hasText = false, hasImage = false;
955 for (int i=0; i<items.length; i++) {
956 ToolItem item = items [i];
958 if (!hasText) hasText = item.text.length () != 0;
959 if (!hasImage) hasImage = item.image != null;
960 if (hasText && hasImage) break;
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);
972 info.fsStyle |= OS.BTNS_DROPDOWN;
974 info.fsStyle &= ~OS.BTNS_DROPDOWN;
976 OS.SendMessage (handle, OS.TB_SETBUTTONINFO, item.id, info);
983 void setDisabledImageList (ImageList imageList) {
984 if (disabledImageList == imageList) return;
986 if ((disabledImageList = imageList) != null) {
987 hImageList = disabledImageList.getHandle ();
989 setDropDownItems (false);
990 OS.SendMessage (handle, OS.TB_SETDISABLEDIMAGELIST, 0, hImageList);
991 setDropDownItems (true);
995 public void setFont (Font font) {
997 setDropDownItems (false);
998 super.setFont (font);
999 setDropDownItems (true);
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.
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;
1013 if (index == items.length) {
1014 OS.SendMessage (handle, OS.TB_SETBITMAPSIZE, 0, 0);
1015 OS.SendMessage (handle, OS.TB_SETBUTTONSIZE, 0, 0);
1020 void setHotImageList (ImageList imageList) {
1021 if (hotImageList == imageList) return;
1022 long hImageList = 0;
1023 if ((hotImageList = imageList) != null) {
1024 hImageList = hotImageList.getHandle ();
1026 setDropDownItems (false);
1027 OS.SendMessage (handle, OS.TB_SETHOTIMAGELIST, 0, hImageList);
1028 setDropDownItems (true);
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 ();
1037 setDropDownItems (false);
1038 OS.SendMessage (handle, OS.TB_SETIMAGELIST, 0, hImageList);
1039 setDropDownItems (true);
1043 public boolean setParent (Composite parent) {
1045 if (!super.setParent (parent)) return false;
1046 long hwndParent = parent.handle;
1047 OS.SendMessage (handle, OS.TB_SETPARENT, hwndParent, 0);
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.
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);
1062 public void setRedraw (boolean redraw) {
1064 setDropDownItems (false);
1065 super.setRedraw (redraw);
1066 setDropDownItems (true);
1069 void setRowCount (int count) {
1070 if ((style & SWT.VERTICAL) != 0) {
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.
1077 RECT rect = new RECT ();
1078 OS.GetWindowRect (handle, rect);
1079 OS.MapWindowPoints (0, parent.handle, rect, 2);
1080 ignoreResize = true;
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.
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;
1104 /*public*/ void setTabItemList (ToolItem [] tabList) {
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);
1113 ToolItem [] newList = new ToolItem [tabList.length];
1114 System.arraycopy (tabList, 0, newList, 0, tabList.length);
1117 this.tabItemList = tabList;
1121 boolean setTabItemFocus () {
1123 while (index < items.length) {
1124 ToolItem item = items [index];
1125 if (item != null && (item.style & SWT.SEPARATOR) == 0) {
1126 if (item.getEnabled ()) break;
1130 if (index == items.length) return false;
1131 return super.setTabItemFocus ();
1135 boolean updateTextDirection(int textDirection) {
1136 if (super.updateTextDirection(textDirection)) {
1137 ToolItem [] items = _getItems ();
1138 int i = items.length;
1140 items[i].updateTextDirection(style & SWT.FLIP_TEXT_DIRECTION);
1148 String toolTipText (NMTTDISPINFO hdr) {
1149 if ((hdr.uFlags & OS.TTF_IDISHWND) != 0) {
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.
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) {
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.
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;
1174 hdr.uFlags &= ~OS.TTF_RTLREADING;
1176 if (toolTipText != null) return ""; //$NON-NLS-1$
1177 if (0 <= index && index < items.length) {
1178 ToolItem item = items [index];
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.
1187 if (lastArrowId != -1) return "";
1188 return item.toolTipText;
1192 return super.toolTipText (hdr);
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);
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);
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;
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.
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
1258 if (!OS.IsAppThemed ()) {
1259 if ((style & SWT.RIGHT) != 0) bits |= OS.TBSTYLE_LIST;
1265 TCHAR windowClass () {
1266 return ToolBarClass;
1270 long windowProc () {
1275 LRESULT WM_CAPTURECHANGED (long wParam, long lParam) {
1276 LRESULT result = super.WM_CAPTURECHANGED (wParam, lParam);
1277 if (result != null) return result;
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.
1283 for (int i=0; i<items.length; i++) {
1284 ToolItem item = items [i];
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);
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) {
1302 int index = (int)OS.SendMessage (handle, OS.TB_GETHOTITEM, 0, 0);
1304 TBBUTTON lpButton = new TBBUTTON ();
1305 long code = OS.SendMessage (handle, OS.TB_GETBUTTON, index, lpButton);
1307 items [lpButton.idCommand].click (false);
1308 return LRESULT.ZERO;
1316 LRESULT WM_COMMAND (long wParam, long lParam) {
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
1335 LRESULT result = super.WM_COMMAND (wParam, lParam);
1336 if (result != null) return result;
1337 return LRESULT.ZERO;
1341 LRESULT WM_GETDLGCODE (long wParam, long lParam) {
1342 LRESULT result = super.WM_GETDLGCODE (wParam, lParam);
1344 * Return DLGC_BUTTON so that mnemonics will be
1345 * processed without needing to press the ALT key
1346 * when the widget has focus.
1348 if (result != null) return result;
1349 return new LRESULT (OS.DLGC_BUTTON | OS.DLGC_WANTARROWS);
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) {
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.
1363 return LRESULT.ZERO;
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);
1378 LRESULT WM_LBUTTONDOWN (long wParam, long lParam) {
1379 if (ignoreMouse) return null;
1380 return super.WM_LBUTTONDOWN (wParam, lParam);
1384 LRESULT WM_LBUTTONUP (long wParam, long lParam) {
1385 if (ignoreMouse) return null;
1386 return super.WM_LBUTTONUP (wParam, lParam);
1390 LRESULT WM_MOUSELEAVE (long wParam, long lParam) {
1391 LRESULT result = super.WM_MOUSELEAVE (wParam, lParam);
1392 if (result != null) return result;
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.
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);
1417 LRESULT WM_MOUSEMOVE (long wParam, long lParam) {
1418 if (OS.GetMessagePos () != display.lastMouse) lastArrowId = -1;
1419 return super.WM_MOUSEMOVE (wParam, lParam);
1423 LRESULT WM_NOTIFY (long wParam, long lParam) {
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
1442 LRESULT result = super.WM_NOTIFY (wParam, lParam);
1443 if (result != null) return result;
1444 return LRESULT.ZERO;
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);
1458 LRESULT WM_SIZE (long wParam, long lParam) {
1460 long code = callWindowProc (handle, OS.WM_SIZE, wParam, lParam);
1461 if (code == 0) return LRESULT.ZERO;
1462 return new LRESULT (code);
1464 LRESULT result = super.WM_SIZE (wParam, lParam);
1465 if (isDisposed ()) return result;
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.
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;
1488 int bits = (int)OS.SendMessage (handle, OS.TB_GETEXTENDEDSTYLE, 0, 0);
1489 if (index == count) {
1490 bits |= OS.TBSTYLE_EX_HIDECLIPPEDBUTTONS;
1492 bits &= ~OS.TBSTYLE_EX_HIDECLIPPEDBUTTONS;
1494 OS.SendMessage (handle, OS.TB_SETEXTENDEDSTYLE, 0, bits);
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;
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
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) {
1522 WINDOWPOS lpwp = new WINDOWPOS ();
1523 OS.MoveMemory (lpwp, lParam, WINDOWPOS.sizeof);
1524 if ((lpwp.flags & (OS.SWP_NOSIZE | OS.SWP_NOREDRAW)) != 0) {
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);
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);
1550 private boolean customDrawing() {
1551 return hasCustomBackground() || (hasCustomForeground() && OS.IsWindowEnabled(handle));
1555 LRESULT wmNotifyChild (NMHDR hdr, long wParam, long lParam) {
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);
1571 case OS.NM_CUSTOMDRAW:
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
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;
1583 switch (nmcd.dwDrawStage) {
1584 case OS.CDDS_PREERASE: {
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.
1590 int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
1591 if ((bits & OS.TBSTYLE_FLAT) == 0) {
1592 drawBackground (nmcd.hdc);
1594 RECT rect = new RECT ();
1595 OS.SetRect (rect, nmcd.left, nmcd.top, nmcd.right, nmcd.bottom);
1596 drawBackground (nmcd.hdc, rect);
1598 return new LRESULT (OS.CDRF_SKIPDEFAULT);
1600 case OS.CDDS_PREPAINT: {
1601 return new LRESULT (customDrawing() ? OS.CDRF_NOTIFYITEMDRAW : OS.CDRF_DODEFAULT);
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);
1610 return new LRESULT (OS.CDRF_DODEFAULT);
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: {
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
1628 if (lastArrowId != -1) return LRESULT.ONE;
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) {
1640 lastArrowId = lpnmhi.idNew;
1646 if ((lpnmhi.dwFlags & OS.HICF_LEAVING) == 0) {
1647 lastHotId = lpnmhi.idNew;
1651 return super.wmNotifyChild (hdr, wParam, lParam);