]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.eclipse.swt.win32.win32.x86_64/src/org/eclipse/swt/widgets/Button.java
feb543a561b590e78bdb61c253bf9024f7e7e376
[simantics/platform.git] / bundles / org.eclipse.swt.win32.win32.x86_64 / src / org / eclipse / swt / widgets / Button.java
1 /*******************************************************************************
2  * Copyright (c) 2000, 2019 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Conrad Groth - Bug 23837 [FEEP] Button, do not respect foreground and background color on Windows
14  *******************************************************************************/
15 package org.eclipse.swt.widgets;
16
17
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.*;
23
24 /**
25  * Instances of this class represent a selectable user interface object that
26  * issues notification when pressed and released.
27  * <dl>
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>
32  * <dd>Selection</dd>
33  * </dl>
34  * <p>
35  * Note: Only one of the styles ARROW, CHECK, PUSH, RADIO, and TOGGLE
36  * may be specified.
37  * </p><p>
38  * Note: Only one of the styles LEFT, RIGHT, and CENTER may be specified.
39  * </p><p>
40  * Note: Only one of the styles UP, DOWN, LEFT, and RIGHT may be specified
41  * when the ARROW style is specified.
42  * </p><p>
43  * IMPORTANT: This class is <em>not</em> intended to be subclassed.
44  * </p>
45  *
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.
50  */
51 public class Button extends Control {
52         String text = "", message = "";
53         Image image, image2, disabledImage;
54         ImageList imageList;
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);
65         static {
66                 long hBitmap = OS.LoadBitmap (0, OS.OBM_CHECKBOXES);
67                 if (hBitmap == 0) {
68                         CHECK_WIDTH = OS.GetSystemMetrics (OS.SM_CXVSCROLL);
69                         CHECK_HEIGHT = OS.GetSystemMetrics (OS.SM_CYVSCROLL);
70                 } else {
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;
76                 }
77                 WNDCLASS lpWndClass = new WNDCLASS ();
78                 OS.GetClassInfo (0, ButtonClass, lpWndClass);
79                 ButtonProc = lpWndClass.lpfnWndProc;
80         }
81
82 /**
83  * Constructs a new instance of this class given its parent
84  * and a style value describing its behavior and appearance.
85  * <p>
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.
93  * </p>
94  *
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
97  *
98  * @exception IllegalArgumentException <ul>
99  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
100  * </ul>
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>
104  * </ul>
105  *
106  * @see SWT#ARROW
107  * @see SWT#CHECK
108  * @see SWT#PUSH
109  * @see SWT#RADIO
110  * @see SWT#TOGGLE
111  * @see SWT#FLAT
112  * @see SWT#UP
113  * @see SWT#DOWN
114  * @see SWT#LEFT
115  * @see SWT#RIGHT
116  * @see SWT#CENTER
117  * @see Widget#checkSubclass
118  * @see Widget#getStyle
119  */
120 public Button (Composite parent, int style) {
121         super (parent, checkStyle (style));
122 }
123
124 void _setImage (Image image) {
125         if ((style & SWT.COMMAND) != 0) return;
126         if (imageList != null) imageList.dispose ();
127         imageList = null;
128         if (image != null) {
129                 imageList = new ImageList (style & SWT.RIGHT_TO_LEFT);
130                 if (OS.IsWindowEnabled (handle)) {
131                         imageList.add (image);
132                 } else {
133                         if (disabledImage != null) disabledImage.dispose ();
134                         disabledImage = new Image (display, image, SWT.IMAGE_DISABLE);
135                         imageList.add (disabledImage);
136                 }
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;
148                 } else {
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;
154                 }
155                 if (newBits != oldBits) {
156                         OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
157                         OS.InvalidateRect (handle, null, true);
158                 }
159                 OS.SendMessage (handle, OS.BCM_SETIMAGELIST, 0, buttonImageList);
160         } else {
161                 OS.SendMessage (handle, OS.BCM_SETIMAGELIST, 0, 0);
162         }
163         /*
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.
167         */
168         OS.InvalidateRect (handle, null, true);
169 }
170
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;
184                 } else {
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;
190                 }
191                 OS.SendMessage (handle, OS.BCM_SETIMAGELIST, 0, buttonImageList);
192         }
193         if (newBits != oldBits) {
194                 OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
195                 OS.InvalidateRect (handle, null, true);
196         }
197         /*
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.
201         */
202         if ((style & SWT.RIGHT_TO_LEFT) != 0) {
203                 if (!OS.IsAppThemed ()) {
204                         text = OS.IsWindowEnabled (handle) ? text : text + " ";
205                 }
206         }
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);
211         }
212 }
213
214 /**
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>
218  * interface.
219  * <p>
220  * <code>widgetSelected</code> is called when the control is selected by the user.
221  * <code>widgetDefaultSelected</code> is not called.
222  * </p>
223  * <p>
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.
228  * </p>
229  *
230  * @param listener the listener which should be notified
231  *
232  * @exception IllegalArgumentException <ul>
233  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
234  * </ul>
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>
238  * </ul>
239  *
240  * @see SelectionListener
241  * @see #removeSelectionListener
242  * @see SelectionEvent
243  */
244 public void addSelectionListener (SelectionListener listener) {
245         checkWidget ();
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);
250 }
251
252 @Override
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);
256 }
257
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);
262         }
263         if ((style & (SWT.CHECK | SWT.RADIO)) != 0) {
264                 return checkBits (style, SWT.LEFT, SWT.RIGHT, SWT.CENTER, 0, 0, 0);
265         }
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);
269         }
270         return style;
271 }
272
273 void click () {
274         /*
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.
279         */
280         ignoreMouse = true;
281         OS.SendMessage (handle, OS.BM_CLICK, 0, 0);
282         ignoreMouse = false;
283 }
284
285 // TODO: this method ignores the style LEFT, CENTER or RIGHT
286 int computeLeftMargin () {
287         if ((style & (SWT.PUSH | SWT.TOGGLE)) == 0) return MARGIN;
288         int margin = 0;
289         if (image != null && text.length () != 0) {
290                 Rectangle bounds = image.getBoundsInPixels ();
291                 margin += bounds.width + MARGIN * 2;
292                 long oldFont = 0;
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);
305         }
306         return margin;
307 }
308
309 @Override Point computeSizeInPixels (int wHint, int hHint, boolean changed) {
310         checkWidget ();
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);
316                 } else {
317                         width += OS.GetSystemMetrics (OS.SM_CXHSCROLL);
318                         height += OS.GetSystemMetrics (OS.SM_CYHSCROLL);
319                 }
320         } else {
321                 if ((style & SWT.COMMAND) != 0) {
322                         SIZE size = new SIZE ();
323                         if (wHint != SWT.DEFAULT) {
324                                 size.cx = wHint;
325                                 OS.SendMessage (handle, OS.BCM_GETIDEALSIZE, 0, size);
326                                 width = size.cx;
327                                 height = size.cy;
328                         } else {
329                                 OS.SendMessage (handle, OS.BCM_GETIDEALSIZE, 0, size);
330                                 width = size.cy;
331                                 height = size.cy;
332                                 size.cy = 0;
333                                 while (size.cy != height) {
334                                         size.cx = width++;
335                                         size.cy = 0;
336                                         OS.SendMessage (handle, OS.BCM_GETIDEALSIZE, 0, size);
337                                 }
338                         }
339                 } else {
340                         int extra = 0;
341                         boolean hasImage = image != null, hasText = true;
342                         if (hasImage) {
343                                 if (image != null) {
344                                         Rectangle rect = image.getBoundsInPixels ();
345                                         width = rect.width;
346                                         if (hasText && text.length () != 0) {
347                                                 width += MARGIN * 2;
348                                         }
349                                         height = rect.height;
350                                         extra = MARGIN * 2;
351                                 }
352                         }
353                         if (hasText) {
354                                 long oldFont = 0;
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 ();
361                                 if (length == 0) {
362                                         height = Math.max (height, lptm.tmHeight);
363                                 } else {
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;
373                                                 } else {
374                                                         rect.right -= 6;
375                                                 }
376                                                 if (!OS.IsAppThemed ()) {
377                                                         rect.right -= 2;
378                                                         if (isRadioOrCheck()) {
379                                                                 rect.right -= 2;
380                                                         }
381                                                 }
382                                         }
383                                         OS.DrawText (hDC, buffer, buffer.length, rect, flags);
384                                         width += rect.right - rect.left;
385                                         height = Math.max (height, rect.bottom - rect.top);
386                                 }
387                                 if (newFont != 0) OS.SelectObject (hDC, oldFont);
388                                 OS.ReleaseDC (handle, hDC);
389                         }
390                         if (isRadioOrCheck()) {
391                                 width += CHECK_WIDTH + extra;
392                                 height = Math.max (height, CHECK_HEIGHT + 3);
393                         }
394                         if ((style & (SWT.PUSH | SWT.TOGGLE)) != 0) {
395                                 width += 12;  height += 10;
396                         }
397                 }
398         }
399         if (wHint != SWT.DEFAULT) width = wHint;
400         if (hHint != SWT.DEFAULT) height = hHint;
401         width += border * 2;
402         height += border * 2;
403         return new Point (width, height);
404 }
405
406 @Override
407 void createHandle () {
408         /*
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().
416         */
417         parent.state |= IGNORE_WM_CHANGEUISTATE;
418         super.createHandle ();
419         parent.state &= ~IGNORE_WM_CHANGEUISTATE;
420
421         if (OS.IsAppThemed ()) {
422                 /* Set the theme background.
423                 *
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
427                 * now.
428                 */
429                 if ((style & (SWT.PUSH | SWT.TOGGLE)) == 0) {
430                         state |= THEME_BACKGROUND;
431                 }
432
433                 /*
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.
440                 *
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.
444                 */
445                 if ((style & SWT.RADIO) != 0) {
446                         state |= DRAW_BACKGROUND;
447                 }
448         }
449 }
450
451 private boolean customBackgroundDrawing() {
452         return buttonBackground != -1 && !isRadioOrCheck();
453 }
454
455 private boolean customDrawing() {
456         return customBackgroundDrawing() || customForegroundDrawing();
457 }
458
459 private boolean customForegroundDrawing() {
460         return foreground != -1 && !text.isEmpty() && OS.IsWindowEnabled(handle);
461 }
462
463 @Override
464 int defaultBackground () {
465         if ((style & (SWT.PUSH | SWT.TOGGLE)) != 0) {
466                 return OS.GetSysColor (OS.COLOR_BTNFACE);
467         }
468         return super.defaultBackground ();
469 }
470
471 @Override
472 int defaultForeground () {
473         return OS.GetSysColor (OS.COLOR_BTNTEXT);
474 }
475
476 @Override
477 void enableWidget (boolean enabled) {
478         super.enableWidget (enabled);
479         /*
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.
483         */
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;
488                         if (!hasImage) {
489                                 String string = enabled ? text : text + " ";
490                                 TCHAR buffer = new TCHAR (getCodePage (), string, true);
491                                 OS.SetWindowText (handle, buffer);
492                         }
493                 }
494         }
495         /*
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.
504         */
505         updateImageList ();
506 }
507
508 /**
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>).
516  *
517  * @return the alignment
518  *
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>
522  * </ul>
523  */
524 public int getAlignment () {
525         checkWidget ();
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;
531                 return SWT.UP;
532         }
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;
536         return SWT.LEFT;
537 }
538
539 @Override
540 public Color getBackground () {
541         if (isRadioOrCheck()) {
542                 return super.getBackground();
543         }
544         checkWidget ();
545         if (buttonBackground != -1) {
546                 return Color.win32_new (display, buttonBackground, buttonBackgroundAlpha);
547         }
548         return Color.win32_new (display, defaultBackground());
549 }
550
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;
555 }
556
557 /**
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.
561  *
562  * @return the grayed state of the checkbox
563  *
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>
567  * </ul>
568  *
569  * @since 3.4
570  */
571 public boolean getGrayed () {
572         checkWidget();
573         if ((style & SWT.CHECK) == 0) return false;
574         return grayed;
575 }
576
577 /**
578  * Returns the receiver's image if it has one, or null
579  * if it does not.
580  *
581  * @return the receiver's image
582  *
583  * @exception SWTException <ul>
584  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
585  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
586  * </ul>
587  */
588 public Image getImage () {
589         checkWidget ();
590         return image;
591 }
592
593 /**
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.
597  *
598  * @return the widget message
599  *
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>
603  * </ul>
604  *
605  * @since 3.3
606  */
607 /*public*/ String getMessage () {
608         checkWidget ();
609         return message;
610 }
611
612 @Override
613 String getNameText () {
614         return getText ();
615 }
616
617 /**
618  * Returns <code>true</code> if the receiver is selected,
619  * and false otherwise.
620  * <p>
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.
625  *
626  * @return the selection state
627  *
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>
631  * </ul>
632  */
633 public boolean getSelection () {
634         checkWidget ();
635         if ((style & (SWT.CHECK | SWT.RADIO | SWT.TOGGLE)) == 0) return false;
636         return isChecked();
637 }
638
639 /**
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.
643  *
644  * @return the receiver's text
645  *
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>
649  * </ul>
650  */
651 public String getText () {
652         checkWidget ();
653         if ((style & SWT.ARROW) != 0) return "";
654         return text;
655 }
656
657 private boolean isChecked() {
658         long flags = OS.SendMessage (handle, OS.BM_GETCHECK, 0, 0);
659         return flags != OS.BST_UNCHECKED;
660 }
661
662 private boolean isRadioOrCheck() {
663         return (style & (SWT.RADIO | SWT.CHECK)) != 0;
664 }
665
666 @Override
667 boolean isTabItem () {
668         if ((style & SWT.PUSH) != 0) return isTabGroup ();
669         return super.isTabItem ();
670 }
671
672 @Override
673 boolean mnemonicHit (char ch) {
674         if (!setFocus ()) return false;
675         /*
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.
680         */
681         if ((style & SWT.RADIO) == 0) click ();
682         return true;
683 }
684
685 @Override
686 boolean mnemonicMatch (char key) {
687         char mnemonic = findMnemonic (getText ());
688         if (mnemonic == '\0') return false;
689         return Character.toUpperCase (key) == Character.toUpperCase (mnemonic);
690 }
691
692 @Override
693 void releaseWidget () {
694         super.releaseWidget ();
695         if (imageList != null) imageList.dispose ();
696         imageList = null;
697         if (disabledImage != null) disabledImage.dispose ();
698         disabledImage = null;
699         if (image2 != null) image2.dispose ();
700         image2 = null;
701         text = null;
702         image = null;
703 }
704
705 /**
706  * Removes the listener from the collection of listeners who will
707  * be notified when the control is selected by the user.
708  *
709  * @param listener the listener which should no longer be notified
710  *
711  * @exception IllegalArgumentException <ul>
712  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
713  * </ul>
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>
717  * </ul>
718  *
719  * @see SelectionListener
720  * @see #addSelectionListener
721  */
722 public void removeSelectionListener (SelectionListener listener) {
723         checkWidget ();
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);
728 }
729
730 @Override
731 int resolveTextDirection() {
732         return (style & SWT.ARROW) != 0 ? SWT.NONE : BidiUtil.resolveTextDirection(text);
733 }
734
735 void selectRadio () {
736         /*
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.
743         */
744 //      int index = 0;
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);
756         }
757         setSelection (true);
758 }
759
760 /**
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>).
768  *
769  * @param alignment the new alignment
770  *
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>
774  * </ul>
775  */
776 public void setAlignment (int alignment) {
777         checkWidget ();
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);
783                 return;
784         }
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;
800                 } else {
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;
806                 }
807                 OS.SendMessage (handle, OS.BCM_SETIMAGELIST, 0, buttonImageList);
808         }
809         if (newBits != oldBits) {
810                 OS.SetWindowLong (handle, OS.GWL_STYLE, newBits);
811                 OS.InvalidateRect (handle, null, true);
812         }
813 }
814
815 /**
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.
819  * <p>
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)}.
824  * </p>
825  * @param color the new color (or null)
826  *
827  * @exception IllegalArgumentException <ul>
828  *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
829  * </ul>
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>
833  * </ul>
834  */
835 @Override
836 public void setBackground (Color color) {
837         checkWidget ();
838         if (isRadioOrCheck()) {
839                 super.setBackground(color);
840         } else {
841                 setButtonBackground (color);
842         }
843 }
844
845 private void setButtonBackground (Color color) {
846         int pixel = -1;
847         int alpha = 255;
848         if (color != null) {
849                 if (color.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT);
850                 pixel = color.handle;
851                 alpha = color.getAlpha();
852         }
853         if (pixel == buttonBackground && alpha == buttonBackgroundAlpha) return;
854         buttonBackground = pixel;
855         buttonBackgroundAlpha = alpha;
856         updateBackgroundColor ();
857 }
858
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);
863         if (value) {
864                 bits |= OS.BS_DEFPUSHBUTTON;
865                 OS.SendMessage (hwndShell, OS.DM_SETDEFID, handle, 0);
866         } else {
867                 bits &= ~OS.BS_DEFPUSHBUTTON;
868                 OS.SendMessage (hwndShell, OS.DM_SETDEFID, 0, 0);
869         }
870         OS.SendMessage (handle, OS.BM_SETSTYLE, bits, 1);
871 }
872
873 @Override
874 public boolean setFocus () {
875         checkWidget ();
876         /*
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.
880         */
881         if ((style & SWT.RADIO) != 0 && !isChecked () && display.fixFocus) return false;
882         return super.setFocus ();
883 }
884
885 /**
886  * Sets the receiver's image to the argument, which may be
887  * <code>null</code> indicating that no image should be displayed.
888  * <p>
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.
893  * </p>
894  * @param image the image to display on the receiver (may be <code>null</code>)
895  *
896  * @exception IllegalArgumentException <ul>
897  *    <li>ERROR_INVALID_ARGUMENT - if the image has been disposed</li>
898  * </ul>
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>
902  * </ul>
903  */
904 public void setImage (Image image) {
905         checkWidget ();
906         if (image != null && image.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT);
907         if ((style & SWT.ARROW) != 0) return;
908         this.image = image;
909         /* This code is intentionally commented */
910 //      if (OS.COMCTL32_MAJOR < 6) {
911 //              if (image == null || text.length () != 0) {
912 //                      _setText (text);
913 //                      return;
914 //              }
915 //      }
916         _setImage (image);
917 }
918
919 /**
920  * Sets the grayed state of the receiver.  This state change
921  * only applies if the control was created with the SWT.CHECK
922  * style.
923  *
924  * @param grayed the new grayed state
925  *
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>
929  * </ul>
930  *
931  * @since 3.4
932  */
933 public void setGrayed (boolean grayed) {
934         checkWidget ();
935         if ((style & SWT.CHECK) == 0) return;
936         this.grayed = grayed;
937         long flags = OS.SendMessage (handle, OS.BM_GETCHECK, 0, 0);
938         if (grayed) {
939                 if (flags == OS.BST_CHECKED) updateSelection (OS.BST_INDETERMINATE);
940         } else {
941                 if (flags == OS.BST_INDETERMINATE) updateSelection (OS.BST_CHECKED);
942         }
943 }
944
945 /**
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.
949  *
950  * @param message the new message
951  *
952  * @exception IllegalArgumentException <ul>
953  *    <li>ERROR_NULL_ARGUMENT - if the string is null</li>
954  * </ul>
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>
958  * </ul>
959  *
960  * @since 3.3
961  */
962 /*public*/ void setMessage (String message) {
963         checkWidget ();
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);
971         }
972 }
973
974 @Override
975 boolean setRadioFocus (boolean tabbing) {
976         if ((style & SWT.RADIO) == 0 || !getSelection ()) return false;
977         return tabbing ? setTabItemFocus () : setFocus ();
978 }
979
980 @Override
981 boolean setRadioSelection (boolean value) {
982         if ((style & SWT.RADIO) == 0) return false;
983         if (getSelection () != value) {
984                 setSelection (value);
985                 sendSelectionEvent (SWT.Selection);
986         }
987         return true;
988 }
989
990 /**
991  * Sets the selection state of the receiver, if it is of type <code>CHECK</code>,
992  * <code>RADIO</code>, or <code>TOGGLE</code>.
993  *
994  * <p>
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.
998  *
999  * @param selected the new selection state
1000  *
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>
1004  * </ul>
1005  */
1006 public void setSelection (boolean selected) {
1007         checkWidget ();
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;
1012         }
1013         updateSelection (flags);
1014 }
1015
1016 /**
1017  * Sets the receiver's text.
1018  * <p>
1019  * This method sets the button label.  The label may include
1020  * the mnemonic character but must not contain line delimiters.
1021  * </p>
1022  * <p>
1023  * Mnemonics are indicated by an '&amp;' 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 '&amp;' can be
1029  * escaped by doubling it in the string, causing a single
1030  * '&amp;' to be displayed.
1031  * </p><p>
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.
1036  * </p><p>
1037  * Also note, if control characters like '\n', '\t' etc. are used
1038  * in the string, then the behavior is platform dependent.
1039  * </p>
1040  * @param string the new text
1041  *
1042  * @exception IllegalArgumentException <ul>
1043  *    <li>ERROR_NULL_ARGUMENT - if the text is null</li>
1044  * </ul>
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>
1048  * </ul>
1049  */
1050 public void setText (String string) {
1051         checkWidget ();
1052         if (string == null) error (SWT.ERROR_NULL_ARGUMENT);
1053         if ((style & SWT.ARROW) != 0) return;
1054         text = string;
1055         /* This code is intentionally commented */
1056 //      if (OS.COMCTL32_MAJOR < 6) {
1057 //              if (text.length () == 0 && image != null) {
1058 //                      _setImage (image);
1059 //                      return;
1060 //              }
1061 //      }
1062         _setText (string);
1063 }
1064
1065 @Override
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;
1071 //              style &= ~flags;
1072 //              style |= textDirection & flags;
1073 //              updateOrientation ();
1074 //              checkMirrored ();
1075                 return true;
1076         }
1077         return false;
1078 }
1079
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);
1088                 } else {
1089                         if (disabledImage != null) disabledImage.dispose ();
1090                         disabledImage = new Image (display, image, SWT.IMAGE_DISABLE);
1091                         imageList.add (disabledImage);
1092                 }
1093                 buttonImageList.himl = imageList.getHandle ();
1094                 OS.SendMessage (handle, OS.BCM_SETIMAGELIST, 0, buttonImageList);
1095                 /*
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.
1099                 */
1100                 OS.InvalidateRect (handle, null, true);
1101         }
1102 }
1103
1104 @Override
1105 void updateOrientation () {
1106         super.updateOrientation ();
1107         updateImageList ();
1108 }
1109
1110 void updateSelection (int flags) {
1111         if (flags != OS.SendMessage (handle, OS.BM_GETCHECK, 0, 0)) {
1112                 /*
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.
1118                 */
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;
1124                         } else {
1125                                 bits |= OS.BS_CHECKBOX;
1126                                 bits &= ~OS.BS_3STATE;
1127                         }
1128                         if (bits != OS.GetWindowLong (handle, OS.GWL_STYLE)) {
1129                                 OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
1130                         }
1131                 }
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);
1135                 }
1136         }
1137 }
1138
1139 @Override
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;
1154 }
1155
1156 @Override
1157 TCHAR windowClass () {
1158         return ButtonClass;
1159 }
1160
1161 @Override
1162 long windowProc () {
1163         return ButtonProc;
1164 }
1165
1166 @Override
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);
1172         }
1173         return result;
1174 }
1175
1176 @Override
1177 LRESULT WM_GETOBJECT (long wParam, long lParam) {
1178         /*
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.
1182         */
1183         if ((style & SWT.RADIO) != 0) {
1184                 if (accessible == null) accessible = new_Accessible (this);
1185         }
1186         return super.WM_GETOBJECT (wParam, lParam);
1187 }
1188
1189 @Override
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);
1194         }
1195         return result;
1196 }
1197
1198 @Override
1199 LRESULT WM_LBUTTONDOWN (long wParam, long lParam) {
1200         if (ignoreMouse) return null;
1201         return super.WM_LBUTTONDOWN (wParam, lParam);
1202 }
1203
1204 @Override
1205 LRESULT WM_LBUTTONUP (long wParam, long lParam) {
1206         if (ignoreMouse) return null;
1207         return super.WM_LBUTTONUP (wParam, lParam);
1208 }
1209
1210 @Override
1211 LRESULT WM_SETFOCUS (long wParam, long lParam) {
1212         /*
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.
1217         */
1218         int bits = 0;
1219         if ((style & SWT.RADIO) != 0) {
1220                 bits = OS.GetWindowLong (handle, OS.GWL_STYLE);
1221         }
1222         LRESULT result = super.WM_SETFOCUS (wParam, lParam);
1223         if ((style & SWT.RADIO) != 0) {
1224                 OS.SetWindowLong (handle, OS.GWL_STYLE, bits);
1225         }
1226         if ((style & SWT.PUSH) != 0) {
1227                 menuShell ().setDefaultButton (this, false);
1228         }
1229         return result;
1230 }
1231
1232 @Override
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);
1244                 }
1245         }
1246         return result;
1247 }
1248
1249 @Override
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);
1254         return result;
1255 }
1256
1257 @Override
1258 LRESULT WM_UPDATEUISTATE (long wParam, long lParam) {
1259         LRESULT result = super.WM_UPDATEUISTATE (wParam, lParam);
1260         if (result != null) return result;
1261         /*
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.
1268         *
1269         * NOTE:  This only happens for radio, check and toggle
1270         * buttons.
1271         */
1272         if ((style & (SWT.RADIO | SWT.CHECK | SWT.TOGGLE)) != 0) {
1273                 boolean redraw = findImageControl () != null;
1274                 if (!redraw) {
1275                         if ((state & THEME_BACKGROUND) != 0) {
1276                                 if (OS.IsAppThemed ()) {
1277                                         redraw = findThemeControl () != null;
1278                                 }
1279                         }
1280                         if (!redraw) redraw = findBackgroundControl () != null;
1281                 }
1282                 if (redraw) {
1283                         OS.InvalidateRect (handle, null, false);
1284                         long code = OS.DefWindowProc (handle, OS.WM_UPDATEUISTATE, wParam, lParam);
1285                         return new LRESULT (code);
1286                 }
1287         }
1288         /*
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.
1294         */
1295         if ((style & (SWT.PUSH | SWT.TOGGLE)) != 0) {
1296                 if (hooks (SWT.Paint) || filters (SWT.Paint) || customDrawing()) {
1297                         OS.InvalidateRect (handle, null, true);
1298                 }
1299         }
1300         return result;
1301 }
1302
1303 @Override
1304 LRESULT wmCommandChild (long wParam, long lParam) {
1305         int code = OS.HIWORD (wParam);
1306         switch (code) {
1307                 case OS.BN_CLICKED:
1308                 case OS.BN_DOUBLECLICKED:
1309                         if ((style & (SWT.CHECK | SWT.TOGGLE)) != 0) {
1310                                 setSelection (!getSelection ());
1311                         } else {
1312                                 if ((style & SWT.RADIO) != 0) {
1313                                         if ((parent.getStyle () & SWT.NO_RADIO_GROUP) != 0) {
1314                                                 setSelection (!getSelection ());
1315                                         } else {
1316                                                 selectRadio ();
1317                                         }
1318                                 }
1319                         }
1320                         sendSelectionEvent (SWT.Selection);
1321         }
1322         return super.wmCommandChild (wParam, lParam);
1323 }
1324
1325 @Override
1326 LRESULT wmNotifyChild (NMHDR hdr, long wParam, long lParam) {
1327         switch (hdr.code) {
1328                 case OS.NM_CUSTOMDRAW:
1329                         // this message will not appear for owner-draw buttons (currently the ARROW button).
1330
1331                         NMCUSTOMDRAW nmcd = new NMCUSTOMDRAW ();
1332                         OS.MoveMemory (nmcd, lParam, NMCUSTOMDRAW.sizeof);
1333
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);
1343                                                 }
1344                                                 if ((style & SWT.TOGGLE) != 0 && isChecked()) {
1345                                                         pixel = getDifferentColor(buttonBackground);
1346                                                 }
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);
1352                                         }
1353                                         if (customForegroundDrawing()) {
1354                                                 /*
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
1360                                                  */
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);
1369
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
1373
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));
1377                                                         gc.dispose ();
1378                                                 }
1379
1380                                                 left += isRadioOrCheck() ? radioOrCheckTextPadding : 0;
1381                                                 RECT textRect = new RECT ();
1382                                                 OS.SetRect (textRect, left, nmcd.top + border, right, nmcd.bottom - border);
1383
1384                                                 // draw text
1385                                                 char [] buffer = text.toCharArray ();
1386                                                 int flags = 0;
1387                                                 if ((style & SWT.WRAP) != 0) {
1388                                                         flags |= OS.DT_WORDBREAK;
1389                                                         if (!isRadioOrCheck() && image != null) {
1390                                                                 textRect.right -= MARGIN;
1391                                                         }
1392                                                 } else {
1393                                                         flags |= OS.DT_SINGLELINE; // TODO: this always draws the prefix
1394                                                 }
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);
1402                                                         }
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);
1408                                                 } else {
1409                                                         flags |= OS.DT_CENTER;
1410                                                         OS.OffsetRect(textRect, (right - textRect.right) / 2, 0);
1411                                                 }
1412                                                 OS.SetBkMode(nmcd.hdc, OS.TRANSPARENT);
1413                                                 OS.SetTextColor(nmcd.hdc, foreground);
1414                                                 OS.DrawText(nmcd.hdc, buffer, buffer.length, textRect, flags);
1415
1416                                                 // draw focus rect
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));
1422                                                                 } else {
1423                                                                         /*
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.
1427                                                                          */
1428                                                                         OS.SetRect (focusRect, nmcd.left+1+radioOrCheckTextPadding, nmcd.top, nmcd.right-2, nmcd.bottom-1);
1429                                                                 }
1430                                                         } else {
1431                                                                 OS.SetRect (focusRect, nmcd.left+2, nmcd.top+3, nmcd.right-2, nmcd.bottom-3);
1432                                                         }
1433                                                         OS.DrawFocusRect(nmcd.hdc, focusRect);
1434                                                 }
1435                                                 return new LRESULT (OS.CDRF_SKIPDEFAULT);
1436                                         }
1437                                         return new LRESULT (OS.CDRF_DODEFAULT);
1438                                 }
1439                         }
1440                         break;
1441         }
1442         return super.wmNotifyChild (hdr, wParam, lParam);
1443 }
1444
1445 @Override
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;
1459                 }
1460                 /*
1461                 * Feature in Windows.  DrawThemeBackground() does not mirror the drawing.
1462                 * The fix is switch left to right and right to left.
1463                 */
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;
1467                         }
1468                 }
1469                 /*
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).
1473                 */
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);
1477         } else {
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;
1484                 }
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);
1489         }
1490         return null;
1491 }
1492
1493 }