win32u: Use KBDTABLES for NtUserVkKeyScanEx.
[wine.git] / dlls / win32u / defwnd.c
blob182703e22be809f7a10cefc68e3f9ce9c134fe45
1 /*
2 * Default window procedure
4 * Copyright 1993, 1996 Alexandre Julliard
5 * Copyright 1995 Alex Korobka
6 * Copyright 2022 Jacek Caban for CodeWeavers
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #if 0
24 #pragma makedep unix
25 #endif
27 #include "ntgdi_private.h"
28 #include "ntuser_private.h"
29 #include "wine/server.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(win);
34 #define DRAG_FILE 0x454c4946
36 /* bits in the dwKeyData */
37 #define KEYDATA_ALT 0x2000
38 #define KEYDATA_PREVSTATE 0x4000
40 static short f10_key = 0;
41 static short menu_sys_key = 0;
43 static BOOL has_dialog_frame( UINT style, UINT ex_style )
45 return (ex_style & WS_EX_DLGMODALFRAME) || ((style & WS_DLGFRAME) && !(style & WS_THICKFRAME));
48 static BOOL has_thick_frame( UINT style, UINT ex_style )
50 return (style & WS_THICKFRAME) && (style & (WS_DLGFRAME|WS_BORDER)) != WS_DLGFRAME;
53 static BOOL has_thin_frame( UINT style )
55 return (style & WS_BORDER) || !(style & (WS_CHILD | WS_POPUP));
58 static BOOL has_big_frame( UINT style, UINT ex_style )
60 return (style & (WS_THICKFRAME | WS_DLGFRAME)) || (ex_style & WS_EX_DLGMODALFRAME);
63 static BOOL has_static_outer_frame( UINT ex_style )
65 return (ex_style & (WS_EX_STATICEDGE|WS_EX_DLGMODALFRAME)) == WS_EX_STATICEDGE;
68 static BOOL has_menu( HWND hwnd, UINT style )
70 return (style & (WS_CHILD | WS_POPUP)) != WS_CHILD && get_menu( hwnd );
73 void fill_rect( HDC dc, const RECT *rect, HBRUSH hbrush )
75 HBRUSH prev_brush;
77 if (hbrush <= (HBRUSH)(COLOR_MENUBAR + 1)) hbrush = get_sys_color_brush( HandleToULong(hbrush) - 1 );
79 prev_brush = NtGdiSelectBrush( dc, hbrush );
80 NtGdiPatBlt( dc, rect->left, rect->top, rect->right - rect->left, rect->bottom - rect->top, PATCOPY );
81 if (prev_brush) NtGdiSelectBrush( dc, prev_brush );
84 /* see DrawFocusRect */
85 static BOOL draw_focus_rect( HDC hdc, const RECT *rc )
87 DWORD prev_draw_mode, prev_bk_mode;
88 HPEN prev_pen, pen;
89 HBRUSH prev_brush;
91 prev_brush = NtGdiSelectBrush(hdc, GetStockObject(NULL_BRUSH));
92 pen = NtGdiExtCreatePen( PS_COSMETIC|PS_ALTERNATE, 1, BS_SOLID,
93 0, 0, 0, 0, NULL, 0, FALSE, NULL );
94 prev_pen = NtGdiSelectPen(hdc, pen);
95 NtGdiGetAndSetDCDword( hdc, NtGdiSetROP2, R2_NOT, &prev_draw_mode );
96 NtGdiGetAndSetDCDword( hdc, NtGdiSetBkMode, TRANSPARENT, &prev_bk_mode );
98 NtGdiRectangle( hdc, rc->left, rc->top, rc->right, rc->bottom );
100 NtGdiGetAndSetDCDword( hdc, NtGdiSetBkMode, prev_bk_mode, NULL );
101 NtGdiGetAndSetDCDword( hdc, NtGdiSetROP2, prev_draw_mode, NULL );
102 NtGdiSelectPen( hdc, prev_pen );
103 NtGdiDeleteObjectApp( pen );
104 NtGdiSelectBrush( hdc, prev_brush );
105 return TRUE;
108 static const signed char lt_inner_normal[] = {
109 -1, -1, -1, -1,
110 -1, COLOR_BTNHIGHLIGHT, COLOR_BTNHIGHLIGHT, -1,
111 -1, COLOR_3DDKSHADOW, COLOR_3DDKSHADOW, -1,
112 -1, -1, -1, -1
115 static const signed char lt_outer_normal[] = {
116 -1, COLOR_3DLIGHT, COLOR_BTNSHADOW, -1,
117 COLOR_BTNHIGHLIGHT, COLOR_3DLIGHT, COLOR_BTNSHADOW, -1,
118 COLOR_3DDKSHADOW, COLOR_3DLIGHT, COLOR_BTNSHADOW, -1,
119 -1, COLOR_3DLIGHT, COLOR_BTNSHADOW, -1
122 static const signed char rb_inner_normal[] = {
123 -1, -1, -1, -1,
124 -1, COLOR_BTNSHADOW, COLOR_BTNSHADOW, -1,
125 -1, COLOR_3DLIGHT, COLOR_3DLIGHT, -1,
126 -1, -1, -1, -1
129 static const signed char rb_outer_normal[] = {
130 -1, COLOR_3DDKSHADOW, COLOR_BTNHIGHLIGHT, -1,
131 COLOR_BTNSHADOW, COLOR_3DDKSHADOW, COLOR_BTNHIGHLIGHT, -1,
132 COLOR_3DLIGHT, COLOR_3DDKSHADOW, COLOR_BTNHIGHLIGHT, -1,
133 -1, COLOR_3DDKSHADOW, COLOR_BTNHIGHLIGHT, -1
136 static const signed char ltrb_outer_mono[] = {
137 -1, COLOR_WINDOWFRAME, COLOR_WINDOWFRAME, COLOR_WINDOWFRAME,
138 COLOR_WINDOW, COLOR_WINDOWFRAME, COLOR_WINDOWFRAME, COLOR_WINDOWFRAME,
139 COLOR_WINDOW, COLOR_WINDOWFRAME, COLOR_WINDOWFRAME, COLOR_WINDOWFRAME,
140 COLOR_WINDOW, COLOR_WINDOWFRAME, COLOR_WINDOWFRAME, COLOR_WINDOWFRAME,
143 static const signed char ltrb_inner_mono[] = {
144 -1, -1, -1, -1,
145 -1, COLOR_WINDOW, COLOR_WINDOW, COLOR_WINDOW,
146 -1, COLOR_WINDOW, COLOR_WINDOW, COLOR_WINDOW,
147 -1, COLOR_WINDOW, COLOR_WINDOW, COLOR_WINDOW,
150 BOOL draw_rect_edge( HDC hdc, RECT *rc, UINT type, UINT flags, UINT width )
152 int lbi_offset = 0, lti_offset = 0, rti_offset = 0, rbi_offset = 0;
153 signed char lt_inner, lt_outer, rb_inner, rb_outer;
154 HBRUSH lti_brush, lto_brush, rbi_brush, rbo_brush;
155 RECT inner_rect = *rc, rect;
156 BOOL retval;
158 retval = !((type & BDR_INNER) == BDR_INNER || (type & BDR_OUTER) == BDR_OUTER) &&
159 !(flags & (BF_FLAT|BF_MONO));
161 lti_brush = lto_brush = rbi_brush = rbo_brush = GetStockObject( NULL_BRUSH );
163 /* Determine the colors of the edges */
164 lt_inner = lt_inner_normal[type & (BDR_INNER|BDR_OUTER)];
165 lt_outer = lt_outer_normal[type & (BDR_INNER|BDR_OUTER)];
166 rb_inner = rb_inner_normal[type & (BDR_INNER|BDR_OUTER)];
167 rb_outer = rb_outer_normal[type & (BDR_INNER|BDR_OUTER)];
169 if ((flags & BF_BOTTOMLEFT) == BF_BOTTOMLEFT) lbi_offset = width;
170 if ((flags & BF_TOPRIGHT) == BF_TOPRIGHT) rti_offset = width;
171 if ((flags & BF_BOTTOMRIGHT) == BF_BOTTOMRIGHT) rbi_offset = width;
172 if ((flags & BF_TOPLEFT) == BF_TOPLEFT) lti_offset = width;
174 if (lt_inner != -1) lti_brush = get_sys_color_brush( lt_inner );
175 if (lt_outer != -1) lto_brush = get_sys_color_brush( lt_outer );
176 if (rb_inner != -1) rbi_brush = get_sys_color_brush( rb_inner );
177 if (rb_outer != -1) rbo_brush = get_sys_color_brush( rb_outer );
179 /* Draw the outer edge */
180 if (flags & BF_TOP)
182 rect = inner_rect;
183 rect.bottom = rect.top + width;
184 fill_rect( hdc, &rect, lto_brush );
186 if (flags & BF_LEFT)
188 rect = inner_rect;
189 rect.right = rect.left + width;
190 fill_rect( hdc, &rect, lto_brush );
192 if (flags & BF_BOTTOM)
194 rect = inner_rect;
195 rect.top = rect.bottom - width;
196 fill_rect( hdc, &rect, rbo_brush );
198 if (flags & BF_RIGHT)
200 rect = inner_rect;
201 rect.left = rect.right - width;
202 fill_rect( hdc, &rect, rbo_brush );
205 /* Draw the inner edge */
206 if (flags & BF_TOP)
208 SetRect( &rect, inner_rect.left + lti_offset, inner_rect.top + width,
209 inner_rect.right - rti_offset, inner_rect.top + 2 * width );
210 fill_rect( hdc, &rect, lti_brush );
212 if (flags & BF_LEFT)
214 SetRect( &rect, inner_rect.left + width, inner_rect.top + lti_offset,
215 inner_rect.left + 2 * width, inner_rect.bottom - lbi_offset );
216 fill_rect( hdc, &rect, lti_brush );
218 if (flags & BF_BOTTOM)
220 SetRect( &rect, inner_rect.left + lbi_offset, inner_rect.bottom - 2 * width,
221 inner_rect.right - rbi_offset, inner_rect.bottom - width );
222 fill_rect( hdc, &rect, rbi_brush );
224 if (flags & BF_RIGHT)
226 SetRect( &rect, inner_rect.right - 2 * width, inner_rect.top + rti_offset,
227 inner_rect.right - width, inner_rect.bottom - rbi_offset );
228 fill_rect( hdc, &rect, rbi_brush );
231 if (((flags & BF_MIDDLE) && retval) || (flags & BF_ADJUST))
233 int add = (ltrb_inner_mono[type & (BDR_INNER|BDR_OUTER)] != -1 ? width : 0)
234 + (ltrb_outer_mono[type & (BDR_INNER|BDR_OUTER)] != -1 ? width : 0);
236 if (flags & BF_LEFT) inner_rect.left += add;
237 if (flags & BF_RIGHT) inner_rect.right -= add;
238 if (flags & BF_TOP) inner_rect.top += add;
239 if (flags & BF_BOTTOM) inner_rect.bottom -= add;
241 if ((flags & BF_MIDDLE) && retval)
243 fill_rect( hdc, &inner_rect, get_sys_color_brush( flags & BF_MONO ?
244 COLOR_WINDOW : COLOR_BTNFACE ));
247 if (flags & BF_ADJUST) *rc = inner_rect;
250 return retval;
253 /***********************************************************************
254 * AdjustWindowRectEx (win32u.so)
256 BOOL WINAPI AdjustWindowRectEx( RECT *rect, DWORD style, BOOL menu, DWORD ex_style )
258 NONCLIENTMETRICSW ncm;
259 int adjust = 0;
261 ncm.cbSize = sizeof(ncm);
262 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &ncm, 0 );
264 if ((ex_style & (WS_EX_STATICEDGE|WS_EX_DLGMODALFRAME)) == WS_EX_STATICEDGE)
265 adjust = 1; /* for the outer frame always present */
266 else if ((ex_style & WS_EX_DLGMODALFRAME) || (style & (WS_THICKFRAME|WS_DLGFRAME)))
267 adjust = 2; /* outer */
269 if (style & WS_THICKFRAME)
270 adjust += ncm.iBorderWidth + ncm.iPaddedBorderWidth; /* The resize border */
272 if ((style & (WS_BORDER|WS_DLGFRAME)) || (ex_style & WS_EX_DLGMODALFRAME))
273 adjust++; /* The other border */
275 InflateRect( rect, adjust, adjust );
277 if ((style & WS_CAPTION) == WS_CAPTION)
279 if (ex_style & WS_EX_TOOLWINDOW)
280 rect->top -= ncm.iSmCaptionHeight + 1;
281 else
282 rect->top -= ncm.iCaptionHeight + 1;
284 if (menu) rect->top -= ncm.iMenuHeight + 1;
286 if (ex_style & WS_EX_CLIENTEDGE)
287 InflateRect( rect, get_system_metrics(SM_CXEDGE), get_system_metrics(SM_CYEDGE) );
288 return TRUE;
291 static BOOL set_window_text( HWND hwnd, const void *text, BOOL ansi )
293 static const WCHAR emptyW[] = { 0 };
294 WCHAR *str;
295 WND *win;
297 /* check for string, as static icons, bitmaps (SS_ICON, SS_BITMAP)
298 * may have child window IDs instead of window name */
299 if (text && IS_INTRESOURCE(text)) return FALSE;
301 if (text)
303 if (ansi) str = towstr( text );
304 else str = wcsdup( text );
305 if (!str) return FALSE;
307 else str = NULL;
309 TRACE( "%s\n", debugstr_w(str) );
311 if (!(win = get_win_ptr( hwnd )))
313 free( str );
314 return FALSE;
317 free( win->text );
318 win->text = str;
319 SERVER_START_REQ( set_window_text )
321 req->handle = wine_server_user_handle( hwnd );
322 if (str) wine_server_add_data( req, str, lstrlenW( str ) * sizeof(WCHAR) );
323 wine_server_call( req );
325 SERVER_END_REQ;
327 release_win_ptr( win );
329 user_driver->pSetWindowText( hwnd, str ? str : emptyW );
331 return TRUE;
334 static int get_window_text( HWND hwnd, WCHAR *str, int count )
336 int ret;
338 if (is_current_process_window( hwnd ))
340 /* FIXME: use packed send message */
341 ret = send_message( hwnd, WM_GETTEXT, count, (LPARAM)str );
343 else
345 /* when window belongs to other process, don't send a message */
346 ret = NtUserInternalGetWindowText( hwnd, str, count );
349 return ret;
352 static HICON get_window_icon( HWND hwnd, WPARAM type )
354 HICON ret;
355 WND *win;
357 if (!(win = get_win_ptr( hwnd ))) return 0;
359 switch(type)
361 case ICON_SMALL:
362 ret = win->hIconSmall;
363 break;
364 case ICON_BIG:
365 ret = win->hIcon;
366 break;
367 case ICON_SMALL2:
368 ret = win->hIconSmall ? win->hIconSmall : win->hIconSmall2;
369 break;
370 default:
371 ret = 0;
372 break;
375 release_win_ptr( win );
376 return ret;
379 static HICON set_window_icon( HWND hwnd, WPARAM type, HICON icon )
381 HICON ret = 0;
382 WND *win;
384 if (!(win = get_win_ptr( hwnd ))) return 0;
386 switch (type)
388 case ICON_SMALL:
389 ret = win->hIconSmall;
390 if (ret && !icon && win->hIcon)
392 win->hIconSmall2 = CopyImage( win->hIcon, IMAGE_ICON,
393 get_system_metrics( SM_CXSMICON ),
394 get_system_metrics( SM_CYSMICON ), 0 );
396 else if (icon && win->hIconSmall2)
398 NtUserDestroyCursor( win->hIconSmall2, 0 );
399 win->hIconSmall2 = NULL;
401 win->hIconSmall = icon;
402 break;
404 case ICON_BIG:
405 ret = win->hIcon;
406 if (win->hIconSmall2)
408 NtUserDestroyCursor( win->hIconSmall2, 0 );
409 win->hIconSmall2 = NULL;
411 if (icon && !win->hIconSmall)
413 win->hIconSmall2 = CopyImage( icon, IMAGE_ICON,
414 get_system_metrics( SM_CXSMICON ),
415 get_system_metrics( SM_CYSMICON ), 0 );
417 win->hIcon = icon;
418 break;
420 release_win_ptr( win );
422 user_driver->pSetWindowIcon( hwnd, type, icon );
423 return ret;
426 static LRESULT handle_set_cursor( HWND hwnd, WPARAM wparam, LPARAM lparam )
428 UINT cursor_id = IDC_ARROW;
429 HCURSOR cursor;
431 hwnd = get_full_window_handle( (HWND)wparam );
433 switch((short)LOWORD( lparam ))
435 case HTERROR:
437 WORD msg = HIWORD( lparam );
438 if (msg == WM_LBUTTONDOWN || msg == WM_MBUTTONDOWN ||
439 msg == WM_RBUTTONDOWN || msg == WM_XBUTTONDOWN)
440 message_beep( 0 );
442 break;
444 case HTCLIENT:
445 cursor = (HCURSOR)get_class_long_ptr( hwnd, GCLP_HCURSOR, FALSE );
446 if (!cursor) return FALSE;
447 NtUserSetCursor( cursor );
448 return TRUE;
450 case HTLEFT:
451 case HTRIGHT:
452 cursor_id = IDC_SIZEWE;
453 break;
455 case HTTOP:
456 case HTBOTTOM:
457 cursor_id = IDC_SIZENS;
458 break;
460 case HTTOPLEFT:
461 case HTBOTTOMRIGHT:
462 cursor_id = IDC_SIZENWSE;
463 break;
465 case HTTOPRIGHT:
466 case HTBOTTOMLEFT:
467 cursor_id = IDC_SIZENESW;
470 cursor = LoadImageW( 0, MAKEINTRESOURCEW( cursor_id ), IMAGE_CURSOR,
471 0, 0, LR_SHARED | LR_DEFAULTSIZE );
472 return (LRESULT)NtUserSetCursor( cursor );
475 static LONG handle_window_pos_changing( HWND hwnd, WINDOWPOS *winpos )
477 LONG style = get_window_long( hwnd, GWL_STYLE );
479 if (winpos->flags & SWP_NOSIZE) return 0;
480 if ((style & WS_THICKFRAME) || ((style & (WS_POPUP | WS_CHILD)) == 0))
482 MINMAXINFO info = get_min_max_info( hwnd );
483 winpos->cx = min( winpos->cx, info.ptMaxTrackSize.x );
484 winpos->cy = min( winpos->cy, info.ptMaxTrackSize.y );
485 if (!(style & WS_MINIMIZE))
487 winpos->cx = max( winpos->cx, info.ptMinTrackSize.x );
488 winpos->cy = max( winpos->cy, info.ptMinTrackSize.y );
491 return 0;
494 static void handle_window_pos_changed( HWND hwnd, const WINDOWPOS *winpos )
496 RECT rect;
498 get_window_rects( hwnd, COORDS_PARENT, NULL, &rect, get_thread_dpi() );
499 if (!(winpos->flags & SWP_NOCLIENTMOVE))
500 send_message( hwnd, WM_MOVE, 0, MAKELONG( rect.left, rect.top ));
502 if (!(winpos->flags & SWP_NOCLIENTSIZE) || (winpos->flags & SWP_STATECHANGED))
504 if (is_iconic( hwnd ))
506 send_message( hwnd, WM_SIZE, SIZE_MINIMIZED, 0 );
508 else
510 WPARAM wp = is_zoomed( hwnd ) ? SIZE_MAXIMIZED : SIZE_RESTORED;
511 send_message( hwnd, WM_SIZE, wp,
512 MAKELONG( rect.right-rect.left, rect.bottom-rect.top ));
517 /***********************************************************************
518 * draw_moving_frame
520 * Draw the frame used when moving or resizing window.
522 static void draw_moving_frame( HWND parent, HDC hdc, RECT *screen_rect, BOOL thickframe )
524 RECT rect = *screen_rect;
526 if (parent) map_window_points( 0, parent, (POINT *)&rect, 2, get_thread_dpi() );
527 if (thickframe)
529 const int width = get_system_metrics( SM_CXFRAME );
530 const int height = get_system_metrics( SM_CYFRAME );
532 HBRUSH hbrush = NtGdiSelectBrush( hdc, GetStockObject( GRAY_BRUSH ));
533 NtGdiPatBlt( hdc, rect.left, rect.top,
534 rect.right - rect.left - width, height, PATINVERT );
535 NtGdiPatBlt( hdc, rect.left, rect.top + height, width,
536 rect.bottom - rect.top - height, PATINVERT );
537 NtGdiPatBlt( hdc, rect.left + width, rect.bottom - 1,
538 rect.right - rect.left - width, -height, PATINVERT );
539 NtGdiPatBlt( hdc, rect.right - 1, rect.top, -width,
540 rect.bottom - rect.top - height, PATINVERT );
541 NtGdiSelectBrush( hdc, hbrush );
543 else draw_focus_rect( hdc, &rect );
546 /***********************************************************************
547 * start_size_move
549 * Initialization of a move or resize, when initiated from a menu choice.
550 * Return hit test code for caption or sizing border.
552 static LONG start_size_move( HWND hwnd, WPARAM wparam, POINT *capture_point, LONG style )
554 RECT window_rect;
555 LONG hittest = 0;
556 POINT pt;
557 MSG msg;
559 get_window_rect( hwnd, &window_rect, get_thread_dpi() );
561 if ((wparam & 0xfff0) == SC_MOVE)
563 /* Move pointer at the center of the caption */
564 RECT rect = window_rect;
565 /* Note: to be exactly centered we should take the different types
566 * of border into account, but it shouldn't make more than a few pixels
567 * of difference so let's not bother with that */
568 rect.top += get_system_metrics( SM_CYBORDER );
569 if (style & WS_SYSMENU) rect.left += get_system_metrics( SM_CXSIZE ) + 1;
570 if (style & WS_MINIMIZEBOX) rect.right -= get_system_metrics( SM_CXSIZE ) + 1;
571 if (style & WS_MAXIMIZEBOX) rect.right -= get_system_metrics( SM_CXSIZE ) + 1;
572 pt.x = (rect.right + rect.left) / 2;
573 pt.y = rect.top + get_system_metrics( SM_CYSIZE ) / 2;
574 hittest = HTCAPTION;
575 *capture_point = pt;
577 else /* SC_SIZE */
579 HCURSOR cursor;
580 cursor = LoadImageW( 0, (WCHAR *)IDC_SIZEALL, IMAGE_CURSOR, 0, 0, LR_SHARED | LR_DEFAULTSIZE );
581 NtUserSetCursor( cursor );
582 pt.x = pt.y = 0;
583 while (!hittest)
585 if (!NtUserGetMessage( &msg, 0, 0, 0 )) return 0;
586 if (NtUserCallMsgFilter( &msg, MSGF_SIZE )) continue;
588 switch (msg.message)
590 case WM_MOUSEMOVE:
591 pt.x = min( max( msg.pt.x, window_rect.left ), window_rect.right - 1 );
592 pt.y = min( max( msg.pt.y, window_rect.top ), window_rect.bottom - 1 );
593 hittest = send_message( hwnd, WM_NCHITTEST, 0, MAKELONG( pt.x, pt.y ));
594 if (hittest < HTLEFT || hittest > HTBOTTOMRIGHT) hittest = 0;
595 break;
597 case WM_LBUTTONUP:
598 return 0;
600 case WM_KEYDOWN:
601 switch (msg.wParam)
603 case VK_UP:
604 hittest = HTTOP;
605 pt.x = (window_rect.left + window_rect.right) / 2;
606 pt.y = window_rect.top + get_system_metrics( SM_CYFRAME ) / 2;
607 break;
608 case VK_DOWN:
609 hittest = HTBOTTOM;
610 pt.x = (window_rect.left + window_rect.right) / 2;
611 pt.y = window_rect.bottom - get_system_metrics( SM_CYFRAME ) / 2;
612 break;
613 case VK_LEFT:
614 hittest = HTLEFT;
615 pt.x = window_rect.left + get_system_metrics( SM_CXFRAME ) / 2;
616 pt.y = (window_rect.top + window_rect.bottom) / 2;
617 break;
618 case VK_RIGHT:
619 hittest = HTRIGHT;
620 pt.x = window_rect.right - get_system_metrics( SM_CXFRAME ) / 2;
621 pt.y = (window_rect.top + window_rect.bottom) / 2;
622 break;
623 case VK_RETURN:
624 case VK_ESCAPE:
625 return 0;
627 break;
628 default:
629 NtUserTranslateMessage( &msg, 0 );
630 NtUserDispatchMessage( &msg );
631 break;
634 *capture_point = pt;
636 NtUserSetCursorPos( pt.x, pt.y );
637 send_message( hwnd, WM_SETCURSOR, (WPARAM)hwnd, MAKELONG( hittest, WM_MOUSEMOVE ));
638 return hittest;
641 static BOOL on_left_border( int hit )
643 return hit == HTLEFT || hit == HTTOPLEFT || hit == HTBOTTOMLEFT;
646 static BOOL on_right_border( int hit )
648 return hit == HTRIGHT || hit == HTTOPRIGHT || hit == HTBOTTOMRIGHT;
651 static BOOL on_top_border( int hit )
653 return hit == HTTOP || hit == HTTOPLEFT || hit == HTTOPRIGHT;
656 static BOOL on_bottom_border( int hit )
658 return hit == HTBOTTOM || hit == HTBOTTOMLEFT || hit == HTBOTTOMRIGHT;
661 /***********************************************************************
662 * sys_command_size_move
664 * Perform SC_MOVE and SC_SIZE commands.
666 static void sys_command_size_move( HWND hwnd, WPARAM wparam )
668 DWORD msg_pos = NtUserGetThreadInfo()->message_pos;
669 BOOL thickframe, drag_full_windows = TRUE, moved = FALSE;
670 RECT sizing_rect, mouse_rect, orig_rect;
671 UINT hittest = wparam & 0x0f;
672 UINT syscommand = wparam & 0xfff0;
673 UINT style = get_window_long( hwnd, GWL_STYLE );
674 POINT capture_point, pt;
675 MINMAXINFO minmax;
676 HMONITOR mon = 0;
677 HWND parent;
678 UINT dpi;
679 HDC hdc;
680 MSG msg;
682 if (is_zoomed( hwnd ) || !is_window_visible( hwnd )) return;
684 thickframe = (style & WS_THICKFRAME) && !((style & (WS_DLGFRAME | WS_BORDER)) == WS_DLGFRAME);
686 pt.x = (short)LOWORD(msg_pos);
687 pt.y = (short)HIWORD(msg_pos);
688 capture_point = pt;
689 NtUserClipCursor( NULL );
691 TRACE( "hwnd %p command %04x, hittest %d, pos %d,%d\n",
692 hwnd, syscommand, hittest, (int)pt.x, (int)pt.y );
694 if (syscommand == SC_MOVE)
696 if (!hittest) hittest = start_size_move( hwnd, wparam, &capture_point, style );
697 if (!hittest) return;
699 else /* SC_SIZE */
701 if (hittest && syscommand != SC_MOUSEMENU)
702 hittest += (HTLEFT - WMSZ_LEFT);
703 else
705 set_capture_window( hwnd, GUI_INMOVESIZE, NULL );
706 hittest = start_size_move( hwnd, wparam, &capture_point, style );
707 if (!hittest)
709 set_capture_window( 0, GUI_INMOVESIZE, NULL );
710 return;
715 minmax = get_min_max_info( hwnd );
716 dpi = get_thread_dpi();
717 get_window_rects( hwnd, COORDS_PARENT, &sizing_rect, NULL, dpi );
718 orig_rect = sizing_rect;
719 if (style & WS_CHILD)
721 parent = get_parent( hwnd );
722 get_client_rect( parent, &mouse_rect );
723 map_window_points( parent, 0, (POINT *)&mouse_rect, 2, dpi );
724 map_window_points( parent, 0, (POINT *)&sizing_rect, 2, dpi );
726 else
728 parent = 0;
729 mouse_rect = get_virtual_screen_rect( get_thread_dpi() );
730 mon = monitor_from_point( pt, MONITOR_DEFAULTTONEAREST, dpi );
733 if (on_left_border( hittest ))
735 mouse_rect.left = max( mouse_rect.left,
736 sizing_rect.right - minmax.ptMaxTrackSize.x + capture_point.x - sizing_rect.left );
737 mouse_rect.right = min( mouse_rect.right,
738 sizing_rect.right - minmax.ptMinTrackSize.x + capture_point.x - sizing_rect.left );
740 else if (on_right_border( hittest ))
742 mouse_rect.left = max( mouse_rect.left,
743 sizing_rect.left + minmax.ptMinTrackSize.x + capture_point.x - sizing_rect.right );
744 mouse_rect.right = min( mouse_rect.right,
745 sizing_rect.left + minmax.ptMaxTrackSize.x + capture_point.x - sizing_rect.right );
748 if (on_top_border( hittest ))
750 mouse_rect.top = max( mouse_rect.top,
751 sizing_rect.bottom - minmax.ptMaxTrackSize.y + capture_point.y - sizing_rect.top );
752 mouse_rect.bottom = min( mouse_rect.bottom,
753 sizing_rect.bottom - minmax.ptMinTrackSize.y + capture_point.y - sizing_rect.top );
755 else if (on_bottom_border( hittest ))
757 mouse_rect.top = max( mouse_rect.top,
758 sizing_rect.top + minmax.ptMinTrackSize.y + capture_point.y - sizing_rect.bottom );
759 mouse_rect.bottom = min( mouse_rect.bottom,
760 sizing_rect.top + minmax.ptMaxTrackSize.y + capture_point.y - sizing_rect.bottom );
763 /* Retrieve a default cache DC (without using the window style) */
764 hdc = NtUserGetDCEx( parent, 0, DCX_CACHE );
766 /* we only allow disabling the full window drag for child windows */
767 if (parent) NtUserSystemParametersInfo( SPI_GETDRAGFULLWINDOWS, 0, &drag_full_windows, 0 );
769 /* repaint the window before moving it around */
770 NtUserRedrawWindow( hwnd, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN );
772 send_message( hwnd, WM_ENTERSIZEMOVE, 0, 0 );
773 set_capture_window( hwnd, GUI_INMOVESIZE, NULL );
775 for (;;)
777 int dx = 0, dy = 0;
779 if (!NtUserGetMessage( &msg, 0, 0, 0 )) break;
780 if (NtUserCallMsgFilter( &msg, MSGF_SIZE )) continue;
782 /* Exit on button-up, Return, or Esc */
783 if (msg.message == WM_LBUTTONUP ||
784 (msg.message == WM_KEYDOWN && (msg.wParam == VK_RETURN || msg.wParam == VK_ESCAPE)))
785 break;
787 if (msg.message != WM_KEYDOWN && msg.message != WM_MOUSEMOVE)
789 NtUserTranslateMessage( &msg, 0 );
790 NtUserDispatchMessage( &msg );
791 continue; /* We are not interested in other messages */
794 pt = msg.pt;
796 if (msg.message == WM_KEYDOWN)
798 switch (msg.wParam)
800 case VK_UP: pt.y -= 8; break;
801 case VK_DOWN: pt.y += 8; break;
802 case VK_LEFT: pt.x -= 8; break;
803 case VK_RIGHT: pt.x += 8; break;
807 pt.x = max( pt.x, mouse_rect.left );
808 pt.x = min( pt.x, mouse_rect.right - 1 );
809 pt.y = max( pt.y, mouse_rect.top );
810 pt.y = min( pt.y, mouse_rect.bottom - 1 );
812 if (!parent)
814 HMONITOR newmon;
815 MONITORINFO info;
817 if ((newmon = monitor_from_point( pt, MONITOR_DEFAULTTONULL, get_thread_dpi() )))
818 mon = newmon;
820 info.cbSize = sizeof(info);
821 if (mon && get_monitor_info( mon, &info ))
823 pt.x = max( pt.x, info.rcWork.left );
824 pt.x = min( pt.x, info.rcWork.right - 1 );
825 pt.y = max( pt.y, info.rcWork.top );
826 pt.y = min( pt.y, info.rcWork.bottom - 1 );
830 dx = pt.x - capture_point.x;
831 dy = pt.y - capture_point.y;
833 if (dx || dy)
835 if (!moved)
837 moved = TRUE;
838 if (!drag_full_windows)
839 draw_moving_frame( parent, hdc, &sizing_rect, thickframe );
842 if (msg.message == WM_KEYDOWN) NtUserSetCursorPos( pt.x, pt.y );
843 else
845 if (!drag_full_windows) draw_moving_frame( parent, hdc, &sizing_rect, thickframe );
846 if (hittest == HTCAPTION || hittest == HTBORDER) OffsetRect( &sizing_rect, dx, dy );
847 if (on_left_border( hittest )) sizing_rect.left += dx;
848 else if (on_right_border( hittest )) sizing_rect.right += dx;
849 if (on_top_border( hittest )) sizing_rect.top += dy;
850 else if (on_bottom_border( hittest )) sizing_rect.bottom += dy;
851 capture_point = pt;
853 /* determine the hit location */
854 if (syscommand == SC_SIZE && hittest != HTBORDER)
856 WPARAM sizing_hit = 0;
858 if (hittest >= HTLEFT && hittest <= HTBOTTOMRIGHT)
859 sizing_hit = WMSZ_LEFT + (hittest - HTLEFT);
860 send_message( hwnd, WM_SIZING, sizing_hit, (LPARAM)&sizing_rect );
862 else
863 send_message( hwnd, WM_MOVING, 0, (LPARAM)&sizing_rect );
865 if (!drag_full_windows)
866 draw_moving_frame( parent, hdc, &sizing_rect, thickframe );
867 else
869 RECT rect = sizing_rect;
870 map_window_points( 0, parent, (POINT *)&rect, 2, get_thread_dpi() );
871 NtUserSetWindowPos( hwnd, 0, rect.left, rect.top,
872 rect.right - rect.left, rect.bottom - rect.top,
873 hittest == HTCAPTION ? SWP_NOSIZE : 0 );
879 if (moved && !drag_full_windows)
880 draw_moving_frame( parent, hdc, &sizing_rect, thickframe );
882 set_capture_window( 0, GUI_INMOVESIZE, NULL );
883 NtUserReleaseDC( parent, hdc );
884 if (parent) map_window_points( 0, parent, (POINT *)&sizing_rect, 2, get_thread_dpi() );
886 if (call_hooks( WH_CBT, HCBT_MOVESIZE, (WPARAM)hwnd, (LPARAM)&sizing_rect, sizeof(sizing_rect) ))
887 moved = FALSE;
889 send_message( hwnd, WM_EXITSIZEMOVE, 0, 0 );
890 send_message( hwnd, WM_SETVISIBLE, !is_iconic(hwnd), 0 );
892 /* window moved or resized */
893 if (moved)
895 /* if the moving/resizing isn't canceled call SetWindowPos
896 * with the new position or the new size of the window
898 if (!(msg.message == WM_KEYDOWN && msg.wParam == VK_ESCAPE) )
900 /* NOTE: SWP_NOACTIVATE prevents document window activation in Word 6 */
901 if (!drag_full_windows)
902 NtUserSetWindowPos( hwnd, 0, sizing_rect.left, sizing_rect.top,
903 sizing_rect.right - sizing_rect.left,
904 sizing_rect.bottom - sizing_rect.top,
905 hittest == HTCAPTION ? SWP_NOSIZE : 0 );
907 else
909 /* restore previous size/position */
910 if (drag_full_windows)
911 NtUserSetWindowPos( hwnd, 0, orig_rect.left, orig_rect.top,
912 orig_rect.right - orig_rect.left,
913 orig_rect.bottom - orig_rect.top,
914 hittest == HTCAPTION ? SWP_NOSIZE : 0 );
918 if (is_iconic(hwnd) && !moved && (style & WS_SYSMENU))
920 /* Single click brings up the system menu when iconized */
921 send_message( hwnd, WM_SYSCOMMAND, SC_MOUSEMENU + HTSYSMENU, MAKELONG(pt.x, pt.y) );
925 /***********************************************************************
926 * track_nc_scroll_bar
928 * Track a mouse button press on the horizontal or vertical scroll-bar.
930 static void track_nc_scroll_bar( HWND hwnd, WPARAM wparam, POINT pt )
932 int scrollbar;
934 if ((wparam & 0xfff0) == SC_HSCROLL)
936 if ((wparam & 0x0f) != HTHSCROLL) return;
937 scrollbar = SB_HORZ;
939 else /* SC_VSCROLL */
941 if ((wparam & 0x0f) != HTVSCROLL) return;
942 scrollbar = SB_VERT;
944 track_scroll_bar( hwnd, scrollbar, pt );
947 static LRESULT handle_sys_command( HWND hwnd, WPARAM wparam, LPARAM lparam )
949 TRACE( "hwnd %p WM_SYSCOMMAND %lx %lx\n", hwnd, (long)wparam, lparam );
951 if (!is_window_enabled( hwnd )) return 0;
953 if (call_hooks( WH_CBT, HCBT_SYSCOMMAND, wparam, lparam, 0 ))
954 return 0;
956 if (!user_driver->pSysCommand( hwnd, wparam, lparam ))
957 return 0;
959 switch (wparam & 0xfff0)
961 case SC_SIZE:
962 case SC_MOVE:
963 sys_command_size_move( hwnd, wparam );
964 break;
966 case SC_MINIMIZE:
967 show_owned_popups( hwnd, FALSE );
968 NtUserShowWindow( hwnd, SW_MINIMIZE );
969 break;
971 case SC_MAXIMIZE:
972 if (is_iconic(hwnd)) show_owned_popups( hwnd, TRUE );
973 NtUserShowWindow( hwnd, SW_MAXIMIZE );
974 break;
976 case SC_CLOSE:
977 return send_message( hwnd, WM_CLOSE, 0, 0 );
979 case SC_VSCROLL:
980 case SC_HSCROLL:
982 POINT pt;
983 pt.x = (short)LOWORD( lparam );
984 pt.y = (short)HIWORD( lparam );
985 track_nc_scroll_bar( hwnd, wparam, pt );
987 break;
989 case SC_MOUSEMENU:
990 track_mouse_menu_bar( hwnd, wparam & 0x000F, (short)LOWORD(lparam), (short)HIWORD(lparam) );
991 break;
993 case SC_KEYMENU:
994 track_keyboard_menu_bar( hwnd, wparam, lparam );
995 break;
997 case SC_RESTORE:
998 if (is_iconic( hwnd )) show_owned_popups( hwnd, TRUE );
999 NtUserShowWindow( hwnd, SW_RESTORE );
1000 break;
1002 case SC_TASKLIST:
1003 case SC_SCREENSAVE:
1004 return 1; /* FIXME: handle on client side */
1006 case SC_HOTKEY:
1007 case SC_ARRANGE:
1008 case SC_NEXTWINDOW:
1009 case SC_PREVWINDOW:
1010 FIXME( "unimplemented WM_SYSCOMMAND %04lx\n", (long)wparam );
1011 break;
1013 default:
1014 return 1; /* handle on client side */
1016 return 0;
1019 /* Get the 'inside' rectangle of a window, i.e. the whole window rectangle
1020 * but without the borders (if any). */
1021 static void get_inside_rect( HWND hwnd, enum coords_relative relative, RECT *rect,
1022 DWORD style, DWORD ex_style )
1024 get_window_rects( hwnd, relative, rect, NULL, get_thread_dpi() );
1026 /* Remove frame from rectangle */
1027 if (has_thick_frame( style, ex_style ))
1029 InflateRect( rect, -get_system_metrics( SM_CXFRAME ), -get_system_metrics( SM_CYFRAME ));
1031 else if (has_dialog_frame( style, ex_style ))
1033 InflateRect( rect, -get_system_metrics( SM_CXDLGFRAME ), -get_system_metrics( SM_CYDLGFRAME ));
1035 else if (has_thin_frame( style ))
1037 InflateRect( rect, -get_system_metrics( SM_CXBORDER ), -get_system_metrics( SM_CYBORDER ));
1040 /* We have additional border information if the window
1041 * is a child (but not an MDI child) */
1042 if ((style & WS_CHILD) && !(ex_style & WS_EX_MDICHILD))
1044 if (ex_style & WS_EX_CLIENTEDGE)
1045 InflateRect( rect, -get_system_metrics( SM_CXEDGE ), -get_system_metrics( SM_CYEDGE ));
1046 if (ex_style & WS_EX_STATICEDGE)
1047 InflateRect( rect, -get_system_metrics( SM_CXBORDER ), -get_system_metrics( SM_CYBORDER ));
1051 void get_sys_popup_pos( HWND hwnd, RECT *rect )
1053 if (is_iconic(hwnd)) get_window_rect( hwnd, rect, get_thread_dpi() );
1054 else
1056 DWORD style = get_window_long( hwnd, GWL_STYLE );
1057 DWORD ex_style = get_window_long( hwnd, GWL_EXSTYLE );
1059 get_inside_rect( hwnd, COORDS_CLIENT, rect, style, ex_style );
1060 rect->right = rect->left + get_system_metrics( SM_CYCAPTION ) - 1;
1061 rect->bottom = rect->top + get_system_metrics( SM_CYCAPTION ) - 1;
1062 map_window_points( hwnd, 0, (POINT *)rect, 2, get_thread_dpi() );
1066 /* Draw a window frame inside the given rectangle, and update the rectangle. */
1067 static void draw_nc_frame( HDC hdc, RECT *rect, BOOL active, DWORD style, DWORD ex_style )
1069 INT width, height;
1071 if (style & WS_THICKFRAME)
1073 width = get_system_metrics( SM_CXFRAME ) - get_system_metrics( SM_CXDLGFRAME );
1074 height = get_system_metrics( SM_CYFRAME ) - get_system_metrics( SM_CYDLGFRAME );
1076 NtGdiSelectBrush( hdc, get_sys_color_brush( active ? COLOR_ACTIVEBORDER :
1077 COLOR_INACTIVEBORDER ));
1078 /* Draw frame */
1079 NtGdiPatBlt( hdc, rect->left, rect->top, rect->right - rect->left, height, PATCOPY );
1080 NtGdiPatBlt( hdc, rect->left, rect->top, width, rect->bottom - rect->top, PATCOPY );
1081 NtGdiPatBlt( hdc, rect->left, rect->bottom - 1, rect->right - rect->left, -height, PATCOPY );
1082 NtGdiPatBlt( hdc, rect->right - 1, rect->top, -width, rect->bottom - rect->top, PATCOPY );
1084 InflateRect( rect, -width, -height );
1087 /* Now the other bit of the frame */
1088 if ((style & (WS_BORDER|WS_DLGFRAME)) || (ex_style & WS_EX_DLGMODALFRAME))
1090 DWORD color;
1092 width = get_system_metrics( SM_CXDLGFRAME ) - get_system_metrics( SM_CXEDGE );
1093 height = get_system_metrics( SM_CYDLGFRAME ) - get_system_metrics( SM_CYEDGE );
1094 /* This should give a value of 1 that should also work for a border */
1096 if (ex_style & (WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE)) color = COLOR_3DFACE;
1097 else if (ex_style & WS_EX_STATICEDGE) color = COLOR_WINDOWFRAME;
1098 else if (style & (WS_DLGFRAME|WS_THICKFRAME)) color = COLOR_3DFACE;
1099 else color = COLOR_WINDOWFRAME;
1100 NtGdiSelectBrush( hdc, get_sys_color_brush( color ));
1102 /* Draw frame */
1103 NtGdiPatBlt( hdc, rect->left, rect->top,
1104 rect->right - rect->left, height, PATCOPY );
1105 NtGdiPatBlt( hdc, rect->left, rect->top,
1106 width, rect->bottom - rect->top, PATCOPY );
1107 NtGdiPatBlt( hdc, rect->left, rect->bottom - 1,
1108 rect->right - rect->left, -height, PATCOPY );
1109 NtGdiPatBlt( hdc, rect->right - 1, rect->top,
1110 -width, rect->bottom - rect->top, PATCOPY );
1112 InflateRect( rect, -width, -height );
1116 static HICON get_nc_icon_for_window( HWND hwnd )
1118 HICON icon = 0;
1119 WND *win = get_win_ptr( hwnd );
1121 if (win && win != WND_OTHER_PROCESS && win != WND_DESKTOP)
1123 icon = win->hIconSmall;
1124 if (!icon) icon = win->hIcon;
1125 release_win_ptr( win );
1127 if (!icon) icon = (HICON) get_class_long_ptr( hwnd, GCLP_HICONSM, FALSE );
1128 if (!icon) icon = (HICON) get_class_long_ptr( hwnd, GCLP_HICON, FALSE );
1130 /* If there is no icon specified and this is not a modal dialog, get the default one. */
1131 if (!icon && !(get_window_long( hwnd, GWL_EXSTYLE ) & WS_EX_DLGMODALFRAME))
1132 icon = LoadImageW( 0, (LPCWSTR)IDI_WINLOGO, IMAGE_ICON, get_system_metrics( SM_CXSMICON ),
1133 get_system_metrics( SM_CYSMICON ), LR_DEFAULTCOLOR | LR_SHARED );
1134 return icon;
1137 /* Draws the bar part (ie the big rectangle) of the caption */
1138 static void draw_caption_bar( HDC hdc, const RECT *rect, DWORD style, BOOL active, BOOL gradient )
1140 if (gradient)
1142 TRIVERTEX vertices[4];
1143 DWORD left, right;
1144 int buttons_size = get_system_metrics( SM_CYCAPTION ) - 1;
1146 static GRADIENT_RECT mesh[] = {{0, 1}, {1, 2}, {2, 3}};
1148 left = get_sys_color( active ? COLOR_ACTIVECAPTION : COLOR_INACTIVECAPTION );
1149 right = get_sys_color( active ? COLOR_GRADIENTACTIVECAPTION : COLOR_GRADIENTINACTIVECAPTION );
1150 vertices[0].Red = vertices[1].Red = GetRValue( left ) << 8;
1151 vertices[0].Green = vertices[1].Green = GetGValue( left ) << 8;
1152 vertices[0].Blue = vertices[1].Blue = GetBValue( left ) << 8;
1153 vertices[0].Alpha = vertices[1].Alpha = 0xff00;
1154 vertices[2].Red = vertices[3].Red = GetRValue( right ) << 8;
1155 vertices[2].Green = vertices[3].Green = GetGValue( right ) << 8;
1156 vertices[2].Blue = vertices[3].Blue = GetBValue( right ) << 8;
1157 vertices[2].Alpha = vertices[3].Alpha = 0xff00;
1159 if ((style & WS_SYSMENU) && ((style & WS_MAXIMIZEBOX) || (style & WS_MINIMIZEBOX)))
1160 buttons_size += 2 * (get_system_metrics( SM_CXSIZE ) + 1);
1162 /* area behind icon; solid filled with left color */
1163 vertices[0].x = rect->left;
1164 vertices[0].y = rect->top;
1165 if (style & WS_SYSMENU)
1166 vertices[1].x = min( rect->left + get_system_metrics( SM_CXSMICON ), rect->right );
1167 else
1168 vertices[1].x = vertices[0].x;
1169 vertices[1].y = rect->bottom;
1171 /* area behind text; gradient */
1172 vertices[2].x = max( vertices[1].x, rect->right - buttons_size );
1173 vertices[2].y = rect->top;
1175 /* area behind buttons; solid filled with right color */
1176 vertices[3].x = rect->right;
1177 vertices[3].y = rect->bottom;
1179 NtGdiGradientFill( hdc, vertices, 4, mesh, 3, GRADIENT_FILL_RECT_H );
1181 else
1183 DWORD color = active ? COLOR_ACTIVECAPTION : COLOR_INACTIVECAPTION;
1184 fill_rect( hdc, rect, get_sys_color_brush( color ));
1188 /* Draw the system icon */
1189 BOOL draw_nc_sys_button( HWND hwnd, HDC hdc, BOOL down )
1191 HICON icon = get_nc_icon_for_window( hwnd );
1193 if (icon)
1195 RECT rect;
1196 POINT pt;
1197 DWORD style = get_window_long( hwnd, GWL_STYLE );
1198 DWORD ex_style = get_window_long( hwnd, GWL_EXSTYLE );
1200 get_inside_rect( hwnd, COORDS_WINDOW, &rect, style, ex_style );
1201 pt.x = rect.left + 2;
1202 pt.y = rect.top + (get_system_metrics( SM_CYCAPTION ) - get_system_metrics( SM_CYSMICON )) / 2;
1203 NtUserDrawIconEx( hdc, pt.x, pt.y, icon,
1204 get_system_metrics( SM_CXSMICON ),
1205 get_system_metrics( SM_CYSMICON ), 0, 0, DI_NORMAL );
1208 return icon != 0;
1211 /* Create a square rectangle and return its width */
1212 static int make_square_rect( RECT *src, RECT *dst )
1214 int width = src->right - src->left;
1215 int height = src->bottom - src->top;
1216 int small_diam = width > height ? height : width;
1218 *dst = *src;
1220 /* Make it a square box */
1221 if (width < height)
1223 dst->top += (height - width) / 2;
1224 dst->bottom = dst->top + small_diam;
1226 else if (width > height)
1228 dst->left += (width - height) / 2;
1229 dst->right = dst->left + small_diam;
1232 return small_diam;
1235 static void draw_checked_rect( HDC dc, RECT *rect )
1237 if (get_sys_color( COLOR_BTNHIGHLIGHT ) == RGB( 255, 255, 255 ))
1239 HBRUSH prev_brush;
1240 DWORD prev_bg;
1242 fill_rect( dc, rect, get_sys_color_brush( COLOR_BTNFACE ));
1243 NtGdiGetAndSetDCDword( dc, NtGdiSetBkColor, RGB(255, 255, 255), &prev_bg );
1244 prev_brush = NtGdiSelectBrush( dc, get_55aa_brush() );
1245 NtGdiPatBlt( dc, rect->left, rect->top, rect->right-rect->left,
1246 rect->bottom-rect->top, 0x00fa0089 );
1247 NtGdiSelectBrush( dc, prev_brush );
1248 NtGdiGetAndSetDCDword( dc, NtGdiSetBkColor, prev_bg, NULL );
1250 else
1252 fill_rect( dc, rect, get_sys_color_brush( COLOR_BTNHIGHLIGHT ));
1256 static BOOL draw_push_button( HDC dc, RECT *r, UINT flags )
1258 RECT rect = *r;
1259 UINT edge;
1261 if (flags & (DFCS_PUSHED | DFCS_CHECKED | DFCS_FLAT))
1262 edge = EDGE_SUNKEN;
1263 else
1264 edge = EDGE_RAISED;
1266 if (flags & DFCS_CHECKED)
1268 if (flags & DFCS_MONO)
1269 draw_rect_edge( dc, &rect, edge, BF_MONO|BF_RECT|BF_ADJUST, 1 );
1270 else
1271 draw_rect_edge( dc, &rect, edge, (flags & DFCS_FLAT)|BF_RECT|BF_SOFT|BF_ADJUST, 1 );
1272 if (!(flags & DFCS_TRANSPARENT)) draw_checked_rect( dc, &rect );
1274 else
1276 if (flags & DFCS_MONO)
1278 draw_rect_edge( dc, &rect, edge, BF_MONO|BF_RECT|BF_ADJUST, 1 );
1279 if (!(flags & DFCS_TRANSPARENT))
1280 fill_rect( dc, &rect, get_sys_color_brush( COLOR_BTNFACE ));
1282 else
1284 UINT edge_flags = BF_RECT | BF_SOFT | (flags & DFCS_FLAT);
1285 if (!(flags & DFCS_TRANSPARENT)) edge_flags |= BF_MIDDLE;
1286 draw_rect_edge( dc, r, edge, edge_flags, 1 );
1290 /* Adjust rectangle if asked */
1291 if (flags & DFCS_ADJUSTRECT) InflateRect( r, -2, -2 );
1292 return TRUE;
1295 static BOOL draw_frame_caption( HDC dc, RECT *r, UINT flags )
1297 RECT rect;
1298 int small_diam = make_square_rect( r, &rect ) - 2;
1299 HFONT prev_font, font;
1300 int color_idx = flags & DFCS_INACTIVE ? COLOR_BTNSHADOW : COLOR_BTNTEXT;
1301 int xc = (rect.left + rect.right) / 2;
1302 int yc = (rect.top + rect.bottom) / 2;
1303 LOGFONTW lf = { 0 };
1304 WCHAR str[] = {0, 0};
1305 DWORD prev_align, prev_bk;
1306 COLORREF prev_color;
1307 SIZE size;
1309 static const WCHAR marlettW[] = {'M','a','r','l','e','t','t',0};
1311 draw_push_button( dc, r, flags & 0xff00 );
1313 switch (flags & 0xf)
1315 case DFCS_CAPTIONCLOSE: str[0] = 0x72; break;
1316 case DFCS_CAPTIONHELP: str[0] = 0x73; break;
1317 case DFCS_CAPTIONMIN: str[0] = 0x30; break;
1318 case DFCS_CAPTIONMAX: str[0] = 0x31; break;
1319 case DFCS_CAPTIONRESTORE: str[0] = 0x32; break;
1320 default:
1321 WARN( "Invalid caption; flags=0x%04x\n", flags );
1322 return FALSE;
1325 lf.lfHeight = -small_diam;
1326 lf.lfWeight = FW_NORMAL;
1327 lf.lfCharSet = SYMBOL_CHARSET;
1328 lf.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE;
1329 memcpy( lf.lfFaceName, marlettW, sizeof(marlettW) );
1330 font = NtGdiHfontCreate( &lf, sizeof(lf), 0, 0, NULL );
1331 NtGdiGetAndSetDCDword( dc, NtGdiSetTextAlign, TA_TOP | TA_LEFT, &prev_align );
1332 NtGdiGetAndSetDCDword( dc, NtGdiSetBkMode, TRANSPARENT, &prev_bk );
1333 NtGdiGetDCDword( dc, NtGdiGetTextColor, &prev_color );
1334 prev_font = NtGdiSelectFont( dc, font );
1335 NtGdiGetTextExtentExW( dc, str, 1, 0, NULL, NULL, &size, 0 );
1337 if (flags & DFCS_INACTIVE)
1339 NtGdiGetAndSetDCDword( dc, NtGdiSetTextColor, get_sys_color(COLOR_BTNHIGHLIGHT), NULL );
1340 NtGdiExtTextOutW( dc, xc-size.cx/2+1, yc-size.cy/2+1, 0, NULL, str, 1, NULL, 0 );
1342 NtGdiGetAndSetDCDword( dc, NtGdiSetTextColor, get_sys_color( color_idx ), NULL );
1343 NtGdiExtTextOutW( dc, xc-size.cx/2, yc-size.cy/2, 0, NULL, str, 1, NULL, 0 );
1345 NtGdiSelectFont(dc, prev_font);
1346 NtGdiGetAndSetDCDword( dc, NtGdiSetTextColor, prev_color, NULL );
1347 NtGdiGetAndSetDCDword( dc, NtGdiSetTextAlign, prev_align, NULL );
1348 NtGdiGetAndSetDCDword( dc, NtGdiSetBkMode, prev_bk, NULL );
1349 NtGdiDeleteObjectApp( font );
1351 return TRUE;
1354 BOOL draw_menu_button( HWND hwnd, HDC dc, RECT *r, enum NONCLIENT_BUTTON_TYPE type, BOOL down,
1355 BOOL grayed )
1357 struct draw_non_client_button_params params;
1358 void *ret_ptr;
1359 ULONG ret_len;
1361 params.hwnd = hwnd;
1362 params.hdc = dc;
1363 params.type = type;
1364 params.rect = *r;
1365 params.down = down;
1366 params.grayed = grayed;
1367 return KeUserModeCallback( NtUserDrawNonClientButton, &params, sizeof(params), &ret_ptr, &ret_len );
1370 BOOL draw_frame_menu( HDC dc, RECT *r, UINT flags )
1372 RECT rect;
1373 int dmall_diam = make_square_rect( r, &rect );
1374 HBRUSH prev_brush;
1375 HPEN prev_pen;
1376 POINT points[6];
1377 int xe, ye;
1378 int xc, yc;
1379 BOOL retval = TRUE;
1380 ULONG count;
1381 int i;
1383 fill_rect( dc, r, GetStockObject( WHITE_BRUSH ));
1385 prev_brush = NtGdiSelectBrush( dc, GetStockObject( BLACK_BRUSH ));
1386 prev_pen = NtGdiSelectPen( dc, GetStockObject( BLACK_PEN ));
1388 switch (flags & 0xff)
1390 case DFCS_MENUARROW:
1391 i = 187 * dmall_diam / 750;
1392 points[2].x = rect.left + 468 * dmall_diam/ 750;
1393 points[2].y = rect.top + 352 * dmall_diam/ 750 + 1;
1394 points[0].y = points[2].y - i;
1395 points[1].y = points[2].y + i;
1396 points[0].x = points[1].x = points[2].x - i;
1397 count = 3;
1398 NtGdiPolyPolyDraw( dc, points, &count, 1, NtGdiPolyPolygon );
1399 break;
1401 case DFCS_MENUBULLET:
1402 xe = rect.left;
1403 ye = rect.top + dmall_diam - dmall_diam / 2;
1404 xc = rect.left + dmall_diam - dmall_diam / 2;
1405 yc = rect.top + dmall_diam - dmall_diam / 2;
1406 i = 234 * dmall_diam / 750;
1407 i = i < 1 ? 1 : i;
1408 SetRect( &rect, xc - i + i / 2, yc - i + i / 2, xc + i / 2, yc + i / 2 );
1409 NtGdiArcInternal( NtGdiPie, dc, rect.left, rect.top, rect.right, rect.bottom,
1410 xe, ye, xe, ye );
1411 break;
1413 case DFCS_MENUCHECK:
1414 points[0].x = rect.left + 253 * dmall_diam / 1000;
1415 points[0].y = rect.top + 445 * dmall_diam / 1000;
1416 points[1].x = rect.left + 409 * dmall_diam / 1000;
1417 points[1].y = points[0].y + (points[1].x - points[0].x);
1418 points[2].x = rect.left + 690 * dmall_diam / 1000;
1419 points[2].y = points[1].y - (points[2].x - points[1].x);
1420 points[3].x = points[2].x;
1421 points[3].y = points[2].y + 3 * dmall_diam / 16;
1422 points[4].x = points[1].x;
1423 points[4].y = points[1].y + 3 * dmall_diam / 16;
1424 points[5].x = points[0].x;
1425 points[5].y = points[0].y + 3 * dmall_diam / 16;
1426 count = 6;
1427 NtGdiPolyPolyDraw( dc, points, &count, 1, NtGdiPolyPolygon );
1428 break;
1430 default:
1431 WARN( "Invalid menu; flags=0x%04x\n", flags );
1432 retval = FALSE;
1433 break;
1436 NtGdiSelectPen( dc, prev_pen );
1437 NtGdiSelectBrush( dc, prev_brush );
1438 return retval;
1441 static void draw_close_button( HWND hwnd, HDC hdc, BOOL down, BOOL grayed )
1443 RECT rect;
1444 DWORD style = get_window_long( hwnd, GWL_STYLE );
1445 DWORD ex_style = get_window_long( hwnd, GWL_EXSTYLE );
1446 UINT flags = DFCS_CAPTIONCLOSE;
1448 get_inside_rect( hwnd, COORDS_WINDOW, &rect, style, ex_style );
1450 /* A tool window has a smaller Close button */
1451 if (ex_style & WS_EX_TOOLWINDOW)
1453 /* Windows does not use SM_CXSMSIZE and SM_CYSMSIZE
1454 * it uses 11x11 for the close button in tool window */
1455 const int bmp_height = 11;
1456 const int bmp_width = 11;
1457 int caption_height = get_system_metrics( SM_CYSMCAPTION );
1459 rect.top = rect.top + (caption_height - 1 - bmp_height) / 2;
1460 rect.left = rect.right - (caption_height + 1 + bmp_width) / 2;
1461 rect.bottom = rect.top + bmp_height;
1462 rect.right = rect.left + bmp_width;
1464 else
1466 rect.left = rect.right - get_system_metrics( SM_CXSIZE );
1467 rect.bottom = rect.top + get_system_metrics( SM_CYSIZE ) - 2;
1468 rect.top += 2;
1469 rect.right -= 2;
1472 if (down) flags |= DFCS_PUSHED;
1473 if (grayed) flags |= DFCS_INACTIVE;
1474 draw_frame_caption( hdc, &rect, flags );
1477 static void draw_max_button( HWND hwnd, HDC hdc, BOOL down, BOOL grayed )
1479 RECT rect;
1480 UINT flags;
1481 DWORD style = get_window_long( hwnd, GWL_STYLE );
1482 DWORD ex_style = get_window_long( hwnd, GWL_EXSTYLE );
1484 /* never draw maximize box when window has WS_EX_TOOLWINDOW style */
1485 if (ex_style & WS_EX_TOOLWINDOW) return;
1487 flags = (style & WS_MAXIMIZE) ? DFCS_CAPTIONRESTORE : DFCS_CAPTIONMAX;
1489 get_inside_rect( hwnd, COORDS_WINDOW, &rect, style, ex_style );
1490 if (style & WS_SYSMENU) rect.right -= get_system_metrics( SM_CXSIZE );
1491 rect.left = rect.right - get_system_metrics( SM_CXSIZE );
1492 rect.bottom = rect.top + get_system_metrics( SM_CYSIZE ) - 2;
1493 rect.top += 2;
1494 rect.right -= 2;
1495 if (down) flags |= DFCS_PUSHED;
1496 if (grayed) flags |= DFCS_INACTIVE;
1497 draw_frame_caption( hdc, &rect, flags );
1500 static void draw_min_button( HWND hwnd, HDC hdc, BOOL down, BOOL grayed )
1502 RECT rect;
1503 UINT flags;
1504 DWORD style = get_window_long( hwnd, GWL_STYLE );
1505 DWORD ex_style = get_window_long( hwnd, GWL_EXSTYLE );
1507 /* never draw minimize box when window has WS_EX_TOOLWINDOW style */
1508 if (ex_style & WS_EX_TOOLWINDOW) return;
1510 flags = (style & WS_MINIMIZE) ? DFCS_CAPTIONRESTORE : DFCS_CAPTIONMIN;
1512 get_inside_rect( hwnd, COORDS_WINDOW, &rect, style, ex_style );
1513 if (style & WS_SYSMENU)
1514 rect.right -= get_system_metrics( SM_CXSIZE );
1515 if (style & (WS_MAXIMIZEBOX|WS_MINIMIZEBOX))
1516 rect.right -= get_system_metrics( SM_CXSIZE ) - 2;
1517 rect.left = rect.right - get_system_metrics( SM_CXSIZE );
1518 rect.bottom = rect.top + get_system_metrics( SM_CYSIZE ) - 2;
1519 rect.top += 2;
1520 rect.right -= 2;
1521 if (down) flags |= DFCS_PUSHED;
1522 if (grayed) flags |= DFCS_INACTIVE;
1523 draw_frame_caption( hdc, &rect, flags );
1526 static void draw_nc_caption( HDC hdc, RECT *rect, HWND hwnd, DWORD style,
1527 DWORD ex_style, BOOL active )
1529 RECT r = *rect;
1530 WCHAR buffer[256];
1531 HPEN prev_pen;
1532 HMENU sys_menu;
1533 BOOL gradient = FALSE;
1534 UINT pen_color = COLOR_3DFACE;
1535 int len;
1537 if ((ex_style & (WS_EX_STATICEDGE|WS_EX_CLIENTEDGE|WS_EX_DLGMODALFRAME)) == WS_EX_STATICEDGE)
1538 pen_color = COLOR_WINDOWFRAME;
1539 prev_pen = NtGdiSelectPen( hdc, get_sys_color_pen( pen_color ));
1540 NtGdiMoveTo( hdc, r.left, r.bottom - 1, NULL );
1541 NtGdiLineTo( hdc, r.right, r.bottom - 1 );
1542 NtGdiSelectPen( hdc, prev_pen );
1543 r.bottom--;
1545 NtUserSystemParametersInfo( SPI_GETGRADIENTCAPTIONS, 0, &gradient, 0 );
1546 draw_caption_bar( hdc, &r, style, active, gradient );
1548 if ((style & WS_SYSMENU) && !(ex_style & WS_EX_TOOLWINDOW))
1550 if (draw_nc_sys_button( hwnd, hdc, FALSE ))
1551 r.left += get_system_metrics( SM_CXSMICON ) + 2;
1554 if (style & WS_SYSMENU)
1556 UINT state;
1558 /* Go get the sysmenu */
1559 sys_menu = NtUserGetSystemMenu( hwnd, FALSE );
1560 state = get_menu_state( sys_menu, SC_CLOSE, MF_BYCOMMAND );
1562 /* Draw a grayed close button if disabled or if SC_CLOSE is not there */
1563 draw_close_button( hwnd, hdc, FALSE,
1564 (state & (MF_DISABLED | MF_GRAYED)) || (state == 0xFFFFFFFF) );
1565 r.right -= get_system_metrics( SM_CYCAPTION ) - 1;
1567 if ((style & WS_MAXIMIZEBOX) || (style & WS_MINIMIZEBOX))
1569 draw_max_button( hwnd, hdc, FALSE, !(style & WS_MAXIMIZEBOX) );
1570 r.right -= get_system_metrics( SM_CXSIZE ) + 1;
1572 draw_min_button( hwnd, hdc, FALSE, !(style & WS_MINIMIZEBOX) );
1573 r.right -= get_system_metrics( SM_CXSIZE ) + 1;
1577 len = get_window_text( hwnd, buffer, ARRAY_SIZE( buffer ));
1578 if (len)
1580 NONCLIENTMETRICSW nclm;
1581 HFONT hFont, hOldFont;
1582 nclm.cbSize = sizeof(nclm);
1583 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &nclm, 0 );
1584 if (ex_style & WS_EX_TOOLWINDOW)
1585 hFont = NtGdiHfontCreate( &nclm.lfSmCaptionFont, sizeof(nclm.lfSmCaptionFont), 0, 0, NULL );
1586 else
1587 hFont = NtGdiHfontCreate( &nclm.lfCaptionFont, sizeof(nclm.lfCaptionFont), 0, 0, NULL );
1588 hOldFont = NtGdiSelectFont( hdc, hFont );
1589 if (active)
1590 NtGdiGetAndSetDCDword( hdc, NtGdiSetTextColor, get_sys_color( COLOR_CAPTIONTEXT ), NULL );
1591 else
1592 NtGdiGetAndSetDCDword( hdc, NtGdiSetTextColor, get_sys_color( COLOR_INACTIVECAPTIONTEXT ), NULL );
1593 NtGdiGetAndSetDCDword( hdc, NtGdiSetBkMode, TRANSPARENT, NULL );
1594 r.left += 2;
1595 DrawTextW( hdc, buffer, -1, &r,
1596 DT_SINGLELINE | DT_VCENTER | DT_NOPREFIX | DT_LEFT );
1597 NtGdiDeleteObjectApp( NtGdiSelectFont( hdc, hOldFont ));
1601 /***********************************************************************
1602 * NtUserDrawCaptionTemp (win32u.@)
1604 BOOL WINAPI NtUserDrawCaptionTemp( HWND hwnd, HDC hdc, const RECT *rect, HFONT font,
1605 HICON icon, const WCHAR *str, UINT flags )
1607 RECT rc = *rect;
1609 TRACE( "(%p,%p,%p,%p,%p,%s,%08x)\n", hwnd, hdc, rect, font, icon, debugstr_w(str), flags );
1611 /* drawing background */
1612 if (flags & DC_INBUTTON)
1614 fill_rect( hdc, &rc, get_sys_color_brush( COLOR_3DFACE ));
1616 if (flags & DC_ACTIVE)
1618 HBRUSH hbr = NtGdiSelectBrush( hdc, get_55aa_brush() );
1619 NtGdiPatBlt( hdc, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, 0xfa0089 );
1620 NtGdiSelectBrush( hdc, hbr );
1623 else
1625 DWORD style = get_window_long( hwnd, GWL_STYLE );
1626 draw_caption_bar( hdc, &rc, style, flags & DC_ACTIVE, flags & DC_GRADIENT );
1629 /* drawing icon */
1630 if ((flags & DC_ICON) && !(flags & DC_SMALLCAP))
1632 POINT pt;
1634 pt.x = rc.left + 2;
1635 pt.y = (rc.bottom + rc.top - get_system_metrics( SM_CYSMICON )) / 2;
1637 if (!icon) icon = get_nc_icon_for_window( hwnd );
1638 NtUserDrawIconEx( hdc, pt.x, pt.y, icon, get_system_metrics( SM_CXSMICON ),
1639 get_system_metrics( SM_CYSMICON ), 0, 0, DI_NORMAL );
1640 rc.left = pt.x + get_system_metrics( SM_CXSMICON );
1643 /* drawing text */
1644 if (flags & DC_TEXT)
1646 HFONT prev_font;
1647 WCHAR text[128];
1648 DWORD color;
1650 if (flags & DC_INBUTTON)
1651 color = COLOR_BTNTEXT;
1652 else if (flags & DC_ACTIVE)
1653 color = COLOR_CAPTIONTEXT;
1654 else
1655 color = COLOR_INACTIVECAPTIONTEXT;
1656 NtGdiGetAndSetDCDword( hdc, NtGdiSetTextColor, get_sys_color( color ), NULL );
1657 NtGdiGetAndSetDCDword( hdc, NtGdiSetBkMode, TRANSPARENT, NULL );
1659 if (font)
1660 prev_font = NtGdiSelectFont( hdc, font );
1661 else
1663 NONCLIENTMETRICSW nclm;
1664 HFONT new_font;
1665 LOGFONTW *lf;
1666 nclm.cbSize = sizeof(NONCLIENTMETRICSW);
1667 NtUserSystemParametersInfo( SPI_GETNONCLIENTMETRICS, 0, &nclm, 0 );
1668 lf = (flags & DC_SMALLCAP) ? &nclm.lfSmCaptionFont : &nclm.lfCaptionFont;
1669 new_font = NtGdiHfontCreate( lf, sizeof(*lf), 0, 0, NULL );
1670 prev_font = NtGdiSelectFont( hdc, new_font );
1673 if (!str)
1675 if (!get_window_text( hwnd, text, ARRAY_SIZE( text ))) text[0] = 0;
1676 str = text;
1678 rc.left += 2;
1679 DrawTextW( hdc, str, -1, &rc, ((flags & 0x4000) ? DT_CENTER : DT_LEFT) |
1680 DT_SINGLELINE | DT_VCENTER | DT_NOPREFIX | DT_END_ELLIPSIS );
1682 if (font)
1683 NtGdiSelectFont( hdc, prev_font );
1684 else
1685 NtGdiDeleteObjectApp( NtGdiSelectFont( hdc, prev_font ));
1688 if (flags & 0x2000) FIXME( "undocumented flag (0x2000)!\n" );
1689 return FALSE;
1692 /* Paint the non-client area for windows */
1693 static void nc_paint( HWND hwnd, HRGN clip )
1695 HDC hdc;
1696 RECT rfuzz, rect, clip_rect;
1697 BOOL active;
1698 WND *win;
1699 DWORD style, ex_style;
1700 WORD flags;
1701 HRGN hrgn;
1702 RECT rectClient;
1704 if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS) return;
1705 style = win->dwStyle;
1706 ex_style = win->dwExStyle;
1707 flags = win->flags;
1708 release_win_ptr( win );
1710 active = flags & WIN_NCACTIVATED;
1712 TRACE( "%p %d\n", hwnd, active );
1714 get_window_rects( hwnd, COORDS_SCREEN, NULL, &rectClient, get_thread_dpi() );
1715 hrgn = NtGdiCreateRectRgn( rectClient.left, rectClient.top,
1716 rectClient.right, rectClient.bottom );
1718 if (clip > (HRGN)1)
1720 NtGdiCombineRgn( hrgn, clip, hrgn, RGN_DIFF );
1721 hdc = NtUserGetDCEx( hwnd, hrgn, DCX_USESTYLE | DCX_WINDOW | DCX_INTERSECTRGN );
1723 else
1725 hdc = NtUserGetDCEx( hwnd, hrgn, DCX_USESTYLE | DCX_WINDOW | DCX_EXCLUDERGN );
1728 if (!hdc)
1730 NtGdiDeleteObjectApp( hrgn );
1731 return;
1734 get_window_rects( hwnd, COORDS_WINDOW, &rect, NULL, get_thread_dpi() );
1735 NtGdiGetAppClipBox( hdc, &clip_rect );
1737 NtGdiSelectPen( hdc, get_sys_color_pen( COLOR_WINDOWFRAME ));
1739 if (has_static_outer_frame( ex_style ))
1740 draw_rect_edge( hdc, &rect, BDR_SUNKENOUTER, BF_RECT | BF_ADJUST, 1 );
1741 else if (has_big_frame( style, ex_style ))
1742 draw_rect_edge( hdc, &rect, EDGE_RAISED, BF_RECT | BF_ADJUST, 1 );
1744 draw_nc_frame( hdc, &rect, active, style, ex_style );
1746 if ((style & WS_CAPTION) == WS_CAPTION)
1748 RECT r = rect;
1749 if (ex_style & WS_EX_TOOLWINDOW)
1751 r.bottom = rect.top + get_system_metrics( SM_CYSMCAPTION );
1752 rect.top += get_system_metrics( SM_CYSMCAPTION );
1754 else {
1755 r.bottom = rect.top + get_system_metrics( SM_CYCAPTION );
1756 rect.top += get_system_metrics( SM_CYCAPTION );
1759 if (intersect_rect( &rfuzz, &r, &clip_rect ))
1760 draw_nc_caption( hdc, &r, hwnd, style, ex_style, active );
1763 if (has_menu( hwnd, style ))
1765 RECT r = rect;
1766 HMENU menu;
1768 r.bottom = rect.top + get_system_metrics( SM_CYMENU );
1770 TRACE( "drawing menu with rect %s\n", wine_dbgstr_rect( &r ));
1772 menu = get_menu( hwnd );
1773 if (!is_menu( menu )) rect.top += get_system_metrics( SM_CYMENU );
1774 else rect.top += NtUserDrawMenuBarTemp( hwnd, hdc, &r, menu, NULL );
1777 TRACE( "rect after menu %s\n", wine_dbgstr_rect( &rect ));
1779 if (ex_style & WS_EX_CLIENTEDGE)
1780 draw_rect_edge( hdc, &rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST, 1 );
1782 /* Draw the scroll-bars */
1783 draw_nc_scrollbar( hwnd, hdc, style & WS_HSCROLL, style & WS_VSCROLL );
1785 /* Draw the "size-box" */
1786 if ((style & WS_VSCROLL) && (style & WS_HSCROLL))
1788 RECT r = rect;
1789 if ((ex_style & WS_EX_LEFTSCROLLBAR) != 0)
1790 r.right = r.left + get_system_metrics( SM_CXVSCROLL ) + 1;
1791 else
1792 r.left = r.right - get_system_metrics( SM_CXVSCROLL ) + 1;
1793 r.top = r.bottom - get_system_metrics( SM_CYHSCROLL ) + 1;
1794 fill_rect( hdc, &r, get_sys_color_brush( COLOR_BTNFACE ) );
1797 NtUserReleaseDC( hwnd, hdc );
1800 static LRESULT handle_nc_paint( HWND hwnd , HRGN clip )
1802 HWND parent = NtUserGetAncestor( hwnd, GA_PARENT );
1803 DWORD style = get_window_long( hwnd, GWL_STYLE );
1805 if (style & WS_VISIBLE)
1807 nc_paint( hwnd, clip );
1809 if (parent == get_desktop_window())
1810 NtUserPostMessage( parent, WM_PARENTNOTIFY, WM_NCPAINT, (LPARAM)hwnd );
1812 return 0;
1815 static LRESULT handle_nc_activate( HWND hwnd, WPARAM wparam, LPARAM lparam )
1817 /* Lotus Notes draws menu descriptions in the caption of its main
1818 * window. When it wants to restore original "system" view, it just
1819 * sends WM_NCACTIVATE message to itself. Any optimizations here in
1820 * attempt to minimize redrawings lead to a not restored caption.
1822 if (wparam) win_set_flags( hwnd, WIN_NCACTIVATED, 0 );
1823 else win_set_flags( hwnd, 0, WIN_NCACTIVATED );
1825 /* This isn't documented but is reproducible in at least XP SP2 and
1826 * Outlook 2007 depends on it
1828 if (lparam != -1)
1830 nc_paint( hwnd, (HRGN)1 );
1832 if (NtUserGetAncestor( hwnd, GA_PARENT ) == get_desktop_window())
1833 NtUserPostMessage( get_desktop_window(), WM_PARENTNOTIFY, WM_NCACTIVATE, (LPARAM)hwnd );
1836 return TRUE;
1839 static void handle_nc_calc_size( HWND hwnd, WPARAM wparam, RECT *win_rect )
1841 RECT rect = { 0, 0, 0, 0 };
1842 LONG style = get_window_long( hwnd, GWL_STYLE );
1843 LONG ex_style = get_window_long( hwnd, GWL_EXSTYLE );
1845 if (!win_rect) return;
1847 if (!(style & WS_MINIMIZE))
1849 AdjustWindowRectEx( &rect, style, FALSE, ex_style & ~WS_EX_CLIENTEDGE );
1851 win_rect->left -= rect.left;
1852 win_rect->top -= rect.top;
1853 win_rect->right -= rect.right;
1854 win_rect->bottom -= rect.bottom;
1856 if (((style & (WS_CHILD | WS_POPUP)) != WS_CHILD) && get_menu( hwnd ))
1858 TRACE( "getting menu bar height with hwnd %p, width %d, at (%d, %d)\n",
1859 hwnd, (int)(win_rect->right - win_rect->left), (int)-rect.left, (int)-rect.top );
1861 win_rect->top += get_menu_bar_height( hwnd, win_rect->right - win_rect->left,
1862 -rect.left, -rect.top );
1865 if (ex_style & WS_EX_CLIENTEDGE)
1866 if (win_rect->right - win_rect->left > 2 * get_system_metrics( SM_CXEDGE ) &&
1867 win_rect->bottom - win_rect->top > 2 * get_system_metrics( SM_CYEDGE ))
1868 InflateRect( win_rect, -get_system_metrics( SM_CXEDGE ),
1869 -get_system_metrics( SM_CYEDGE ));
1871 if ((style & WS_VSCROLL) &&
1872 win_rect->right - win_rect->left >= get_system_metrics( SM_CXVSCROLL ))
1874 /* rectangle is in screen coords when wparam is false */
1875 if (!wparam && (ex_style & WS_EX_LAYOUTRTL)) ex_style ^= WS_EX_LEFTSCROLLBAR;
1877 if (ex_style & WS_EX_LEFTSCROLLBAR)
1878 win_rect->left += get_system_metrics( SM_CXVSCROLL );
1879 else
1880 win_rect->right -= get_system_metrics( SM_CXVSCROLL );
1883 if ((style & WS_HSCROLL) &&
1884 win_rect->bottom - win_rect->top > get_system_metrics( SM_CYHSCROLL ))
1886 win_rect->bottom -= get_system_metrics( SM_CYHSCROLL );
1889 if (win_rect->top > win_rect->bottom) win_rect->bottom = win_rect->top;
1890 if (win_rect->left > win_rect->right) win_rect->right = win_rect->left;
1892 else
1894 win_rect->right = win_rect->left;
1895 win_rect->bottom = win_rect->top;
1899 LRESULT handle_nc_hit_test( HWND hwnd, POINT pt )
1901 RECT rect, client_rect;
1902 DWORD style, ex_style;
1904 TRACE( "hwnd %p pt %d,%d\n", hwnd, (int)pt.x, (int)pt.y );
1906 get_window_rects( hwnd, COORDS_SCREEN, &rect, &client_rect, get_thread_dpi() );
1907 if (!PtInRect( &rect, pt )) return HTNOWHERE;
1909 style = get_window_long( hwnd, GWL_STYLE );
1910 ex_style = get_window_long( hwnd, GWL_EXSTYLE );
1912 if (PtInRect( &client_rect, pt )) return HTCLIENT;
1914 /* Check borders */
1915 if (has_thick_frame( style, ex_style ))
1917 InflateRect( &rect, -get_system_metrics( SM_CXFRAME ), -get_system_metrics( SM_CYFRAME ));
1918 if (!PtInRect( &rect, pt ))
1920 /* Check top sizing border */
1921 if (pt.y < rect.top)
1923 if (pt.x < rect.left + get_system_metrics( SM_CXSIZE )) return HTTOPLEFT;
1924 if (pt.x >= rect.right - get_system_metrics( SM_CXSIZE )) return HTTOPRIGHT;
1925 return HTTOP;
1927 /* Check bottom sizing border */
1928 if (pt.y >= rect.bottom)
1930 if (pt.x < rect.left + get_system_metrics( SM_CXSIZE )) return HTBOTTOMLEFT;
1931 if (pt.x >= rect.right - get_system_metrics( SM_CXSIZE )) return HTBOTTOMRIGHT;
1932 return HTBOTTOM;
1934 /* Check left sizing border */
1935 if (pt.x < rect.left)
1937 if (pt.y < rect.top + get_system_metrics( SM_CYSIZE )) return HTTOPLEFT;
1938 if (pt.y >= rect.bottom - get_system_metrics( SM_CYSIZE )) return HTBOTTOMLEFT;
1939 return HTLEFT;
1941 /* Check right sizing border */
1942 if (pt.x >= rect.right)
1944 if (pt.y < rect.top + get_system_metrics( SM_CYSIZE )) return HTTOPRIGHT;
1945 if (pt.y >= rect.bottom-get_system_metrics( SM_CYSIZE )) return HTBOTTOMRIGHT;
1946 return HTRIGHT;
1950 else /* No thick frame */
1952 if (has_dialog_frame( style, ex_style ))
1953 InflateRect( &rect, -get_system_metrics( SM_CXDLGFRAME ),
1954 -get_system_metrics( SM_CYDLGFRAME ));
1955 else if (has_thin_frame( style ))
1956 InflateRect(&rect, -get_system_metrics( SM_CXBORDER ),
1957 -get_system_metrics( SM_CYBORDER ));
1958 if (!PtInRect( &rect, pt )) return HTBORDER;
1961 /* Check caption */
1962 if ((style & WS_CAPTION) == WS_CAPTION)
1964 if (ex_style & WS_EX_TOOLWINDOW)
1965 rect.top += get_system_metrics( SM_CYSMCAPTION ) - 1;
1966 else
1967 rect.top += get_system_metrics( SM_CYCAPTION ) - 1;
1968 if (!PtInRect( &rect, pt ))
1970 BOOL min_or_max_box = (style & WS_SYSMENU) && (style & (WS_MINIMIZEBOX | WS_MAXIMIZEBOX));
1971 if (ex_style & WS_EX_LAYOUTRTL)
1973 /* Check system menu */
1974 if ((style & WS_SYSMENU) && !(ex_style & WS_EX_TOOLWINDOW) &&
1975 get_nc_icon_for_window( hwnd ))
1977 rect.right -= get_system_metrics( SM_CYCAPTION ) - 1;
1978 if (pt.x > rect.right) return HTSYSMENU;
1981 /* Check close button */
1982 if (style & WS_SYSMENU)
1984 rect.left += get_system_metrics( SM_CYCAPTION );
1985 if (pt.x < rect.left) return HTCLOSE;
1988 if (min_or_max_box && !(ex_style & WS_EX_TOOLWINDOW))
1990 /* Check maximize box */
1991 rect.left += get_system_metrics( SM_CXSIZE );
1992 if (pt.x < rect.left) return HTMAXBUTTON;
1994 /* Check minimize box */
1995 rect.left += get_system_metrics( SM_CXSIZE );
1996 if (pt.x < rect.left) return HTMINBUTTON;
1999 else
2001 /* Check system menu */
2002 if ((style & WS_SYSMENU) && !(ex_style & WS_EX_TOOLWINDOW) &&
2003 get_nc_icon_for_window( hwnd ))
2005 rect.left += get_system_metrics( SM_CYCAPTION ) - 1;
2006 if (pt.x < rect.left) return HTSYSMENU;
2009 /* Check close button */
2010 if (style & WS_SYSMENU)
2012 rect.right -= get_system_metrics( SM_CYCAPTION );
2013 if (pt.x > rect.right) return HTCLOSE;
2016 if (min_or_max_box && !(ex_style & WS_EX_TOOLWINDOW))
2018 /* Check maximize box */
2019 rect.right -= get_system_metrics( SM_CXSIZE );
2020 if (pt.x > rect.right) return HTMAXBUTTON;
2022 /* Check minimize box */
2023 rect.right -= get_system_metrics( SM_CXSIZE );
2024 if (pt.x > rect.right) return HTMINBUTTON;
2027 return HTCAPTION;
2031 /* Check menu bar */
2032 if (has_menu( hwnd, style ) && (pt.y < client_rect.top) &&
2033 (pt.x >= client_rect.left) && (pt.x < client_rect.right))
2034 return HTMENU;
2036 /* Check vertical scroll bar */
2037 if (ex_style & WS_EX_LAYOUTRTL) ex_style ^= WS_EX_LEFTSCROLLBAR;
2038 if (style & WS_VSCROLL)
2040 if (ex_style & WS_EX_LEFTSCROLLBAR)
2041 client_rect.left -= get_system_metrics( SM_CXVSCROLL );
2042 else
2043 client_rect.right += get_system_metrics( SM_CXVSCROLL );
2044 if (PtInRect( &client_rect, pt )) return HTVSCROLL;
2047 /* Check horizontal scroll bar */
2048 if (style & WS_HSCROLL)
2050 client_rect.bottom += get_system_metrics( SM_CYHSCROLL );
2051 if (PtInRect( &client_rect, pt ))
2053 /* Check size box */
2054 if ((style & WS_VSCROLL) &&
2055 ((ex_style & WS_EX_LEFTSCROLLBAR)
2056 ? (pt.x <= client_rect.left + get_system_metrics( SM_CXVSCROLL ))
2057 : (pt.x >= client_rect.right - get_system_metrics( SM_CXVSCROLL ))))
2058 return HTSIZE;
2059 return HTHSCROLL;
2063 /* Has to return HTNOWHERE if nothing was found
2064 Could happen when a window has a customized non client area */
2065 return HTNOWHERE;
2068 static void track_min_max_box( HWND hwnd, WORD wparam )
2070 HDC hdc = NtUserGetWindowDC( hwnd );
2071 DWORD style = get_window_long( hwnd, GWL_STYLE );
2072 HMENU sys_menu = NtUserGetSystemMenu(hwnd, FALSE);
2073 void (*paint_button)( HWND, HDC, BOOL, BOOL );
2074 BOOL pressed = TRUE;
2075 UINT state;
2076 MSG msg;
2078 if (wparam == HTMINBUTTON)
2080 /* If the style is not present, do nothing */
2081 if (!(style & WS_MINIMIZEBOX)) return;
2083 /* Check if the sysmenu item for minimize is there */
2084 state = get_menu_state( sys_menu, SC_MINIMIZE, MF_BYCOMMAND );
2085 paint_button = draw_min_button;
2087 else
2089 /* If the style is not present, do nothing */
2090 if (!(style & WS_MAXIMIZEBOX)) return;
2092 /* Check if the sysmenu item for maximize is there */
2093 state = get_menu_state( sys_menu, SC_MAXIMIZE, MF_BYCOMMAND );
2094 paint_button = draw_max_button;
2097 NtUserSetCapture( hwnd );
2098 paint_button( hwnd, hdc, TRUE, FALSE);
2100 for (;;)
2102 BOOL oldstate = pressed;
2104 if (!NtUserGetMessage( &msg, 0, WM_MOUSEFIRST, WM_MOUSELAST )) break;
2105 if (NtUserCallMsgFilter( &msg, MSGF_MAX )) continue;
2106 if(msg.message == WM_LBUTTONUP) break;
2107 if(msg.message != WM_MOUSEMOVE) continue;
2109 pressed = handle_nc_hit_test( hwnd, msg.pt ) == wparam;
2110 if (pressed != oldstate) paint_button( hwnd, hdc, pressed, FALSE);
2113 if (pressed) paint_button( hwnd, hdc, FALSE, FALSE );
2115 release_capture();
2116 NtUserReleaseDC( hwnd, hdc );
2118 /* If the minimize or maximize items of the sysmenu are not there
2119 * or if the style is not present, do nothing */
2120 if (!pressed || state == 0xffffffff) return;
2122 if (wparam == HTMINBUTTON)
2123 send_message( hwnd, WM_SYSCOMMAND,
2124 is_iconic( hwnd ) ? SC_RESTORE : SC_MINIMIZE, MAKELONG( msg.pt.x, msg.pt.y ));
2125 else
2126 send_message( hwnd, WM_SYSCOMMAND,
2127 is_zoomed( hwnd ) ? SC_RESTORE : SC_MAXIMIZE, MAKELONG( msg.pt.x, msg.pt.y ));
2130 static void track_close_button( HWND hwnd, WPARAM wparam, LPARAM lparam )
2132 HMENU sys_menu;
2133 BOOL pressed = TRUE;
2134 UINT state;
2135 MSG msg;
2136 HDC hdc;
2138 if (!(sys_menu = NtUserGetSystemMenu( hwnd, FALSE ))) return;
2140 state = get_menu_state( sys_menu, SC_CLOSE, MF_BYCOMMAND );
2142 /* If the close item of the sysmenu is disabled or not present do nothing */
2143 if((state & MF_DISABLED) || (state & MF_GRAYED) || state == 0xFFFFFFFF) return;
2144 hdc = NtUserGetWindowDC( hwnd );
2145 NtUserSetCapture( hwnd );
2146 draw_close_button( hwnd, hdc, TRUE, FALSE );
2148 for (;;)
2150 BOOL oldstate = pressed;
2152 if (!NtUserGetMessage( &msg, 0, WM_MOUSEFIRST, WM_MOUSELAST )) break;
2153 if (NtUserCallMsgFilter( &msg, MSGF_MAX )) continue;
2154 if (msg.message == WM_LBUTTONUP) break;
2155 if (msg.message != WM_MOUSEMOVE) continue;
2157 pressed = handle_nc_hit_test( hwnd, msg.pt ) == wparam;
2158 if (pressed != oldstate) draw_close_button( hwnd, hdc, pressed, FALSE );
2161 if (pressed) draw_close_button( hwnd, hdc, FALSE, FALSE );
2163 release_capture();
2164 NtUserReleaseDC( hwnd, hdc );
2165 if (pressed) send_message( hwnd, WM_SYSCOMMAND, SC_CLOSE, lparam );
2168 static LRESULT handle_nc_lbutton_down( HWND hwnd, WPARAM wparam, LPARAM lparam )
2170 LONG style = get_window_long( hwnd, GWL_STYLE );
2172 switch (wparam) /* Hit test */
2174 case HTCAPTION:
2176 HWND top = hwnd, parent;
2177 for (;;)
2179 if ((get_window_long( top, GWL_STYLE ) & (WS_POPUP | WS_CHILD)) != WS_CHILD)
2180 break;
2181 parent = NtUserGetAncestor( top, GA_PARENT );
2182 if (!parent || parent == get_desktop_window()) break;
2183 top = parent;
2186 if (set_foreground_window( top, TRUE ) || (get_active_window() == top))
2187 send_message( hwnd, WM_SYSCOMMAND, SC_MOVE + HTCAPTION, lparam );
2188 break;
2191 case HTSYSMENU:
2192 if (style & WS_SYSMENU)
2194 HDC hdc = NtUserGetWindowDC( hwnd );
2195 draw_nc_sys_button( hwnd, hdc, TRUE );
2196 NtUserReleaseDC( hwnd, hdc );
2197 send_message( hwnd, WM_SYSCOMMAND, SC_MOUSEMENU + HTSYSMENU, lparam );
2199 break;
2201 case HTMENU:
2202 send_message( hwnd, WM_SYSCOMMAND, SC_MOUSEMENU, lparam );
2203 break;
2205 case HTHSCROLL:
2206 send_message( hwnd, WM_SYSCOMMAND, SC_HSCROLL + HTHSCROLL, lparam );
2207 break;
2209 case HTVSCROLL:
2210 send_message( hwnd, WM_SYSCOMMAND, SC_VSCROLL + HTVSCROLL, lparam );
2211 break;
2213 case HTMINBUTTON:
2214 case HTMAXBUTTON:
2215 track_min_max_box( hwnd, wparam );
2216 break;
2218 case HTCLOSE:
2219 track_close_button( hwnd, wparam, lparam );
2220 break;
2222 case HTLEFT:
2223 case HTRIGHT:
2224 case HTTOP:
2225 case HTTOPLEFT:
2226 case HTTOPRIGHT:
2227 case HTBOTTOM:
2228 case HTBOTTOMLEFT:
2229 case HTBOTTOMRIGHT:
2230 send_message( hwnd, WM_SYSCOMMAND, SC_SIZE + wparam - (HTLEFT - WMSZ_LEFT), lparam );
2231 break;
2233 case HTBORDER:
2234 break;
2236 return 0;
2239 static LRESULT handle_nc_rbutton_down( HWND hwnd, WPARAM wparam, LPARAM lparam )
2241 int hittest = wparam;
2242 MSG msg;
2244 switch (hittest)
2246 case HTCAPTION:
2247 case HTSYSMENU:
2248 NtUserSetCapture( hwnd );
2249 for (;;)
2251 if (!NtUserGetMessage( &msg, 0, WM_MOUSEFIRST, WM_MOUSELAST )) break;
2252 if (NtUserCallMsgFilter( &msg, MSGF_MAX )) continue;
2253 if (msg.message == WM_RBUTTONUP)
2255 hittest = handle_nc_hit_test( hwnd, msg.pt );
2256 break;
2259 release_capture();
2260 if (hittest == HTCAPTION || hittest == HTSYSMENU)
2261 send_message( hwnd, WM_CONTEXTMENU, (WPARAM)hwnd, MAKELPARAM( msg.pt.x, msg.pt.y ));
2262 break;
2264 return 0;
2267 static LRESULT handle_nc_button_dbl_click( HWND hwnd, WPARAM wparam, LPARAM lparam )
2269 /* if this is an icon, send a restore since we are handling a double click */
2270 if (is_iconic(hwnd))
2272 send_message( hwnd, WM_SYSCOMMAND, SC_RESTORE, lparam );
2273 return 0;
2276 switch (wparam) /* Hit test */
2278 case HTCAPTION:
2279 /* stop processing if WS_MAXIMIZEBOX is missing */
2280 if (get_window_long( hwnd, GWL_STYLE ) & WS_MAXIMIZEBOX)
2281 send_message( hwnd, WM_SYSCOMMAND,
2282 is_zoomed( hwnd ) ? SC_RESTORE : SC_MAXIMIZE, lparam );
2283 break;
2285 case HTSYSMENU:
2287 HMENU hSysMenu = NtUserGetSystemMenu( hwnd, FALSE );
2288 UINT state = get_menu_state( hSysMenu, SC_CLOSE, MF_BYCOMMAND );
2290 /* If the close item of the sysmenu is disabled or not present do nothing */
2291 if ((state & (MF_DISABLED | MF_GRAYED)) || state == 0xffffffff)
2292 break;
2294 send_message( hwnd, WM_SYSCOMMAND, SC_CLOSE, lparam );
2295 break;
2298 case HTHSCROLL:
2299 send_message( hwnd, WM_SYSCOMMAND, SC_HSCROLL + HTHSCROLL, lparam );
2300 break;
2302 case HTVSCROLL:
2303 send_message( hwnd, WM_SYSCOMMAND, SC_VSCROLL + HTVSCROLL, lparam );
2304 break;
2306 return 0;
2309 static HBRUSH handle_control_color( HDC hdc, UINT type )
2311 if (type == CTLCOLOR_SCROLLBAR)
2313 HBRUSH hb = get_sys_color_brush( COLOR_SCROLLBAR );
2314 COLORREF bk = get_sys_color( COLOR_3DHILIGHT );
2315 NtGdiGetAndSetDCDword( hdc, NtGdiSetTextColor, get_sys_color( COLOR_3DFACE ), NULL );
2316 NtGdiGetAndSetDCDword( hdc, NtGdiSetBkColor, bk, NULL );
2318 /* if COLOR_WINDOW happens to be the same as COLOR_3DHILIGHT
2319 * we better use 0x55aa bitmap brush to make scrollbar's background
2320 * look different from the window background.
2322 if (bk == get_sys_color( COLOR_WINDOW )) return get_55aa_brush();
2324 NtGdiUnrealizeObject( hb );
2325 return hb;
2328 NtGdiGetAndSetDCDword( hdc, NtGdiSetTextColor, get_sys_color( COLOR_WINDOWTEXT ), NULL );
2330 if (type == CTLCOLOR_EDIT || type == CTLCOLOR_LISTBOX)
2331 NtGdiGetAndSetDCDword( hdc, NtGdiSetBkColor, get_sys_color( COLOR_WINDOW ), NULL );
2332 else
2334 NtGdiGetAndSetDCDword( hdc, NtGdiSetBkColor, get_sys_color( COLOR_3DFACE ), NULL);
2335 return get_sys_color_brush( COLOR_3DFACE );
2338 return get_sys_color_brush( COLOR_WINDOW );
2341 static LRESULT handle_nc_mouse_move( HWND hwnd, WPARAM wparam, LPARAM lparam )
2343 RECT rect;
2344 POINT pt;
2346 TRACE( "hwnd=%p wparam=%#lx lparam=%#lx\n", hwnd, (long)wparam, lparam );
2348 if (wparam != HTHSCROLL && wparam != HTVSCROLL)
2349 return 0;
2351 get_window_rects( hwnd, COORDS_CLIENT, &rect, NULL, get_thread_dpi() );
2353 pt.x = (short)LOWORD( lparam );
2354 pt.y = (short)HIWORD( lparam );
2355 screen_to_client( hwnd, &pt );
2356 pt.x -= rect.left;
2357 pt.y -= rect.top;
2358 handle_scroll_event( hwnd, wparam == HTHSCROLL ? SB_HORZ : SB_VERT, WM_NCMOUSEMOVE, pt );
2359 return 0;
2362 static LRESULT handle_nc_mouse_leave( HWND hwnd )
2364 LONG style = get_window_long( hwnd, GWL_STYLE );
2365 POINT pt = {0, 0};
2367 TRACE( "hwnd=%p\n", hwnd );
2369 if (style & WS_HSCROLL)
2370 handle_scroll_event( hwnd, SB_HORZ, WM_NCMOUSELEAVE, pt );
2371 if (style & WS_VSCROLL)
2372 handle_scroll_event( hwnd, SB_VERT, WM_NCMOUSELEAVE, pt );
2373 return 0;
2377 LRESULT default_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, BOOL ansi )
2379 LRESULT result = 0;
2381 switch (msg)
2383 case WM_NCCREATE:
2384 if (lparam)
2386 CREATESTRUCTW *cs = (CREATESTRUCTW *)lparam;
2387 set_window_text( hwnd, cs->lpszName, ansi );
2389 if (cs->style & (WS_HSCROLL | WS_VSCROLL))
2391 SCROLLINFO si = { .cbSize = sizeof(si), .fMask = SIF_ALL, .nMax = 100 };
2392 NtUserSetScrollInfo( hwnd, SB_HORZ, &si, FALSE );
2393 NtUserSetScrollInfo( hwnd, SB_VERT, &si, FALSE );
2396 result = 1;
2398 break;
2400 case WM_NCDESTROY:
2402 WND *win = get_win_ptr( hwnd );
2403 if (!win) return 0;
2404 free( win->text );
2405 win->text = NULL;
2406 free( win->pScroll );
2407 win->pScroll = NULL;
2408 release_win_ptr( win );
2409 break;
2412 case WM_NCCALCSIZE:
2413 handle_nc_calc_size( hwnd, wparam, (RECT *)lparam );
2414 break;
2416 case WM_NCHITTEST:
2418 POINT pt;
2419 pt.x = (short)LOWORD( lparam );
2420 pt.y = (short)HIWORD( lparam );
2421 return handle_nc_hit_test( hwnd, pt );
2424 case WM_NCPAINT:
2425 return handle_nc_paint( hwnd, (HRGN)wparam );
2427 case WM_NCACTIVATE:
2428 return handle_nc_activate( hwnd, wparam, lparam );
2430 case WM_NCLBUTTONDOWN:
2431 return handle_nc_lbutton_down( hwnd, wparam, lparam );
2433 case WM_NCRBUTTONDOWN:
2434 return handle_nc_rbutton_down( hwnd, wparam, lparam );
2436 case WM_LBUTTONDBLCLK:
2437 return handle_nc_button_dbl_click( hwnd, HTCLIENT, lparam );
2439 case WM_NCLBUTTONDBLCLK:
2440 return handle_nc_button_dbl_click( hwnd, wparam, lparam );
2442 case WM_NCMOUSEMOVE:
2443 result = handle_nc_mouse_move( hwnd, wparam, lparam );
2444 break;
2446 case WM_NCMOUSELEAVE:
2447 result = handle_nc_mouse_leave( hwnd );
2448 break;
2450 case WM_RBUTTONUP:
2452 POINT pt;
2453 pt.x = (short)LOWORD( lparam );
2454 pt.y = (short)HIWORD( lparam );
2455 client_to_screen( hwnd, &pt );
2456 send_message( hwnd, WM_CONTEXTMENU, (WPARAM)hwnd, MAKELPARAM( pt.x, pt.y ));
2458 break;
2460 case WM_NCRBUTTONUP:
2461 break;
2463 case WM_XBUTTONUP:
2464 case WM_NCXBUTTONUP:
2465 if (HIWORD(wparam) == XBUTTON1 || HIWORD(wparam) == XBUTTON2)
2467 send_message( hwnd, WM_APPCOMMAND, (WPARAM)hwnd,
2468 MAKELPARAM( LOWORD( wparam ), FAPPCOMMAND_MOUSE | HIWORD( wparam )));
2470 break;
2472 case WM_CONTEXTMENU:
2473 if (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD)
2474 send_message( get_parent( hwnd ), msg, (WPARAM)hwnd, lparam );
2475 else
2477 LONG hitcode;
2478 POINT pt;
2479 pt.x = (short)LOWORD( lparam );
2480 pt.y = (short)HIWORD( lparam );
2481 hitcode = handle_nc_hit_test( hwnd, pt );
2483 /* Track system popup if click was in the caption area. */
2484 if (hitcode == HTCAPTION || hitcode == HTSYSMENU)
2485 NtUserTrackPopupMenuEx( NtUserGetSystemMenu( hwnd, FALSE ),
2486 TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
2487 pt.x, pt.y, hwnd, NULL );
2489 break;
2491 case WM_POPUPSYSTEMMENU:
2492 /* This is an undocumented message used by the windows taskbar to
2493 * display the system menu of windows that belong to other processes. */
2494 NtUserTrackPopupMenuEx( NtUserGetSystemMenu( hwnd, FALSE ), TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
2495 (short)LOWORD(lparam), (short)HIWORD(lparam), hwnd, NULL );
2496 return 0;
2498 case WM_WINDOWPOSCHANGING:
2499 return handle_window_pos_changing( hwnd, (WINDOWPOS *)lparam );
2501 case WM_WINDOWPOSCHANGED:
2502 handle_window_pos_changed( hwnd, (const WINDOWPOS *)lparam );
2503 break;
2505 case WM_PAINTICON:
2506 case WM_PAINT:
2508 PAINTSTRUCT ps;
2509 HDC hdc = NtUserBeginPaint( hwnd, &ps );
2510 if (hdc)
2512 HICON icon;
2513 if (is_iconic(hwnd) && ((icon = UlongToHandle( get_class_long( hwnd, GCLP_HICON, FALSE )))))
2515 RECT rc;
2516 int x, y;
2518 get_client_rect( hwnd, &rc );
2519 x = (rc.right - rc.left - get_system_metrics( SM_CXICON )) / 2;
2520 y = (rc.bottom - rc.top - get_system_metrics( SM_CYICON )) / 2;
2521 TRACE( "Painting class icon: vis rect=(%s)\n", wine_dbgstr_rect(&ps.rcPaint) );
2522 NtUserDrawIconEx( hdc, x, y, icon, 0, 0, 0, 0, DI_NORMAL | DI_COMPAT | DI_DEFAULTSIZE );
2524 NtUserEndPaint( hwnd, &ps );
2526 break;
2529 case WM_SYNCPAINT:
2530 NtUserRedrawWindow ( hwnd, NULL, 0, RDW_ERASENOW | RDW_ERASE | RDW_ALLCHILDREN );
2531 return 0;
2533 case WM_SETREDRAW:
2534 if (wparam) set_window_style( hwnd, WS_VISIBLE, 0 );
2535 else
2537 NtUserRedrawWindow( hwnd, NULL, 0, RDW_ALLCHILDREN | RDW_VALIDATE );
2538 set_window_style( hwnd, 0, WS_VISIBLE );
2540 return 0;
2542 case WM_CLOSE:
2543 NtUserDestroyWindow( hwnd );
2544 return 0;
2546 case WM_MOUSEACTIVATE:
2547 if (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD)
2549 result = send_message( get_parent(hwnd), WM_MOUSEACTIVATE, wparam, lparam );
2550 if (result) break;
2553 /* Caption clicks are handled by handle_nc_lbutton_down() */
2554 result = HIWORD(lparam) == WM_LBUTTONDOWN && LOWORD(lparam) == HTCAPTION ?
2555 MA_NOACTIVATE : MA_ACTIVATE;
2556 break;
2558 case WM_ACTIVATE:
2559 /* The default action in Windows is to set the keyboard focus to
2560 * the window, if it's being activated and not minimized */
2561 if (LOWORD(wparam) != WA_INACTIVE && !is_iconic( hwnd )) NtUserSetFocus( hwnd );
2562 break;
2564 case WM_MOUSEWHEEL:
2565 if (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD)
2566 result = send_message( get_parent( hwnd ), WM_MOUSEWHEEL, wparam, lparam );
2567 break;
2569 case WM_ERASEBKGND:
2570 case WM_ICONERASEBKGND:
2572 RECT rect;
2573 HDC hdc = (HDC)wparam;
2574 HBRUSH hbr = UlongToHandle( get_class_long( hwnd, GCLP_HBRBACKGROUND, FALSE ));
2575 if (!hbr) break;
2577 if (get_class_long( hwnd, GCL_STYLE, FALSE ) & CS_PARENTDC)
2579 /* can't use GetClipBox with a parent DC or we fill the whole parent */
2580 get_client_rect( hwnd, &rect );
2581 NtGdiTransformPoints( hdc, (POINT *)&rect, (POINT *)&rect, 1, NtGdiDPtoLP );
2583 else NtGdiGetAppClipBox( hdc, &rect );
2584 fill_rect( hdc, &rect, hbr );
2585 return 1;
2588 case WM_GETDLGCODE:
2589 break;
2591 case WM_CANCELMODE:
2592 menu_sys_key = 0;
2593 end_menu( hwnd );
2594 if (get_capture() == hwnd) release_capture();
2595 break;
2597 case WM_SETTEXT:
2598 result = set_window_text( hwnd, (void *)lparam, ansi );
2599 if (result && (get_window_long( hwnd, GWL_STYLE ) & WS_CAPTION) == WS_CAPTION)
2600 handle_nc_paint( hwnd , (HRGN)1 ); /* repaint caption */
2601 break;
2603 case WM_GETTEXTLENGTH:
2605 WND *win = get_win_ptr( hwnd );
2606 if (win && win->text)
2608 if (ansi)
2609 result = win32u_wctomb_size( &ansi_cp, win->text, wcslen( win->text ));
2610 else
2611 result = wcslen( win->text );
2613 release_win_ptr( win );
2615 break;
2617 case WM_GETTEXT:
2618 if (wparam)
2620 WND *win;
2622 if (!(win = get_win_ptr( hwnd ))) break;
2624 __TRY
2626 if (ansi)
2628 char *dest = (char *)lparam;
2629 if (win->text)
2630 result = win32u_wctomb( &ansi_cp, dest, wparam - 1,
2631 win->text, wcslen( win->text ));
2632 dest[result] = 0;
2634 else
2636 WCHAR *dest = (WCHAR *)lparam;
2637 if (win->text) result = min( wcslen( win->text ), wparam - 1 );
2638 if (result) memcpy( dest, win->text, result * sizeof(WCHAR) );
2639 dest[result] = 0;
2642 __EXCEPT
2645 __ENDTRY
2647 release_win_ptr( win );
2649 break;
2651 case WM_SETICON:
2652 result = (LRESULT)set_window_icon( hwnd, wparam, (HICON)lparam );
2653 if ((get_window_long( hwnd, GWL_STYLE ) & WS_CAPTION) == WS_CAPTION)
2654 handle_nc_paint( hwnd , (HRGN)1 ); /* repaint caption */
2655 break;
2657 case WM_GETICON:
2658 result = (LRESULT)get_window_icon( hwnd, wparam );
2659 break;
2661 case WM_SETCURSOR:
2662 if (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD)
2664 /* with the exception of the border around a resizable window,
2665 * give the parent first chance to set the cursor */
2666 if ((LOWORD( lparam ) < HTSIZEFIRST) || (LOWORD( lparam ) > HTSIZELAST))
2668 HWND parent = get_parent( hwnd );
2669 if (parent != get_desktop_window() &&
2670 send_message( parent, WM_SETCURSOR, wparam, lparam )) return TRUE;
2673 handle_set_cursor( hwnd, wparam, lparam );
2674 break;
2676 case WM_SHOWWINDOW:
2678 LONG style = get_window_long( hwnd, GWL_STYLE );
2679 WND *win;
2680 if (!lparam) break; /* sent from ShowWindow */
2681 if ((style & WS_VISIBLE) && wparam) break;
2682 if (!(style & WS_VISIBLE) && !wparam) break;
2683 if (!get_window_relative( hwnd, GW_OWNER )) break;
2684 if (!(win = get_win_ptr( hwnd ))) break;
2685 if (win == WND_OTHER_PROCESS) break;
2686 if (wparam)
2688 if (!(win->flags & WIN_NEEDS_SHOW_OWNEDPOPUP))
2690 release_win_ptr( win );
2691 break;
2693 win->flags &= ~WIN_NEEDS_SHOW_OWNEDPOPUP;
2695 else win->flags |= WIN_NEEDS_SHOW_OWNEDPOPUP;
2696 release_win_ptr( win );
2697 NtUserShowWindow( hwnd, wparam ? SW_SHOWNOACTIVATE : SW_HIDE );
2698 break;
2701 case WM_CTLCOLORMSGBOX:
2702 case WM_CTLCOLOREDIT:
2703 case WM_CTLCOLORLISTBOX:
2704 case WM_CTLCOLORBTN:
2705 case WM_CTLCOLORDLG:
2706 case WM_CTLCOLORSTATIC:
2707 case WM_CTLCOLORSCROLLBAR:
2708 return (LRESULT)handle_control_color( (HDC)wparam, msg - WM_CTLCOLORMSGBOX );
2710 case WM_CTLCOLOR:
2711 return (LRESULT)handle_control_color( (HDC)wparam, HIWORD( lparam ));
2713 case WM_SYSCOMMAND:
2714 result = handle_sys_command( hwnd, wparam, lparam );
2715 break;
2717 case WM_LBUTTONDOWN:
2718 case WM_RBUTTONDOWN:
2719 case WM_MBUTTONDOWN:
2720 f10_key = menu_sys_key = 0;
2721 break;
2723 case WM_KEYDOWN:
2724 if (wparam == VK_F10) f10_key = VK_F10;
2725 break;
2727 case WM_SYSKEYDOWN:
2728 if (HIWORD( lparam ) & KEYDATA_ALT)
2730 if ((wparam == VK_MENU || wparam == VK_LMENU || wparam == VK_RMENU) && !menu_sys_key)
2731 menu_sys_key = 1;
2732 else
2733 menu_sys_key = 0;
2735 f10_key = 0;
2737 if (wparam == VK_F4) /* try to close the window */
2739 HWND top = NtUserGetAncestor( hwnd, GA_ROOT );
2740 if (!(get_class_long( top, GCL_STYLE, FALSE ) & CS_NOCLOSE))
2741 NtUserPostMessage( top, WM_SYSCOMMAND, SC_CLOSE, 0 );
2744 else if (wparam == VK_F10)
2746 if (NtUserGetKeyState(VK_SHIFT) & 0x8000)
2747 send_message( hwnd, WM_CONTEXTMENU, (WPARAM)hwnd, -1 );
2748 f10_key = 1;
2750 else if (wparam == VK_ESCAPE && (NtUserGetKeyState( VK_SHIFT ) & 0x8000))
2751 send_message( hwnd, WM_SYSCOMMAND, SC_KEYMENU, ' ' );
2752 break;
2754 case WM_KEYUP:
2755 case WM_SYSKEYUP:
2756 /* Press and release F10 or ALT */
2757 if (((wparam == VK_MENU || wparam == VK_LMENU || wparam == VK_RMENU) && menu_sys_key) ||
2758 (wparam == VK_F10 && f10_key))
2759 send_message( NtUserGetAncestor( hwnd, GA_ROOT ), WM_SYSCOMMAND, SC_KEYMENU, 0 );
2760 menu_sys_key = f10_key = 0;
2761 break;
2763 case WM_SYSCHAR:
2764 menu_sys_key = 0;
2765 if (wparam == '\r' && is_iconic( hwnd ))
2767 NtUserPostMessage( hwnd, WM_SYSCOMMAND, SC_RESTORE, 0 );
2768 break;
2770 if ((HIWORD( lparam ) & KEYDATA_ALT) && wparam)
2772 WCHAR wch;
2773 if (ansi)
2775 char ch = wparam;
2776 win32u_mbtowc( &ansi_cp, &wch, 1, &ch, 1 );
2778 else wch = wparam;
2779 if (wch == '\t' || wch == '\x1b') break;
2780 if (wch == ' ' && (get_window_long( hwnd, GWL_STYLE ) & WS_CHILD))
2781 send_message( get_parent( hwnd ), msg, wch, lparam );
2782 else
2783 send_message( hwnd, WM_SYSCOMMAND, SC_KEYMENU, wch );
2785 else if (wparam != '\x1b') /* Ctrl-Esc */
2786 message_beep(0);
2787 break;
2789 case WM_KEYF1:
2791 HELPINFO hi;
2793 hi.cbSize = sizeof(HELPINFO);
2794 get_cursor_pos( &hi.MousePos );
2795 if (is_menu_active())
2797 MENUINFO info = { .cbSize = sizeof(info), .fMask = MIM_HELPID };
2798 hi.iContextType = HELPINFO_MENUITEM;
2799 hi.hItemHandle = is_menu_active();
2800 hi.iCtrlId = NtUserMenuItemFromPoint( hwnd, hi.hItemHandle,
2801 hi.MousePos.x, hi.MousePos.y );
2802 get_menu_info( hi.hItemHandle, &info );
2803 hi.dwContextId = info.dwContextHelpID;
2805 else
2807 hi.iContextType = HELPINFO_WINDOW;
2808 hi.hItemHandle = hwnd;
2809 hi.iCtrlId = get_window_long_ptr( hwnd, GWLP_ID, FALSE );
2810 hi.dwContextId = get_window_context_help_id( hwnd );
2812 send_message( hwnd, WM_HELP, 0, (LPARAM)&hi );
2813 break;
2816 case WM_PRINT:
2817 if ((lparam & PRF_CHECKVISIBLE) && !is_window_visible ( hwnd )) break;
2819 if (lparam & (PRF_CHILDREN | PRF_OWNED | PRF_NONCLIENT))
2820 WARN( "WM_PRINT message with unsupported lparam %lx\n", lparam );
2822 if (lparam & PRF_ERASEBKGND) send_message( hwnd, WM_ERASEBKGND, wparam, 0 );
2823 if (lparam & PRF_CLIENT) send_message(hwnd, WM_PRINTCLIENT, wparam, lparam );
2824 break;
2826 case WM_APPCOMMAND:
2828 HWND parent = get_parent( hwnd );
2829 if (!parent)
2830 call_hooks( WH_SHELL, HSHELL_APPCOMMAND, wparam, lparam, 0 );
2831 else
2832 send_message( parent, msg, wparam, lparam );
2833 break;
2836 case WM_VKEYTOITEM:
2837 case WM_CHARTOITEM:
2838 result = -1;
2839 break;
2841 case WM_DROPOBJECT:
2842 result = DRAG_FILE;
2843 break;
2845 case WM_QUERYDROPOBJECT:
2846 result = (get_window_long( hwnd, GWL_EXSTYLE ) & WS_EX_ACCEPTFILES) != 0;
2847 break;
2849 case WM_QUERYDRAGICON:
2851 UINT len;
2852 HICON icon = (HICON)get_class_long_ptr( hwnd, GCLP_HICON, FALSE );
2853 HINSTANCE instance = (HINSTANCE)get_window_long_ptr( hwnd, GWLP_HINSTANCE, FALSE );
2855 if (icon)
2857 result = (LRESULT)icon;
2858 break;
2861 for (len = 1; len < 64; len++)
2863 if((icon = LoadImageW( instance, MAKEINTRESOURCEW( len ), IMAGE_ICON, 0, 0,
2864 LR_SHARED | LR_DEFAULTSIZE )))
2866 result = (LRESULT)icon;
2867 break;
2870 if (!result) result = (LRESULT)LoadImageW( 0, (WCHAR *)IDI_APPLICATION, IMAGE_ICON,
2871 0, 0, LR_SHARED | LR_DEFAULTSIZE );
2872 break;
2875 case WM_ISACTIVEICON:
2876 result = (win_get_flags( hwnd ) & WIN_NCACTIVATED) != 0;
2877 break;
2879 case WM_NOTIFYFORMAT:
2880 result = is_window_unicode(hwnd) ? NFR_UNICODE : NFR_ANSI;
2881 break;
2883 case WM_QUERYOPEN:
2884 case WM_QUERYENDSESSION:
2885 result = 1;
2886 break;
2888 case WM_HELP:
2889 send_message( get_parent( hwnd ), msg, wparam, lparam );
2890 break;
2892 case WM_STYLECHANGED:
2893 if (wparam == GWL_STYLE && (get_window_long( hwnd, GWL_EXSTYLE ) & WS_EX_LAYERED))
2895 STYLESTRUCT *style = (STYLESTRUCT *)lparam;
2896 if ((style->styleOld ^ style->styleNew) & (WS_CAPTION|WS_THICKFRAME|WS_VSCROLL|WS_HSCROLL))
2897 NtUserSetWindowPos( hwnd, 0, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOZORDER |
2898 SWP_NOSIZE | SWP_NOMOVE | SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE );
2900 break;
2902 case WM_INPUTLANGCHANGEREQUEST:
2903 NtUserActivateKeyboardLayout( (HKL)lparam, 0 );
2904 break;
2906 case WM_INPUTLANGCHANGE:
2908 struct user_thread_info *info = get_user_thread_info();
2909 HWND *win_array = list_window_children( 0, hwnd, NULL, 0 );
2910 int count = 0;
2911 info->kbd_layout = (HKL)lparam;
2913 if (!win_array)
2914 break;
2915 while (win_array[count])
2916 send_message( win_array[count++], WM_INPUTLANGCHANGE, wparam, lparam );
2917 free( win_array );
2918 break;
2921 case WM_IME_SETCONTEXT:
2922 case WM_IME_COMPOSITION:
2923 case WM_IME_STARTCOMPOSITION:
2924 case WM_IME_ENDCOMPOSITION:
2925 case WM_IME_SELECT:
2926 case WM_IME_NOTIFY:
2927 case WM_IME_CONTROL:
2929 HWND ime_hwnd = get_default_ime_window( hwnd );
2930 if (ime_hwnd)
2931 result = NtUserMessageCall( ime_hwnd, msg, wparam, lparam,
2932 0, NtUserSendMessage, ansi );
2934 break;
2937 return result;
2940 LRESULT desktop_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
2942 static const WCHAR wine_display_device_guidW[] =
2943 {'_','_','w','i','n','e','_','d','i','s','p','l','a','y','_','d','e','v','i','c','e',
2944 '_','g','u','i','d',0};
2946 switch (msg)
2948 case WM_NCCREATE:
2950 CREATESTRUCTW *cs = (CREATESTRUCTW *)lparam;
2951 const GUID *guid = cs->lpCreateParams;
2953 if (guid)
2955 ATOM atom = 0;
2956 char buffer[37];
2957 WCHAR bufferW[37];
2959 if (NtUserGetAncestor( hwnd, GA_PARENT )) return FALSE; /* refuse to create non-desktop window */
2961 sprintf( buffer, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
2962 (unsigned int)guid->Data1, guid->Data2, guid->Data3,
2963 guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
2964 guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7] );
2965 NtAddAtom( bufferW, asciiz_to_unicode( bufferW, buffer ) - sizeof(WCHAR), &atom );
2966 NtUserSetProp( hwnd, wine_display_device_guidW, ULongToHandle( atom ) );
2968 return TRUE;
2970 case WM_NCCALCSIZE:
2971 return 0;
2972 case WM_DISPLAYCHANGE:
2973 return user_driver->pDesktopWindowProc( hwnd, msg, wparam, lparam );
2974 default:
2975 if (msg >= WM_USER && hwnd == get_desktop_window())
2976 return user_driver->pDesktopWindowProc( hwnd, msg, wparam, lparam );
2979 return default_window_proc( hwnd, msg, wparam, lparam, FALSE );
2982 /***********************************************************************
2983 * NtUserGetTitleBarInfo (win32u.@)
2985 BOOL WINAPI NtUserGetTitleBarInfo( HWND hwnd, TITLEBARINFO *info )
2987 DWORD style, ex_style;
2989 TRACE( "(%p %p)\n", hwnd, info );
2991 if (!info)
2993 RtlSetLastWin32Error( ERROR_NOACCESS );
2994 return FALSE;
2997 if (info->cbSize != sizeof(TITLEBARINFO))
2999 TRACE( "Invalid TITLEBARINFO size: %d\n", (int)info->cbSize );
3000 RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
3001 return FALSE;
3004 style = get_window_long( hwnd, GWL_STYLE );
3005 ex_style = get_window_long( hwnd, GWL_EXSTYLE );
3006 get_inside_rect( hwnd, COORDS_SCREEN, &info->rcTitleBar, style, ex_style );
3008 info->rcTitleBar.bottom = info->rcTitleBar.top;
3009 if (ex_style & WS_EX_TOOLWINDOW)
3010 info->rcTitleBar.bottom += get_system_metrics( SM_CYSMCAPTION );
3011 else
3013 info->rcTitleBar.bottom += get_system_metrics( SM_CYCAPTION );
3014 info->rcTitleBar.left += get_system_metrics( SM_CXSIZE );
3017 memset( info->rgstate, 0, sizeof(info->rgstate) );
3018 info->rgstate[0] = STATE_SYSTEM_FOCUSABLE;
3020 if (style & WS_CAPTION)
3022 info->rgstate[1] = STATE_SYSTEM_INVISIBLE;
3023 if (style & WS_SYSMENU)
3025 if (!(style & (WS_MINIMIZEBOX|WS_MAXIMIZEBOX)))
3027 info->rgstate[2] = STATE_SYSTEM_INVISIBLE;
3028 info->rgstate[3] = STATE_SYSTEM_INVISIBLE;
3030 else
3032 if (!(style & WS_MINIMIZEBOX))
3033 info->rgstate[2] = STATE_SYSTEM_UNAVAILABLE;
3034 if (!(style & WS_MAXIMIZEBOX))
3035 info->rgstate[3] = STATE_SYSTEM_UNAVAILABLE;
3037 if (!(ex_style & WS_EX_CONTEXTHELP))
3038 info->rgstate[4] = STATE_SYSTEM_INVISIBLE;
3039 if (get_class_long( hwnd, GCL_STYLE, FALSE ) & CS_NOCLOSE )
3040 info->rgstate[5] = STATE_SYSTEM_UNAVAILABLE;
3042 else
3044 info->rgstate[2] = STATE_SYSTEM_INVISIBLE;
3045 info->rgstate[3] = STATE_SYSTEM_INVISIBLE;
3046 info->rgstate[4] = STATE_SYSTEM_INVISIBLE;
3047 info->rgstate[5] = STATE_SYSTEM_INVISIBLE;
3050 else
3051 info->rgstate[0] |= STATE_SYSTEM_INVISIBLE;
3052 return TRUE;