1 /*******************************************************************************
2 * Copyright (c) 2000, 2019 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 * Conrad Groth - Bug 23837 [FEEP] Button, do not respect foreground and background color on Windows
14 *******************************************************************************/
15 package org.eclipse.swt.widgets;
18 import org.eclipse.swt.*;
19 import org.eclipse.swt.events.*;
20 import org.eclipse.swt.graphics.*;
21 import org.eclipse.swt.internal.*;
22 import org.eclipse.swt.internal.win32.*;
25 * Instances of this class represent a selectable user interface object that
26 * issues notification when pressed and released.
28 * <dt><b>Styles:</b></dt>
29 * <dd>ARROW, CHECK, PUSH, RADIO, TOGGLE, FLAT, WRAP</dd>
30 * <dd>UP, DOWN, LEFT, RIGHT, CENTER</dd>
31 * <dt><b>Events:</b></dt>
35 * Note: Only one of the styles ARROW, CHECK, PUSH, RADIO, and TOGGLE
38 * Note: Only one of the styles LEFT, RIGHT, and CENTER may be specified.
40 * Note: Only one of the styles UP, DOWN, LEFT, and RIGHT may be specified
41 * when the ARROW style is specified.
43 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
46 * @see <a href="http://www.eclipse.org/swt/snippets/#button">Button snippets</a>
47 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a>
48 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
49 * @noextend This class is not intended to be subclassed by clients.
51 public class Button extends Control {
52 String text = "", message = "";
53 Image image, image2, disabledImage;
55 boolean ignoreMouse, grayed;
56 int buttonBackground = -1;
57 // we need our own field, because setting Control.background causes two colored pixels around the button.
58 int buttonBackgroundAlpha = 255;
59 static final int MARGIN = 4;
60 static final int CHECK_WIDTH, CHECK_HEIGHT;
61 static final int ICON_WIDTH = 128, ICON_HEIGHT = 128;
62 static /*final*/ boolean COMMAND_LINK = false;
63 static final long ButtonProc;
64 static final TCHAR ButtonClass = new TCHAR (0, "BUTTON", true);
66 long hBitmap = OS.LoadBitmap (0, OS.OBM_CHECKBOXES);
68 CHECK_WIDTH = OS.GetSystemMetrics (OS.SM_CXVSCROLL);
69 CHECK_HEIGHT = OS.GetSystemMetrics (OS.SM_CYVSCROLL);
71 BITMAP bitmap = new BITMAP ();
72 OS.GetObject (hBitmap, BITMAP.sizeof, bitmap);
73 OS.DeleteObject (hBitmap);
74 CHECK_WIDTH = bitmap.bmWidth / 4;
75 CHECK_HEIGHT = bitmap.bmHeight / 3;
77 WNDCLASS lpWndClass = new WNDCLASS ();
78 OS.GetClassInfo (0, ButtonClass, lpWndClass);
79 ButtonProc = lpWndClass.lpfnWndProc;
83 * Constructs a new instance of this class given its parent
84 * and a style value describing its behavior and appearance.
86 * The style value is either one of the style constants defined in
87 * class <code>SWT</code> which is applicable to instances of this
88 * class, or must be built by <em>bitwise OR</em>'ing together
89 * (that is, using the <code>int</code> "|" operator) two or more
90 * of those <code>SWT</code> style constants. The class description
91 * lists the style constants that are applicable to the class.
92 * Style bits are also inherited from superclasses.
95 * @param parent a composite control which will be the parent of the new instance (cannot be null)
96 * @param style the style of control to construct
98 * @exception IllegalArgumentException <ul>
99 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
101 * @exception SWTException <ul>
102 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
103 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
117 * @see Widget#checkSubclass
118 * @see Widget#getStyle
120 public Button (Composite parent, int style) {
121 super (parent, checkStyle (style));
124 void _setImage (Image image) {
125 if ((style & SWT.COMMAND) != 0) return;
126 if (imageList != null) imageList.dispose ();
129 imageList = new ImageList (style & SWT.RIGHT_TO_LEFT);
130 if (OS.IsWindowEnabled (handle)) {
131 imageList.add (image);
133 if (disabledImage != null) disabledImage.dispose ();
134 disabledImage = new Image (display, image, SWT.IMAGE_DISABLE);
135 imageList.add (disabledImage);
137 BUTTON_IMAGELIST buttonImageList = new BUTTON_IMAGELIST ();
138 buttonImageList.himl = imageList.getHandle ();
139 int oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE), newBits = oldBits;
140 newBits &= ~(OS.BS_LEFT | OS.BS_CENTER | OS.BS_RIGHT);
141 if ((style & SWT.LEFT) != 0) newBits |= OS.BS_LEFT;
142 if ((style & SWT.CENTER) != 0) newBits |= OS.BS_CENTER;
143 if ((style & SWT.RIGHT) != 0) newBits |= OS.BS_RIGHT;
144 if (text.length () == 0) {
145 if ((style & SWT.LEFT) != 0) buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_LEFT;
146 if ((style & SWT.CENTER) != 0) buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_CENTER;
147 if ((style & SWT.RIGHT) != 0) buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_RIGHT;
149 buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_LEFT;
150 buttonImageList.margin_left = computeLeftMargin ();
151 buttonImageList.margin_right = MARGIN;
152 newBits &= ~(OS.BS_CENTER | OS.BS_RIGHT);
153 newBits |= OS.BS_LEFT;
155 if (newBits != oldBits) {
156 OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
157 OS.InvalidateRect (handle, null, true);
159 OS.SendMessage (handle, OS.BCM_SETIMAGELIST, 0, buttonImageList);
161 OS.SendMessage (handle, OS.BCM_SETIMAGELIST, 0, 0);
164 * Bug in Windows. Under certain cirumstances yet to be
165 * isolated, BCM_SETIMAGELIST does not redraw the control
166 * when a new image is set. The fix is to force a redraw.
168 OS.InvalidateRect (handle, null, true);
171 void _setText (String text) {
172 int oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE), newBits = oldBits;
173 newBits &= ~(OS.BS_LEFT | OS.BS_CENTER | OS.BS_RIGHT);
174 if ((style & SWT.LEFT) != 0) newBits |= OS.BS_LEFT;
175 if ((style & SWT.CENTER) != 0) newBits |= OS.BS_CENTER;
176 if ((style & SWT.RIGHT) != 0) newBits |= OS.BS_RIGHT;
177 if (imageList != null) {
178 BUTTON_IMAGELIST buttonImageList = new BUTTON_IMAGELIST ();
179 buttonImageList.himl = imageList.getHandle ();
180 if (text.length () == 0) {
181 if ((style & SWT.LEFT) != 0) buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_LEFT;
182 if ((style & SWT.CENTER) != 0) buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_CENTER;
183 if ((style & SWT.RIGHT) != 0) buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_RIGHT;
185 buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_LEFT;
186 buttonImageList.margin_left = computeLeftMargin ();
187 buttonImageList.margin_right = MARGIN;
188 newBits &= ~(OS.BS_CENTER | OS.BS_RIGHT);
189 newBits |= OS.BS_LEFT;
191 OS.SendMessage (handle, OS.BCM_SETIMAGELIST, 0, buttonImageList);
193 if (newBits != oldBits) {
194 OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
195 OS.InvalidateRect (handle, null, true);
198 * Bug in Windows. When a Button control is right-to-left and
199 * is disabled, the first pixel of the text is clipped. The fix
200 * is to append a space to the text.
202 if ((style & SWT.RIGHT_TO_LEFT) != 0) {
203 if (!OS.IsAppThemed ()) {
204 text = OS.IsWindowEnabled (handle) ? text : text + " ";
207 TCHAR buffer = new TCHAR (getCodePage (), text, true);
208 OS.SetWindowText (handle, buffer);
209 if ((state & HAS_AUTO_DIRECTION) != 0) {
210 updateTextDirection (AUTO_TEXT_DIRECTION);
215 * Adds the listener to the collection of listeners who will
216 * be notified when the control is selected by the user, by sending
217 * it one of the messages defined in the <code>SelectionListener</code>
220 * <code>widgetSelected</code> is called when the control is selected by the user.
221 * <code>widgetDefaultSelected</code> is not called.
224 * When the <code>SWT.RADIO</code> style bit is set, the <code>widgetSelected</code> method is
225 * also called when the receiver loses selection because another item in the same radio group
226 * was selected by the user. During <code>widgetSelected</code> the application can use
227 * <code>getSelection()</code> to determine the current selected state of the receiver.
230 * @param listener the listener which should be notified
232 * @exception IllegalArgumentException <ul>
233 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
235 * @exception SWTException <ul>
236 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
237 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
240 * @see SelectionListener
241 * @see #removeSelectionListener
242 * @see SelectionEvent
244 public void addSelectionListener (SelectionListener listener) {
246 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
247 TypedListener typedListener = new TypedListener (listener);
248 addListener (SWT.Selection,typedListener);
249 addListener (SWT.DefaultSelection,typedListener);
253 long callWindowProc (long hwnd, int msg, long wParam, long lParam) {
254 if (handle == 0) return 0;
255 return OS.CallWindowProc (ButtonProc, hwnd, msg, wParam, lParam);
258 static int checkStyle (int style) {
259 style = checkBits (style, SWT.PUSH, SWT.ARROW, SWT.CHECK, SWT.RADIO, SWT.TOGGLE, COMMAND_LINK ? SWT.COMMAND : 0);
260 if ((style & (SWT.PUSH | SWT.TOGGLE)) != 0) {
261 return checkBits (style, SWT.CENTER, SWT.LEFT, SWT.RIGHT, 0, 0, 0);
263 if ((style & (SWT.CHECK | SWT.RADIO)) != 0) {
264 return checkBits (style, SWT.LEFT, SWT.RIGHT, SWT.CENTER, 0, 0, 0);
266 if ((style & SWT.ARROW) != 0) {
267 style |= SWT.NO_FOCUS;
268 return checkBits (style, SWT.UP, SWT.DOWN, SWT.LEFT, SWT.RIGHT, 0, 0);
275 * Feature in Windows. BM_CLICK sends a fake WM_LBUTTONDOWN and
276 * WM_LBUTTONUP in order to click the button. This causes the
277 * application to get unexpected mouse events. The fix is to
278 * ignore mouse events when they are caused by BM_CLICK.
281 OS.SendMessage (handle, OS.BM_CLICK, 0, 0);
285 // TODO: this method ignores the style LEFT, CENTER or RIGHT
286 int computeLeftMargin () {
287 if ((style & (SWT.PUSH | SWT.TOGGLE)) == 0) return MARGIN;
289 if (image != null && text.length () != 0) {
290 Rectangle bounds = image.getBoundsInPixels ();
291 margin += bounds.width + MARGIN * 2;
293 long hDC = OS.GetDC (handle);
294 long newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
295 if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont);
296 char [] buffer = text.toCharArray ();
297 RECT rect = new RECT ();
298 int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE;
299 OS.DrawText (hDC, buffer, buffer.length, rect, flags);
300 margin += rect.right - rect.left;
301 if (newFont != 0) OS.SelectObject (hDC, oldFont);
302 OS.ReleaseDC (handle, hDC);
303 OS.GetClientRect (handle, rect);
304 margin = Math.max (MARGIN, (rect.right - rect.left - margin) / 2);
309 @Override Point computeSizeInPixels (int wHint, int hHint, boolean changed) {
311 int width = 0, height = 0, border = getBorderWidthInPixels ();
312 if ((style & SWT.ARROW) != 0) {
313 if ((style & (SWT.UP | SWT.DOWN)) != 0) {
314 width += OS.GetSystemMetrics (OS.SM_CXVSCROLL);
315 height += OS.GetSystemMetrics (OS.SM_CYVSCROLL);
317 width += OS.GetSystemMetrics (OS.SM_CXHSCROLL);
318 height += OS.GetSystemMetrics (OS.SM_CYHSCROLL);
321 if ((style & SWT.COMMAND) != 0) {
322 SIZE size = new SIZE ();
323 if (wHint != SWT.DEFAULT) {
325 OS.SendMessage (handle, OS.BCM_GETIDEALSIZE, 0, size);
329 OS.SendMessage (handle, OS.BCM_GETIDEALSIZE, 0, size);
333 while (size.cy != height) {
336 OS.SendMessage (handle, OS.BCM_GETIDEALSIZE, 0, size);
341 boolean hasImage = image != null, hasText = true;
344 Rectangle rect = image.getBoundsInPixels ();
346 if (hasText && text.length () != 0) {
349 height = rect.height;
355 long hDC = OS.GetDC (handle);
356 long newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
357 if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont);
358 TEXTMETRIC lptm = new TEXTMETRIC ();
359 OS.GetTextMetrics (hDC, lptm);
360 int length = text.length ();
362 height = Math.max (height, lptm.tmHeight);
364 extra = Math.max (MARGIN * 2, lptm.tmAveCharWidth);
365 char [] buffer = text.toCharArray ();
366 RECT rect = new RECT ();
367 int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE;
368 if ((style & SWT.WRAP) != 0 && wHint != SWT.DEFAULT) {
369 flags = OS.DT_CALCRECT | OS.DT_WORDBREAK;
370 rect.right = wHint - width - 2 * border;
371 if (isRadioOrCheck()) {
372 rect.right -= CHECK_WIDTH + 3;
376 if (!OS.IsAppThemed ()) {
378 if (isRadioOrCheck()) {
383 OS.DrawText (hDC, buffer, buffer.length, rect, flags);
384 width += rect.right - rect.left;
385 height = Math.max (height, rect.bottom - rect.top);
387 if (newFont != 0) OS.SelectObject (hDC, oldFont);
388 OS.ReleaseDC (handle, hDC);
390 if (isRadioOrCheck()) {
391 width += CHECK_WIDTH + extra;
392 height = Math.max (height, CHECK_HEIGHT + 3);
394 if ((style & (SWT.PUSH | SWT.TOGGLE)) != 0) {
395 width += 12; height += 10;
399 if (wHint != SWT.DEFAULT) width = wHint;
400 if (hHint != SWT.DEFAULT) height = hHint;
402 height += border * 2;
403 return new Point (width, height);
407 void createHandle () {
409 * Feature in Windows. When a button is created,
410 * it clears the UI state for all controls in the
411 * shell by sending WM_CHANGEUISTATE with UIS_SET,
412 * UISF_HIDEACCEL and UISF_HIDEFOCUS to the parent.
413 * This is undocumented and unexpected. The fix
414 * is to ignore the WM_CHANGEUISTATE, when sent
415 * from CreateWindowEx().
417 parent.state |= IGNORE_WM_CHANGEUISTATE;
418 super.createHandle ();
419 parent.state &= ~IGNORE_WM_CHANGEUISTATE;
421 if (OS.IsAppThemed ()) {
422 /* Set the theme background.
424 * NOTE: On Vista this causes problems when the tab
425 * key is pressed for push buttons so disable the
426 * theme background drawing for these widgets for
429 if ((style & (SWT.PUSH | SWT.TOGGLE)) == 0) {
430 state |= THEME_BACKGROUND;
434 * Bug in Windows. For some reason, the HBRUSH that
435 * is returned from WM_CTRLCOLOR is misaligned when
436 * the button uses it to draw. If the brush is a solid
437 * color, this does not matter. However, if the brush
438 * contains an image, the image is misaligned. The
439 * fix is to draw the background in WM_CTRLCOLOR.
441 * NOTE: For comctl32.dll 6.0 with themes disabled,
442 * drawing in WM_ERASEBKGND will draw on top of the
443 * text of the control.
445 if ((style & SWT.RADIO) != 0) {
446 state |= DRAW_BACKGROUND;
451 private boolean customBackgroundDrawing() {
452 return buttonBackground != -1 && !isRadioOrCheck();
455 private boolean customDrawing() {
456 return customBackgroundDrawing() || customForegroundDrawing();
459 private boolean customForegroundDrawing() {
460 return foreground != -1 && !text.isEmpty() && OS.IsWindowEnabled(handle);
464 int defaultBackground () {
465 if ((style & (SWT.PUSH | SWT.TOGGLE)) != 0) {
466 return OS.GetSysColor (OS.COLOR_BTNFACE);
468 return super.defaultBackground ();
472 int defaultForeground () {
473 return OS.GetSysColor (OS.COLOR_BTNTEXT);
477 void enableWidget (boolean enabled) {
478 super.enableWidget (enabled);
480 * Bug in Windows. When a Button control is right-to-left and
481 * is disabled, the first pixel of the text is clipped. The fix
482 * is to append a space to the text.
484 if ((style & SWT.RIGHT_TO_LEFT) != 0) {
485 if (!OS.IsAppThemed ()) {
486 int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
487 boolean hasImage = (bits & (OS.BS_BITMAP | OS.BS_ICON)) != 0;
489 String string = enabled ? text : text + " ";
490 TCHAR buffer = new TCHAR (getCodePage (), string, true);
491 OS.SetWindowText (handle, buffer);
496 * Bug in Windows. When a button has the style BS_CHECKBOX
497 * or BS_RADIOBUTTON, is checked, and is displaying both an
498 * image and some text, when BCM_SETIMAGELIST is used to
499 * assign an image list for each of the button states, the
500 * button does not draw properly. When the user drags the
501 * mouse in and out of the button, it draws using a blank
502 * image. The fix is to set the complete image list only
503 * when the button is disabled.
509 * Returns a value which describes the position of the
510 * text or image in the receiver. The value will be one of
511 * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code>
512 * unless the receiver is an <code>ARROW</code> button, in
513 * which case, the alignment will indicate the direction of
514 * the arrow (one of <code>LEFT</code>, <code>RIGHT</code>,
515 * <code>UP</code> or <code>DOWN</code>).
517 * @return the alignment
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 int getAlignment () {
526 if ((style & SWT.ARROW) != 0) {
527 if ((style & SWT.UP) != 0) return SWT.UP;
528 if ((style & SWT.DOWN) != 0) return SWT.DOWN;
529 if ((style & SWT.LEFT) != 0) return SWT.LEFT;
530 if ((style & SWT.RIGHT) != 0) return SWT.RIGHT;
533 if ((style & SWT.LEFT) != 0) return SWT.LEFT;
534 if ((style & SWT.CENTER) != 0) return SWT.CENTER;
535 if ((style & SWT.RIGHT) != 0) return SWT.RIGHT;
540 public Color getBackground () {
541 if (isRadioOrCheck()) {
542 return super.getBackground();
545 if (buttonBackground != -1) {
546 return Color.win32_new (display, buttonBackground, buttonBackgroundAlpha);
548 return Color.win32_new (display, defaultBackground());
551 boolean getDefault () {
552 if ((style & SWT.PUSH) == 0) return false;
553 int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
554 return (bits & OS.BS_DEFPUSHBUTTON) != 0;
558 * Returns <code>true</code> if the receiver is grayed,
559 * and false otherwise. When the widget does not have
560 * the <code>CHECK</code> style, return false.
562 * @return the grayed state of the checkbox
564 * @exception SWTException <ul>
565 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
566 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
571 public boolean getGrayed () {
573 if ((style & SWT.CHECK) == 0) return false;
578 * Returns the receiver's image if it has one, or null
581 * @return the receiver's image
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 Image getImage () {
594 * Returns the widget message. When the widget is created
595 * with the style <code>SWT.COMMAND</code>, the message text
596 * is displayed to provide further information for the user.
598 * @return the widget message
600 * @exception SWTException <ul>
601 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
602 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
607 /*public*/ String getMessage () {
613 String getNameText () {
618 * Returns <code>true</code> if the receiver is selected,
619 * and false otherwise.
621 * When the receiver is of type <code>CHECK</code> or <code>RADIO</code>,
622 * it is selected when it is checked. When it is of type <code>TOGGLE</code>,
623 * it is selected when it is pushed in. If the receiver is of any other type,
624 * this method returns false.
626 * @return the selection state
628 * @exception SWTException <ul>
629 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
630 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
633 public boolean getSelection () {
635 if ((style & (SWT.CHECK | SWT.RADIO | SWT.TOGGLE)) == 0) return false;
640 * Returns the receiver's text, which will be an empty
641 * string if it has never been set or if the receiver is
642 * an <code>ARROW</code> button.
644 * @return the receiver's text
646 * @exception SWTException <ul>
647 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
648 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
651 public String getText () {
653 if ((style & SWT.ARROW) != 0) return "";
657 private boolean isChecked() {
658 long flags = OS.SendMessage (handle, OS.BM_GETCHECK, 0, 0);
659 return flags != OS.BST_UNCHECKED;
662 private boolean isRadioOrCheck() {
663 return (style & (SWT.RADIO | SWT.CHECK)) != 0;
667 boolean isTabItem () {
668 if ((style & SWT.PUSH) != 0) return isTabGroup ();
669 return super.isTabItem ();
673 boolean mnemonicHit (char ch) {
674 if (!setFocus ()) return false;
676 * Feature in Windows. When a radio button gets focus,
677 * it selects the button in WM_SETFOCUS. Therefore, it
678 * is not necessary to click the button or send events
679 * because this has already happened in WM_SETFOCUS.
681 if ((style & SWT.RADIO) == 0) click ();
686 boolean mnemonicMatch (char key) {
687 char mnemonic = findMnemonic (getText ());
688 if (mnemonic == '\0') return false;
689 return Character.toUpperCase (key) == Character.toUpperCase (mnemonic);
693 void releaseWidget () {
694 super.releaseWidget ();
695 if (imageList != null) imageList.dispose ();
697 if (disabledImage != null) disabledImage.dispose ();
698 disabledImage = null;
699 if (image2 != null) image2.dispose ();
706 * Removes the listener from the collection of listeners who will
707 * be notified when the control is selected by the user.
709 * @param listener the listener which should no longer be notified
711 * @exception IllegalArgumentException <ul>
712 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
714 * @exception SWTException <ul>
715 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
716 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
719 * @see SelectionListener
720 * @see #addSelectionListener
722 public void removeSelectionListener (SelectionListener listener) {
724 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
725 if (eventTable == null) return;
726 eventTable.unhook (SWT.Selection, listener);
727 eventTable.unhook (SWT.DefaultSelection,listener);
731 int resolveTextDirection() {
732 return (style & SWT.ARROW) != 0 ? SWT.NONE : BidiUtil.resolveTextDirection(text);
735 void selectRadio () {
737 * This code is intentionally commented. When two groups
738 * of radio buttons with the same parent are separated by
739 * another control, the correct behavior should be that
740 * the two groups act independently. This is consistent
741 * with radio tool and menu items. The commented code
742 * implements this behavior.
745 // Control [] children = parent._getChildren ();
746 // while (index < children.length && children [index] != this) index++;
747 // int i = index - 1;
748 // while (i >= 0 && children [i].setRadioSelection (false)) --i;
749 // int j = index + 1;
750 // while (j < children.length && children [j].setRadioSelection (false)) j++;
751 // setSelection (true);
752 Control [] children = parent._getChildren ();
753 for (int i=0; i<children.length; i++) {
754 Control child = children [i];
755 if (this != child) child.setRadioSelection (false);
761 * Controls how text, images and arrows will be displayed
762 * in the receiver. The argument should be one of
763 * <code>LEFT</code>, <code>RIGHT</code> or <code>CENTER</code>
764 * unless the receiver is an <code>ARROW</code> button, in
765 * which case, the argument indicates the direction of
766 * the arrow (one of <code>LEFT</code>, <code>RIGHT</code>,
767 * <code>UP</code> or <code>DOWN</code>).
769 * @param alignment the new alignment
771 * @exception SWTException <ul>
772 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
773 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
776 public void setAlignment (int alignment) {
778 if ((style & SWT.ARROW) != 0) {
779 if ((style & (SWT.UP | SWT.DOWN | SWT.LEFT | SWT.RIGHT)) == 0) return;
780 style &= ~(SWT.UP | SWT.DOWN | SWT.LEFT | SWT.RIGHT);
781 style |= alignment & (SWT.UP | SWT.DOWN | SWT.LEFT | SWT.RIGHT);
782 OS.InvalidateRect (handle, null, true);
785 if ((alignment & (SWT.LEFT | SWT.RIGHT | SWT.CENTER)) == 0) return;
786 style &= ~(SWT.LEFT | SWT.RIGHT | SWT.CENTER);
787 style |= alignment & (SWT.LEFT | SWT.RIGHT | SWT.CENTER);
788 int oldBits = OS.GetWindowLong (handle, OS.GWL_STYLE), newBits = oldBits;
789 newBits &= ~(OS.BS_LEFT | OS.BS_CENTER | OS.BS_RIGHT);
790 if ((style & SWT.LEFT) != 0) newBits |= OS.BS_LEFT;
791 if ((style & SWT.CENTER) != 0) newBits |= OS.BS_CENTER;
792 if ((style & SWT.RIGHT) != 0) newBits |= OS.BS_RIGHT;
793 if (imageList != null) {
794 BUTTON_IMAGELIST buttonImageList = new BUTTON_IMAGELIST ();
795 buttonImageList.himl = imageList.getHandle ();
796 if (text.length () == 0) {
797 if ((style & SWT.LEFT) != 0) buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_LEFT;
798 if ((style & SWT.CENTER) != 0) buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_CENTER;
799 if ((style & SWT.RIGHT) != 0) buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_RIGHT;
801 buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_LEFT;
802 buttonImageList.margin_left = computeLeftMargin ();
803 buttonImageList.margin_right = MARGIN;
804 newBits &= ~(OS.BS_CENTER | OS.BS_RIGHT);
805 newBits |= OS.BS_LEFT;
807 OS.SendMessage (handle, OS.BCM_SETIMAGELIST, 0, buttonImageList);
809 if (newBits != oldBits) {
810 OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
811 OS.InvalidateRect (handle, null, true);
816 * Sets the button's background color to the color specified
817 * by the argument, or to the default system color for the control
818 * if the argument is null.
820 * Note: This is custom paint operation and only affects {@link SWT#PUSH} and {@link SWT#TOGGLE} buttons. If the native button
821 * has a 3D look an feel (e.g. Windows 7), this method will cause the button to look FLAT irrespective of the state of the
822 * {@link SWT#FLAT} style.
823 * For {@link SWT#CHECK} and {@link SWT#RADIO} buttons, this method delegates to {@link Control#setBackground(Color)}.
825 * @param color the new color (or null)
827 * @exception IllegalArgumentException <ul>
828 * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
830 * @exception SWTException <ul>
831 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
832 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
836 public void setBackground (Color color) {
838 if (isRadioOrCheck()) {
839 super.setBackground(color);
841 setButtonBackground (color);
845 private void setButtonBackground (Color color) {
849 if (color.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
850 pixel = color.handle;
851 alpha = color.getAlpha();
853 if (pixel == buttonBackground && alpha == buttonBackgroundAlpha) return;
854 buttonBackground = pixel;
855 buttonBackgroundAlpha = alpha;
856 updateBackgroundColor ();
859 void setDefault (boolean value) {
860 if ((style & SWT.PUSH) == 0) return;
861 long hwndShell = menuShell ().handle;
862 int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
864 bits |= OS.BS_DEFPUSHBUTTON;
865 OS.SendMessage (hwndShell, OS.DM_SETDEFID, handle, 0);
867 bits &= ~OS.BS_DEFPUSHBUTTON;
868 OS.SendMessage (hwndShell, OS.DM_SETDEFID, 0, 0);
870 OS.SendMessage (handle, OS.BM_SETSTYLE, bits, 1);
874 public boolean setFocus () {
877 * Feature in Windows. When a radio button gets focus,
878 * it selects the button in WM_SETFOCUS. The fix is to
879 * not assign focus to an unselected radio button.
881 if ((style & SWT.RADIO) != 0 && !isChecked () && display.fixFocus) return false;
882 return super.setFocus ();
886 * Sets the receiver's image to the argument, which may be
887 * <code>null</code> indicating that no image should be displayed.
889 * Note that a Button can display an image and text simultaneously
890 * on Windows (starting with XP), GTK+ and OSX. On other platforms,
891 * a Button that has an image and text set into it will display the
892 * image or text that was set most recently.
894 * @param image the image to display on the receiver (may be <code>null</code>)
896 * @exception IllegalArgumentException <ul>
897 * <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li>
899 * @exception SWTException <ul>
900 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
901 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
904 public void setImage (Image image) {
906 if (image != null && image.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
907 if ((style & SWT.ARROW) != 0) return;
909 /* This code is intentionally commented */
910 // if (OS.COMCTL32_MAJOR < 6) {
911 // if (image == null || text.length () != 0) {
920 * Sets the grayed state of the receiver. This state change
921 * only applies if the control was created with the SWT.CHECK
924 * @param grayed the new grayed state
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 setGrayed (boolean grayed) {
935 if ((style & SWT.CHECK) == 0) return;
936 this.grayed = grayed;
937 long flags = OS.SendMessage (handle, OS.BM_GETCHECK, 0, 0);
939 if (flags == OS.BST_CHECKED) updateSelection (OS.BST_INDETERMINATE);
941 if (flags == OS.BST_INDETERMINATE) updateSelection (OS.BST_CHECKED);
946 * Sets the widget message. When the widget is created
947 * with the style <code>SWT.COMMAND</code>, the message text
948 * is displayed to provide further information for the user.
950 * @param message the new message
952 * @exception IllegalArgumentException <ul>
953 * <li>ERROR_NULL_ARGUMENT - if the string is null</li>
955 * @exception SWTException <ul>
956 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
957 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
962 /*public*/ void setMessage (String message) {
964 if (message == null) error (SWT.ERROR_NULL_ARGUMENT);
965 this.message = message;
966 if ((style & SWT.COMMAND) != 0) {
967 int length = message.length ();
968 char [] chars = new char [length + 1];
969 message.getChars(0, length, chars, 0);
970 OS.SendMessage (handle, OS.BCM_SETNOTE, 0, chars);
975 boolean setRadioFocus (boolean tabbing) {
976 if ((style & SWT.RADIO) == 0 || !getSelection ()) return false;
977 return tabbing ? setTabItemFocus () : setFocus ();
981 boolean setRadioSelection (boolean value) {
982 if ((style & SWT.RADIO) == 0) return false;
983 if (getSelection () != value) {
984 setSelection (value);
985 sendSelectionEvent (SWT.Selection);
991 * Sets the selection state of the receiver, if it is of type <code>CHECK</code>,
992 * <code>RADIO</code>, or <code>TOGGLE</code>.
995 * When the receiver is of type <code>CHECK</code> or <code>RADIO</code>,
996 * it is selected when it is checked. When it is of type <code>TOGGLE</code>,
997 * it is selected when it is pushed in.
999 * @param selected the new selection state
1001 * @exception SWTException <ul>
1002 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1003 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1006 public void setSelection (boolean selected) {
1008 if ((style & (SWT.CHECK | SWT.RADIO | SWT.TOGGLE)) == 0) return;
1009 int flags = selected ? OS.BST_CHECKED : OS.BST_UNCHECKED;
1010 if ((style & SWT.CHECK) != 0) {
1011 if (selected && grayed) flags = OS.BST_INDETERMINATE;
1013 updateSelection (flags);
1017 * Sets the receiver's text.
1019 * This method sets the button label. The label may include
1020 * the mnemonic character but must not contain line delimiters.
1023 * Mnemonics are indicated by an '&' that causes the next
1024 * character to be the mnemonic. When the user presses a
1025 * key sequence that matches the mnemonic, a selection
1026 * event occurs. On most platforms, the mnemonic appears
1027 * underlined but may be emphasized in a platform specific
1028 * manner. The mnemonic indicator character '&' can be
1029 * escaped by doubling it in the string, causing a single
1030 * '&' to be displayed.
1032 * Note that a Button can display an image and text simultaneously
1033 * on Windows (starting with XP), GTK+ and OSX. On other platforms,
1034 * a Button that has an image and text set into it will display the
1035 * image or text that was set most recently.
1037 * Also note, if control characters like '\n', '\t' etc. are used
1038 * in the string, then the behavior is platform dependent.
1040 * @param string the new text
1042 * @exception IllegalArgumentException <ul>
1043 * <li>ERROR_NULL_ARGUMENT - if the text is null</li>
1045 * @exception SWTException <ul>
1046 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1047 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1050 public void setText (String string) {
1052 if (string == null) error (SWT.ERROR_NULL_ARGUMENT);
1053 if ((style & SWT.ARROW) != 0) return;
1055 /* This code is intentionally commented */
1056 // if (OS.COMCTL32_MAJOR < 6) {
1057 // if (text.length () == 0 && image != null) {
1058 // _setImage (image);
1066 boolean updateTextDirection(int textDirection) {
1067 if (super.updateTextDirection(textDirection)) {
1068 // TODO: Keep for now, to follow up
1069 // int flags = SWT.RIGHT_TO_LEFT | SWT.LEFT_TO_RIGHT;
1070 // style &= ~SWT.MIRRORED;
1072 // style |= textDirection & flags;
1073 // updateOrientation ();
1074 // checkMirrored ();
1080 void updateImageList () {
1081 if (imageList != null) {
1082 BUTTON_IMAGELIST buttonImageList = new BUTTON_IMAGELIST ();
1083 OS.SendMessage (handle, OS.BCM_GETIMAGELIST, 0, buttonImageList);
1084 if (imageList != null) imageList.dispose ();
1085 imageList = new ImageList (style & SWT.RIGHT_TO_LEFT);
1086 if (OS.IsWindowEnabled (handle)) {
1087 imageList.add (image);
1089 if (disabledImage != null) disabledImage.dispose ();
1090 disabledImage = new Image (display, image, SWT.IMAGE_DISABLE);
1091 imageList.add (disabledImage);
1093 buttonImageList.himl = imageList.getHandle ();
1094 OS.SendMessage (handle, OS.BCM_SETIMAGELIST, 0, buttonImageList);
1096 * Bug in Windows. Under certain cirumstances yet to be
1097 * isolated, BCM_SETIMAGELIST does not redraw the control
1098 * when an image is set. The fix is to force a redraw.
1100 OS.InvalidateRect (handle, null, true);
1105 void updateOrientation () {
1106 super.updateOrientation ();
1110 void updateSelection (int flags) {
1111 if (flags != OS.SendMessage (handle, OS.BM_GETCHECK, 0, 0)) {
1113 * Feature in Windows. When BM_SETCHECK is used
1114 * to set the checked state of a radio or check
1115 * button, it sets the WS_TABSTOP style. This
1116 * is undocumented and unwanted. The fix is
1117 * to save and restore the window style bits.
1119 int bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
1120 if ((style & SWT.CHECK) != 0) {
1121 if (flags == OS.BST_INDETERMINATE) {
1122 bits &= ~OS.BS_CHECKBOX;
1123 bits |= OS.BS_3STATE;
1125 bits |= OS.BS_CHECKBOX;
1126 bits &= ~OS.BS_3STATE;
1128 if (bits != OS.GetWindowLong (handle, OS.GWL_STYLE)) {
1129 OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
1132 OS.SendMessage (handle, OS.BM_SETCHECK, flags, 0);
1133 if (bits != OS.GetWindowLong (handle, OS.GWL_STYLE)) {
1134 OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
1140 int widgetStyle () {
1141 int bits = super.widgetStyle ();
1142 if ((style & SWT.FLAT) != 0) bits |= OS.BS_FLAT;
1143 if ((style & SWT.ARROW) != 0) return bits | OS.BS_OWNERDRAW;
1144 if ((style & SWT.LEFT) != 0) bits |= OS.BS_LEFT;
1145 if ((style & SWT.CENTER) != 0) bits |= OS.BS_CENTER;
1146 if ((style & SWT.RIGHT) != 0) bits |= OS.BS_RIGHT;
1147 if ((style & SWT.WRAP) != 0) bits |= OS.BS_MULTILINE;
1148 if ((style & SWT.PUSH) != 0) return bits | OS.BS_PUSHBUTTON | OS.WS_TABSTOP;
1149 if ((style & SWT.CHECK) != 0) return bits | OS.BS_CHECKBOX | OS.WS_TABSTOP;
1150 if ((style & SWT.RADIO) != 0) return bits | OS.BS_RADIOBUTTON;
1151 if ((style & SWT.TOGGLE) != 0) return bits | OS.BS_PUSHLIKE | OS.BS_CHECKBOX | OS.WS_TABSTOP;
1152 if ((style & SWT.COMMAND) != 0) return bits | OS.BS_COMMANDLINK | OS.WS_TABSTOP;
1153 return bits | OS.BS_PUSHBUTTON | OS.WS_TABSTOP;
1157 TCHAR windowClass () {
1162 long windowProc () {
1167 LRESULT WM_GETDLGCODE (long wParam, long lParam) {
1168 LRESULT result = super.WM_GETDLGCODE (wParam, lParam);
1169 if (result != null) return result;
1170 if ((style & SWT.ARROW) != 0) {
1171 return new LRESULT (OS.DLGC_STATIC);
1177 LRESULT WM_GETOBJECT (long wParam, long lParam) {
1179 * Ensure that there is an accessible object created for this
1180 * control because support for radio button position in group
1181 * accessibility is implemented in the accessibility package.
1183 if ((style & SWT.RADIO) != 0) {
1184 if (accessible == null) accessible = new_Accessible (this);
1186 return super.WM_GETOBJECT (wParam, lParam);
1190 LRESULT WM_KILLFOCUS (long wParam, long lParam) {
1191 LRESULT result = super.WM_KILLFOCUS (wParam, lParam);
1192 if ((style & SWT.PUSH) != 0 && getDefault ()) {
1193 menuShell ().setDefaultButton (null, false);
1199 LRESULT WM_LBUTTONDOWN (long wParam, long lParam) {
1200 if (ignoreMouse) return null;
1201 return super.WM_LBUTTONDOWN (wParam, lParam);
1205 LRESULT WM_LBUTTONUP (long wParam, long lParam) {
1206 if (ignoreMouse) return null;
1207 return super.WM_LBUTTONUP (wParam, lParam);
1211 LRESULT WM_SETFOCUS (long wParam, long lParam) {
1213 * Feature in Windows. When Windows sets focus to
1214 * a radio button, it sets the WS_TABSTOP style.
1215 * This is undocumented and unwanted. The fix is
1216 * to save and restore the window style bits.
1219 if ((style & SWT.RADIO) != 0) {
1220 bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
1222 LRESULT result = super.WM_SETFOCUS (wParam, lParam);
1223 if ((style & SWT.RADIO) != 0) {
1224 OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
1226 if ((style & SWT.PUSH) != 0) {
1227 menuShell ().setDefaultButton (this, false);
1233 LRESULT WM_SIZE (long wParam, long lParam) {
1234 LRESULT result = super.WM_SIZE (wParam, lParam);
1235 if (result != null) return result;
1236 if ((style & (SWT.PUSH | SWT.TOGGLE)) != 0) {
1237 if (imageList != null && text.length () != 0) {
1238 BUTTON_IMAGELIST buttonImageList = new BUTTON_IMAGELIST ();
1239 OS.SendMessage (handle, OS.BCM_GETIMAGELIST, 0, buttonImageList);
1240 buttonImageList.uAlign = OS.BUTTON_IMAGELIST_ALIGN_LEFT;
1241 buttonImageList.margin_left = computeLeftMargin ();
1242 buttonImageList.margin_right = MARGIN;
1243 OS.SendMessage (handle, OS.BCM_SETIMAGELIST, 0, buttonImageList);
1250 LRESULT WM_SYSCOLORCHANGE (long wParam, long lParam) {
1251 LRESULT result = super.WM_SYSCOLORCHANGE (wParam, lParam);
1252 if (result != null) return result;
1253 if (image2 != null) _setImage (image);
1258 LRESULT WM_UPDATEUISTATE (long wParam, long lParam) {
1259 LRESULT result = super.WM_UPDATEUISTATE (wParam, lParam);
1260 if (result != null) return result;
1262 * Feature in Windows. When WM_UPDATEUISTATE is sent to
1263 * a button, it sends WM_CTLCOLORBTN to get the foreground
1264 * and background. If drawing happens in WM_CTLCOLORBTN,
1265 * it will overwrite the contents of the control. The
1266 * fix is draw the button without drawing the background
1267 * and avoid the button window proc.
1269 * NOTE: This only happens for radio, check and toggle
1272 if ((style & (SWT.RADIO | SWT.CHECK | SWT.TOGGLE)) != 0) {
1273 boolean redraw = findImageControl () != null;
1275 if ((state & THEME_BACKGROUND) != 0) {
1276 if (OS.IsAppThemed ()) {
1277 redraw = findThemeControl () != null;
1280 if (!redraw) redraw = findBackgroundControl () != null;
1283 OS.InvalidateRect (handle, null, false);
1284 long code = OS.DefWindowProc (handle, OS.WM_UPDATEUISTATE, wParam, lParam);
1285 return new LRESULT (code);
1289 * Feature in Windows. Push and toggle buttons draw directly
1290 * in WM_UPDATEUISTATE rather than damaging and drawing later
1291 * in WM_PAINT. This means that clients who hook WM_PAINT
1292 * expecting to get all the drawing will not. The fix is to
1293 * redraw the control when paint events are hooked.
1295 if ((style & (SWT.PUSH | SWT.TOGGLE)) != 0) {
1296 if (hooks (SWT.Paint) || filters (SWT.Paint) || customDrawing()) {
1297 OS.InvalidateRect (handle, null, true);
1304 LRESULT wmCommandChild (long wParam, long lParam) {
1305 int code = OS.HIWORD (wParam);
1308 case OS.BN_DOUBLECLICKED:
1309 if ((style & (SWT.CHECK | SWT.TOGGLE)) != 0) {
1310 setSelection (!getSelection ());
1312 if ((style & SWT.RADIO) != 0) {
1313 if ((parent.getStyle () & SWT.NO_RADIO_GROUP) != 0) {
1314 setSelection (!getSelection ());
1320 sendSelectionEvent (SWT.Selection);
1322 return super.wmCommandChild (wParam, lParam);
1326 LRESULT wmNotifyChild (NMHDR hdr, long wParam, long lParam) {
1328 case OS.NM_CUSTOMDRAW:
1329 // this message will not appear for owner-draw buttons (currently the ARROW button).
1331 NMCUSTOMDRAW nmcd = new NMCUSTOMDRAW ();
1332 OS.MoveMemory (nmcd, lParam, NMCUSTOMDRAW.sizeof);
1334 switch (nmcd.dwDrawStage) {
1335 case OS.CDDS_PREPAINT: {
1336 // buttons are ignoring SetBkColor, SetBkMode and SetTextColor
1337 if (customBackgroundDrawing()) {
1338 int pixel = buttonBackground;
1339 if ((nmcd.uItemState & OS.CDIS_SELECTED) != 0) {
1340 pixel = getDifferentColor(buttonBackground);
1341 } else if ((nmcd.uItemState & OS.CDIS_HOT) != 0) {
1342 pixel = getSlightlyDifferentColor(buttonBackground);
1344 if ((style & SWT.TOGGLE) != 0 && isChecked()) {
1345 pixel = getDifferentColor(buttonBackground);
1347 RECT rect = new RECT ();
1348 OS.SetRect (rect, nmcd.left+2, nmcd.top+2, nmcd.right-2, nmcd.bottom-2);
1349 long brush = OS.CreateSolidBrush(pixel);
1350 OS.FillRect(nmcd.hdc, rect, brush);
1351 OS.DeleteObject(brush);
1353 if (customForegroundDrawing()) {
1355 * Check-box/Radio buttons are native widget which honors
1356 * the Win OS zoom level for both 'Square' and 'Text' part
1357 * [Note: By-design SWT doesn't control native auto-scaling]
1358 * Hence, custom fore-ground draw logic should auto-scale
1359 * text-padding as per OS Native DPI level to fix bug 506371
1361 int radioOrCheckTextPadding = DPIUtil.autoScaleUpUsingNativeDPI(16);
1362 int border = isRadioOrCheck() ? 0 : 3;
1363 int left = nmcd.left + border;
1364 int right = nmcd.right - border;
1365 if (image != null) {
1366 GCData data = new GCData();
1367 data.device = display;
1368 GC gc = GC.win32_new (nmcd.hdc, data);
1370 int margin = computeLeftMargin();
1371 int imageWidth = image.getBoundsInPixels().width;
1372 left += (imageWidth + (isRadioOrCheck() ? 2 * MARGIN : MARGIN)); // for SWT.RIGHT_TO_LEFT right and left are inverted
1374 int x = margin + (isRadioOrCheck() ? radioOrCheckTextPadding : 3);
1375 int y = Math.max (0, (nmcd.bottom - image.getBoundsInPixels().height) / 2);
1376 gc.drawImage (image, DPIUtil.autoScaleDown(x), DPIUtil.autoScaleDown(y));
1380 left += isRadioOrCheck() ? radioOrCheckTextPadding : 0;
1381 RECT textRect = new RECT ();
1382 OS.SetRect (textRect, left, nmcd.top + border, right, nmcd.bottom - border);
1385 char [] buffer = text.toCharArray ();
1387 if ((style & SWT.WRAP) != 0) {
1388 flags |= OS.DT_WORDBREAK;
1389 if (!isRadioOrCheck() && image != null) {
1390 textRect.right -= MARGIN;
1393 flags |= OS.DT_SINGLELINE; // TODO: this always draws the prefix
1395 OS.DrawText(nmcd.hdc, buffer, buffer.length, textRect, flags | OS.DT_CALCRECT);
1396 OS.OffsetRect(textRect, 0, Math.max(0, (nmcd.bottom - textRect.bottom - border) / 2));
1397 if (image != null) {
1398 // The default button with an image doesn't respect the text alignment. So we do the same for styled buttons.
1399 flags |= OS.DT_LEFT;
1400 if (!isRadioOrCheck()) {
1401 OS.OffsetRect(textRect, Math.max(MARGIN, (right - textRect.right) / 2 + 1), 0);
1403 } else if ((style & SWT.LEFT) != 0) {
1404 flags |= OS.DT_LEFT;
1405 } else if ((style & SWT.RIGHT) != 0) {
1406 flags |= OS.DT_RIGHT;
1407 OS.OffsetRect(textRect, right - textRect.right, 0);
1409 flags |= OS.DT_CENTER;
1410 OS.OffsetRect(textRect, (right - textRect.right) / 2, 0);
1412 OS.SetBkMode(nmcd.hdc, OS.TRANSPARENT);
1413 OS.SetTextColor(nmcd.hdc, foreground);
1414 OS.DrawText(nmcd.hdc, buffer, buffer.length, textRect, flags);
1417 if ((nmcd.uItemState & OS.CDIS_FOCUS) != 0) {
1418 RECT focusRect = new RECT ();
1419 if (isRadioOrCheck()) {
1420 if (text.length() > 0) {
1421 OS.SetRect(focusRect, textRect.left-1, textRect.top, Math.min(nmcd.right, textRect.right+1), Math.min(nmcd.bottom, textRect.bottom+1));
1424 * With custom foreground, draw focus rectangle for CheckBox
1425 * and Radio buttons considering the native text padding
1426 * value(which is DPI aware). See bug 508141 for details.
1428 OS.SetRect (focusRect, nmcd.left+1+radioOrCheckTextPadding, nmcd.top, nmcd.right-2, nmcd.bottom-1);
1431 OS.SetRect (focusRect, nmcd.left+2, nmcd.top+3, nmcd.right-2, nmcd.bottom-3);
1433 OS.DrawFocusRect(nmcd.hdc, focusRect);
1435 return new LRESULT (OS.CDRF_SKIPDEFAULT);
1437 return new LRESULT (OS.CDRF_DODEFAULT);
1442 return super.wmNotifyChild (hdr, wParam, lParam);
1446 LRESULT wmDrawChild (long wParam, long lParam) {
1447 if ((style & SWT.ARROW) == 0) return super.wmDrawChild (wParam, lParam);
1448 DRAWITEMSTRUCT struct = new DRAWITEMSTRUCT ();
1449 OS.MoveMemory (struct, lParam, DRAWITEMSTRUCT.sizeof);
1450 RECT rect = new RECT ();
1451 OS.SetRect (rect, struct.left, struct.top, struct.right, struct.bottom);
1452 if (OS.IsAppThemed ()) {
1453 int iStateId = OS.ABS_LEFTNORMAL;
1454 switch (style & (SWT.UP | SWT.DOWN | SWT.LEFT | SWT.RIGHT)) {
1455 case SWT.UP: iStateId = OS.ABS_UPNORMAL; break;
1456 case SWT.DOWN: iStateId = OS.ABS_DOWNNORMAL; break;
1457 case SWT.LEFT: iStateId = OS.ABS_LEFTNORMAL; break;
1458 case SWT.RIGHT: iStateId = OS.ABS_RIGHTNORMAL; break;
1461 * Feature in Windows. DrawThemeBackground() does not mirror the drawing.
1462 * The fix is switch left to right and right to left.
1464 if ((style & SWT.MIRRORED) != 0) {
1465 if ((style & (SWT.LEFT | SWT.RIGHT)) != 0) {
1466 iStateId = iStateId == OS.ABS_RIGHTNORMAL ? OS.ABS_LEFTNORMAL : OS.ABS_RIGHTNORMAL;
1470 * NOTE: The normal, hot, pressed and disabled state is
1471 * computed relying on the fact that the increment between
1472 * the direction states is invariant (always separated by 4).
1474 if (!getEnabled ()) iStateId += OS.ABS_UPDISABLED - OS.ABS_UPNORMAL;
1475 if ((struct.itemState & OS.ODS_SELECTED) != 0) iStateId += OS.ABS_UPPRESSED - OS.ABS_UPNORMAL;
1476 OS.DrawThemeBackground (display.hScrollBarTheme (), struct.hDC, OS.SBP_ARROWBTN, iStateId, rect, null);
1478 int uState = OS.DFCS_SCROLLLEFT;
1479 switch (style & (SWT.UP | SWT.DOWN | SWT.LEFT | SWT.RIGHT)) {
1480 case SWT.UP: uState = OS.DFCS_SCROLLUP; break;
1481 case SWT.DOWN: uState = OS.DFCS_SCROLLDOWN; break;
1482 case SWT.LEFT: uState = OS.DFCS_SCROLLLEFT; break;
1483 case SWT.RIGHT: uState = OS.DFCS_SCROLLRIGHT; break;
1485 if (!getEnabled ()) uState |= OS.DFCS_INACTIVE;
1486 if ((style & SWT.FLAT) == SWT.FLAT) uState |= OS.DFCS_FLAT;
1487 if ((struct.itemState & OS.ODS_SELECTED) != 0) uState |= OS.DFCS_PUSHED;
1488 OS.DrawFrameControl (struct.hDC, rect, OS.DFC_SCROLL, uState);