1 /*******************************************************************************
2 * Copyright (c) 2000, 2012 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 provide an area for dynamically
24 * positioning the items they contain.
26 * The item children that may be added to instances of this class
27 * must be of type <code>CoolItem</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, HORIZONTAL, VERTICAL</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/#coolbar">CoolBar 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 CoolBar extends Composite {
52 CoolItem [] originalItems;
55 static final long ReBarProc;
56 static final TCHAR ReBarClass = new TCHAR (0, OS.REBARCLASSNAME, true);
58 INITCOMMONCONTROLSEX icex = new INITCOMMONCONTROLSEX ();
59 icex.dwSize = INITCOMMONCONTROLSEX.sizeof;
60 icex.dwICC = OS.ICC_COOL_CLASSES;
61 OS.InitCommonControlsEx (icex);
62 WNDCLASS lpWndClass = new WNDCLASS ();
63 OS.GetClassInfo (0, ReBarClass, lpWndClass);
64 ReBarProc = lpWndClass.lpfnWndProc;
66 static final int SEPARATOR_WIDTH = 2;
67 static final int MAX_WIDTH = 0x7FFF;
68 static final int DEFAULT_COOLBAR_WIDTH = 0;
69 static final int DEFAULT_COOLBAR_HEIGHT = 0;
72 * Constructs a new instance of this class given its parent
73 * and a style value describing its behavior and appearance.
75 * The style value is either one of the style constants defined in
76 * class <code>SWT</code> which is applicable to instances of this
77 * class, or must be built by <em>bitwise OR</em>'ing together
78 * (that is, using the <code>int</code> "|" operator) two or more
79 * of those <code>SWT</code> style constants. The class description
80 * lists the style constants that are applicable to the class.
81 * Style bits are also inherited from superclasses.
84 * @param parent a composite control which will be the parent of the new instance (cannot be null)
85 * @param style the style of control to construct
87 * @exception IllegalArgumentException <ul>
88 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
90 * @exception SWTException <ul>
91 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
92 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
99 * @see Widget#checkSubclass
100 * @see Widget#getStyle
102 public CoolBar (Composite parent, int style) {
103 super (parent, checkStyle (style));
105 * Ensure that either of HORIZONTAL or VERTICAL is set.
106 * NOTE: HORIZONTAL and VERTICAL have the same values
107 * as H_SCROLL and V_SCROLL so it is necessary to first
108 * clear these bits to avoid scroll bars and then reset
109 * the bits using the original style supplied by the
112 * NOTE: The CCS_VERT style cannot be applied when the
113 * widget is created because of this conflict.
115 if ((style & SWT.VERTICAL) != 0) {
116 this.style |= SWT.VERTICAL;
117 int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
118 OS.SetWindowLong (handle, OS.GWL_STYLE, bits | OS.CCS_VERT);
120 this.style |= SWT.HORIZONTAL;
125 long callWindowProc (long hwnd, int msg, long wParam, long lParam) {
126 if (handle == 0) return 0;
127 return OS.CallWindowProc (ReBarProc, hwnd, msg, wParam, lParam);
130 static int checkStyle (int style) {
131 style |= SWT.NO_FOCUS;
133 * Even though it is legal to create this widget
134 * with scroll bars, they serve no useful purpose
135 * because they do not automatically scroll the
136 * widget's client area. The fix is to clear
139 return style & ~(SWT.H_SCROLL | SWT.V_SCROLL);
143 protected void checkSubclass () {
144 if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
147 @Override Point computeSizeInPixels (int wHint, int hHint, boolean changed) {
148 int width = 0, height = 0;
149 int border = getBorderWidthInPixels ();
150 int newWidth = wHint == SWT.DEFAULT ? 0x3FFF : wHint + (border * 2);
151 int newHeight = hHint == SWT.DEFAULT ? 0x3FFF : hHint + (border * 2);
152 int count = (int)OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0);
155 boolean redraw = false;
156 if (OS.IsWindowVisible (handle)) {
158 OS.UpdateWindow (handle);
159 OS.DefWindowProc (handle, OS.WM_SETREDRAW, 0, 0);
161 RECT oldRect = new RECT ();
162 OS.GetWindowRect (handle, oldRect);
163 int oldWidth = oldRect.right - oldRect.left;
164 int oldHeight = oldRect.bottom - oldRect.top;
165 int flags = OS.SWP_NOACTIVATE | OS.SWP_NOMOVE | OS.SWP_NOREDRAW | OS.SWP_NOZORDER;
166 OS.SetWindowPos (handle, 0, 0, 0, newWidth, newHeight, flags);
167 RECT rect = new RECT ();
168 OS.SendMessage (handle, OS.RB_GETRECT, count - 1, rect);
169 height = Math.max (height, rect.bottom);
170 OS.SetWindowPos (handle, 0, 0, 0, oldWidth, oldHeight, flags);
171 REBARBANDINFO rbBand = new REBARBANDINFO ();
172 rbBand.cbSize = REBARBANDINFO.sizeof;
173 rbBand.fMask = OS.RBBIM_IDEALSIZE | OS.RBBIM_STYLE;
175 for (int i = 0; i < count; i++) {
176 OS.SendMessage(handle, OS.RB_GETBANDINFO, i, rbBand);
177 if ((rbBand.fStyle & OS.RBBS_BREAK) != 0) {
178 width = Math.max(width, rowWidth);
181 rowWidth += rbBand.cxIdeal + getMargin (i);
183 width = Math.max(width, rowWidth);
185 OS.DefWindowProc (handle, OS.WM_SETREDRAW, 1, 0);
187 ignoreResize = false;
189 if (width == 0) width = DEFAULT_COOLBAR_WIDTH;
190 if (height == 0) height = DEFAULT_COOLBAR_HEIGHT;
191 if ((style & SWT.VERTICAL) != 0) {
196 if (wHint != SWT.DEFAULT) width = wHint;
197 if (hHint != SWT.DEFAULT) height = hHint;
198 height += border * 2;
200 return new Point (width, height);
204 void createHandle () {
205 super.createHandle ();
206 state &= ~(CANVAS | THEME_BACKGROUND);
209 * Feature in Windows. When the control is created,
210 * it does not use the default system font. A new HFONT
211 * is created and destroyed when the control is destroyed.
212 * This means that a program that queries the font from
213 * this control, uses the font in another control and then
214 * destroys this control will have the font unexpectedly
215 * destroyed in the other control. The fix is to assign
216 * the font ourselves each time the control is created.
217 * The control will not destroy a font that it did not
220 long hFont = OS.GetStockObject (OS.SYSTEM_FONT);
221 OS.SendMessage (handle, OS.WM_SETFONT, hFont, 0);
224 void createItem (CoolItem item, int index) {
225 int count = (int)OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0);
226 if (!(0 <= index && index <= count)) error (SWT.ERROR_INVALID_RANGE);
228 while (id < items.length && items [id] != null) id++;
229 if (id == items.length) {
230 CoolItem [] newItems = new CoolItem [items.length + 4];
231 System.arraycopy (items, 0, newItems, 0, items.length);
234 long hHeap = OS.GetProcessHeap ();
235 long lpText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, TCHAR.sizeof);
236 REBARBANDINFO rbBand = new REBARBANDINFO ();
237 rbBand.cbSize = REBARBANDINFO.sizeof;
238 rbBand.fMask = OS.RBBIM_TEXT | OS.RBBIM_STYLE | OS.RBBIM_ID;
239 rbBand.fStyle = OS.RBBS_VARIABLEHEIGHT | OS.RBBS_GRIPPERALWAYS;
240 if ((item.style & SWT.DROP_DOWN) != 0) {
241 rbBand.fStyle |= OS.RBBS_USECHEVRON;
243 rbBand.lpText = lpText;
247 * Feature in Windows. When inserting an item at end of a row,
248 * sometimes, Windows will begin to place the item on the right
249 * side of the cool bar. The fix is to resize the new items to
250 * the maximum size and then resize the next to last item to the
253 int lastIndex = getLastIndexOfRow (index - 1);
254 boolean fixLast = index == lastIndex + 1;
256 rbBand.fMask |= OS.RBBIM_SIZE;
257 rbBand.cx = MAX_WIDTH;
261 * Feature in Windows. Is possible that the item at index zero
262 * has the RBBS_BREAK flag set. When a new item is inserted at
263 * position zero, the previous item at position zero moves to
264 * a new line. The fix is to detect this case and clear the
265 * RBBS_BREAK flag on the previous item before inserting the
268 if (index == 0 && count > 0) {
269 getItem (0).setWrap (false);
272 /* Insert the item */
273 if (OS.SendMessage (handle, OS.RB_INSERTBAND, index, rbBand) == 0) {
274 error (SWT.ERROR_ITEM_NOT_ADDED);
277 /* Resize the next to last item to the ideal size */
279 resizeToPreferredWidth (lastIndex);
282 OS.HeapFree (hHeap, 0, lpText);
283 items [item.id = id] = item;
284 int length = originalItems.length;
285 CoolItem [] newOriginals = new CoolItem [length + 1];
286 System.arraycopy (originalItems, 0, newOriginals, 0, index);
287 System.arraycopy (originalItems, index, newOriginals, index + 1, length - index);
288 newOriginals [index] = item;
289 originalItems = newOriginals;
293 void createWidget () {
294 super.createWidget ();
295 items = new CoolItem [4];
296 originalItems = new CoolItem [0];
299 void destroyItem (CoolItem item) {
300 int index = (int)OS.SendMessage (handle, OS.RB_IDTOINDEX, item.id, 0);
301 int count = (int)OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0);
303 int lastIndex = getLastIndexOfRow (index);
304 if (index == lastIndex) {
306 * Feature in Windows. If the last item in a row is
307 * given its ideal size, it will be placed at the far
308 * right hand edge of the coolbar. It is preferred
309 * that the last item appear next to the second last
310 * item. The fix is to size the last item of each row
311 * so that it occupies all the available space to the
314 resizeToMaximumWidth (lastIndex - 1);
319 * Feature in Windows. When Windows removed a rebar
320 * band, it makes the band child invisible. The fix
321 * is to show the child.
323 Control control = item.control;
324 boolean wasVisible = control != null && !control.isDisposed() && control.getVisible ();
327 * When a wrapped item is being deleted, make the next
328 * item in the row wrapped in order to preserve the row.
329 * In order to avoid an unnecessary layout, temporarily
330 * ignore WM_SIZE. If the next item is wrapped then a
331 * row will be deleted and the WM_SIZE is necessary.
333 CoolItem nextItem = null;
334 if (item.getWrap ()) {
335 if (index + 1 < count) {
336 nextItem = getItem (index + 1);
337 ignoreResize = !nextItem.getWrap ();
340 if (OS.SendMessage (handle, OS.RB_DELETEBAND, index, 0) == 0) {
341 error (SWT.ERROR_ITEM_NOT_REMOVED);
343 items [item.id] = null;
346 nextItem.setWrap (true);
347 ignoreResize = false;
350 /* Restore the visible state of the control */
351 if (wasVisible) control.setVisible (true);
354 while (index < originalItems.length) {
355 if (originalItems [index] == item) break;
358 int length = originalItems.length - 1;
359 CoolItem [] newOriginals = new CoolItem [length];
360 System.arraycopy (originalItems, 0, newOriginals, 0, index);
361 System.arraycopy (originalItems, index + 1, newOriginals, index, length - index);
362 originalItems = newOriginals;
366 void drawThemeBackground (long hDC, long hwnd, RECT rect) {
367 if (OS.IsAppThemed ()) {
368 if (background == -1 && (style & SWT.FLAT) != 0) {
369 Control control = findBackgroundControl ();
370 if (control != null && control.backgroundImage != null) {
371 fillBackground (hDC, control.getBackgroundPixel (), rect);
376 RECT rect2 = new RECT ();
377 OS.GetClientRect (handle, rect2);
378 OS.MapWindowPoints (handle, hwnd, rect2, 2);
379 POINT lpPoint = new POINT ();
380 OS.SetWindowOrgEx (hDC, -rect2.left, -rect2.top, lpPoint);
381 OS.SendMessage (handle, OS.WM_PRINT, hDC, OS.PRF_CLIENT | OS.PRF_ERASEBKGND);
382 OS.SetWindowOrgEx (hDC, lpPoint.x, lpPoint.y, null);
386 Control findThemeControl () {
387 if ((style & SWT.FLAT) != 0) return this;
388 return background == -1 && backgroundImage == null ? this : super.findThemeControl ();
391 int getMargin (int index) {
393 MARGINS margins = new MARGINS ();
394 OS.SendMessage (handle, OS.RB_GETBANDMARGINS, 0, margins);
395 margin += margins.cxLeftWidth + margins.cxRightWidth;
396 RECT rect = new RECT ();
397 OS.SendMessage (handle, OS.RB_GETBANDBORDERS, index, rect);
398 if ((style & SWT.FLAT) != 0) {
400 * Bug in Windows. When the style bit RBS_BANDBORDERS is not set
401 * the rectangle returned by RBS_BANDBORDERS is four pixels too small.
402 * The fix is to add four pixels to the result.
404 if ((style & SWT.VERTICAL) != 0) {
405 margin += rect.top + 4;
407 margin += rect.left + 4;
410 if ((style & SWT.VERTICAL) != 0) {
411 margin += rect.top + rect.bottom;
413 margin += rect.left + rect.right;
416 if ((style & SWT.FLAT) == 0) {
417 if (!isLastItemOfRow (index)) {
418 margin += CoolBar.SEPARATOR_WIDTH;
425 * Returns the item that is currently displayed at the given,
426 * zero-relative index. Throws an exception if the index is
429 * @param index the visual index of the item to return
430 * @return the item at the given visual index
432 * @exception IllegalArgumentException <ul>
433 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
435 * @exception SWTException <ul>
436 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
437 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
440 public CoolItem getItem (int index) {
442 int count = (int)OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0);
443 if (!(0 <= index && index < count)) error (SWT.ERROR_INVALID_RANGE);
444 REBARBANDINFO rbBand = new REBARBANDINFO ();
445 rbBand.cbSize = REBARBANDINFO.sizeof;
446 rbBand.fMask = OS.RBBIM_ID;
447 OS.SendMessage (handle, OS.RB_GETBANDINFO, index, rbBand);
448 return items [rbBand.wID];
452 * Returns the number of items contained in the receiver.
454 * @return the number of items
456 * @exception SWTException <ul>
457 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
458 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
461 public int getItemCount () {
463 return (int)OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0);
467 * Returns an array of zero-relative ints that map
468 * the creation order of the receiver's items to the
469 * order in which they are currently being displayed.
471 * Specifically, the indices of the returned array represent
472 * the current visual order of the items, and the contents
473 * of the array represent the creation order of the items.
475 * Note: This is not the actual structure used by the receiver
476 * to maintain its list of items, so modifying the array will
477 * not affect the receiver.
480 * @return the current visual order of the receiver's items
482 * @exception SWTException <ul>
483 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
484 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
487 public int [] getItemOrder () {
489 int count = (int)OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0);
490 int [] indices = new int [count];
491 REBARBANDINFO rbBand = new REBARBANDINFO ();
492 rbBand.cbSize = REBARBANDINFO.sizeof;
493 rbBand.fMask = OS.RBBIM_ID;
494 for (int i=0; i<count; i++) {
495 OS.SendMessage (handle, OS.RB_GETBANDINFO, i, rbBand);
496 CoolItem item = items [rbBand.wID];
498 while (index<originalItems.length) {
499 if (originalItems [index] == item) break;
502 if (index == originalItems.length) error (SWT.ERROR_CANNOT_GET_ITEM);
509 * Returns an array of <code>CoolItem</code>s in the order
510 * in which they are currently being displayed.
512 * Note: This is not the actual structure used by the receiver
513 * to maintain its list of items, so modifying the array will
514 * not affect the receiver.
517 * @return the receiver's items in their current visual order
519 * @exception SWTException <ul>
520 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
521 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
524 public CoolItem [] getItems () {
526 int count = (int)OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0);
527 CoolItem [] result = new CoolItem [count];
528 REBARBANDINFO rbBand = new REBARBANDINFO ();
529 rbBand.cbSize = REBARBANDINFO.sizeof;
530 rbBand.fMask = OS.RBBIM_ID;
531 for (int i=0; i<count; i++) {
532 OS.SendMessage (handle, OS.RB_GETBANDINFO, i, rbBand);
533 result [i] = items [rbBand.wID];
539 * Returns an array of points whose x and y coordinates describe
540 * the widths and heights (respectively) of the items in the receiver
541 * in the order in which they are currently being displayed.
543 * @return the receiver's item sizes in their current visual order
545 * @exception SWTException <ul>
546 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
547 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
550 public Point [] getItemSizes () {
552 Point [] sizes = getItemSizesInPixels();
554 for (int i = 0; i < sizes.length; i++) {
555 sizes[i] = DPIUtil.autoScaleDown(sizes[i]);
561 Point [] getItemSizesInPixels () {
562 int count = (int)OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0);
563 Point [] sizes = new Point [count];
564 REBARBANDINFO rbBand = new REBARBANDINFO ();
565 rbBand.cbSize = REBARBANDINFO.sizeof;
566 rbBand.fMask = OS.RBBIM_CHILDSIZE;
567 int separator = (style & SWT.FLAT) == 0 ? SEPARATOR_WIDTH : 0;
568 MARGINS margins = new MARGINS ();
569 for (int i=0; i<count; i++) {
570 RECT rect = new RECT ();
571 OS.SendMessage (handle, OS.RB_GETRECT, i, rect);
572 OS.SendMessage (handle, OS.RB_GETBANDINFO, i, rbBand);
573 OS.SendMessage (handle, OS.RB_GETBANDMARGINS, 0, margins);
574 rect.left -= margins.cxLeftWidth;
575 rect.right += margins.cxRightWidth;
576 if (!isLastItemOfRow(i)) rect.right += separator;
577 if ((style & SWT.VERTICAL) != 0) {
578 sizes [i] = new Point (rbBand.cyChild, rect.right - rect.left);
580 sizes [i] = new Point (rect.right - rect.left, rbBand.cyChild);
586 int getLastIndexOfRow (int index) {
587 int count = (int)OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0);
588 if (count == 0) return -1;
589 REBARBANDINFO rbBand = new REBARBANDINFO ();
590 rbBand.cbSize = REBARBANDINFO.sizeof;
591 rbBand.fMask = OS.RBBIM_STYLE;
592 for (int i=index + 1; i<count; i++) {
593 OS.SendMessage (handle, OS.RB_GETBANDINFO, i, rbBand);
594 if ((rbBand.fStyle & OS.RBBS_BREAK) != 0) {
601 boolean isLastItemOfRow (int index) {
602 int count = (int)OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0);
603 if (index + 1 == count) return true;
604 REBARBANDINFO rbBand = new REBARBANDINFO ();
605 rbBand.cbSize = REBARBANDINFO.sizeof;
606 rbBand.fMask = OS.RBBIM_STYLE;
607 OS.SendMessage (handle, OS.RB_GETBANDINFO, index + 1, rbBand);
608 return (rbBand.fStyle & OS.RBBS_BREAK) != 0;
612 * Returns whether or not the receiver is 'locked'. When a coolbar
613 * is locked, its items cannot be repositioned.
615 * @return true if the coolbar is locked, false otherwise
617 * @exception SWTException <ul>
618 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
619 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
624 public boolean getLocked () {
630 * Returns an array of ints that describe the zero-relative
631 * indices of any item(s) in the receiver that will begin on
632 * a new row. The 0th visible item always begins the first row,
633 * therefore it does not count as a wrap index.
635 * @return an array containing the receiver's wrap indices, or an empty array if all items are in one row
637 * @exception SWTException <ul>
638 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
639 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
642 public int [] getWrapIndices () {
644 CoolItem [] items = getItems ();
645 int [] indices = new int [items.length];
647 for (int i=0; i<items.length; i++) {
648 if (items [i].getWrap ()) indices [count++] = i;
650 int [] result = new int [count];
651 System.arraycopy (indices, 0, result, 0, count);
656 * Searches the receiver's items in the order they are currently
657 * being displayed, starting at the first item (index 0), until
658 * an item is found that is equal to the argument, and returns
659 * the index of that item. If no item is found, returns -1.
661 * @param item the search item
662 * @return the visual order index of the search item, or -1 if the item is not found
664 * @exception IllegalArgumentException <ul>
665 * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
666 * <li>ERROR_INVALID_ARGUMENT - if the item is disposed</li>
668 * @exception SWTException <ul>
669 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
670 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
673 public int indexOf (CoolItem item) {
675 if (item == null) error (SWT.ERROR_NULL_ARGUMENT);
676 if (item.isDisposed()) error (SWT.ERROR_INVALID_ARGUMENT);
677 return (int)OS.SendMessage (handle, OS.RB_IDTOINDEX, item.id, 0);
680 void resizeToPreferredWidth (int index) {
682 * Bug in Windows. When RB_GETBANDBORDERS is sent
683 * with an index out of range, Windows GP's. The
684 * fix is to ensure the index is in range.
686 int count = (int)OS.SendMessage(handle, OS.RB_GETBANDCOUNT, 0, 0);
687 if (0 <= index && index < count) {
688 REBARBANDINFO rbBand = new REBARBANDINFO();
689 rbBand.cbSize = REBARBANDINFO.sizeof;
690 rbBand.fMask = OS.RBBIM_IDEALSIZE;
691 OS.SendMessage (handle, OS.RB_GETBANDINFO, index, rbBand);
692 RECT rect = new RECT ();
693 OS.SendMessage (handle, OS.RB_GETBANDBORDERS, index, rect);
694 rbBand.cx = rbBand.cxIdeal + rect.left;
695 if ((style & SWT.FLAT) == 0) rbBand.cx += rect.right;
696 rbBand.fMask = OS.RBBIM_SIZE;
697 OS.SendMessage (handle, OS.RB_SETBANDINFO, index, rbBand);
701 void resizeToMaximumWidth (int index) {
702 REBARBANDINFO rbBand = new REBARBANDINFO();
703 rbBand.cbSize = REBARBANDINFO.sizeof;
704 rbBand.fMask = OS.RBBIM_SIZE;
705 rbBand.cx = MAX_WIDTH;
706 OS.SendMessage (handle, OS.RB_SETBANDINFO, index, rbBand);
710 void releaseChildren (boolean destroy) {
712 for (int i=0; i<items.length; i++) {
713 CoolItem item = items [i];
714 if (item != null && !item.isDisposed ()) {
715 item.release (false);
720 super.releaseChildren (destroy);
724 void removeControl (Control control) {
725 super.removeControl (control);
726 for (int i=0; i<items.length; i++) {
727 CoolItem item = items [i];
728 if (item != null && item.control == control) {
729 item.setControl (null);
735 void reskinChildren (int flags) {
737 for (int i=0; i<items.length; i++) {
738 CoolItem item = items [i];
739 if (item != null) item.reskin (flags);
742 super.reskinChildren (flags);
746 void setBackgroundPixel (int pixel) {
747 if (pixel == -1) pixel = defaultBackground ();
748 OS.SendMessage (handle, OS.RB_SETBKCOLOR, 0, pixel);
749 setItemColors ((int)OS.SendMessage (handle, OS.RB_GETTEXTCOLOR, 0, 0), pixel);
751 * Feature in Windows. For some reason, Windows
752 * does not fully erase the coolbar area and coolbar
753 * items when you set the background. The fix is
754 * to invalidate the coolbar area.
756 if (!OS.IsWindowVisible (handle)) return;
757 int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN;
758 OS.RedrawWindow (handle, null, 0, flags);
762 void setForegroundPixel (int pixel) {
763 if (pixel == -1) pixel = defaultForeground ();
764 OS.SendMessage (handle, OS.RB_SETTEXTCOLOR, 0, pixel);
765 setItemColors (pixel, (int)OS.SendMessage (handle, OS.RB_GETBKCOLOR, 0, 0));
768 void setItemColors (int foreColor, int backColor) {
769 int count = (int)OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0);
770 REBARBANDINFO rbBand = new REBARBANDINFO ();
771 rbBand.cbSize = REBARBANDINFO.sizeof;
772 rbBand.fMask = OS.RBBIM_COLORS;
773 rbBand.clrFore = foreColor;
774 rbBand.clrBack = backColor;
775 for (int i=0; i<count; i++) {
776 OS.SendMessage (handle, OS.RB_SETBANDINFO, i, rbBand);
781 * Sets the receiver's item order, wrap indices, and item sizes
782 * all at once. This method is typically used to restore the
783 * displayed state of the receiver to a previously stored state.
785 * The item order is the order in which the items in the receiver
786 * should be displayed, given in terms of the zero-relative ordering
787 * of when the items were added.
789 * The wrap indices are the indices of all item(s) in the receiver
790 * that will begin on a new row. The indices are given in the order
791 * specified by the item order. The 0th item always begins the first
792 * row, therefore it does not count as a wrap index. If wrap indices
793 * is null or empty, the items will be placed on one line.
795 * The sizes are specified in an array of points whose x and y
796 * coordinates describe the new widths and heights (respectively)
797 * of the receiver's items in the order specified by the item order.
800 * @param itemOrder an array of indices that describe the new order to display the items in
801 * @param wrapIndices an array of wrap indices, or null
802 * @param sizes an array containing the new sizes for each of the receiver's items in visual order
804 * @exception SWTException <ul>
805 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
806 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
808 * @exception IllegalArgumentException <ul>
809 * <li>ERROR_NULL_ARGUMENT - if item order or sizes is null</li>
810 * <li>ERROR_INVALID_ARGUMENT - if item order or sizes is not the same length as the number of items</li>
813 public void setItemLayout (int [] itemOrder, int [] wrapIndices, Point [] sizes) {
815 if (sizes == null) error (SWT.ERROR_NULL_ARGUMENT);
816 Point [] sizesInPoints = new Point [sizes.length];
817 for (int i = 0; i < sizes.length; i++) {
818 sizesInPoints[i] = DPIUtil.autoScaleUp(sizes[i]);
820 setItemLayoutInPixels (itemOrder, wrapIndices, sizesInPoints);
823 void setItemLayoutInPixels (int [] itemOrder, int [] wrapIndices, Point [] sizes) {
825 setItemOrder (itemOrder);
826 setWrapIndices (wrapIndices);
827 setItemSizes (sizes);
832 * Sets the order that the items in the receiver should
833 * be displayed in to the given argument which is described
834 * in terms of the zero-relative ordering of when the items
837 * @param itemOrder the new order to display the items in
839 * @exception SWTException <ul>
840 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
841 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
843 * @exception IllegalArgumentException <ul>
844 * <li>ERROR_NULL_ARGUMENT - if the item order is null</li>
845 * <li>ERROR_INVALID_ARGUMENT - if the item order is not the same length as the number of items</li>
848 void setItemOrder (int [] itemOrder) {
849 if (itemOrder == null) error (SWT.ERROR_NULL_ARGUMENT);
850 int itemCount = (int)OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0);
851 if (itemOrder.length != itemCount) error (SWT.ERROR_INVALID_ARGUMENT);
853 /* Ensure that itemOrder does not contain any duplicates. */
854 boolean [] set = new boolean [itemCount];
855 for (int i=0; i<itemOrder.length; i++) {
856 int index = itemOrder [i];
857 if (index < 0 || index >= itemCount) error (SWT.ERROR_INVALID_RANGE);
858 if (set [index]) error (SWT.ERROR_INVALID_ARGUMENT);
862 REBARBANDINFO rbBand = new REBARBANDINFO ();
863 rbBand.cbSize = REBARBANDINFO.sizeof;
864 for (int i=0; i<itemOrder.length; i++) {
865 int id = originalItems [itemOrder [i]].id;
866 int index = (int)OS.SendMessage (handle, OS.RB_IDTOINDEX, id, 0);
868 int lastItemSrcRow = getLastIndexOfRow (index);
869 int lastItemDstRow = getLastIndexOfRow (i);
870 if (index == lastItemSrcRow) {
871 resizeToPreferredWidth (index);
873 if (i == lastItemDstRow) {
874 resizeToPreferredWidth (i);
878 OS.SendMessage (handle, OS.RB_MOVEBAND, index, i);
880 if (index == lastItemSrcRow && index - 1 >= 0) {
881 resizeToMaximumWidth (index - 1);
883 if (i == lastItemDstRow) {
884 resizeToMaximumWidth (i);
891 * Sets the width and height of the receiver's items to the ones
892 * specified by the argument, which is an array of points whose x
893 * and y coordinates describe the widths and heights (respectively)
894 * in the order in which the items are currently being displayed.
896 * @param sizes an array containing the new sizes for each of the receiver's items in visual order
898 * @exception IllegalArgumentException <ul>
899 * <li>ERROR_NULL_ARGUMENT - if the array of sizes is null</li>
900 * <li>ERROR_INVALID_ARGUMENT - if the array of sizes is not the same length as the number of items</li>
902 * @exception SWTException <ul>
903 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
904 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
907 void setItemSizes (Point [] sizes) {
908 if (sizes == null) error (SWT.ERROR_NULL_ARGUMENT);
909 int count = (int)OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0);
910 if (sizes.length != count) error (SWT.ERROR_INVALID_ARGUMENT);
911 REBARBANDINFO rbBand = new REBARBANDINFO ();
912 rbBand.cbSize = REBARBANDINFO.sizeof;
913 rbBand.fMask = OS.RBBIM_ID;
914 for (int i=0; i<count; i++) {
915 OS.SendMessage (handle, OS.RB_GETBANDINFO, i, rbBand);
916 items [rbBand.wID].setSizeInPixels (sizes [i].x, sizes [i].y);
921 * Sets whether or not the receiver is 'locked'. When a coolbar
922 * is locked, its items cannot be repositioned.
924 * @param locked lock the coolbar if true, otherwise unlock the coolbar
926 * @exception SWTException <ul>
927 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
928 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
933 public void setLocked (boolean locked) {
935 this.locked = locked;
936 int count = (int)OS.SendMessage (handle, OS.RB_GETBANDCOUNT, 0, 0);
937 REBARBANDINFO rbBand = new REBARBANDINFO ();
938 rbBand.cbSize = REBARBANDINFO.sizeof;
939 rbBand.fMask = OS.RBBIM_STYLE;
940 for (int i=0; i<count; i++) {
941 OS.SendMessage (handle, OS.RB_GETBANDINFO, i, rbBand);
943 rbBand.fStyle |= OS.RBBS_NOGRIPPER;
945 rbBand.fStyle &= ~OS.RBBS_NOGRIPPER;
947 OS.SendMessage (handle, OS.RB_SETBANDINFO, i, rbBand);
952 * Sets the indices of all item(s) in the receiver that will
953 * begin on a new row. The indices are given in the order in
954 * which they are currently being displayed. The 0th item
955 * always begins the first row, therefore it does not count
956 * as a wrap index. If indices is null or empty, the items
957 * will be placed on one line.
959 * @param indices an array of wrap indices, or null
961 * @exception SWTException <ul>
962 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
963 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
966 public void setWrapIndices (int [] indices) {
968 if (indices == null) indices = new int [0];
969 int count = getItemCount ();
970 for (int i=0; i<indices.length; i++) {
971 if (indices [i] < 0 || indices [i] >= count) {
972 error (SWT.ERROR_INVALID_RANGE);
976 CoolItem [] items = getItems ();
977 for (int i=0; i<items.length; i++) {
978 CoolItem item = items [i];
979 if (item.getWrap ()) {
980 resizeToPreferredWidth (i - 1);
981 item.setWrap (false);
984 resizeToMaximumWidth (count - 1);
985 for (int i=0; i<indices.length; i++) {
986 int index = indices [i];
987 if (0 <= index && index < items.length) {
988 CoolItem item = items [index];
990 resizeToMaximumWidth (index - 1);
998 int bits = super.widgetStyle () | OS.CCS_NODIVIDER | OS.CCS_NORESIZE;
999 bits |= OS.RBS_VARHEIGHT | OS.RBS_DBLCLKTOGGLE;
1000 if ((style & SWT.FLAT) == 0) bits |= OS.RBS_BANDBORDERS;
1005 TCHAR windowClass () {
1010 long windowProc () {
1015 LRESULT WM_COMMAND (long wParam, long lParam) {
1017 * Feature in Windows. When the coolbar window
1018 * proc processes WM_COMMAND, it forwards this
1019 * message to its parent. This is done so that
1020 * children of this control that send this message
1021 * type to their parent will notify not only
1022 * this control but also the parent of this control,
1023 * which is typically the application window and
1024 * the window that is looking for the message.
1025 * If the control did not forward the message,
1026 * applications would have to subclass the control
1027 * window to see the message. Because the control
1028 * window is subclassed by SWT, the message
1029 * is delivered twice, once by SWT and once when
1030 * the message is forwarded by the window proc.
1031 * The fix is to avoid calling the window proc
1034 LRESULT result = super.WM_COMMAND (wParam, lParam);
1035 if (result != null) return result;
1036 return LRESULT.ZERO;
1040 LRESULT WM_ERASEBKGND (long wParam, long lParam) {
1041 LRESULT result = super.WM_ERASEBKGND (wParam, lParam);
1043 * Feature in Windows. For some reason, Windows
1044 * does not fully erase the area that the cool bar
1045 * occupies when the size of the cool bar is larger
1046 * than the space occupied by the cool bar items.
1047 * The fix is to erase the cool bar background.
1049 * NOTE: On versions of Windows prior to XP, for
1050 * some reason, the cool bar draws separators in
1051 * WM_ERASEBKGND. Therefore it is essential to run
1052 * the cool bar window proc after the background has
1055 if (!OS.IsAppThemed ()) {
1056 drawBackground (wParam);
1063 LRESULT WM_NOTIFY (long wParam, long lParam) {
1065 * Feature in Windows. When the cool bar window
1066 * proc processes WM_NOTIFY, it forwards this
1067 * message to its parent. This is done so that
1068 * children of this control that send this message
1069 * type to their parent will notify not only
1070 * this control but also the parent of this control,
1071 * which is typically the application window and
1072 * the window that is looking for the message.
1073 * If the control did not forward the message,
1074 * applications would have to subclass the control
1075 * window to see the message. Because the control
1076 * window is subclassed by SWT, the message
1077 * is delivered twice, once by SWT and once when
1078 * the message is forwarded by the window proc.
1079 * The fix is to avoid calling the window proc
1082 LRESULT result = super.WM_NOTIFY (wParam, lParam);
1083 if (result != null) return result;
1084 return LRESULT.ZERO;
1088 LRESULT WM_SETREDRAW (long wParam, long lParam) {
1089 LRESULT result = super.WM_SETREDRAW (wParam, lParam);
1090 if (result != null) return result;
1092 * Feature in Windows. When redraw is turned off, the rebar
1093 * control does not call the default window proc. This means
1094 * that the rebar will redraw and children of the rebar will
1095 * also redraw. The fix is to call both the rebar window proc
1096 * and the default window proc.
1098 * NOTE: The rebar control can resize itself in WM_SETREDRAW.
1099 * When redraw is turned off by the default window proc, this
1100 * can leave pixel corruption in the parent. The fix is to
1101 * detect the size change and damage the previous area in the
1104 * NOTE: In version 6.00 of COMCTL32.DLL, when WM_SETREDRAW
1105 * is off, we cannot detect that the size has changed causing
1106 * pixel corruption. The fix is to disallow WM_SETREDRAW by
1107 * not running the default window proc or the rebar window
1110 return LRESULT.ZERO;
1114 LRESULT WM_SIZE (long wParam, long lParam) {
1116 long code = callWindowProc (handle, OS.WM_SIZE, wParam, lParam);
1117 if (code == 0) return LRESULT.ZERO;
1118 return new LRESULT (code);
1121 // if (OS.IsAppThemed ()) {
1122 // if (background == -1 && (style & SWT.FLAT) == 0) {
1123 // OS.InvalidateRect (handle, null, true);
1126 return super.WM_SIZE (wParam, lParam);
1130 LRESULT wmNotifyChild (NMHDR hdr, long wParam, long lParam) {
1132 case OS.RBN_BEGINDRAG: {
1133 int pos = OS.GetMessagePos ();
1134 POINT pt = new POINT ();
1135 OS.POINTSTOPOINT (pt, pos);
1136 OS.ScreenToClient (handle, pt);
1137 int button = display.lastButton != 0 ? display.lastButton : 1;
1138 if (!sendDragEvent (button, pt.x, pt.y)) return LRESULT.ONE;
1141 case OS.RBN_CHILDSIZE: {
1143 * Bug in Windows. When Windows sets the size of the rebar band
1144 * child and the child is a combo box, the size of the drop down
1145 * portion of the combo box is resized to zero. The fix is to set
1146 * the size of the control to the current size after the rebar has
1147 * already resized it. If the control is not a combo, this does
1148 * nothing. If the control is a combo, the drop down portion is
1151 NMREBARCHILDSIZE lprbcs = new NMREBARCHILDSIZE ();
1152 OS.MoveMemory (lprbcs, lParam, NMREBARCHILDSIZE.sizeof);
1153 if (lprbcs.uBand != -1) {
1154 CoolItem item = items [lprbcs.wID];
1155 Control control = item.control;
1156 if (control != null) {
1157 int width = lprbcs.rcChild_right - lprbcs.rcChild_left;
1158 int height = lprbcs.rcChild_bottom - lprbcs.rcChild_top;
1159 control.setBoundsInPixels (lprbcs.rcChild_left, lprbcs.rcChild_top, width, height);
1164 case OS.RBN_HEIGHTCHANGE: {
1165 if (!ignoreResize) {
1166 Point size = getSizeInPixels ();
1167 int border = getBorderWidthInPixels ();
1168 int barHeight = (int)OS.SendMessage (handle, OS.RB_GETBARHEIGHT, 0, 0);
1169 if ((style & SWT.VERTICAL) != 0) {
1170 setSizeInPixels (barHeight + 2 * border, size.y);
1172 setSizeInPixels (size.x, barHeight + 2 * border);
1177 case OS.RBN_CHEVRONPUSHED: {
1178 NMREBARCHEVRON lpnm = new NMREBARCHEVRON ();
1179 OS.MoveMemory (lpnm, lParam, NMREBARCHEVRON.sizeof);
1180 CoolItem item = items [lpnm.wID];
1182 Event event = new Event();
1183 event.detail = SWT.ARROW;
1184 if ((style & SWT.VERTICAL) != 0) {
1185 event.setLocationInPixels(lpnm.right, lpnm.top);
1187 event.setLocationInPixels(lpnm.left, lpnm.bottom);
1189 item.sendSelectionEvent(SWT.Selection, event, false);
1193 case OS.NM_CUSTOMDRAW: {
1194 if (findBackgroundControl () != null || (style & SWT.FLAT) != 0) {
1195 NMCUSTOMDRAW nmcd = new NMCUSTOMDRAW ();
1196 OS.MoveMemory (nmcd, lParam, NMCUSTOMDRAW.sizeof);
1197 switch (nmcd.dwDrawStage) {
1198 case OS.CDDS_PREERASE:
1199 return new LRESULT (OS.CDRF_SKIPDEFAULT | OS.CDRF_NOTIFYPOSTERASE);
1200 case OS.CDDS_POSTERASE:
1201 drawBackground (nmcd.hdc);
1208 return super.wmNotifyChild (hdr, wParam, lParam);