From 6dfa71c487dbb193ed7fb1a249a8c964ec3aef0d Mon Sep 17 00:00:00 2001 From: James Tabor Date: Tue, 1 Sep 2015 22:10:13 +0000 Subject: [PATCH] [Win32SS|UxTheme] - Move Menu to server side. See CORE-7797 and CORE-8299. - This was for speed while moving windows about the desktop and fixed test results too. svn path=/trunk/; revision=68904 --- reactos/dll/win32/uxtheme/nonclient.c | 6 + reactos/dll/win32/uxtheme/themehooks.c | 12 - reactos/include/reactos/undocuser.h | 17 + reactos/win32ss/CMakeLists.txt | 1 + reactos/win32ss/include/ntuser.h | 55 +- reactos/win32ss/user/ntuser/callback.c | 2 + reactos/win32ss/user/ntuser/class.c | 9 + reactos/win32ss/user/ntuser/defwnd.c | 537 ++- reactos/win32ss/user/ntuser/desktop.c | 9 +- reactos/win32ss/user/ntuser/draw.c | 30 +- reactos/win32ss/user/ntuser/focus.c | 10 +- reactos/win32ss/user/ntuser/keyboard.c | 3 +- reactos/win32ss/user/ntuser/menu.c | 3779 +++++++++++++++- reactos/win32ss/user/ntuser/menu.h | 67 + reactos/win32ss/user/ntuser/message.c | 33 +- reactos/win32ss/user/ntuser/misc.c | 23 +- reactos/win32ss/user/ntuser/msgqueue.c | 22 +- reactos/win32ss/user/ntuser/msgqueue.h | 4 + reactos/win32ss/user/ntuser/nonclient.c | 1099 +++++ reactos/win32ss/user/ntuser/ntstubs.c | 46 - reactos/win32ss/user/ntuser/painting.c | 62 +- reactos/win32ss/user/ntuser/painting.h | 4 +- reactos/win32ss/user/ntuser/scroll.h | 10 +- reactos/win32ss/user/ntuser/scrollbar.c | 301 ++ reactos/win32ss/user/ntuser/scrollex.c | 235 + reactos/win32ss/user/ntuser/userfuncs.h | 36 +- reactos/win32ss/user/ntuser/win32.h | 4 +- reactos/win32ss/user/ntuser/window.c | 96 +- reactos/win32ss/user/ntuser/window.h | 4 + reactos/win32ss/user/ntuser/winpos.c | 4 +- reactos/win32ss/user/ntuser/winpos.h | 2 + reactos/win32ss/user/ntuser/winsta.c | 3 + reactos/win32ss/user/rtl/text.c | 1365 ++++++ reactos/win32ss/user/user32/CMakeLists.txt | 1 + reactos/win32ss/user/user32/misc/usrapihk.c | 2 +- reactos/win32ss/user/user32/user32.spec | 16 +- reactos/win32ss/user/user32/windows/defwnd.c | 445 +- reactos/win32ss/user/user32/windows/draw.c | 8 + reactos/win32ss/user/user32/windows/font.c | 1194 +---- reactos/win32ss/user/user32/windows/menu.c | 4011 +---------------- .../win32ss/user/user32/windows/nonclient.c | 933 +--- 41 files changed, 7700 insertions(+), 6800 deletions(-) create mode 100644 reactos/win32ss/user/rtl/text.c diff --git a/reactos/dll/win32/uxtheme/nonclient.c b/reactos/dll/win32/uxtheme/nonclient.c index 4cb7079cbab..f64717ff89e 100644 --- a/reactos/dll/win32/uxtheme/nonclient.c +++ b/reactos/dll/win32/uxtheme/nonclient.c @@ -1070,7 +1070,13 @@ ThemeWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam, WNDPROC DefWndPr { case WM_NCPAINT: return ThemeHandleNCPaint(hWnd, (HRGN)wParam); + // + // WM_NCUAHDRAWCAPTION : wParam are DC_* flags. + // case WM_NCUAHDRAWCAPTION: + // + // WM_NCUAHDRAWFRAME : wParam is HDC, lParam are DC_ACTIVE and or DC_REDRAWHUNGWND. + // case WM_NCUAHDRAWFRAME: case WM_NCACTIVATE: ThemeHandleNCPaint(hWnd, (HRGN)1); diff --git a/reactos/dll/win32/uxtheme/themehooks.c b/reactos/dll/win32/uxtheme/themehooks.c index f5101e03bf8..20b541dbf8f 100644 --- a/reactos/dll/win32/uxtheme/themehooks.c +++ b/reactos/dll/win32/uxtheme/themehooks.c @@ -186,12 +186,6 @@ ThemeDefWindowProcW(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { if(!IsAppThemed()) { - if (Msg == WM_NCUAHDRAWCAPTION) - { - user32ApiHook.DrawCaption(hWnd, NULL, NULL, 0); - return 0; - } - return user32ApiHook.DefWindowProcW(hWnd, Msg, wParam, @@ -210,12 +204,6 @@ ThemeDefWindowProcA(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { if(!IsAppThemed()) { - if (Msg == WM_NCUAHDRAWCAPTION) - { - user32ApiHook.DrawCaption(hWnd, NULL, NULL, 0); - return 0; - } - return user32ApiHook.DefWindowProcA(hWnd, Msg, wParam, diff --git a/reactos/include/reactos/undocuser.h b/reactos/include/reactos/undocuser.h index 50f1465c647..76bc938de78 100644 --- a/reactos/include/reactos/undocuser.h +++ b/reactos/include/reactos/undocuser.h @@ -43,6 +43,7 @@ extern "C" { #define WM_LBTRACKPOINT 0x00000131 #define LB_CARETON 0x000001a3 #define LB_CARETOFF 0x000001a4 +#define MN_SETHMENU 0x000001e0 #define WM_DROPOBJECT 0x0000022A #define WM_QUERYDROPOBJECT 0x0000022B #define WM_BEGINDRAG 0x0000022C @@ -60,6 +61,9 @@ extern "C" { #define DCX_KEEPLAYOUT 0x40000000 #define DCX_PROCESSOWNED 0x80000000 +/* Non SDK TPM types.*/ +#define TPM_SYSTEM_MENU 0x00000200 + /* NtUserCreateWindowEx dwFlags bits. */ #define NUCWE_ANSI 0x00000001 #define NUCWE_SIDEBYSIDE 0x40000000 @@ -132,7 +136,20 @@ extern "C" { // // Undocumented flags for DrawCaptionTemp // +#define DC_NOVISIBLE 0x0800 #define DC_NOSENDMSG 0x2000 +#define DC_FRAME 0x8000 // Missing from WinUser.H! + +#define DC_DRAWCAPTIONMD 0x10000000 +#define DC_REDRAWHUNGWND 0x20000000 +#define DC_DRAWFRAMEMD 0x80000000 + +// +// Undocumented states for DrawFrameControl +// +#define DFCS_MENUARROWUP 0x0008 +#define DFCS_MENUARROWDOWN 0x0010 + #define STARTF_SCRNSAVER 0x80000000 diff --git a/reactos/win32ss/CMakeLists.txt b/reactos/win32ss/CMakeLists.txt index 24477f2dab9..ca75c75a8a5 100644 --- a/reactos/win32ss/CMakeLists.txt +++ b/reactos/win32ss/CMakeLists.txt @@ -149,6 +149,7 @@ list(APPEND SOURCE user/ntuser/winpos.c user/ntuser/winsta.c user/ntuser/object.c + user/rtl/text.c gdi/ntgdi/arc.c gdi/ntgdi/bezier.c gdi/ntgdi/bitblt.c diff --git a/reactos/win32ss/include/ntuser.h b/reactos/win32ss/include/ntuser.h index 47e16ef0640..7be826017cd 100644 --- a/reactos/win32ss/include/ntuser.h +++ b/reactos/win32ss/include/ntuser.h @@ -375,6 +375,9 @@ typedef struct tagMENULIST /* Hack */ #define MNF_SYSMENU 0x0200 +/* (other FocusedItem values give the position of the focused item) */ +#define NO_SELECTED_ITEM 0xffff + typedef struct tagMENU { PROCDESKHEAD head; @@ -3515,58 +3518,6 @@ NtUserGetMonitorInfo( /* Should be done in usermode */ -/* (other FocusedItem values give the position of the focused item) */ -#define NO_SELECTED_ITEM 0xffff - -typedef struct tagROSMENUINFO -{ - /* ----------- MENUINFO ----------- */ - DWORD cbSize; - DWORD fMask; - DWORD dwStyle; - UINT cyMax; - HBRUSH hbrBack; - DWORD dwContextHelpID; - ULONG_PTR dwMenuData; - /* ----------- Extra ----------- */ - ULONG fFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */ - UINT iItem; /* Currently focused item */ - UINT cItems; /* Number of items in the menu */ - WORD cxMenu; /* Width of the whole menu */ - WORD cyMenu; /* Height of the whole menu */ - ULONG cxTextAlign; - PWND spwndNotify; /* window receiving the messages for ownerdraw */ - INT iTop; - INT iMaxTop; - DWORD dwArrowsOn:2; - - HMENU Self; /* Handle of this menu */ - HWND Wnd; /* Window containing the menu */ - BOOL TimeToHide; /* Request hiding when receiving a second click in the top-level menu item */ -} ROSMENUINFO, *PROSMENUINFO; - -typedef struct tagROSMENUITEMINFO -{ - /* ----------- MENUITEMINFOW ----------- */ - UINT cbSize; - UINT fMask; - UINT fType; - UINT fState; - UINT wID; - HMENU hSubMenu; - HBITMAP hbmpChecked; - HBITMAP hbmpUnchecked; - DWORD dwItemData; - LPWSTR dwTypeData; - UINT cch; - HBITMAP hbmpItem; - /* ----------- Extra ----------- */ - RECT Rect; /* Item area (relative to menu window) */ - UINT dxTab; /* X position of text after Tab */ - LPWSTR lpstr; /* Copy of the text pointer in MenuItem->Text */ - SIZE maxBmpSize; /* Maximum size of the bitmap items in MIIM_BITMAP state */ -} ROSMENUITEMINFO, *PROSMENUITEMINFO; - HMONITOR NTAPI NtUserMonitorFromPoint( diff --git a/reactos/win32ss/user/ntuser/callback.c b/reactos/win32ss/user/ntuser/callback.c index 7ba72094d93..fbad4a2fd2f 100644 --- a/reactos/win32ss/user/ntuser/callback.c +++ b/reactos/win32ss/user/ntuser/callback.c @@ -393,6 +393,8 @@ co_IntCallWindowProc(WNDPROC Proc, case WM_WINDOWPOSCHANGING: case WM_SIZING: case WM_MOVING: + case WM_MEASUREITEM: + case WM_NEXTMENU: TRACE("Copy lParam, Message %u Size %d lParam %d!\n", Message, lParamBufferSize, lParam); if (InSendMessage) // Copy into kernel space. diff --git a/reactos/win32ss/user/ntuser/class.c b/reactos/win32ss/user/ntuser/class.c index b39b7cf7632..29db65c091b 100644 --- a/reactos/win32ss/user/ntuser/class.c +++ b/reactos/win32ss/user/ntuser/class.c @@ -2556,6 +2556,15 @@ InvalidParameter: Offset, dwNewLong, Ansi); + switch(Offset) + { + case GCLP_HICONSM: + case GCLP_HICON: + { + if (Ret && Ret != dwNewLong) + UserPaintCaption(Window, DC_ICON); + } + } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { diff --git a/reactos/win32ss/user/ntuser/defwnd.c b/reactos/win32ss/user/ntuser/defwnd.c index 410019add69..0fccbbc181b 100644 --- a/reactos/win32ss/user/ntuser/defwnd.c +++ b/reactos/win32ss/user/ntuser/defwnd.c @@ -11,6 +11,23 @@ DBG_DEFAULT_CHANNEL(UserDefwnd); +INT WINAPI DrawTextExWorker( HDC hdc, LPWSTR str, INT i_count, + LPRECT rect, UINT flags, LPDRAWTEXTPARAMS dtp ); + +INT WINAPI DrawTextW( HDC hdc, LPCWSTR str, INT count, LPRECT rect, UINT flags ) +{ + DRAWTEXTPARAMS dtp; + + memset (&dtp, 0, sizeof(dtp)); + dtp.cbSize = sizeof(dtp); + if (flags & DT_TABSTOP) + { + dtp.iTabLength = (flags >> 8) & 0xff; + flags &= 0xffff00ff; + } + return DrawTextExWorker(hdc, (LPWSTR)str, count, rect, flags, &dtp); +} + HBRUSH FASTCALL DefWndControlColor(HDC hDC, UINT ctlType) @@ -171,6 +188,19 @@ DefWndHandleSysCommand(PWND pWnd, WPARAM wParam, LPARAM lParam) } } break; +// case SC_DEFAULT: + case SC_MOUSEMENU: + { + POINT Pt; + Pt.x = (short)LOWORD(lParam); + Pt.y = (short)HIWORD(lParam); + MENU_TrackMouseMenuBar(pWnd, wParam & 0x000f, Pt); + } + break; + + case SC_KEYMENU: + MENU_TrackKbdMenuBar(pWnd, wParam, (WCHAR)lParam); + break; default: @@ -362,6 +392,138 @@ VOID FASTCALL DefWndPrint( PWND pwnd, HDC hdc, ULONG uFlags) co_IntSendMessage(UserHMGetHandle(pwnd), WM_PRINTCLIENT, (WPARAM)hdc, uFlags); } +BOOL +UserPaintCaption(PWND pWnd, INT Flags) +{ + BOOL Ret = FALSE; + + if ( pWnd->style & WS_VISIBLE && (pWnd->style & WS_CAPTION) == WS_CAPTION ) + { + + if (pWnd->state & WNDS_HASCAPTION && pWnd->head.pti->MessageQueue == gpqForeground) + Flags |= DC_ACTIVE; + /* + * When themes are not enabled we can go on and paint the non client area. + * However if we do that with themes enabled we will draw a classic frame. + * This is sovled by sending a themes specific message to notify the themes + * engine that the caption needs to be redrawn + */ + if (gpsi->dwSRVIFlags & SRVINFO_APIHOOK) + { + /* + * This will cause uxtheme to either paint the themed caption or call + * RealUserDrawCaption in order to draw the classic caption when themes + * are disabled but the themes service is enabled + */ + co_IntSendMessage(UserHMGetHandle(pWnd), WM_NCUAHDRAWCAPTION, Flags, 0); + } + else + { + HDC hDC = UserGetDCEx(pWnd, NULL, DCX_WINDOW|DCX_USESTYLE); + UserDrawCaptionBar(pWnd, hDC, Flags); + UserReleaseDC(pWnd, hDC, FALSE); + } + Ret = TRUE; + } + // Support window tray + return Ret; +} + +// WM_SETICON +LRESULT FASTCALL +DefWndSetIcon(PWND pWnd, WPARAM wParam, LPARAM lParam) +{ + HICON hIcon, hIconSmall, hIconOld; + + if ( wParam > ICON_SMALL2 ) + { + EngSetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + hIconSmall = UserGetProp(pWnd, gpsi->atomIconSmProp); + hIcon = UserGetProp(pWnd, gpsi->atomIconProp); + + hIconOld = wParam == ICON_BIG ? hIcon : hIconSmall; + + switch(wParam) + { + case ICON_BIG: + hIcon = (HICON)lParam; + break; + case ICON_SMALL: + hIconSmall = (HICON)lParam; + break; + case ICON_SMALL2: + ERR("FIXME: Set ICON_SMALL2 support!\n"); + default: + break; + } + + IntSetProp(pWnd, gpsi->atomIconProp, hIcon); + IntSetProp(pWnd, gpsi->atomIconSmProp, hIconSmall); + + if ((pWnd->style & WS_CAPTION ) == WS_CAPTION) + UserPaintCaption(pWnd, DC_ICON); + + return (LRESULT)hIconOld; +} + +LRESULT FASTCALL +DefWndGetIcon(PWND pWnd, WPARAM wParam, LPARAM lParam) +{ + HICON hIconRet; + if ( wParam > ICON_SMALL2 ) + { + EngSetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + switch(wParam) + { + case ICON_BIG: + hIconRet = UserGetProp(pWnd, gpsi->atomIconProp); + break; + case ICON_SMALL: + case ICON_SMALL2: + hIconRet = UserGetProp(pWnd, gpsi->atomIconSmProp); + break; + default: + break; + } + return (LRESULT)hIconRet; +} + +VOID FASTCALL +DefWndScreenshot(PWND pWnd) +{ + RECT rect; + HDC hdc; + INT w; + INT h; + HBITMAP hbitmap; + HDC hdc2; + SETCLIPBDATA scd = {FALSE, FALSE}; + + UserOpenClipboard(UserHMGetHandle(pWnd)); + UserEmptyClipboard(); + + hdc = UserGetWindowDC(pWnd); + IntGetWindowRect(pWnd, &rect); + w = rect.right - rect.left; + h = rect.bottom - rect.top; + + hbitmap = NtGdiCreateCompatibleBitmap(hdc, w, h); + hdc2 = NtGdiCreateCompatibleDC(hdc); + NtGdiSelectBitmap(hdc2, hbitmap); + + NtGdiBitBlt(hdc2, 0, 0, w, h, hdc, 0, 0, SRCCOPY, 0, 0); + + UserSetClipboardData(CF_BITMAP, hbitmap, &scd); + + UserReleaseDC(pWnd, hdc, FALSE); + UserReleaseDC(pWnd, hdc2, FALSE); + + UserCloseClipboard(); +} /* Win32k counterpart of User DefWindowProc @@ -374,6 +536,7 @@ IntDefWindowProc( LPARAM lParam, BOOL Ansi) { + PTHREADINFO pti = PsGetCurrentThreadWin32Thread(); LRESULT lResult = 0; USER_REFERENCE_ENTRY Ref; @@ -381,12 +544,74 @@ IntDefWindowProc( switch (Msg) { + case WM_GETTEXTLENGTH: + { + PWSTR buf; + ULONG len; + + if (Wnd != NULL && Wnd->strName.Length != 0) + { + buf = Wnd->strName.Buffer; + if (buf != NULL && + NT_SUCCESS(RtlUnicodeToMultiByteSize(&len, + buf, + Wnd->strName.Length))) + { + lResult = (LRESULT) (Wnd->strName.Length / sizeof(WCHAR)); + } + } + else lResult = 0L; + + break; + } + + case WM_GETTEXT: // FIXME: Handle Ansi + { + PWSTR buf = NULL; + PWSTR outbuf = (PWSTR)lParam; + + if (Wnd != NULL && wParam != 0) + { + if (Wnd->strName.Buffer != NULL) + buf = Wnd->strName.Buffer; + else + outbuf[0] = L'\0'; + + if (buf != NULL) + { + if (Wnd->strName.Length != 0) + { + lResult = min(Wnd->strName.Length / sizeof(WCHAR), wParam - 1); + RtlCopyMemory(outbuf, + buf, + lResult * sizeof(WCHAR)); + outbuf[lResult] = L'\0'; + } + else + outbuf[0] = L'\0'; + } + } + break; + } + + case WM_SETTEXT: // FIXME: Handle Ansi + { + DefSetText(Wnd, (PCWSTR)lParam); + + if ((Wnd->style & WS_CAPTION) == WS_CAPTION) + UserPaintCaption(Wnd, DC_TEXT); + IntNotifyWinEvent(EVENT_OBJECT_NAMECHANGE, Wnd, OBJID_WINDOW, CHILDID_SELF, 0); + lResult = 1; + break; + } + case WM_SYSCOMMAND: { - ERR("hwnd %p WM_SYSCOMMAND %lx %lx\n", Wnd->head.h, wParam, lParam ); + TRACE("hwnd %p WM_SYSCOMMAND %lx %lx\n", Wnd->head.h, wParam, lParam ); lResult = DefWndHandleSysCommand(Wnd, wParam, lParam); break; } + case WM_SHOWWINDOW: { if ((Wnd->style & WS_VISIBLE) && wParam) break; @@ -411,7 +636,6 @@ IntDefWindowProc( return IntClientShutdown(Wnd, wParam, lParam); case WM_APPCOMMAND: - ERR("WM_APPCOMMAND\n"); if ( (Wnd->style & (WS_POPUP|WS_CHILD)) != WS_CHILD && Wnd != co_GetDesktopWindow(Wnd) ) { @@ -424,6 +648,241 @@ IntDefWindowProc( UserDerefObjectCo(Wnd->spwndParent); break; + case WM_KEYF1: + { + HELPINFO hi; + HMENU hMenu = UlongToHandle(Wnd->IDMenu); + PWND pwndActive = MENU_IsMenuActive(); + hi.cbSize = sizeof(HELPINFO); + hi.MousePos = gpsi->ptCursor; + hi.iContextType = HELPINFO_MENUITEM; + hi.hItemHandle = pwndActive ? UserHMGetHandle(pwndActive) : UserHMGetHandle(Wnd); + hi.iCtrlId = (Wnd->style & (WS_POPUP|WS_CHILD)) == WS_CHILD ? IntMenuItemFromPoint(Wnd, hMenu, hi.MousePos) : 0; + hi.dwContextId = IntGetWindowContextHelpId(Wnd); + + co_IntSendMessage( UserHMGetHandle(Wnd), WM_HELP, 0, (LPARAM)&hi ); + break; + } + + case WM_SETICON: + { + return DefWndSetIcon(Wnd, wParam, lParam); + } + + case WM_GETICON: + { + return DefWndGetIcon(Wnd, wParam, lParam); + } + + case WM_HELP: + { + PWND Parent = IntGetParent(Wnd); + co_IntSendMessage(UserHMGetHandle(Parent), Msg, wParam, lParam); + break; + } + + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_MBUTTONDOWN: + pti->MessageQueue->QF_flags &= ~(QF_FMENUSTATUS|QF_FMENUSTATUSBREAK); + break; + + case WM_NCLBUTTONDOWN: + return (NC_HandleNCLButtonDown( Wnd, wParam, lParam)); + + case WM_LBUTTONDBLCLK: + return (NC_HandleNCLButtonDblClk( Wnd, HTCLIENT, lParam)); + + case WM_NCLBUTTONDBLCLK: + return (NC_HandleNCLButtonDblClk( Wnd, wParam, lParam)); + + case WM_NCRBUTTONDOWN: + return NC_HandleNCRButtonDown( Wnd, wParam, lParam ); + + case WM_RBUTTONUP: + { + POINT Pt; + + Pt.x = GET_X_LPARAM(lParam); + Pt.y = GET_Y_LPARAM(lParam); + IntClientToScreen(Wnd, &Pt); + lParam = MAKELPARAM(Pt.x, Pt.y); + co_IntSendMessage(UserHMGetHandle(Wnd), WM_CONTEXTMENU, (WPARAM)UserHMGetHandle(Wnd), lParam); + break; + } + + case WM_NCRBUTTONUP: + /* + * FIXME : we must NOT send WM_CONTEXTMENU on a WM_NCRBUTTONUP (checked + * in Windows), but what _should_ we do? According to MSDN : + * "If it is appropriate to do so, the system sends the WM_SYSCOMMAND + * message to the window". When is it appropriate? + */ + ERR("WM_NCRBUTTONUP\n"); + break; + + case WM_CONTEXTMENU: + { + if (Wnd->style & WS_CHILD) + { + co_IntSendMessage(UserHMGetHandle(IntGetParent(Wnd)), Msg, wParam, lParam); + } + else + { + POINT Pt; + LONG_PTR Style; + LONG HitCode; + + Style = Wnd->style; + + Pt.x = GET_X_LPARAM(lParam); + Pt.y = GET_Y_LPARAM(lParam); + if (Style & WS_CHILD) + { + IntScreenToClient(IntGetParent(Wnd), &Pt); + } + + HitCode = GetNCHitEx(Wnd, Pt); + + if (HitCode == HTCAPTION || HitCode == HTSYSMENU) + { + PMENU SystemMenu; + UINT Flags; + + if((SystemMenu = IntGetSystemMenu(Wnd, FALSE))) + { + MENU_InitSysMenuPopup(SystemMenu, Wnd->style, Wnd->pcls->style, HitCode); + + if(HitCode == HTCAPTION) + Flags = TPM_LEFTBUTTON | TPM_RIGHTBUTTON; + else + Flags = TPM_LEFTBUTTON; + + IntTrackPopupMenuEx(SystemMenu, Flags|TPM_SYSTEM_MENU, Pt.x, Pt.y, Wnd, NULL); + } + } + if (HitCode == HTHSCROLL || HitCode == HTVSCROLL) + { + WARN("Scroll Menu Not Supported\n"); + } + } + break; + } + + case WM_KEYDOWN: + if (wParam == VK_F10) + { + pti->MessageQueue->QF_flags |= QF_FF10STATUS; + + if (UserGetKeyState(VK_SHIFT) & 0x8000) + { + co_IntSendMessage(UserHMGetHandle(Wnd), WM_CONTEXTMENU, (WPARAM)UserHMGetHandle(Wnd), MAKELPARAM(-1, -1)); + } + } + break; + + case WM_SYSKEYDOWN: + { + if (HIWORD(lParam) & KF_ALTDOWN) + { /* Previous state, if the key was down before this message, + this is a cheap way to ignore autorepeat keys. */ + if ( !(HIWORD(lParam) & KF_REPEAT) ) + { + if ( ( wParam == VK_MENU || + wParam == VK_LMENU || + wParam == VK_RMENU ) && !(pti->MessageQueue->QF_flags & QF_FMENUSTATUS)) //iMenuSysKey ) + pti->MessageQueue->QF_flags |= QF_FMENUSTATUS; //iMenuSysKey = 1; + else + pti->MessageQueue->QF_flags &= ~QF_FMENUSTATUS; //iMenuSysKey = 0; + } + + pti->MessageQueue->QF_flags &= ~QF_FF10STATUS; //iF10Key = 0; + + if (wParam == VK_F4) /* Try to close the window */ + { + PWND top = UserGetAncestor(Wnd, GA_ROOT); + if (!(top->style & CS_NOCLOSE)) + UserPostMessage(UserHMGetHandle(top), WM_SYSCOMMAND, SC_CLOSE, 0); + } + else if (wParam == VK_SNAPSHOT) // Alt-VK_SNAPSHOT? + { + PWND pwnd = Wnd; + while (IntGetParent(pwnd) != NULL) + { + pwnd = IntGetParent(pwnd); + } + ERR("DefWndScreenshot\n"); + DefWndScreenshot(pwnd); + } + else if ( wParam == VK_ESCAPE || wParam == VK_TAB ) // Alt-Tab/ESC Alt-Shift-Tab/ESC + { + WPARAM wParamTmp; + HWND Active = UserGetActiveWindow(); // Noticed MDI problem. + if (!Active) + { + FIXME("WM_SYSKEYDOWN VK_ESCAPE no active\n"); + break; + } + wParamTmp = UserGetKeyState(VK_SHIFT) & 0x8000 ? SC_PREVWINDOW : SC_NEXTWINDOW; + co_IntSendMessage( Active, WM_SYSCOMMAND, wParamTmp, wParam ); + } + } + else if( wParam == VK_F10 ) + { + if (UserGetKeyState(VK_SHIFT) & 0x8000) + co_IntSendMessage( UserHMGetHandle(Wnd), WM_CONTEXTMENU, (WPARAM)UserHMGetHandle(Wnd), MAKELPARAM(-1, -1) ); + pti->MessageQueue->QF_flags |= QF_FF10STATUS; //iF10Key = 1; + } + else if( wParam == VK_ESCAPE && (UserGetKeyState(VK_SHIFT) & 0x8000)) + co_IntSendMessage( UserHMGetHandle(Wnd), WM_SYSCOMMAND, SC_KEYMENU, ' ' ); + break; + } + + case WM_KEYUP: + case WM_SYSKEYUP: + { + /* Press and release F10 or ALT */ + if (((wParam == VK_MENU || wParam == VK_LMENU || wParam == VK_RMENU) + && (pti->MessageQueue->QF_flags & (QF_FMENUSTATUS|QF_FMENUSTATUSBREAK)) == QF_FMENUSTATUS /*iMenuSysKey*/) || + ((wParam == VK_F10) && pti->MessageQueue->QF_flags & QF_FF10STATUS /*iF10Key*/)) + co_IntSendMessage( UserHMGetHandle(UserGetAncestor( Wnd, GA_ROOT )), WM_SYSCOMMAND, SC_KEYMENU, 0L ); + pti->MessageQueue->QF_flags &= ~(QF_FMENUSTATUS|QF_FMENUSTATUSBREAK|QF_FF10STATUS); //iMenuSysKey = iF10Key = 0; + break; + } + + case WM_SYSCHAR: + { + pti->MessageQueue->QF_flags &= ~(QF_FMENUSTATUS|QF_FMENUSTATUSBREAK); //iMenuSysKey = 0; + if (wParam == VK_RETURN && (Wnd->style & WS_MINIMIZE) != 0) + { + UserPostMessage( UserHMGetHandle(Wnd), WM_SYSCOMMAND, SC_RESTORE, 0L ); + break; + } + if ((HIWORD(lParam) & KF_ALTDOWN) && wParam) + { + if (wParam == VK_TAB || wParam == VK_ESCAPE) break; + if (wParam == VK_SPACE && Wnd->style & WS_CHILD) + co_IntSendMessage( UserHMGetHandle(IntGetParent(Wnd)), Msg, wParam, lParam ); + else + co_IntSendMessage( UserHMGetHandle(Wnd), WM_SYSCOMMAND, SC_KEYMENU, wParam ); + } + else /* check for Ctrl-Esc */ + if (wParam != VK_ESCAPE) UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_MESSAGE_BEEP, 0); //MessageBeep(0); + break; + } + + case WM_CANCELMODE: + { + pti->MessageQueue->QF_flags &= ~(QF_FMENUSTATUS|QF_FMENUSTATUSBREAK); + + MENU_EndMenu( Wnd ); + if (IntGetCaptureWindow() == UserHMGetHandle(Wnd)) + { + IntReleaseCapture(); + } + break; + } + case WM_CLOSE: co_UserDestroyWindow(Wnd); break; @@ -457,6 +916,18 @@ IntDefWindowProc( return DefWndHandleSetCursor(Wnd, wParam, lParam); } + case WM_MOUSEACTIVATE: + if (Wnd->style & WS_CHILD) + { + LONG Ret; + HWND hwndParent; + PWND pwndParent = IntGetParent(Wnd); + hwndParent = pwndParent ? UserHMGetHandle(pwndParent) : NULL; + if (hwndParent) Ret = co_IntSendMessage(hwndParent, WM_MOUSEACTIVATE, wParam, lParam); + if (Ret) return (Ret); + } + return ( (HIWORD(lParam) == WM_LBUTTONDOWN && LOWORD(lParam) == HTCAPTION) ? MA_NOACTIVATE : MA_ACTIVATE ); + case WM_ACTIVATE: /* The default action in Windows is to set the keyboard focus to * the window, if it's being activated and not minimized */ @@ -523,6 +994,18 @@ IntDefWindowProc( return (0); } + case WM_SYSCOLORCHANGE: + { + /* force to redraw non-client area */ + UserPaintCaption(Wnd, DC_NC); + /* Use InvalidateRect to redraw client area, enable + * erase to redraw all subcontrols otherwise send the + * WM_SYSCOLORCHANGE to child windows/controls is required + */ + co_UserRedrawWindow( Wnd, NULL, NULL, RDW_ALLCHILDREN|RDW_INVALIDATE|RDW_ERASE); + return (0); + } + case WM_PAINTICON: case WM_PAINT: { @@ -548,7 +1031,9 @@ IntDefWindowProc( IntGetClientRect(Wnd, &ClientRect); x = (ClientRect.right - ClientRect.left - UserGetSystemMetrics(SM_CXICON)) / 2; y = (ClientRect.bottom - ClientRect.top - UserGetSystemMetrics(SM_CYICON)) / 2; + UserReferenceObject(Wnd->pcls->spicn); UserDrawIconEx(hDC, x, y, Wnd->pcls->spicn, 0, 0, 0, 0, DI_NORMAL | DI_COMPAT | DI_DEFAULTSIZE); + UserDereferenceObject(Wnd->pcls->spicn); } IntEndPaint(Wnd, &Ps); @@ -578,7 +1063,6 @@ IntDefWindowProc( } case WM_SETREDRAW: - ERR("WM_SETREDRAW\n"); if (wParam) { if (!(Wnd->style & WS_VISIBLE)) @@ -607,6 +1091,53 @@ IntDefWindowProc( return (DefWndHandleWindowPosChanged(Wnd, (WINDOWPOS*)lParam)); } + case WM_NCCALCSIZE: + { + return NC_HandleNCCalcSize( Wnd, wParam, (RECTL *)lParam ); + } + + case WM_NCACTIVATE: + { + return NC_HandleNCActivate( Wnd, wParam, lParam ); + } + + // + // NC Paint mode. + // + case WM_NCPAINT: + { + HDC hDC = UserGetDCEx(Wnd, (HRGN)wParam, DCX_WINDOW | DCX_INTERSECTRGN | DCX_USESTYLE | DCX_KEEPCLIPRGN); + Wnd->state |= WNDS_FORCEMENUDRAW; + NC_DoNCPaint(Wnd, hDC, -1); + Wnd->state &= ~WNDS_FORCEMENUDRAW; + UserReleaseDC(Wnd, hDC, FALSE); + return 0; + } + // + // Draw Caption mode. + // + // wParam are DC_* flags. + // + case WM_NCUAHDRAWCAPTION: + { + HDC hDC = UserGetDCEx(Wnd, NULL, DCX_WINDOW|DCX_USESTYLE); + TRACE("WM_NCUAHDRAWCAPTION: wParam DC_ flags %08x\n",wParam); + UserDrawCaptionBar(Wnd, hDC, wParam|DC_FRAME); // Include DC_FRAME to comp for drawing glich. + UserReleaseDC(Wnd, hDC, FALSE); + return 0; + } + // + // Draw Frame mode. + // + // wParam is HDC, lParam are DC_ACTIVE and or DC_REDRAWHUNGWND. + // + case WM_NCUAHDRAWFRAME: + { + TRACE("WM_NCUAHDRAWFRAME: wParam hDC %p lParam DC_ flags %08x\n",wParam,lParam); + NC_DoNCPaint(Wnd, (HDC)wParam, lParam|DC_NC); + return 0; + } + /* ReactOS only. */ case WM_CBT: { diff --git a/reactos/win32ss/user/ntuser/desktop.c b/reactos/win32ss/user/ntuser/desktop.c index cbd6b2040c1..a1eb591bf78 100644 --- a/reactos/win32ss/user/ntuser/desktop.c +++ b/reactos/win32ss/user/ntuser/desktop.c @@ -664,6 +664,7 @@ DesktopWindowProc(PWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT *lRe case WM_SYSCOLORCHANGE: co_UserRedrawWindow(Wnd, NULL, NULL, RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN); return TRUE; + case WM_SETCURSOR: { PCURICON_OBJECT pcurOld, pcurNew; @@ -691,8 +692,11 @@ DesktopWindowProc(PWND Wnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT *lRe HDESK hdesk = IntGetDesktopObjectHandle(gpdeskInputDesktop); IntSetThreadDesktop(hdesk, FALSE); } + break; } - + default: + TRACE("DWP calling IDWP Msg %d\n",Msg); + *lResult = IntDefWindowProc(Wnd, Msg, wParam, lParam, FALSE); } return TRUE; /* We are done. Do not do any callbacks to user mode */ } @@ -711,6 +715,9 @@ UserMessageWindowProc(PWND pwnd, UINT Msg, WPARAM wParam, LPARAM lParam, LRESULT case WM_DESTROY: pwnd->fnid |= FNID_DESTROY; break; + default: + ERR("UMWP calling IDWP\n"); + *lResult = IntDefWindowProc(pwnd, Msg, wParam, lParam, FALSE); } return TRUE; /* We are done. Do not do any callbacks to user mode */ diff --git a/reactos/win32ss/user/ntuser/draw.c b/reactos/win32ss/user/ntuser/draw.c index c663185da3c..bd7283702f4 100644 --- a/reactos/win32ss/user/ntuser/draw.c +++ b/reactos/win32ss/user/ntuser/draw.c @@ -948,6 +948,14 @@ BOOL FASTCALL UITOOLS95_DrawFrameMenu(HDC dc, LPRECT r, UINT uFlags) WCHAR Symbol; switch(uFlags & 0xff) { + case DFCS_MENUARROWUP: + Symbol = '5'; + break; + + case DFCS_MENUARROWDOWN: + Symbol = '6'; + break; + case DFCS_MENUARROW: Symbol = '8'; break; @@ -980,8 +988,6 @@ BOOL FASTCALL UITOOLS95_DrawFrameMenu(HDC dc, LPRECT r, UINT uFlags) hFont = GreCreateFontIndirectW(&lf); /* save font */ hOldFont = NtGdiSelectFont(dc, hFont); - // FIXME selecting color doesn't work -#if 0 if(uFlags & DFCS_INACTIVE) { /* draw shadow */ @@ -989,7 +995,6 @@ BOOL FASTCALL UITOOLS95_DrawFrameMenu(HDC dc, LPRECT r, UINT uFlags) GreTextOutW(dc, r->left + 1, r->top + 1, &Symbol, 1); } IntGdiSetTextColor(dc, IntGetSysColor((uFlags & DFCS_INACTIVE) ? COLOR_BTNSHADOW : COLOR_BTNTEXT)); -#endif /* draw selected symbol */ GreTextOutW(dc, r->left, r->top, &Symbol, 1); /* restore previous settings */ @@ -1005,6 +1010,25 @@ BOOL FASTCALL UITOOLS95_DrawFrameMenu(HDC dc, LPRECT r, UINT uFlags) // +INT WINAPI +FrameRect(HDC hDC, CONST RECT *lprc, HBRUSH hbr) +{ + HBRUSH oldbrush; + RECT r = *lprc; + + if ((r.right <= r.left) || (r.bottom <= r.top)) return 0; + if (!(oldbrush = NtGdiSelectBrush(hDC, hbr))) return 0; + + NtGdiPatBlt(hDC, r.left, r.top, 1, r.bottom - r.top, PATCOPY); + NtGdiPatBlt(hDC, r.right - 1, r.top, 1, r.bottom - r.top, PATCOPY); + NtGdiPatBlt(hDC, r.left, r.top, r.right - r.left, 1, PATCOPY); + NtGdiPatBlt(hDC, r.left, r.bottom - 1, r.right - r.left, 1, PATCOPY); + + NtGdiSelectBrush(hDC, oldbrush); + return TRUE; +} + + INT WINAPI FillRect(HDC hDC, CONST RECT *lprc, HBRUSH hbr) { diff --git a/reactos/win32ss/user/ntuser/focus.c b/reactos/win32ss/user/ntuser/focus.c index ada0f2cd16c..ec5c6fc7a5c 100644 --- a/reactos/win32ss/user/ntuser/focus.c +++ b/reactos/win32ss/user/ntuser/focus.c @@ -66,7 +66,7 @@ co_IntSendDeactivateMessages(HWND hWndPrev, HWND hWnd) (LPARAM)hWnd); if (WndPrev) - WndPrev->state &= ~WNDS_ACTIVEFRAME; + WndPrev->state &= ~(WNDS_ACTIVEFRAME|WNDS_HASCAPTION); } else { @@ -700,7 +700,7 @@ co_IntSetActiveWindow(PWND Wnd OPTIONAL, BOOL bMouse, BOOL bFocus, BOOL Async) } // FIXME: Used in the menu loop!!! - //ThreadQueue->QF_flags |= QF_ACTIVATIONCHANGE; + ThreadQueue->QF_flags |= QF_ACTIVATIONCHANGE; //ERR("co_IntSetActiveWindow Exit\n"); if (Wnd) Wnd->state &= ~WNDS_BEINGACTIVATED; @@ -946,7 +946,11 @@ co_UserSetCapture(HWND hWnd) if (Window) IntNotifyWinEvent(EVENT_SYSTEM_CAPTURESTART, Window, OBJID_WINDOW, CHILDID_SELF, WEF_SETBYWNDPTI); - if (hWndPrev && hWndPrev != hWnd) + // + // Only send the message if we have a previous Window! + // Fix msg_menu tracking popup menu and win test_capture_4!!!! + // + if (hWndPrev) { if (ThreadQueue->MenuOwner && Window) ThreadQueue->QF_flags |= QF_CAPTURELOCKED; diff --git a/reactos/win32ss/user/ntuser/keyboard.c b/reactos/win32ss/user/ntuser/keyboard.c index b5452cf0d60..ab0dcfc1643 100644 --- a/reactos/win32ss/user/ntuser/keyboard.c +++ b/reactos/win32ss/user/ntuser/keyboard.c @@ -936,7 +936,7 @@ ProcessKeyEvent(WORD wVk, WORD wScanCode, DWORD dwFlags, BOOL bInjected, DWORD d /* FIXME: Set KF_DLGMODE and KF_MENUMODE when needed */ if (pFocusQueue->QF_flags & QF_DIALOGACTIVE) Msg.lParam |= KF_DLGMODE << 16; - if (pFocusQueue->MenuOwner) // pFocusQueue->MenuState) // MenuState needs a start flag... + if (pFocusQueue->MenuOwner) // pti->pMenuState->fMenuStarted Msg.lParam |= KF_MENUMODE << 16; } @@ -951,7 +951,6 @@ ProcessKeyEvent(WORD wVk, WORD wScanCode, DWORD dwFlags, BOOL bInjected, DWORD d if (!Wnd) {ERR("Window is NULL\n");} MsqPostMessage(pti, &Msg, TRUE, QS_KEY, 0, dwExtraInfo); } - return TRUE; } diff --git a/reactos/win32ss/user/ntuser/menu.c b/reactos/win32ss/user/ntuser/menu.c index 6ce59d02848..d672ef39c19 100644 --- a/reactos/win32ss/user/ntuser/menu.c +++ b/reactos/win32ss/user/ntuser/menu.c @@ -11,7 +11,29 @@ DBG_DEFAULT_CHANNEL(UserMenu); /* INTERNAL ******************************************************************/ -BOOL FASTCALL IntSetMenuItemInfo(PMENU, PITEM, PROSMENUITEMINFO, PUNICODE_STRING); +HFONT ghMenuFont = NULL; +HFONT ghMenuFontBold = NULL; +static SIZE MenuCharSize; + +/* Use global popup window because there's no way 2 menus can + * be tracked at the same time. */ +static HWND top_popup = NULL; +static HMENU top_popup_hmenu = NULL; + +BOOL fInsideMenuLoop = FALSE; +BOOL fInEndMenu = FALSE; + +/* internal popup menu window messages */ + +#define MM_SETMENUHANDLE (WM_USER + 0) +#define MM_GETMENUHANDLE (WM_USER + 1) + +/* internal flags for menu tracking */ + +#define TF_ENDMENU 0x10000 +#define TF_SUSPENDPOPUP 0x20000 +#define TF_SKIPREMOVE 0x40000 + /* maximum allowed depth of any branch in the menu tree. * This value is slightly larger than in windows (25) to @@ -33,10 +55,50 @@ BOOL FASTCALL IntSetMenuItemInfo(PMENU, PITEM, PROSMENUITEMINFO, PUNICODE_STRING #define MII_STATE_MASK (MFS_GRAYED|MFS_CHECKED|MFS_HILITE|MFS_DEFAULT) +#define IS_SYSTEM_MENU(MenuInfo) \ + (0 == ((MenuInfo)->fFlags & MNF_POPUP) && 0 != ((MenuInfo)->fFlags & MNF_SYSMENU)) + +#define IS_SYSTEM_POPUP(MenuInfo) \ + (0 != ((MenuInfo)->fFlags & MNF_POPUP) && 0 != ((MenuInfo)->fFlags & MNF_SYSMENU)) + +#define IS_BITMAP_ITEM(flags) (MF_BITMAP == MENU_ITEM_TYPE(flags)) + +#define IS_MAGIC_BITMAP(id) ((id) && ((INT_PTR)(id) < 12) && ((INT_PTR)(id) >= -1)) +#define IS_STRING_ITEM(flags) (MF_STRING == MENU_ITEM_TYPE(flags)) + /* Maximum number of menu items a menu can contain */ #define MAX_MENU_ITEMS (0x4000) #define MAX_GOINTOSUBMENU (0x10) +/* Space between 2 columns */ +#define MENU_COL_SPACE 4 + +/* top and bottom margins for popup menus */ +#define MENU_TOP_MARGIN 2 //3 +#define MENU_BOTTOM_MARGIN 2 + +#define MENU_ITEM_HBMP_SPACE (5) +#define MENU_BAR_ITEMS_SPACE (12) +#define SEPARATOR_HEIGHT (5) +#define MENU_TAB_SPACE (8) + +typedef struct +{ + UINT TrackFlags; + PMENU CurrentMenu; /* current submenu (can be equal to hTopMenu)*/ + PMENU TopMenu; /* initial menu */ + PWND OwnerWnd; /* where notifications are sent */ + POINT Pt; +} MTRACKER; + +/* Internal MenuTrackMenu() flags */ +#define TPM_INTERNAL 0xF0000000 +#define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */ +#define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */ + +#define ITEM_PREV -1 +#define ITEM_NEXT 1 + #define UpdateMenuItemState(state, change) \ {\ if((change) & MF_GRAYED) { \ @@ -147,9 +209,12 @@ PMENU FASTCALL VerifyMenu(PMENU pMenu) { HMENU hMenu; PITEM pItem; + ULONG Error; UINT i; if (!pMenu) return NULL; + Error = EngGetLastError(); + _SEH2_TRY { hMenu = UserHMGetHandle(pMenu); @@ -163,6 +228,7 @@ PMENU FASTCALL VerifyMenu(PMENU pMenu) _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { ERR("Run away LOOP!\n"); + EngSetLastError(Error); _SEH2_YIELD(return NULL); } _SEH2_END @@ -170,12 +236,34 @@ PMENU FASTCALL VerifyMenu(PMENU pMenu) if ( UserObjectInDestroy(hMenu)) { ERR("Menu is marked for destruction!\n"); - return NULL; + pMenu = NULL; } - + EngSetLastError(Error); return pMenu; } +PMENU WINAPI +IntGetMenu(HWND hWnd) +{ + PWND Wnd = ValidateHwndNoErr(hWnd); + + if (!Wnd) + return NULL; + + return UserGetMenuObject(UlongToHandle(Wnd->IDMenu)); +} + +PMENU get_win_sys_menu( HWND hwnd ) +{ + PMENU ret = 0; + WND *win = ValidateHwndNoErr( hwnd ); + if (win) + { + ret = UserGetMenuObject(win->SystemMenu); + } + return ret; +} + BOOL IntDestroyMenu( PMENU pMenu, BOOL bRecurse) { PMENU SubMenu; @@ -228,6 +316,7 @@ IntDestroyMenuObject(PMENU Menu, BOOL bRecurse) if(Menu) { PWND Window; + ULONG Error; /* Remove all menu items */ IntDestroyMenu( Menu, bRecurse); @@ -237,10 +326,10 @@ IntDestroyMenuObject(PMENU Menu, BOOL bRecurse) BOOL ret; if (Menu->hWnd) { - Window = UserGetWindowObject(Menu->hWnd); + Window = ValidateHwndNoErr(Menu->hWnd); if (Window) { - Window->IDMenu = 0; + //Window->IDMenu = 0; Only in Win9x!! wine win test_SetMenu test... /* DestroyMenu should not destroy system menu popup owner */ if ((Menu->fFlags & (MNF_POPUP | MNF_SYSSUBMENU)) == MNF_POPUP) @@ -260,8 +349,13 @@ IntDestroyMenuObject(PMENU Menu, BOOL bRecurse) ret = UserDeleteObject(Menu->head.h, TYPE_MENU); if (!ret) { // Make sure it is really dead or just marked for deletion. + Error = EngGetLastError(); ret = UserObjectInDestroy(Menu->head.h); - if (ret && EngGetLastError() == ERROR_INVALID_HANDLE) ret = FALSE; + if (ret && EngGetLastError() == ERROR_INVALID_HANDLE) + { + EngSetLastError(Error); + ret = FALSE; + } } // See test_subpopup_locked_by_menu tests.... return ret; } @@ -269,6 +363,47 @@ IntDestroyMenuObject(PMENU Menu, BOOL bRecurse) return FALSE; } +BOOL +MenuInit(VOID) +{ + NONCLIENTMETRICSW ncm; + + /* get the menu font */ + if (!ghMenuFont || !ghMenuFontBold) + { + ncm.cbSize = sizeof(ncm); + if(!UserSystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0)) + { + ERR("MenuInit(): SystemParametersInfo(SPI_GETNONCLIENTMETRICS) failed!\n"); + return FALSE; + } + + ghMenuFont = GreCreateFontIndirectW(&ncm.lfMenuFont); + if (ghMenuFont == NULL) + { + ERR("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n"); + return FALSE; + } + ncm.lfMenuFont.lfWeight = max(ncm.lfMenuFont.lfWeight + 300, 1000); + ghMenuFontBold = GreCreateFontIndirectW(&ncm.lfMenuFont); + if (ghMenuFontBold == NULL) + { + ERR("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n"); + GreDeleteObject(ghMenuFont); + ghMenuFont = NULL; + return FALSE; + } + + GreSetObjectOwner(ghMenuFont, GDI_OBJ_HMGR_PUBLIC); + GreSetObjectOwner(ghMenuFontBold, GDI_OBJ_HMGR_PUBLIC); + + co_IntSetupOBM(); + } + + return TRUE; +} + + /********************************************************************** * MENU_depth * @@ -299,6 +434,78 @@ int FASTCALL MENU_depth( PMENU pmenu, int depth) return subdepth; } + +/****************************************************************************** + * + * UINT MenuGetStartOfNextColumn( + * PMENU Menu) + * + *****************************************************************************/ + +static UINT MENU_GetStartOfNextColumn( + PMENU menu ) +{ + PITEM pItem; + UINT i; + + if(!menu) + return NO_SELECTED_ITEM; + + i = menu->iItem + 1; + if( i == NO_SELECTED_ITEM ) + return i; + + pItem = menu->rgItems; + if (!pItem) return NO_SELECTED_ITEM; + + for( ; i < menu->cItems; ++i ) { + if (pItem[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK)) + return i; + } + + return NO_SELECTED_ITEM; +} + +/****************************************************************************** + * + * UINT MenuGetStartOfPrevColumn( + * PMENU Menu) + * + *****************************************************************************/ +static UINT MENU_GetStartOfPrevColumn( + PMENU menu ) +{ + UINT i; + PITEM pItem; + + if( !menu ) + return NO_SELECTED_ITEM; + + if( menu->iItem == 0 || menu->iItem == NO_SELECTED_ITEM ) + return NO_SELECTED_ITEM; + + pItem = menu->rgItems; + if (!pItem) return NO_SELECTED_ITEM; + + /* Find the start of the column */ + + for(i = menu->iItem; i != 0 && + !(pItem[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK)); + --i); /* empty */ + + if(i == 0) + return NO_SELECTED_ITEM; + + for(--i; i != 0; --i) { + if (pItem[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK)) + break; + } + + TRACE("ret %d.\n", i ); + + return i; +} + /*********************************************************************** * MENU_FindItem * @@ -355,6 +562,44 @@ PITEM FASTCALL MENU_FindItem( PMENU *pmenu, UINT *nPos, UINT wFlags ) return fallback; } +/*********************************************************************** + * MenuFindSubMenu + * + * Find a Sub menu. Return the position of the submenu, and modifies + * *hmenu in case it is found in another sub-menu. + * If the submenu cannot be found, NO_SELECTED_ITEM is returned. + */ +static UINT FASTCALL MENU_FindSubMenu(PMENU *menu, PMENU SubTarget ) +{ + UINT i; + PITEM item; + + item = ((PMENU)*menu)->rgItems; + for (i = 0; i < ((PMENU)*menu)->cItems; i++, item++) + { + if (!item->spSubMenu) + continue; + else + { + if (item->spSubMenu == SubTarget) + { + return i; + } + else + { + PMENU pSubMenu = item->spSubMenu; + UINT pos = MENU_FindSubMenu( &pSubMenu, SubTarget ); + if (pos != NO_SELECTED_ITEM) + { + *menu = pSubMenu; + return pos; + } + } + } + } + return NO_SELECTED_ITEM; +} + BOOL FASTCALL IntRemoveMenuItem( PMENU pMenu, UINT nPos, UINT wFlags, BOOL bRecurse ) { @@ -990,12 +1235,15 @@ IntEnableMenuItem(PMENU MenuObject, UINT uIDEnableItem, UINT uEnable) case SC_SIZE: if (MenuObject->fFlags & MNF_SYSSUBMENU && MenuObject->spwndNotify != 0) { - RECTL rc = MenuObject->spwndNotify->rcWindow; + //RECTL rc = MenuObject->spwndNotify->rcWindow; /* Refresh the frame to reflect the change */ - IntMapWindowPoints(0, MenuObject->spwndNotify, (POINT *)&rc, 2); - rc.bottom = 0; - co_UserRedrawWindow(MenuObject->spwndNotify, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN); + //IntMapWindowPoints(0, MenuObject->spwndNotify, (POINT *)&rc, 2); + //rc.bottom = 0; + //co_UserRedrawWindow(MenuObject->spwndNotify, &rc, 0, RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN); + + // Allow UxTheme! + UserPaintCaption(MenuObject->spwndNotify, DC_BUTTONS); } default: break; @@ -1019,29 +1267,6 @@ IntCheckMenuItem(PMENU MenuObject, UINT uIDCheckItem, UINT uCheck) return res; } -BOOL FASTCALL -IntHiliteMenuItem(PWND WindowObject, - PMENU MenuObject, - UINT uItemHilite, - UINT uHilite) -{ - PITEM MenuItem; - - if (!(MenuItem = MENU_FindItem( &MenuObject, &uItemHilite, uHilite ))) return FALSE; - - if (uHilite & MF_HILITE) - { - MenuItem->fState |= MF_HILITE; - } - else - { - MenuItem->fState &= ~MF_HILITE; - } - /* FIXME: Update the window's menu */ - - return TRUE; // Always returns true!!!! -} - BOOL FASTCALL UserSetMenuDefaultItem(PMENU MenuObject, UINT uItem, UINT fByPos) { @@ -1083,7 +1308,6 @@ UserSetMenuDefaultItem(PMENU MenuObject, UINT uItem, UINT fByPos) return FALSE; } - UINT FASTCALL IntGetMenuDefaultItem(PMENU MenuObject, UINT fByPos, UINT gmdiFlags, DWORD *gismc) { @@ -1116,46 +1340,3191 @@ IntGetMenuDefaultItem(PMENU MenuObject, UINT fByPos, UINT gmdiFlags, DWORD *gism return ( fByPos ) ? i : MenuItem->wID; } -VOID FASTCALL -co_IntInitTracking(PWND Window, PMENU Menu, BOOL Popup, - UINT Flags) +PMENU +FASTCALL +co_IntGetSubMenu( + PMENU pMenu, + int nPos) { - /* FIXME: Hide caret */ - - if(!(Flags & TPM_NONOTIFY)) - co_IntSendMessage(Window->head.h, WM_SETCURSOR, (WPARAM)Window->head.h, HTCAPTION); - - /* FIXME: Send WM_SETCURSOR message */ - - if(!(Flags & TPM_NONOTIFY)) - co_IntSendMessage(Window->head.h, WM_INITMENU, (WPARAM)Menu->head.h, 0); + PITEM pItem; + if (!(pItem = MENU_FindItem( &pMenu, (UINT*)&nPos, MF_BYPOSITION ))) return NULL; + return pItem->spSubMenu; } -VOID FASTCALL -co_IntExitTracking(PWND Window, PMENU Menu, BOOL Popup, - UINT Flags) +/*********************************************************************** + * MenuInitSysMenuPopup + * + * Grey the appropriate items in System menu. + */ +void FASTCALL MENU_InitSysMenuPopup(PMENU menu, DWORD style, DWORD clsStyle, LONG HitTest ) { - if(!(Flags & TPM_NONOTIFY)) - co_IntSendMessage(Window->head.h, WM_EXITMENULOOP, 0 /* FIXME */, 0); + BOOL gray; + UINT DefItem; - /* FIXME: Show caret again */ + gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE)); + IntEnableMenuItem( menu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) ); + gray = ((style & WS_MAXIMIZE) != 0); + IntEnableMenuItem( menu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) ); + gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE); + IntEnableMenuItem( menu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) ); + gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE); + IntEnableMenuItem( menu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) ); + gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE)); + IntEnableMenuItem( menu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) ); + gray = (clsStyle & CS_NOCLOSE) != 0; + + /* The menu item must keep its state if it's disabled */ + if(gray) + IntEnableMenuItem( menu, SC_CLOSE, MF_GRAYED); + + /* Set default menu item */ + if(style & WS_MINIMIZE) DefItem = SC_RESTORE; + else if(HitTest == HTCAPTION) DefItem = ((style & (WS_MAXIMIZE | WS_MINIMIZE)) ? SC_RESTORE : SC_MAXIMIZE); + else DefItem = SC_CLOSE; + + UserSetMenuDefaultItem(menu, DefItem, MF_BYCOMMAND); } -INT FASTCALL -IntTrackMenu(PMENU Menu, PWND Window, INT x, INT y, - RECTL lprect) + +/*********************************************************************** + * MenuDrawPopupGlyph + * + * Draws popup magic glyphs (can be found in system menu). + */ +static void FASTCALL +MENU_DrawPopupGlyph(HDC dc, LPRECT r, INT_PTR popupMagic, BOOL inactive, BOOL hilite) { - return 0; + LOGFONTW lf; + HFONT hFont, hOldFont; + COLORREF clrsave; + INT bkmode; + WCHAR symbol; + switch (popupMagic) + { + case (INT_PTR) HBMMENU_POPUP_RESTORE: + symbol = '2'; + break; + case (INT_PTR) HBMMENU_POPUP_MINIMIZE: + symbol = '0'; + break; + case (INT_PTR) HBMMENU_POPUP_MAXIMIZE: + symbol = '1'; + break; + case (INT_PTR) HBMMENU_POPUP_CLOSE: + symbol = 'r'; + break; + default: + ERR("Invalid popup magic bitmap %d\n", (int)popupMagic); + return; + } + RtlZeroMemory(&lf, sizeof(LOGFONTW)); + RECTL_vInflateRect(r, -2, -2); + lf.lfHeight = r->bottom - r->top; + lf.lfWidth = 0; + lf.lfWeight = FW_NORMAL; + lf.lfCharSet = DEFAULT_CHARSET; + RtlCopyMemory(lf.lfFaceName, L"Marlett", sizeof(L"Marlett")); + hFont = GreCreateFontIndirectW(&lf); + /* save font and text color */ + hOldFont = NtGdiSelectFont(dc, hFont); + clrsave = GreGetTextColor(dc); + bkmode = GreGetBkMode(dc); + /* set color and drawing mode */ + IntGdiSetBkMode(dc, TRANSPARENT); + if (inactive) + { + /* draw shadow */ + if (!hilite) + { + IntGdiSetTextColor(dc, IntGetSysColor(COLOR_HIGHLIGHTTEXT)); + GreTextOutW(dc, r->left + 1, r->top + 1, &symbol, 1); + } + } + IntGdiSetTextColor(dc, IntGetSysColor(inactive ? COLOR_GRAYTEXT : (hilite ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT))); + /* draw selected symbol */ + GreTextOutW(dc, r->left, r->top, &symbol, 1); + /* restore previous settings */ + IntGdiSetTextColor(dc, clrsave); + NtGdiSelectFont(dc, hOldFont); + IntGdiSetBkMode(dc, bkmode); + GreDeleteObject(hFont); +} + +/*********************************************************************** + * MENU_AdjustMenuItemRect + * + * Adjust menu item rectangle according to scrolling state. + */ +VOID FASTCALL +MENU_AdjustMenuItemRect(PMENU menu, PRECTL rect) +{ + if (menu->dwArrowsOn) + { + UINT arrow_bitmap_height; + arrow_bitmap_height = gpsi->oembmi[OBI_UPARROW].cy; ///// Menu up arrow! OBM_UPARROW + rect->top += arrow_bitmap_height - menu->iTop; + rect->bottom += arrow_bitmap_height - menu->iTop; + } +} + +/*********************************************************************** + * MENU_FindItemByCoords + * + * Find the item at the specified coordinates (screen coords). Does + * not work for child windows and therefore should not be called for + * an arbitrary system menu. + */ +static ITEM *MENU_FindItemByCoords( MENU *menu, POINT pt, UINT *pos ) +{ + ITEM *item; + UINT i; + RECT rect; + PWND pWnd = ValidateHwndNoErr(menu->hWnd); + + if (!IntGetWindowRect(pWnd, &rect)) return NULL; + if (pWnd->ExStyle & WS_EX_LAYOUTRTL) + pt.x = rect.right - 1 - pt.x; + else + pt.x -= rect.left; + pt.y -= rect.top; + item = menu->rgItems; + for (i = 0; i < menu->cItems; i++, item++) + { + //rect = item->rect; + rect.left = item->xItem; + rect.top = item->yItem; + rect.right = item->cxItem; // Do this for now...... + rect.bottom = item->cyItem; + + MENU_AdjustMenuItemRect(menu, &rect); + if (RECTL_bPointInRect(&rect, pt.x, pt.y)) + { + if (pos) *pos = i; + return item; + } + } + return NULL; +} + +INT FASTCALL IntMenuItemFromPoint(PWND pWnd, HMENU hMenu, POINT ptScreen) +{ + MENU *menu = UserGetMenuObject(hMenu); + UINT pos; + + /*FIXME: Do we have to handle hWnd here? */ + if (!menu) return -1; + if (!MENU_FindItemByCoords(menu, ptScreen, &pos)) return -1; + return pos; +} + +/*********************************************************************** + * MenuFindItemByKey + * + * Find the menu item selected by a key press. + * Return item id, -1 if none, -2 if we should close the menu. + */ +static UINT FASTCALL MENU_FindItemByKey(PWND WndOwner, PMENU menu, + WCHAR Key, BOOL ForceMenuChar) +{ + LRESULT MenuChar; + WORD Flags = 0; + + TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)Key, Key, menu ); + + if (!menu || !VerifyMenu(menu)) + menu = co_IntGetSubMenu( UserGetMenuObject(WndOwner->SystemMenu), 0 ); + if (menu) + { + ITEM *item = menu->rgItems; + + if ( !ForceMenuChar ) + { + UINT i; + BOOL cjk = UserGetSystemMetrics( SM_DBCSENABLED ); + + for (i = 0; i < menu->cItems; i++, item++) + { + LPWSTR text = item->Xlpstr; + if( text) + { + const WCHAR *p = text - 2; + do + { + const WCHAR *q = p + 2; + p = wcschr (q, '&'); + if (!p && cjk) p = wcschr (q, '\036'); /* Japanese Win16 */ + } + while (p != NULL && p [1] == '&'); + if (p && (towupper(p[1]) == towupper(Key))) return i; + } + } + } + + Flags |= menu->fFlags & MNF_POPUP ? MF_POPUP : 0; + Flags |= menu->fFlags & MNF_SYSMENU ? MF_SYSMENU : 0; + + MenuChar = co_IntSendMessage( UserHMGetHandle(WndOwner), WM_MENUCHAR, + MAKEWPARAM(Key, Flags), (LPARAM) UserHMGetHandle(menu)); + if (HIWORD(MenuChar) == MNC_EXECUTE) return LOWORD(MenuChar); + if (HIWORD(MenuChar) == MNC_CLOSE) return (UINT)(-2); + } + return (UINT)(-1); +} + +/*********************************************************************** + * MenuGetBitmapItemSize + * + * Get the size of a bitmap item. + */ +static void FASTCALL MENU_GetBitmapItemSize(PITEM lpitem, SIZE *size, PWND WndOwner) +{ + BITMAP bm; + HBITMAP bmp = lpitem->hbmp; + + size->cx = size->cy = 0; + + /* check if there is a magic menu item associated with this item */ + if (IS_MAGIC_BITMAP(bmp)) + { + switch((INT_PTR) bmp) + { + case (INT_PTR)HBMMENU_CALLBACK: + { + MEASUREITEMSTRUCT measItem; + measItem.CtlType = ODT_MENU; + measItem.CtlID = 0; + measItem.itemID = lpitem->wID; + measItem.itemWidth = lpitem->cxItem - lpitem->xItem; //lpitem->Rect.right - lpitem->Rect.left; + measItem.itemHeight = lpitem->cyItem - lpitem->yItem; //lpitem->Rect.bottom - lpitem->Rect.top; + measItem.itemData = lpitem->dwItemData; + co_IntSendMessage( UserHMGetHandle(WndOwner), WM_MEASUREITEM, 0, (LPARAM)&measItem); + size->cx = measItem.itemWidth; + size->cy = measItem.itemHeight; + TRACE("HBMMENU_CALLBACK Height %d Width %d\n",measItem.itemHeight,measItem.itemWidth); + return; + } + break; + + case (INT_PTR) HBMMENU_SYSTEM: + if (lpitem->dwItemData) + { + bmp = (HBITMAP) lpitem->dwItemData; + break; + } + /* fall through */ + case (INT_PTR) HBMMENU_MBAR_RESTORE: + case (INT_PTR) HBMMENU_MBAR_MINIMIZE: + case (INT_PTR) HBMMENU_MBAR_CLOSE: + case (INT_PTR) HBMMENU_MBAR_MINIMIZE_D: + case (INT_PTR) HBMMENU_MBAR_CLOSE_D: + case (INT_PTR) HBMMENU_POPUP_CLOSE: + case (INT_PTR) HBMMENU_POPUP_RESTORE: + case (INT_PTR) HBMMENU_POPUP_MAXIMIZE: + case (INT_PTR) HBMMENU_POPUP_MINIMIZE: + /* FIXME: Why we need to subtract these magic values? */ + /* to make them smaller than the menu bar? */ + size->cx = UserGetSystemMetrics(SM_CXSIZE) - 2; + size->cy = UserGetSystemMetrics(SM_CYSIZE) - 4; + return; + } + } + + if (GreGetObject(bmp, sizeof(BITMAP), &bm)) + { + size->cx = bm.bmWidth; + size->cy = bm.bmHeight; + } +} + +/*********************************************************************** + * MenuDrawBitmapItem + * + * Draw a bitmap item. + */ +static void FASTCALL MENU_DrawBitmapItem(HDC hdc, PITEM lpitem, const RECT *rect, + PMENU Menu, PWND WndOwner, UINT odaction, BOOL MenuBar) +{ + BITMAP bm; + DWORD rop; + HDC hdcMem; + HBITMAP bmp; + int w = rect->right - rect->left; + int h = rect->bottom - rect->top; + int bmp_xoffset = 0; + int left, top; + HBITMAP hbmToDraw = lpitem->hbmp; + bmp = hbmToDraw; + + /* Check if there is a magic menu item associated with this item */ + if (IS_MAGIC_BITMAP(hbmToDraw)) + { + UINT flags = 0; + RECT r; + + r = *rect; + switch ((INT_PTR)hbmToDraw) + { + case (INT_PTR)HBMMENU_SYSTEM: + if (lpitem->dwItemData) + { + if (ValidateHwndNoErr((HWND)lpitem->dwItemData)) + { + ERR("Get Item Data from this Window!!!\n"); + } + + ERR("Draw Bitmap\n"); + bmp = (HBITMAP)lpitem->dwItemData; + if (!GreGetObject( bmp, sizeof(bm), &bm )) return; + } + else + { + PCURICON_OBJECT pIcon = NULL; + //if (!BmpSysMenu) BmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE)); + //bmp = BmpSysMenu; + //if (! GreGetObject(bmp, sizeof(bm), &bm)) return; + /* only use right half of the bitmap */ + //bmp_xoffset = bm.bmWidth / 2; + //bm.bmWidth -= bmp_xoffset; + if (WndOwner) + { + pIcon = NC_IconForWindow(WndOwner); + // FIXME: NC_IconForWindow should reference it for us */ + if (pIcon) UserReferenceObject(pIcon); + } + ERR("Draw ICON\n"); + if (pIcon) + { + LONG cx = UserGetSystemMetrics(SM_CXSMICON); + LONG cy = UserGetSystemMetrics(SM_CYSMICON); + LONG x = rect->left - cx/2 + 1 + (rect->bottom - rect->top)/2; // this is really what Window does + LONG y = (rect->top + rect->bottom)/2 - cy/2; // center + UserDrawIconEx(hdc, x, y, pIcon, cx, cy, 0, NULL, DI_NORMAL); + UserDereferenceObject(pIcon); + } + return; + } + goto got_bitmap; + case (INT_PTR)HBMMENU_MBAR_RESTORE: + flags = DFCS_CAPTIONRESTORE; + break; + case (INT_PTR)HBMMENU_MBAR_MINIMIZE: + r.right += 1; + flags = DFCS_CAPTIONMIN; + break; + case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D: + r.right += 1; + flags = DFCS_CAPTIONMIN | DFCS_INACTIVE; + break; + case (INT_PTR)HBMMENU_MBAR_CLOSE: + flags = DFCS_CAPTIONCLOSE; + break; + case (INT_PTR)HBMMENU_MBAR_CLOSE_D: + flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE; + break; + case (INT_PTR)HBMMENU_CALLBACK: + { + DRAWITEMSTRUCT drawItem; + POINT origorg; + drawItem.CtlType = ODT_MENU; + drawItem.CtlID = 0; + drawItem.itemID = lpitem->wID; + drawItem.itemAction = odaction; + drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0; + drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0; + drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0; + drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0; + drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0; + drawItem.itemState |= (!(Menu->fFlags & MNF_UNDERLINE))?ODS_NOACCEL:0; + drawItem.itemState |= (Menu->fFlags & MNF_INACTIVE)?ODS_INACTIVE:0; + drawItem.hwndItem = (HWND)UserHMGetHandle(Menu); + drawItem.hDC = hdc; + drawItem.rcItem = *rect; + drawItem.itemData = lpitem->dwItemData; + /* some applications make this assumption on the DC's origin */ + GreSetViewportOrgEx( hdc, lpitem->xItem, lpitem->yItem, &origorg); + RECTL_vOffsetRect( &drawItem.rcItem, - lpitem->xItem, - lpitem->yItem); + co_IntSendMessage( UserHMGetHandle(WndOwner), WM_DRAWITEM, 0, (LPARAM)&drawItem); + GreSetViewportOrgEx( hdc, origorg.x, origorg.y, NULL); + return; + } + break; + + case (INT_PTR) HBMMENU_POPUP_CLOSE: + case (INT_PTR) HBMMENU_POPUP_RESTORE: + case (INT_PTR) HBMMENU_POPUP_MAXIMIZE: + case (INT_PTR) HBMMENU_POPUP_MINIMIZE: + MENU_DrawPopupGlyph(hdc, &r, (INT_PTR)hbmToDraw, lpitem->fState & MF_GRAYED, lpitem->fState & MF_HILITE); + return; + } + RECTL_vInflateRect(&r, -1, -1); + if (lpitem->fState & MF_HILITE) flags |= DFCS_PUSHED; + DrawFrameControl(hdc, &r, DFC_CAPTION, flags); + return; + } + + if (!bmp || !GreGetObject( bmp, sizeof(bm), &bm )) return; + + got_bitmap: + hdcMem = NtGdiCreateCompatibleDC( hdc ); + NtGdiSelectBitmap( hdcMem, bmp ); + /* handle fontsize > bitmap_height */ + top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top; + left=rect->left; + rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY; + if ((lpitem->fState & MF_HILITE) && lpitem->hbmp) + IntGdiSetBkColor(hdc, IntGetSysColor(COLOR_HIGHLIGHT)); + NtGdiBitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop , 0, 0); + IntGdiDeleteDC( hdcMem, FALSE ); +} + +LONG +IntGetDialogBaseUnits(VOID) +{ + static DWORD units; + + if (!units) + { + HDC hdc; + SIZE size; + + if ((hdc = UserGetDCEx(NULL, NULL, DCX_CACHE))) + { + size.cx = IntGetCharDimensions( hdc, NULL, (PDWORD)&size.cy ); + if (size.cx) units = MAKELONG( size.cx, size.cy ); + UserReleaseDC( 0, hdc, FALSE); + } + } + return units; +} + + +/*********************************************************************** + * MenuCalcItemSize + * + * Calculate the size of the menu item and store it in lpitem->rect. + */ +static void FASTCALL MENU_CalcItemSize( HDC hdc, PITEM lpitem, PMENU Menu, PWND pwndOwner, + INT orgX, INT orgY, BOOL menuBar, BOOL textandbmp) +{ + WCHAR *p; + UINT check_bitmap_width = UserGetSystemMetrics( SM_CXMENUCHECK ); + UINT arrow_bitmap_width; + RECT Rect; + INT itemheight = 0; + + TRACE("dc=%x owner=%x (%d,%d)\n", hdc, pwndOwner, orgX, orgY); + + arrow_bitmap_width = gpsi->oembmi[OBI_MNARROW].cx; + + MenuCharSize.cx = IntGetCharDimensions( hdc, NULL, (PDWORD)&MenuCharSize.cy ); + + RECTL_vSetRect( &Rect, orgX, orgY, orgX, orgY ); + + if (lpitem->fType & MF_OWNERDRAW) + { + MEASUREITEMSTRUCT mis; + mis.CtlType = ODT_MENU; + mis.CtlID = 0; + mis.itemID = lpitem->wID; + mis.itemData = lpitem->dwItemData; + mis.itemHeight = HIWORD( IntGetDialogBaseUnits()); + mis.itemWidth = 0; + co_IntSendMessage( UserHMGetHandle(pwndOwner), WM_MEASUREITEM, 0, (LPARAM)&mis ); + /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average + * width of a menufont character to the width of an owner-drawn menu. + */ + Rect.right += mis.itemWidth + 2 * MenuCharSize.cx; + if (menuBar) { + /* under at least win95 you seem to be given a standard + height for the menu and the height value is ignored */ + Rect.bottom += UserGetSystemMetrics(SM_CYMENUSIZE); + } else + Rect.bottom += mis.itemHeight; + // Or this, + //lpitem->cxBmp = mis.itemWidth; + //lpitem->cyBmp = mis.itemHeight; + TRACE("MF_OWNERDRAW Height %d Width %d\n",mis.itemHeight,mis.itemWidth); + TRACE("MF_OWNERDRAW id=%04lx size=%dx%d cx %d cy %d\n", + lpitem->wID, Rect.right-Rect.left, + Rect.bottom-Rect.top, MenuCharSize.cx, MenuCharSize.cy); + + lpitem->xItem = Rect.left; + lpitem->yItem = Rect.top; + lpitem->cxItem = Rect.right; + lpitem->cyItem = Rect.bottom; + + return; + } + + lpitem->xItem = orgX; + lpitem->yItem = orgY; + lpitem->cxItem = orgX; + lpitem->cyItem = orgY; + + if (lpitem->fType & MF_SEPARATOR) + { + lpitem->cyItem += UserGetSystemMetrics( SM_CYMENUSIZE)/2;//SEPARATOR_HEIGHT; + if( !menuBar) + lpitem->cxItem += arrow_bitmap_width + MenuCharSize.cx; + return; + } + + lpitem->dxTab = 0; + + if (lpitem->hbmp) + { + SIZE size; + + if (!menuBar) { + MENU_GetBitmapItemSize(lpitem, &size, pwndOwner ); + /* Keep the size of the bitmap in callback mode to be able + * to draw it correctly */ + lpitem->cxBmp = size.cx; + lpitem->cyBmp = size.cy; + Menu->cxTextAlign = max(Menu->cxTextAlign, size.cx); + lpitem->cxItem += size.cx + 2; + itemheight = size.cy + 2; + + if( !((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK)) + lpitem->cxItem += 2 * check_bitmap_width; + lpitem->cxItem += 4 + MenuCharSize.cx; + lpitem->dxTab = lpitem->cxItem; + lpitem->cxItem += arrow_bitmap_width; + } else /* hbmpItem & MenuBar */ { + MENU_GetBitmapItemSize(lpitem, &size, pwndOwner ); + lpitem->cxItem += size.cx; + if( lpitem->Xlpstr) lpitem->cxItem += 2; + itemheight = size.cy; + + /* Special case: Minimize button doesn't have a space behind it. */ + if (lpitem->hbmp == (HBITMAP)HBMMENU_MBAR_MINIMIZE || + lpitem->hbmp == (HBITMAP)HBMMENU_MBAR_MINIMIZE_D) + lpitem->cxItem -= 1; + } + } + else if (!menuBar) { + if( !((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK)) + lpitem->cxItem += check_bitmap_width; + lpitem->cxItem += 4 + MenuCharSize.cx; + lpitem->dxTab = lpitem->cxItem; + lpitem->cxItem += arrow_bitmap_width; + } + + /* it must be a text item - unless it's the system menu */ + if (!(lpitem->fType & MF_SYSMENU) && lpitem->Xlpstr) { + HFONT hfontOld = NULL; + RECT rc;// = lpitem->Rect; + LONG txtheight, txtwidth; + + rc.left = lpitem->xItem; + rc.top = lpitem->yItem; + rc.right = lpitem->cxItem; // Do this for now...... + rc.bottom = lpitem->cyItem; + + if ( lpitem->fState & MFS_DEFAULT ) { + hfontOld = NtGdiSelectFont( hdc, ghMenuFontBold ); + } + if (menuBar) { + txtheight = DrawTextW( hdc, lpitem->Xlpstr, -1, &rc, DT_SINGLELINE|DT_CALCRECT); + + lpitem->cxItem += rc.right - rc.left; + itemheight = max( max( itemheight, txtheight), UserGetSystemMetrics( SM_CYMENU) - 1); + + lpitem->cxItem += 2 * MenuCharSize.cx; + } else { + if ((p = wcschr( lpitem->Xlpstr, '\t' )) != NULL) { + RECT tmprc = rc; + LONG tmpheight; + int n = (int)( p - lpitem->Xlpstr); + /* Item contains a tab (only meaningful in popup menus) */ + /* get text size before the tab */ + txtheight = DrawTextW( hdc, lpitem->Xlpstr, n, &rc, + DT_SINGLELINE|DT_CALCRECT); + txtwidth = rc.right - rc.left; + p += 1; /* advance past the Tab */ + /* get text size after the tab */ + tmpheight = DrawTextW( hdc, p, -1, &tmprc, + DT_SINGLELINE|DT_CALCRECT); + lpitem->dxTab += txtwidth; + txtheight = max( txtheight, tmpheight); + txtwidth += MenuCharSize.cx + /* space for the tab */ + tmprc.right - tmprc.left; /* space for the short cut */ + } else { + txtheight = DrawTextW( hdc, lpitem->Xlpstr, -1, &rc, + DT_SINGLELINE|DT_CALCRECT); + txtwidth = rc.right - rc.left; + lpitem->dxTab += txtwidth; + } + lpitem->cxItem += 2 + txtwidth; + itemheight = max( itemheight, + max( txtheight + 2, MenuCharSize.cy + 4)); + } + if (hfontOld) + { + NtGdiSelectFont (hdc, hfontOld); + } + } else if( menuBar) { + itemheight = max( itemheight, UserGetSystemMetrics(SM_CYMENU)-1); + } + lpitem->cyItem += itemheight; + TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem->xItem, lpitem->yItem, lpitem->cxItem, lpitem->cyItem); +} + +/*********************************************************************** + * MENU_GetMaxPopupHeight + */ +static UINT +MENU_GetMaxPopupHeight(PMENU lppop) +{ + if (lppop->cyMax) + { + //ERR("MGMaxPH cyMax %d\n",lppop->cyMax); + return lppop->cyMax; + } + //ERR("MGMaxPH SyMax %d\n",UserGetSystemMetrics(SM_CYSCREEN) - UserGetSystemMetrics(SM_CYBORDER)); + return UserGetSystemMetrics(SM_CYSCREEN) - UserGetSystemMetrics(SM_CYBORDER); +} + +/*********************************************************************** + * MenuPopupMenuCalcSize + * + * Calculate the size of a popup menu. + */ +static void FASTCALL MENU_PopupMenuCalcSize(PMENU Menu, PWND WndOwner) +{ + PITEM lpitem; + HDC hdc; + int start, i; + int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight; + BOOL textandbmp = FALSE; + + Menu->cxMenu = Menu->cyMenu = 0; + if (Menu->cItems == 0) return; + + hdc = UserGetDCEx(NULL, NULL, DCX_CACHE); + + NtGdiSelectFont( hdc, ghMenuFont ); + + start = 0; + maxX = 2 + 1; + + Menu->cxTextAlign = 0; + + while (start < Menu->cItems) + { + lpitem = &Menu->rgItems[start]; + orgX = maxX; + if( lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK)) + orgX += MENU_COL_SPACE; + orgY = MENU_TOP_MARGIN; + + maxTab = maxTabWidth = 0; + /* Parse items until column break or end of menu */ + for (i = start; i < Menu->cItems; i++, lpitem++) + { + if (i != start && + (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break; + + MENU_CalcItemSize(hdc, lpitem, Menu, WndOwner, orgX, orgY, FALSE, textandbmp); + maxX = max(maxX, lpitem->cxItem); + orgY = lpitem->cyItem; + if (IS_STRING_ITEM(lpitem->fType) && lpitem->dxTab ) + { + maxTab = max( maxTab, lpitem->dxTab ); + maxTabWidth = max(maxTabWidth, lpitem->cxItem - lpitem->dxTab); + } + if( lpitem->Xlpstr && lpitem->hbmp) textandbmp = TRUE; + } + + /* Finish the column (set all items to the largest width found) */ + maxX = max( maxX, maxTab + maxTabWidth ); + for (lpitem = &Menu->rgItems[start]; start < i; start++, lpitem++) + { + lpitem->cxItem = maxX; + if (IS_STRING_ITEM(lpitem->fType) && lpitem->dxTab) + lpitem->dxTab = maxTab; + } + Menu->cyMenu = max(Menu->cyMenu, orgY); + } + + Menu->cxMenu = maxX; + /* if none of the items have both text and bitmap then + * the text and bitmaps are all aligned on the left. If there is at + * least one item with both text and bitmap then bitmaps are + * on the left and texts left aligned with the right hand side + * of the bitmaps */ + if( !textandbmp) Menu->cxTextAlign = 0; + + /* space for 3d border */ + Menu->cyMenu += MENU_BOTTOM_MARGIN; + Menu->cxMenu += 2; + + /* Adjust popup height if it exceeds maximum */ + maxHeight = MENU_GetMaxPopupHeight(Menu); + Menu->iMaxTop = Menu->cyMenu - MENU_TOP_MARGIN; + if (Menu->cyMenu >= maxHeight) + { + Menu->cyMenu = maxHeight; + Menu->dwArrowsOn = 1; + } + else + { + Menu->dwArrowsOn = 0; + } + UserReleaseDC( 0, hdc, FALSE ); +} + +/*********************************************************************** + * MENU_MenuBarCalcSize + * + * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap + * height is off by 1 pixel which causes lengthy window relocations when + * active document window is maximized/restored. + * + * Calculate the size of the menu bar. + */ +static void MENU_MenuBarCalcSize( HDC hdc, LPRECT lprect, PMENU lppop, PWND pwndOwner ) +{ + ITEM *lpitem; + UINT start, i, helpPos; + int orgX, orgY, maxY; + + if ((lprect == NULL) || (lppop == NULL)) return; + if (lppop->cItems == 0) return; + //TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect)); + lppop->cxMenu = lprect->right - lprect->left; + lppop->cyMenu = 0; + maxY = lprect->top+1; + start = 0; + helpPos = ~0U; + lppop->cxTextAlign = 0; + while (start < lppop->cItems) + { + lpitem = &lppop->rgItems[start]; + orgX = lprect->left; + orgY = maxY; + + /* Parse items until line break or end of menu */ + for (i = start; i < lppop->cItems; i++, lpitem++) + { + if ((helpPos == ~0U) && (lpitem->fType & MF_RIGHTJUSTIFY)) helpPos = i; + if ((i != start) && + (lpitem->fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break; + + TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY ); + //debug_print_menuitem (" item: ", lpitem, ""); + //MENU_CalcItemSize( hdc, lpitem, pwndOwner, orgX, orgY, TRUE, lppop ); + MENU_CalcItemSize(hdc, lpitem, lppop, pwndOwner, orgX, orgY, TRUE, FALSE); + + if (lpitem->cxItem > lprect->right) + { + if (i != start) break; + else lpitem->cxItem = lprect->right; + } + maxY = max( maxY, lpitem->cyItem ); + orgX = lpitem->cxItem; + } + + /* Finish the line (set all items to the largest height found) */ + +/* FIXME: Is this really needed? */ /*NO! it is not needed, why make the + HBMMENU_MBAR_CLOSE, MINIMIZE & RESTORE, look the same size as the menu bar! */ +#if 0 + while (start < i) lppop->rgItems[start++].cyItem = maxY; +#endif + start = i; /* This works! */ + } + + lprect->bottom = maxY; + lppop->cyMenu = lprect->bottom - lprect->top; + + /* Flush right all items between the MF_RIGHTJUSTIFY and */ + /* the last item (if several lines, only move the last line) */ + if (helpPos == ~0U) return; + lpitem = &lppop->rgItems[lppop->cItems-1]; + orgY = lpitem->yItem; + orgX = lprect->right; + for (i = lppop->cItems - 1; i >= helpPos; i--, lpitem--) { + if (lpitem->yItem != orgY) break; /* Other line */ + if (lpitem->cxItem >= orgX) break; /* Too far right already */ + lpitem->xItem += orgX - lpitem->cxItem; + lpitem->cxItem = orgX; + orgX = lpitem->xItem; + } +} + +/*********************************************************************** + * MENU_DrawScrollArrows + * + * Draw scroll arrows. + */ +static void MENU_DrawScrollArrows(PMENU lppop, HDC hdc) +{ + UINT arrow_bitmap_width, arrow_bitmap_height; + RECT rect, dfcrc; + UINT Flags = 0; + + arrow_bitmap_width = gpsi->oembmi[OBI_DNARROW].cx; + arrow_bitmap_height = gpsi->oembmi[OBI_DNARROW].cy; + + rect.left = 0; + rect.top = 0; + rect.right = lppop->cxMenu; + rect.bottom = arrow_bitmap_height; + FillRect(hdc, &rect, IntGetSysColorBrush(COLOR_MENU)); + dfcrc.left = (lppop->cxMenu - arrow_bitmap_width) / 2; + dfcrc.top = 0; + dfcrc.right = arrow_bitmap_width; + dfcrc.bottom = arrow_bitmap_height; + DrawFrameControl(hdc, &dfcrc, DFC_MENU, (lppop->iTop ? 0 : DFCS_INACTIVE)|DFCS_MENUARROWUP); + + rect.top = lppop->cyMenu - arrow_bitmap_height; + rect.bottom = lppop->cyMenu; + FillRect(hdc, &rect, IntGetSysColorBrush(COLOR_MENU)); + if (!(lppop->iTop < lppop->iMaxTop - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height))) + Flags = DFCS_INACTIVE; + dfcrc.left = (lppop->cxMenu - arrow_bitmap_width) / 2; + dfcrc.top = lppop->cyMenu - arrow_bitmap_height; + dfcrc.right = arrow_bitmap_width; + dfcrc.bottom = lppop->cyMenu; + DrawFrameControl(hdc, &dfcrc, DFC_MENU, Flags|DFCS_MENUARROWDOWN); +} + +/*********************************************************************** + * MenuDrawMenuItem + * + * Draw a single menu item. + */ +static void FASTCALL MENU_DrawMenuItem(PWND Wnd, PMENU Menu, PWND WndOwner, HDC hdc, + PITEM lpitem, UINT Height, BOOL menuBar, UINT odaction) +{ + RECT rect; + PWCHAR Text; + BOOL flat_menu = FALSE; + int bkgnd; + UINT arrow_bitmap_width = 0; + + if (!menuBar) { + arrow_bitmap_width = gpsi->oembmi[OBI_MNARROW].cx; + } + + if (lpitem->fType & MF_SYSMENU) + { + if ( (Wnd->style & WS_MINIMIZE)) + { + UserGetInsideRectNC(Wnd, &rect); + UserDrawSysMenuButton(Wnd, hdc, &rect, lpitem->fState & (MF_HILITE | MF_MOUSESELECT)); + } + return; + } + + UserSystemParametersInfo (SPI_GETFLATMENU, 0, &flat_menu, 0); + bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU; + + /* Setup colors */ + + if (lpitem->fState & MF_HILITE) + { + if(menuBar && !flat_menu) { + IntGdiSetTextColor(hdc, IntGetSysColor(COLOR_MENUTEXT)); + IntGdiSetBkColor(hdc, IntGetSysColor(COLOR_MENU)); + } else { + if (lpitem->fState & MF_GRAYED) + IntGdiSetTextColor(hdc, IntGetSysColor(COLOR_GRAYTEXT)); + else + IntGdiSetTextColor(hdc, IntGetSysColor(COLOR_HIGHLIGHTTEXT)); + IntGdiSetBkColor(hdc, IntGetSysColor(COLOR_HIGHLIGHT)); + } + } + else + { + if (lpitem->fState & MF_GRAYED) + IntGdiSetTextColor( hdc, IntGetSysColor( COLOR_GRAYTEXT ) ); + else + IntGdiSetTextColor( hdc, IntGetSysColor( COLOR_MENUTEXT ) ); + IntGdiSetBkColor( hdc, IntGetSysColor( bkgnd ) ); + } + + //TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->Rect)); + //rect = lpitem->Rect; + rect.left = lpitem->xItem; + rect.top = lpitem->yItem; + rect.right = lpitem->cxItem; // Do this for now...... + rect.bottom = lpitem->cyItem; + + MENU_AdjustMenuItemRect(Menu, &rect); + + if (lpitem->fType & MF_OWNERDRAW) + { + /* + ** Experimentation under Windows reveals that an owner-drawn + ** menu is given the rectangle which includes the space it requested + ** in its response to WM_MEASUREITEM _plus_ width for a checkmark + ** and a popup-menu arrow. This is the value of lpitem->rect. + ** Windows will leave all drawing to the application except for + ** the popup-menu arrow. Windows always draws that itself, after + ** the menu owner has finished drawing. + */ + DRAWITEMSTRUCT dis; + + dis.CtlType = ODT_MENU; + dis.CtlID = 0; + dis.itemID = lpitem->wID; + dis.itemData = (DWORD)lpitem->dwItemData; + dis.itemState = 0; + if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED; + if (lpitem->fState & MF_DEFAULT) dis.itemState |= ODS_DEFAULT; + if (lpitem->fState & MF_DISABLED) dis.itemState |= ODS_DISABLED; + if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED | ODS_DISABLED; + if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED; + if (!(Menu->fFlags & MNF_UNDERLINE)) dis.itemState |= ODS_NOACCEL; + if (Menu->fFlags & MNF_INACTIVE) dis.itemState |= ODS_INACTIVE; + dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */ + dis.hwndItem = (HWND) UserHMGetHandle(Menu); + dis.hDC = hdc; + dis.rcItem = rect; + TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, " + "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", Wnd, + dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem, + dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right, + dis.rcItem.bottom); + TRACE("Ownerdraw: Width %d Height %d\n", dis.rcItem.right-dis.rcItem.left, dis.rcItem.bottom-dis.rcItem.top); + co_IntSendMessage(UserHMGetHandle(WndOwner), WM_DRAWITEM, 0, (LPARAM) &dis); + /* Draw the popup-menu arrow */ + if (lpitem->spSubMenu) + { + RECT rectTemp; + RtlCopyMemory(&rectTemp, &rect, sizeof(RECT)); + rectTemp.left = rectTemp.right - UserGetSystemMetrics(SM_CXMENUCHECK); + DrawFrameControl(hdc, &rectTemp, DFC_MENU, DFCS_MENUARROW); + } + return; + } + + if (menuBar && (lpitem->fType & MF_SEPARATOR)) return; + + if (lpitem->fState & MF_HILITE) + { + if (flat_menu) + { + RECTL_vInflateRect (&rect, -1, -1); + FillRect(hdc, &rect, IntGetSysColorBrush(COLOR_MENUHILIGHT)); + RECTL_vInflateRect (&rect, 1, 1); + FrameRect(hdc, &rect, IntGetSysColorBrush(COLOR_HIGHLIGHT)); + } + else + { + if(menuBar) + DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT); + else + FillRect(hdc, &rect, IntGetSysColorBrush(COLOR_HIGHLIGHT)); + } + } + else + FillRect( hdc, &rect, IntGetSysColorBrush(bkgnd) ); + + IntGdiSetBkMode( hdc, TRANSPARENT ); + + /* vertical separator */ + if (!menuBar && (lpitem->fType & MF_MENUBARBREAK)) + { + HPEN oldPen; + RECT rc = rect; + + rc.left -= 3;//MENU_COL_SPACE / 2 + 1; == 3!! + rc.top = 3; + rc.bottom = Height - 3; + if (flat_menu) + { + oldPen = NtGdiSelectPen( hdc, NtGdiGetStockObject(DC_PEN) ); + IntSetDCPenColor(hdc, IntGetSysColor(COLOR_BTNSHADOW)); + GreMoveTo( hdc, rc.left, rc.top, NULL ); + NtGdiLineTo( hdc, rc.left, rc.bottom ); + NtGdiSelectPen( hdc, oldPen ); + } + else + DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT); + } + + /* horizontal separator */ + if (lpitem->fType & MF_SEPARATOR) + { + HPEN oldPen; + RECT rc = rect; + + rc.left++; + rc.right--; + rc.top += SEPARATOR_HEIGHT / 2; + if (flat_menu) + { + oldPen = NtGdiSelectPen( hdc, NtGdiGetStockObject(DC_PEN) ); + IntSetDCPenColor( hdc, IntGetSysColor(COLOR_BTNSHADOW)); + GreMoveTo( hdc, rc.left, rc.top, NULL ); + NtGdiLineTo( hdc, rc.right, rc.top ); + NtGdiSelectPen( hdc, oldPen ); + } + else + DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP); + return; + } + +#if 0 + /* helper lines for debugging */ + /* This is a very good test tool when hacking menus! (JT) 07/16/2006 */ + FrameRect(hdc, &rect, NtGdiGetStockObject(BLACK_BRUSH)); + NtGdiSelectPen(hdc, NtGdiGetStockObject(DC_PEN)); + IntSetDCPenColor(hdc, IntGetSysColor(COLOR_WINDOWFRAME)); + GreMoveTo(hdc, rect.left, (rect.top + rect.bottom) / 2, NULL); + NtGdiLineTo(hdc, rect.right, (rect.top + rect.bottom) / 2); +#endif + + if (!menuBar) + { + HBITMAP bm; + INT y = rect.top + rect.bottom; + RECT rc = rect; + BOOL checked = FALSE; + UINT check_bitmap_width = UserGetSystemMetrics( SM_CXMENUCHECK ); + UINT check_bitmap_height = UserGetSystemMetrics( SM_CYMENUCHECK ); + /* Draw the check mark + * + * FIXME: + * Custom checkmark bitmaps are monochrome but not always 1bpp. + */ + if( !((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK)) { + bm = (lpitem->fState & MF_CHECKED) ? lpitem->hbmpChecked : + lpitem->hbmpUnchecked; + if (bm) /* we have a custom bitmap */ + { + HDC hdcMem = NtGdiCreateCompatibleDC( hdc ); + + NtGdiSelectBitmap( hdcMem, bm ); + NtGdiBitBlt( hdc, rc.left, (y - check_bitmap_height) / 2, + check_bitmap_width, check_bitmap_height, + hdcMem, 0, 0, SRCCOPY, 0,0); + IntGdiDeleteDC( hdcMem, FALSE ); + checked = TRUE; + } + else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */ + { + RECT r; + r = rect; + r.right = r.left + UserGetSystemMetrics(SM_CXMENUCHECK); + DrawFrameControl( hdc, &r, DFC_MENU, + (lpitem->fType & MFT_RADIOCHECK) ? + DFCS_MENUBULLET : DFCS_MENUCHECK); + checked = TRUE; + } + } + if ( lpitem->hbmp )//&& !( checked && (Menu->dwStyle & MNS_CHECKORBMP))) + { + RECT bmpRect; + //CopyRect(&bmpRect, &rect); + bmpRect = rect; + if (!((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP) && !((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK)) + bmpRect.left += check_bitmap_width + 2; + if (!(checked && ((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP))) + { + //POINT origorg; + bmpRect.right = bmpRect.left + lpitem->cxBmp; + /* some applications make this assumption on the DC's origin */ + //SetViewportOrgEx( hdc, rect.left, rect.top, &origorg); + MENU_DrawBitmapItem(hdc, lpitem, &bmpRect, Menu, WndOwner, odaction, menuBar); + //SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL); + } + } + /* Draw the popup-menu arrow */ + if (lpitem->spSubMenu) + { + RECT rectTemp; + RtlCopyMemory(&rectTemp, &rect, sizeof(RECT)); + rectTemp.left = rectTemp.right - UserGetSystemMetrics(SM_CXMENUCHECK); + DrawFrameControl(hdc, &rectTemp, DFC_MENU, DFCS_MENUARROW); + } + rect.left += 4; + if( !((Menu->fFlags & MNS_STYLE_MASK) & MNS_NOCHECK)) + rect.left += check_bitmap_width; + rect.right -= arrow_bitmap_width;//check_bitmap_width; + } + else if( lpitem->hbmp) + { /* Draw the bitmap */ + //POINT origorg; + + //SetViewportOrgEx( hdc, rect.left, rect.top, &origorg); + MENU_DrawBitmapItem(hdc, lpitem, &rect, Menu, WndOwner, odaction, menuBar); + //SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL); + } + + /* process text if present */ + if (lpitem->Xlpstr) + { + int i = 0; + HFONT hfontOld = 0; + + UINT uFormat = menuBar ? + DT_CENTER | DT_VCENTER | DT_SINGLELINE : + DT_LEFT | DT_VCENTER | DT_SINGLELINE; + + if (((Menu->fFlags & MNS_STYLE_MASK) & MNS_CHECKORBMP)) + rect.left += max(0, (int)(Menu->cxTextAlign - UserGetSystemMetrics(SM_CXMENUCHECK))); + else + rect.left += Menu->cxTextAlign; + + if ( lpitem->fState & MFS_DEFAULT ) + { + hfontOld = NtGdiSelectFont(hdc, ghMenuFontBold); + } + + if (menuBar) { + if( lpitem->hbmp) + rect.left += lpitem->cxBmp; + if( !(lpitem->hbmp == HBMMENU_CALLBACK)) + rect.left += MenuCharSize.cx; + rect.right -= MenuCharSize.cx; + //rect.left += MENU_BAR_ITEMS_SPACE / 2; + //rect.right -= MENU_BAR_ITEMS_SPACE / 2; + } + + Text = lpitem->Xlpstr; + if(Text) + { + for (i = 0; L'\0' != Text[i]; i++) + if (Text[i] == L'\t' || Text[i] == L'\b') + break; + } + + if(lpitem->fState & MF_GRAYED) + { + if (!(lpitem->fState & MF_HILITE) ) + { + ++rect.left; ++rect.top; ++rect.right; ++rect.bottom; + IntGdiSetTextColor(hdc, RGB(0xff, 0xff, 0xff)); + DrawTextW( hdc, Text, i, &rect, uFormat ); + --rect.left; --rect.top; --rect.right; --rect.bottom; + } + IntGdiSetTextColor(hdc, RGB(0x80, 0x80, 0x80)); + } + DrawTextW( hdc, Text, i, &rect, uFormat); + + /* paint the shortcut text */ + if (!menuBar && L'\0' != Text[i]) /* There's a tab or flush-right char */ + { + if (L'\t' == Text[i]) + { + rect.left = lpitem->dxTab; + uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE; + } + else + { + rect.right = lpitem->dxTab; + uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE; + } + + if (lpitem->fState & MF_GRAYED) + { + if (!(lpitem->fState & MF_HILITE) ) + { + ++rect.left; ++rect.top; ++rect.right; ++rect.bottom; + IntGdiSetTextColor(hdc, RGB(0xff, 0xff, 0xff)); + DrawTextW( hdc, Text + i + 1, -1, &rect, uFormat); + --rect.left; --rect.top; --rect.right; --rect.bottom; + } + IntGdiSetTextColor(hdc, RGB(0x80, 0x80, 0x80)); + } + DrawTextW( hdc, Text + i + 1, -1, &rect, uFormat ); + } + + if (hfontOld) + { + NtGdiSelectFont (hdc, hfontOld); + } + } +} + +/*********************************************************************** + * MenuDrawPopupMenu + * + * Paint a popup menu. + */ +static void FASTCALL MENU_DrawPopupMenu(PWND wnd, HDC hdc, PMENU menu ) +{ + HBRUSH hPrevBrush = 0, brush = IntGetSysColorBrush(COLOR_MENU); + RECT rect; + + TRACE("DPM wnd=%p dc=%p menu=%p\n", wnd, hdc, menu); + + IntGetClientRect( wnd, &rect ); + + if (menu && menu->hbrBack) brush = menu->hbrBack; + if((hPrevBrush = NtGdiSelectBrush( hdc, brush )) + && (NtGdiSelectFont( hdc, ghMenuFont))) + { + HPEN hPrevPen; + + NtGdiRectangle( hdc, rect.left, rect.top, rect.right, rect.bottom ); + + hPrevPen = NtGdiSelectPen( hdc, NtGdiGetStockObject( NULL_PEN ) ); + if ( hPrevPen ) + { + BOOL flat_menu = FALSE; + + UserSystemParametersInfo (SPI_GETFLATMENU, 0, &flat_menu, 0); + if (flat_menu) + FrameRect(hdc, &rect, IntGetSysColorBrush(COLOR_BTNSHADOW)); + else + DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT); + + TRACE("hmenu %p Style %08x\n", UserHMGetHandle(menu), (menu->fFlags & MNS_STYLE_MASK)); + /* draw menu items */ + if (menu && menu->cItems) + { + ITEM *item; + UINT u; + + item = menu->rgItems; + for (u = 0; u < menu->cItems; u++, item++) + { + MENU_DrawMenuItem(wnd, menu, menu->spwndNotify, hdc, item, + menu->cyMenu, FALSE, ODA_DRAWENTIRE); + } + /* draw scroll arrows */ + if (menu->dwArrowsOn) + { + MENU_DrawScrollArrows(menu, hdc); + } + } + } + else + { + NtGdiSelectBrush( hdc, hPrevBrush ); + } + } +} + +/********************************************************************** + * MENU_IsMenuActive + */ +PWND MENU_IsMenuActive(VOID) +{ + return ValidateHwndNoErr(top_popup); +} + +/********************************************************************** + * MENU_EndMenu + * + * Calls EndMenu() if the hwnd parameter belongs to the menu owner + * + * Does the (menu stuff) of the default window handling of WM_CANCELMODE + */ +void MENU_EndMenu( PWND pwnd ) +{ + PMENU menu = NULL; + menu = UserGetMenuObject(top_popup_hmenu); + if ( menu && ( UserHMGetHandle(pwnd) == menu->hWnd || pwnd == menu->spwndNotify ) ) + { + if (fInsideMenuLoop && top_popup) + { + fInsideMenuLoop = FALSE; + + if (fInEndMenu) + { + ERR("Already in End loop\n"); + return; + } + + fInEndMenu = TRUE; + UserPostMessage( top_popup, WM_CANCELMODE, 0, 0); + } + } +} + +DWORD WINAPI +IntDrawMenuBarTemp(PWND pWnd, HDC hDC, LPRECT Rect, PMENU pMenu, HFONT Font) +{ + UINT i; + HFONT FontOld = NULL; + BOOL flat_menu = FALSE; + + UserSystemParametersInfo(SPI_GETFLATMENU, 0, &flat_menu, 0); + + if (!pMenu) + { + pMenu = UserGetMenuObject(UlongToHandle(pWnd->IDMenu)); + } + + if (!Font) + { + Font = ghMenuFont; + } + + if (Rect == NULL || !pMenu) + { + return UserGetSystemMetrics(SM_CYMENU); + } + + TRACE("(%x, %x, %p, %x, %x)\n", pWnd, hDC, Rect, pMenu, Font); + + FontOld = NtGdiSelectFont(hDC, Font); + + if (pMenu->cyMenu == 0) + { + MENU_MenuBarCalcSize(hDC, Rect, pMenu, pWnd); + } + + Rect->bottom = Rect->top + pMenu->cyMenu; + + FillRect(hDC, Rect, IntGetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU)); + + NtGdiSelectPen(hDC, NtGdiGetStockObject(DC_PEN)); + IntSetDCPenColor(hDC, IntGetSysColor(COLOR_3DFACE)); + GreMoveTo(hDC, Rect->left, Rect->bottom - 1, NULL); + NtGdiLineTo(hDC, Rect->right, Rect->bottom - 1); + + if (pMenu->cItems == 0) + { + NtGdiSelectFont(hDC, FontOld); + return UserGetSystemMetrics(SM_CYMENU); + } + + for (i = 0; i < pMenu->cItems; i++) + { + MENU_DrawMenuItem(pWnd, pMenu, pWnd, hDC, &pMenu->rgItems[i], pMenu->cyMenu, TRUE, ODA_DRAWENTIRE); + } + + NtGdiSelectFont(hDC, FontOld); + + return pMenu->cyMenu; +} + +UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, PWND pWnd, BOOL suppress_draw ) +{ + HFONT hfontOld = 0; + PMENU lppop = UserGetMenuObject(UlongToHandle(pWnd->IDMenu)); + + if (lppop == NULL || lprect == NULL) + { + return UserGetSystemMetrics(SM_CYMENU); + } + + if (suppress_draw) + { + hfontOld = NtGdiSelectFont(hDC, ghMenuFont); + + MENU_MenuBarCalcSize(hDC, lprect, lppop, pWnd); + + lprect->bottom = lprect->top + lppop->cyMenu; + + if (hfontOld) NtGdiSelectFont( hDC, hfontOld); + + return lppop->cyMenu; + } + else + { + return IntDrawMenuBarTemp(pWnd, hDC, lprect, lppop, NULL); + } +} + +/*********************************************************************** + * MENU_InitPopup + * + * Popup menu initialization before WM_ENTERMENULOOP. + */ +static BOOL MENU_InitPopup( PWND pWndOwner, PMENU menu, UINT flags ) +{ + PWND pWndCreated; + PPOPUPMENU pPopupMenu; + CREATESTRUCTW Cs; + LARGE_STRING WindowName; + UNICODE_STRING ClassName; + DWORD ex_style = WS_EX_TOOLWINDOW; + + TRACE("owner=%p hmenu=%p\n", pWndOwner, menu); + + menu->spwndNotify = pWndOwner; + + if (flags & TPM_LAYOUTRTL || pWndOwner->ExStyle & WS_EX_LAYOUTRTL) + ex_style = WS_EX_LAYOUTRTL; + + ClassName.Buffer = WC_MENU; + ClassName.Length = 0; + + RtlZeroMemory(&WindowName, sizeof(WindowName)); + RtlZeroMemory(&Cs, sizeof(Cs)); + Cs.style = WS_POPUP; + Cs.dwExStyle = ex_style; + Cs.hInstance = hModClient; // hModuleWin; // Server side winproc! + Cs.lpszName = (LPCWSTR) &WindowName; + Cs.lpszClass = (LPCWSTR) &ClassName; + Cs.lpCreateParams = UserHMGetHandle(menu); + Cs.hwndParent = UserHMGetHandle(pWndOwner); + + /* NOTE: In Windows, top menu popup is not owned. */ + pWndCreated = co_UserCreateWindowEx( &Cs, &ClassName, &WindowName, NULL); + + if( !pWndCreated ) return FALSE; + + // + // Setup pop up menu structure. + // + menu->hWnd = UserHMGetHandle(pWndCreated); + + pPopupMenu = ((PMENUWND)pWndCreated)->ppopupmenu; + + pPopupMenu->spwndActivePopup = pWndCreated; // top_popup = MenuInfo.Wnd or menu->hWnd + pPopupMenu->spwndNotify = pWndOwner; // Same as MenuInfo.spwndNotify(which could be wrong) or menu->hwndOwner + //pPopupMenu->spmenu = menu; Should be set up already from WM_CREATE! + + pPopupMenu->fIsTrackPopup = !!(flags & TPM_POPUPMENU); + pPopupMenu->fIsSysMenu = !!(flags & TPM_SYSTEM_MENU); + pPopupMenu->fNoNotify = !!(flags & TPM_NONOTIFY); + pPopupMenu->fRightButton = !!(flags & TPM_RIGHTBUTTON); + pPopupMenu->fSynchronous = !!(flags & TPM_RETURNCMD); + + if (pPopupMenu->fRightButton) + pPopupMenu->fFirstClick = !!(UserGetKeyState(VK_RBUTTON) & 0x8000); + else + pPopupMenu->fFirstClick = !!(UserGetKeyState(VK_LBUTTON) & 0x8000); + + if (gpsi->aiSysMet[SM_MENUDROPALIGNMENT] || + menu->fFlags & MNF_RTOL) + { + pPopupMenu->fDroppedLeft = TRUE; + } + return TRUE; +} + +/*********************************************************************** + * MenuShowPopup + * + * Display a popup menu. + */ +static BOOL FASTCALL MENU_ShowPopup(PWND pwndOwner, PMENU menu, UINT id, UINT flags, + INT x, INT y, INT xanchor, INT yanchor ) +{ + UINT width, height; + POINT pt; + PMONITOR monitor; + PWND pWnd; + USER_REFERENCE_ENTRY Ref; + + TRACE("owner=%p menu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n", + pwndOwner, menu, id, x, y, xanchor, yanchor); + + if (menu->iItem != NO_SELECTED_ITEM) + { + menu->rgItems[menu->iItem].fState &= ~(MF_HILITE|MF_MOUSESELECT); + menu->iItem = NO_SELECTED_ITEM; + } + + menu->dwArrowsOn = 0; + MENU_PopupMenuCalcSize(menu, pwndOwner); + + /* adjust popup menu pos so that it fits within the desktop */ + + width = menu->cxMenu + UserGetSystemMetrics(SM_CXBORDER); + height = menu->cyMenu + UserGetSystemMetrics(SM_CYBORDER); + + /* FIXME: should use item rect */ + pt.x = x; + pt.y = y; + monitor = UserMonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST ); + + if (flags & TPM_LAYOUTRTL) + flags ^= TPM_RIGHTALIGN; + + if( flags & TPM_RIGHTALIGN ) x -= width; + if( flags & TPM_CENTERALIGN ) x -= width / 2; + + if( flags & TPM_BOTTOMALIGN ) y -= height; + if( flags & TPM_VCENTERALIGN ) y -= height / 2; + + if( x + width > monitor->rcMonitor.right) + { + if( xanchor && x >= width - xanchor ) + x -= width - xanchor; + + if( x + width > monitor->rcMonitor.right) + x = monitor->rcMonitor.right - width; + } + if( x < monitor->rcMonitor.left ) x = monitor->rcMonitor.left; + + if( y + height > monitor->rcMonitor.bottom) + { + if( yanchor && y >= height + yanchor ) + y -= height + yanchor; + + if( y + height > monitor->rcMonitor.bottom) + y = monitor->rcMonitor.bottom - height; + } + if( y < monitor->rcMonitor.top ) y = monitor->rcMonitor.top; + + pWnd = ValidateHwndNoErr( menu->hWnd ); + + if (!pWnd) + { + ERR("menu->hWnd bad hwnd %p\n",menu->hWnd); + return FALSE; + } + + if (!top_popup) { + top_popup = menu->hWnd; + top_popup_hmenu = UserHMGetHandle(menu); + } + + /* Display the window */ + UserRefObjectCo(pWnd, &Ref); + co_WinPosSetWindowPos( pWnd, HWND_TOPMOST, x, y, width, height, SWP_SHOWWINDOW | SWP_NOACTIVATE); + + co_IntUpdateWindows(pWnd, RDW_ALLCHILDREN, FALSE); + + IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPSTART, pWnd, OBJID_CLIENT, CHILDID_SELF, 0); + UserDerefObjectCo(pWnd); + + return TRUE; +} + +/*********************************************************************** + * MENU_EnsureMenuItemVisible + */ +void MENU_EnsureMenuItemVisible(PMENU lppop, UINT wIndex, HDC hdc) +{ + USER_REFERENCE_ENTRY Ref; + if (lppop->dwArrowsOn) + { + ITEM *item = &lppop->rgItems[wIndex]; + UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop); + UINT nOldPos = lppop->iTop; + RECT rc; + UINT arrow_bitmap_height; + PWND pWnd = ValidateHwndNoErr(lppop->hWnd); + + IntGetClientRect(pWnd, &rc); + + arrow_bitmap_height = gpsi->oembmi[OBI_DNARROW].cy; + + rc.top += arrow_bitmap_height; + rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN; + + nMaxHeight -= UserGetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height; + UserRefObjectCo(pWnd, &Ref); + if (item->cyItem > lppop->iTop + nMaxHeight) + { + lppop->iTop = item->cyItem - nMaxHeight; + IntScrollWindow(pWnd, 0, nOldPos - lppop->iTop, &rc, &rc); + MENU_DrawScrollArrows(lppop, hdc); + //ERR("Scroll Down iTop %d iMaxTop %d nMaxHeight %d\n",lppop->iTop,lppop->iMaxTop,nMaxHeight); + } + else if (item->yItem - MENU_TOP_MARGIN < lppop->iTop) + { + lppop->iTop = item->yItem - MENU_TOP_MARGIN; + IntScrollWindow(pWnd, 0, nOldPos - lppop->iTop, &rc, &rc); + MENU_DrawScrollArrows(lppop, hdc); + //ERR("Scroll Up iTop %d iMaxTop %d nMaxHeight %d\n",lppop->iTop,lppop->iMaxTop,nMaxHeight); + } + UserDerefObjectCo(pWnd); + } +} + +/*********************************************************************** + * MenuSelectItem + */ +static void FASTCALL MENU_SelectItem(PWND pwndOwner, PMENU menu, UINT wIndex, + BOOL sendMenuSelect, PMENU topmenu) +{ + HDC hdc; + PWND pWnd = ValidateHwndNoErr(menu->hWnd); + + TRACE("M_SI: owner=%p menu=%p index=0x%04x select=0x%04x\n", pwndOwner, menu, wIndex, sendMenuSelect); + + if (!menu || !menu->cItems || !pWnd) return; + + if (menu->iItem == wIndex) return; + + if (menu->fFlags & MNF_POPUP) + hdc = UserGetDCEx(pWnd, 0, DCX_USESTYLE); + else + hdc = UserGetDCEx(pWnd, 0, DCX_CACHE | DCX_WINDOW); + + if (!top_popup) { + top_popup = menu->hWnd; //pPopupMenu->spwndActivePopup or + //pPopupMenu->fIsTrackPopup set pPopupMenu->spwndPopupMenu; + top_popup_hmenu = UserHMGetHandle(menu); //pPopupMenu->spmenu + } + + NtGdiSelectFont( hdc, ghMenuFont ); + + /* Clear previous highlighted item */ + if (menu->iItem != NO_SELECTED_ITEM) + { + menu->rgItems[menu->iItem].fState &= ~(MF_HILITE|MF_MOUSESELECT); + MENU_DrawMenuItem(pWnd, menu, pwndOwner, hdc, &menu->rgItems[menu->iItem], + menu->cyMenu, !(menu->fFlags & MNF_POPUP), + ODA_SELECT); + } + + /* Highlight new item (if any) */ + menu->iItem = wIndex; + if (menu->iItem != NO_SELECTED_ITEM) + { + if (!(menu->rgItems[wIndex].fType & MF_SEPARATOR)) + { + menu->rgItems[wIndex].fState |= MF_HILITE; + MENU_EnsureMenuItemVisible(menu, wIndex, hdc); + MENU_DrawMenuItem(pWnd, menu, pwndOwner, hdc, + &menu->rgItems[wIndex], menu->cyMenu, !(menu->fFlags & MNF_POPUP), ODA_SELECT); + } + if (sendMenuSelect) + { + ITEM *ip = &menu->rgItems[menu->iItem]; + WPARAM wParam = MAKEWPARAM( ip->spSubMenu ? wIndex : ip->wID, + ip->fType | ip->fState | + (ip->spSubMenu ? MF_POPUP : 0) | + (menu->fFlags & MNF_SYSMENU ? MF_SYSMENU : 0 ) ); + + co_IntSendMessage(UserHMGetHandle(pwndOwner), WM_MENUSELECT, wParam, (LPARAM) UserHMGetHandle(menu)); + } + } + else if (sendMenuSelect) + { + if (topmenu) + { + int pos; + pos = MENU_FindSubMenu(&topmenu, menu); + if (pos != NO_SELECTED_ITEM) + { + ITEM *ip = &topmenu->rgItems[pos]; + WPARAM wParam = MAKEWPARAM( Pos, ip->fType | ip->fState | + (ip->spSubMenu ? MF_POPUP : 0) | + (topmenu->fFlags & MNF_SYSMENU ? MF_SYSMENU : 0 ) ); + + co_IntSendMessage(UserHMGetHandle(pwndOwner), WM_MENUSELECT, wParam, (LPARAM) UserHMGetHandle(topmenu)); + } + } + } + UserReleaseDC(pWnd, hdc, FALSE); +} + +/*********************************************************************** + * MenuMoveSelection + * + * Moves currently selected item according to the Offset parameter. + * If there is no selection then it should select the last item if + * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT. + */ +static void FASTCALL MENU_MoveSelection(PWND pwndOwner, PMENU menu, INT offset) +{ + INT i; + + TRACE("pwnd=%x menu=%x off=0x%04x\n", pwndOwner, menu, offset); + + if ((!menu) || (!menu->rgItems)) return; + + if ( menu->iItem != NO_SELECTED_ITEM ) + { + if ( menu->cItems == 1 ) + return; + else + for (i = menu->iItem + offset ; i >= 0 && i < menu->cItems + ; i += offset) + if (!(menu->rgItems[i].fType & MF_SEPARATOR)) + { + MENU_SelectItem( pwndOwner, menu, i, TRUE, 0 ); + return; + } + } + + for ( i = (offset > 0) ? 0 : menu->cItems - 1; + i >= 0 && i < menu->cItems ; i += offset) + if (!(menu->rgItems[i].fType & MF_SEPARATOR)) + { + MENU_SelectItem( pwndOwner, menu, i, TRUE, 0 ); + return; + } +} + +/*********************************************************************** + * MenuHideSubPopups + * + * Hide the sub-popup menus of this menu. + */ +static void FASTCALL MENU_HideSubPopups(PWND pWndOwner, PMENU Menu, + BOOL SendMenuSelect, UINT wFlags) +{ + TRACE("owner=%x menu=%x 0x%04x\n", pWndOwner, Menu, SendMenuSelect); + + if ( Menu && top_popup ) + { + PITEM Item; + + if (Menu->iItem != NO_SELECTED_ITEM) + { + Item = &Menu->rgItems[Menu->iItem]; + if (!(Item->spSubMenu) || + !(Item->fState & MF_MOUSESELECT)) return; + Item->fState &= ~MF_MOUSESELECT; + } + else + return; + + if (Item->spSubMenu) + { + PWND pWnd; + if (!VerifyMenu(Item->spSubMenu)) return; + pWnd = ValidateHwndNoErr(Item->spSubMenu->hWnd); + MENU_HideSubPopups(pWndOwner, Item->spSubMenu, FALSE, wFlags); + MENU_SelectItem(pWndOwner, Item->spSubMenu, NO_SELECTED_ITEM, SendMenuSelect, NULL); + TRACE("M_HSP top p hm %p pWndOwner IDMenu %p\n",top_popup_hmenu,pWndOwner->IDMenu); + co_UserDestroyWindow(pWnd); + + /* Native returns handle to destroyed window */ + if (!(wFlags & TPM_NONOTIFY)) + { + co_IntSendMessage( UserHMGetHandle(pWndOwner), WM_UNINITMENUPOPUP, (WPARAM)UserHMGetHandle(Item->spSubMenu), + MAKELPARAM(0, IS_SYSTEM_MENU(Item->spSubMenu)) ); + } + //// + // Call WM_UNINITMENUPOPUP FIRST before destroy!! + // Fixes todo_wine User32 test menu.c line 2239 GetMenuBarInfo callback.... + // + Item->spSubMenu->hWnd = NULL; + //// + } + } +} + +/*********************************************************************** + * MenuShowSubPopup + * + * Display the sub-menu of the selected item of this menu. + * Return the handle of the submenu, or menu if no submenu to display. + */ +static PMENU FASTCALL MENU_ShowSubPopup(PWND WndOwner, PMENU Menu, BOOL SelectFirst, UINT Flags) +{ + RECT Rect; + ITEM *Item; + HDC Dc; + PWND pWnd; + + TRACE("owner=%x menu=%p 0x%04x\n", WndOwner, Menu, SelectFirst); + + if (Menu->iItem == NO_SELECTED_ITEM) return Menu; + + Item = &Menu->rgItems[Menu->iItem]; + if (!(Item->spSubMenu) || (Item->fState & (MF_GRAYED | MF_DISABLED))) + return Menu; + + /* message must be sent before using item, + because nearly everything may be changed by the application ! */ + + /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */ + if (!(Flags & TPM_NONOTIFY)) + { + co_IntSendMessage(UserHMGetHandle(WndOwner), WM_INITMENUPOPUP, + (WPARAM) UserHMGetHandle(Item->spSubMenu), + MAKELPARAM(Menu->iItem, IS_SYSTEM_MENU(Menu))); + } + + Item = &Menu->rgItems[Menu->iItem]; + //Rect = ItemInfo.Rect; + Rect.left = Item->xItem; + Rect.top = Item->yItem; + Rect.right = Item->cxItem; // Do this for now...... + Rect.bottom = Item->cyItem; + + pWnd = ValidateHwndNoErr(Menu->hWnd); + + /* correct item if modified as a reaction to WM_INITMENUPOPUP message */ + if (!(Item->fState & MF_HILITE)) + { + if (Menu->fFlags & MNF_POPUP) Dc = UserGetDCEx(pWnd, NULL, DCX_USESTYLE); + else Dc = UserGetDCEx(pWnd, 0, DCX_CACHE | DCX_WINDOW); + + NtGdiSelectFont(Dc, ghMenuFont); + + Item->fState |= MF_HILITE; + MENU_DrawMenuItem(pWnd, Menu, WndOwner, Dc, Item, Menu->cyMenu, + !(Menu->fFlags & MNF_POPUP), ODA_DRAWENTIRE); + + UserReleaseDC(pWnd, Dc, FALSE); + } + + if (!Item->yItem && !Item->xItem && !Item->cyItem && !Item->cxItem) + { + Item->xItem = Rect.left; + Item->yItem = Rect.top; + Item->cxItem = Rect.right; // Do this for now...... + Item->cyItem = Rect.bottom; + } + Item->fState |= MF_MOUSESELECT; + + if (IS_SYSTEM_MENU(Menu)) + { + MENU_InitSysMenuPopup(Item->spSubMenu, pWnd->style, pWnd->pcls->style, HTSYSMENU); + + NC_GetSysPopupPos(pWnd, &Rect); + if (Flags & TPM_LAYOUTRTL) Rect.left = Rect.right; + Rect.top = Rect.bottom; + Rect.right = UserGetSystemMetrics(SM_CXSIZE); + Rect.bottom = UserGetSystemMetrics(SM_CYSIZE); + } + else + { + IntGetWindowRect(pWnd, &Rect); + if (Menu->fFlags & MNF_POPUP) + { + RECT rc; + rc.left = Item->xItem; + rc.top = Item->yItem; + rc.right = Item->cxItem; // Do this for now...... + rc.bottom = Item->cyItem; + + MENU_AdjustMenuItemRect(Menu, &rc); + + /* The first item in the popup menu has to be at the + same y position as the focused menu item */ + if(Flags & TPM_LAYOUTRTL) + Rect.left += UserGetSystemMetrics(SM_CXBORDER); + else + Rect.left += rc.right /*ItemInfo.Rect.right*/ - UserGetSystemMetrics(SM_CXBORDER); + Rect.top += rc.top - MENU_TOP_MARGIN;//3; + Rect.right = rc.left - rc.right + UserGetSystemMetrics(SM_CXBORDER); + Rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN - MENU_BOTTOM_MARGIN/*2*/ + - UserGetSystemMetrics(SM_CYBORDER); + } + else + { + if(Flags & TPM_LAYOUTRTL) + Rect.left += Rect.right - Item->xItem; //ItemInfo.Rect.left; + else + Rect.left += Item->xItem; //ItemInfo.Rect.left; + Rect.top += Item->cyItem; //ItemInfo.Rect.bottom; + Rect.right = Item->cxItem - Item->xItem; //ItemInfo.Rect.right - ItemInfo.Rect.left; + Rect.bottom = Item->cyItem - Item->yItem; //ItemInfo.Rect.bottom - ItemInfo.Rect.top; + } + } + + /* use default alignment for submenus */ + Flags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN); + + MENU_InitPopup( WndOwner, Item->spSubMenu, Flags ); + + MENU_ShowPopup( WndOwner, Item->spSubMenu, Menu->iItem, Flags, + Rect.left, Rect.top, Rect.right, Rect.bottom ); + if (SelectFirst) + { + MENU_MoveSelection(WndOwner, Item->spSubMenu, ITEM_NEXT); + } + return Item->spSubMenu; +} + +/*********************************************************************** + * MenuExecFocusedItem + * + * Execute a menu item (for instance when user pressed Enter). + * Return the wID of the executed item. Otherwise, -1 indicating + * that no menu item was executed, -2 if a popup is shown; + * Have to receive the flags for the TrackPopupMenu options to avoid + * sending unwanted message. + * + */ +static INT FASTCALL MENU_ExecFocusedItem(MTRACKER *pmt, PMENU Menu, UINT Flags) +{ + PITEM Item; + + TRACE("%p menu=%p\n", pmt, Menu); + + if (!Menu || !Menu->cItems || Menu->iItem == NO_SELECTED_ITEM) + { + return -1; + } + + Item = &Menu->rgItems[Menu->iItem]; + + TRACE("%p %08x %p\n", Menu, Item->wID, Item->spSubMenu); + + if (!(Item->spSubMenu)) + { + if (!(Item->fState & (MF_GRAYED | MF_DISABLED)) && !(Item->fType & MF_SEPARATOR)) + { + /* If TPM_RETURNCMD is set you return the id, but + do not send a message to the owner */ + if (!(Flags & TPM_RETURNCMD)) + { + if (Menu->fFlags & MNF_SYSMENU) + { + UserPostMessage(UserHMGetHandle(pmt->OwnerWnd), WM_SYSCOMMAND, Item->wID, + MAKELPARAM((SHORT) pmt->Pt.x, (SHORT) pmt->Pt.y)); + } + else + { + DWORD dwStyle = ((Menu->fFlags & MNS_STYLE_MASK) | ( pmt->TopMenu ? (pmt->TopMenu->fFlags & MNS_STYLE_MASK) : 0) ); + + if (dwStyle & MNS_NOTIFYBYPOS) + UserPostMessage(UserHMGetHandle(pmt->OwnerWnd), WM_MENUCOMMAND, Menu->iItem, (LPARAM)UserHMGetHandle(Menu)); + else + UserPostMessage(UserHMGetHandle(pmt->OwnerWnd), WM_COMMAND, Item->wID, 0); + } + } + return Item->wID; + } + } + else + { + pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, Menu, TRUE, Flags); + return -2; + } + + return -1; +} + +/*********************************************************************** + * MenuSwitchTracking + * + * Helper function for menu navigation routines. + */ +static void FASTCALL MENU_SwitchTracking(MTRACKER* pmt, PMENU PtMenu, UINT Index, UINT wFlags) +{ + TRACE("%x menu=%x 0x%04x\n", pmt, PtMenu, Index); + + if ( pmt->TopMenu != PtMenu && + !((PtMenu->fFlags | pmt->TopMenu->fFlags) & MNF_POPUP) ) + { + /* both are top level menus (system and menu-bar) */ + MENU_HideSubPopups(pmt->OwnerWnd, pmt->TopMenu, FALSE, wFlags); + MENU_SelectItem(pmt->OwnerWnd, pmt->TopMenu, NO_SELECTED_ITEM, FALSE, NULL); + pmt->TopMenu = PtMenu; + } + else + { + MENU_HideSubPopups(pmt->OwnerWnd, PtMenu, FALSE, wFlags); + } + + MENU_SelectItem(pmt->OwnerWnd, PtMenu, Index, TRUE, NULL); +} + +/*********************************************************************** + * MenuButtonDown + * + * Return TRUE if we can go on with menu tracking. + */ +static BOOL FASTCALL MENU_ButtonDown(MTRACKER* pmt, PMENU PtMenu, UINT Flags) +{ + TRACE("%x PtMenu=%p\n", pmt, PtMenu); + + if (PtMenu) + { + UINT id = 0; + PITEM item; + if (IS_SYSTEM_MENU(PtMenu)) + { + item = PtMenu->rgItems; + } + else + { + item = MENU_FindItemByCoords( PtMenu, pmt->Pt, &id ); + } + + if (item) + { + if (PtMenu->iItem != id) + MENU_SwitchTracking(pmt, PtMenu, id, Flags); + + /* If the popup menu is not already "popped" */ + if (!(item->fState & MF_MOUSESELECT)) + { + pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, PtMenu, FALSE, Flags); + } + + return TRUE; + } + /* Else the click was on the menu bar, finish the tracking */ + } + return FALSE; +} + +/*********************************************************************** + * MenuButtonUp + * + * Return the value of MenuExecFocusedItem if + * the selected item was not a popup. Else open the popup. + * A -1 return value indicates that we go on with menu tracking. + * + */ +static INT FASTCALL MENU_ButtonUp(MTRACKER *pmt, PMENU PtMenu, UINT Flags) +{ + TRACE("%p pmenu=%x\n", pmt, PtMenu); + + if (PtMenu) + { + UINT Id = 0; + ITEM *item; + + if ( IS_SYSTEM_MENU(PtMenu) ) + { + item = PtMenu->rgItems; + } + else + { + item = MENU_FindItemByCoords( PtMenu, pmt->Pt, &Id ); + } + + if (item && ( PtMenu->iItem == Id)) + { + if (!(item->spSubMenu)) + { + INT ExecutedMenuId = MENU_ExecFocusedItem( pmt, PtMenu, Flags); + if (ExecutedMenuId == -1 || ExecutedMenuId == -2) return -1; + return ExecutedMenuId; + } + + /* If we are dealing with the menu bar */ + /* and this is a click on an already "popped" item: */ + /* Stop the menu tracking and close the opened submenus */ + if (pmt->TopMenu == PtMenu && PtMenu->TimeToHide) + { + return 0; + } + } + if ( IntGetMenu(PtMenu->hWnd) == PtMenu ) + { + PtMenu->TimeToHide = TRUE; + } + } + return -1; +} + +/*********************************************************************** + * MenuPtMenu + * + * Walks menu chain trying to find a menu pt maps to. + */ +static PMENU FASTCALL MENU_PtMenu(PMENU menu, POINT pt) +{ + PITEM pItem; + PMENU ret = NULL; + + if (!menu) return NULL; + + /* try subpopup first (if any) */ + if (menu->iItem != NO_SELECTED_ITEM) + { + pItem = menu->rgItems; + if ( pItem ) pItem = &pItem[menu->iItem]; + if ( pItem && pItem->spSubMenu && pItem->fState & MF_MOUSESELECT) + { + ret = MENU_PtMenu( pItem->spSubMenu, pt); + } + } + + /* check the current window (avoiding WM_HITTEST) */ + if (!ret) + { + PWND pWnd = ValidateHwndNoErr(menu->hWnd); + INT ht = GetNCHitEx(pWnd, pt); + if ( menu->fFlags & MNF_POPUP ) + { + if (ht != HTNOWHERE && ht != HTERROR) ret = menu; + } + else if (ht == HTSYSMENU) + ret = get_win_sys_menu(menu->hWnd); + else if (ht == HTMENU) + ret = IntGetMenu( menu->hWnd ); + } + return ret; +} + +/*********************************************************************** + * MenuMouseMove + * + * Return TRUE if we can go on with menu tracking. + */ +static BOOL FASTCALL MENU_MouseMove(MTRACKER *pmt, PMENU PtMenu, UINT Flags) +{ + UINT Index = NO_SELECTED_ITEM; + + if ( PtMenu ) + { + if (IS_SYSTEM_MENU(PtMenu)) + Index = 0; + else + MENU_FindItemByCoords( PtMenu, pmt->Pt, &Index ); + } + + if (Index == NO_SELECTED_ITEM) + { + MENU_SelectItem(pmt->OwnerWnd, pmt->CurrentMenu, NO_SELECTED_ITEM, TRUE, pmt->TopMenu); + } + else if (PtMenu->iItem != Index) + { + MENU_SwitchTracking(pmt, PtMenu, Index, Flags); + pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, PtMenu, FALSE, Flags); + } + return TRUE; +} + +/*********************************************************************** + * MenuGetSubPopup + * + * Return the handle of the selected sub-popup menu (if any). + */ +static PMENU MENU_GetSubPopup( PMENU menu ) +{ + ITEM *item; + + if ((!menu) || (menu->iItem == NO_SELECTED_ITEM)) return 0; + + item = &menu->rgItems[menu->iItem]; + if (item && (item->spSubMenu) && (item->fState & MF_MOUSESELECT)) + { + return item->spSubMenu; + } + return 0; +} + +/*********************************************************************** + * MenuDoNextMenu + * + * NOTE: WM_NEXTMENU documented in Win32 is a bit different. + */ +static LRESULT FASTCALL MENU_DoNextMenu(MTRACKER* pmt, UINT Vk, UINT wFlags) +{ + BOOL atEnd = FALSE; + + /* When skipping left, we need to do something special after the + first menu. */ + if (Vk == VK_LEFT && pmt->TopMenu->iItem == 0) + { + atEnd = TRUE; + } + /* When skipping right, for the non-system menu, we need to + handle the last non-special menu item (ie skip any window + icons such as MDI maximize, restore or close) */ + else if ((Vk == VK_RIGHT) && !IS_SYSTEM_MENU(pmt->TopMenu)) + { + UINT i = pmt->TopMenu->iItem + 1; + while (i < pmt->TopMenu->cItems) { + if ((pmt->TopMenu->rgItems[i].wID >= SC_SIZE && + pmt->TopMenu->rgItems[i].wID <= SC_RESTORE)) { + i++; + } else break; + } + if (i == pmt->TopMenu->cItems) { + atEnd = TRUE; + } + } + /* When skipping right, we need to cater for the system menu */ + else if ((Vk == VK_RIGHT) && IS_SYSTEM_MENU(pmt->TopMenu)) + { + if (pmt->TopMenu->iItem == (pmt->TopMenu->cItems - 1)) { + atEnd = TRUE; + } + } + + if ( atEnd ) + { + MDINEXTMENU NextMenu; + PMENU MenuTmp; + PWND pwndTemp; + HMENU hNewMenu; + HWND hNewWnd; + UINT Id = 0; + + MenuTmp = (IS_SYSTEM_MENU(pmt->TopMenu)) ? co_IntGetSubMenu(pmt->TopMenu, 0) : pmt->TopMenu; + NextMenu.hmenuIn = UserHMGetHandle(MenuTmp); + NextMenu.hmenuNext = NULL; + NextMenu.hwndNext = NULL; + co_IntSendMessage(UserHMGetHandle(pmt->OwnerWnd), WM_NEXTMENU, Vk, (LPARAM) &NextMenu); + + TRACE("%p [%p] -> %p [%p]\n", + pmt->CurrentMenu, pmt->OwnerWnd, NextMenu.hmenuNext, NextMenu.hwndNext ); + + if (NULL == NextMenu.hmenuNext || NULL == NextMenu.hwndNext) + { + hNewWnd = UserHMGetHandle(pmt->OwnerWnd); + if (IS_SYSTEM_MENU(pmt->TopMenu)) + { + /* switch to the menu bar */ + + if (pmt->OwnerWnd->style & WS_CHILD || !(MenuTmp = IntGetMenu(hNewWnd))) return FALSE; + + if (Vk == VK_LEFT) + { + Id = MenuTmp->cItems - 1; + + /* Skip backwards over any system predefined icons, + eg. MDI close, restore etc icons */ + while ((Id > 0) && + (MenuTmp->rgItems[Id].wID >= SC_SIZE && + MenuTmp->rgItems[Id].wID <= SC_RESTORE)) Id--; + + } + hNewMenu = UserHMGetHandle(MenuTmp); + } + else if (pmt->OwnerWnd->style & WS_SYSMENU) + { + /* switch to the system menu */ + MenuTmp = get_win_sys_menu(hNewWnd); + hNewMenu = UserHMGetHandle(MenuTmp); + } + else + return FALSE; + } + else /* application returned a new menu to switch to */ + { + hNewMenu = NextMenu.hmenuNext; + hNewWnd = NextMenu.hwndNext; + + if ((MenuTmp = UserGetMenuObject(hNewMenu)) && (pwndTemp = ValidateHwndNoErr(hNewWnd))) + { + if ( pwndTemp->style & WS_SYSMENU && (get_win_sys_menu(hNewWnd) == MenuTmp) ) + { + /* get the real system menu */ + MenuTmp = get_win_sys_menu(hNewWnd); + hNewMenu = UserHMGetHandle(MenuTmp); + } + else if (pwndTemp->style & WS_CHILD || IntGetMenu(hNewWnd) != MenuTmp) + { + /* FIXME: Not sure what to do here; + * perhaps try to track NewMenu as a popup? */ + + WARN(" -- got confused.\n"); + return FALSE; + } + } + else return FALSE; + } + + if (hNewMenu != UserHMGetHandle(pmt->TopMenu)) + { + MENU_SelectItem(pmt->OwnerWnd, pmt->TopMenu, NO_SELECTED_ITEM, FALSE, 0 ); + + if (pmt->CurrentMenu != pmt->TopMenu) + MENU_HideSubPopups(pmt->OwnerWnd, pmt->TopMenu, FALSE, wFlags); + } + + if (hNewWnd != UserHMGetHandle(pmt->OwnerWnd)) + { + PTHREADINFO pti = PsGetCurrentThreadWin32Thread(); + pmt->OwnerWnd = ValidateHwndNoErr(hNewWnd); + ///// Use thread pms!!!! + MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, hNewWnd); + pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED; + co_UserSetCapture(UserHMGetHandle(pmt->OwnerWnd)); + pti->MessageQueue->QF_flags |= QF_CAPTURELOCKED; + } + + pmt->TopMenu = pmt->CurrentMenu = UserGetMenuObject(hNewMenu); /* all subpopups are hidden */ + MENU_SelectItem(pmt->OwnerWnd, pmt->TopMenu, Id, TRUE, 0); + + return TRUE; + } + return FALSE; +} + +/*********************************************************************** + * MenuSuspendPopup + * + * The idea is not to show the popup if the next input message is + * going to hide it anyway. + */ +static BOOL FASTCALL MENU_SuspendPopup(MTRACKER* pmt, UINT uMsg) +{ + MSG msg; + + msg.hwnd = UserHMGetHandle(pmt->OwnerWnd); ////// ? silly wine'isms? + + co_IntGetPeekMessage( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE, FALSE); + pmt->TrackFlags |= TF_SKIPREMOVE; + + switch( uMsg ) + { + case WM_KEYDOWN: + co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE, FALSE); + if( msg.message == WM_KEYUP || msg.message == WM_PAINT ) + { + co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE, FALSE); + co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE, FALSE); + if( msg.message == WM_KEYDOWN && + (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT)) + { + pmt->TrackFlags |= TF_SUSPENDPOPUP; + return TRUE; + } + } + break; + } + /* failures go through this */ + pmt->TrackFlags &= ~TF_SUSPENDPOPUP; + return FALSE; +} + +/*********************************************************************** + * MenuKeyEscape + * + * Handle a VK_ESCAPE key event in a menu. + */ +static BOOL FASTCALL MENU_KeyEscape(MTRACKER *pmt, UINT Flags) +{ + BOOL EndMenu = TRUE; + + if (pmt->CurrentMenu != pmt->TopMenu) + { + if (pmt->CurrentMenu && (pmt->CurrentMenu->fFlags & MNF_POPUP)) + { + PMENU MenuPrev, MenuTmp; + + MenuPrev = MenuTmp = pmt->TopMenu; + + /* close topmost popup */ + while (MenuTmp != pmt->CurrentMenu) + { + MenuPrev = MenuTmp; + MenuTmp = MENU_GetSubPopup(MenuPrev); + } + + MENU_HideSubPopups(pmt->OwnerWnd, MenuPrev, TRUE, Flags); + pmt->CurrentMenu = MenuPrev; + EndMenu = FALSE; + } + } + + return EndMenu; +} + +/*********************************************************************** + * MenuKeyLeft + * + * Handle a VK_LEFT key event in a menu. + */ +static void FASTCALL MENU_KeyLeft(MTRACKER* pmt, UINT Flags) +{ + PMENU MenuTmp, MenuPrev; + UINT PrevCol; + + MenuPrev = MenuTmp = pmt->TopMenu; + + /* Try to move 1 column left (if possible) */ + if ( (PrevCol = MENU_GetStartOfPrevColumn(pmt->CurrentMenu)) != NO_SELECTED_ITEM) + { + MENU_SelectItem(pmt->OwnerWnd, pmt->CurrentMenu, PrevCol, TRUE, 0); + return; + } + + /* close topmost popup */ + while (MenuTmp != pmt->CurrentMenu) + { + MenuPrev = MenuTmp; + MenuTmp = MENU_GetSubPopup(MenuPrev); + } + + MENU_HideSubPopups(pmt->OwnerWnd, MenuPrev, TRUE, Flags); + pmt->CurrentMenu = MenuPrev; + + if ((MenuPrev == pmt->TopMenu) && !(pmt->TopMenu->fFlags & MNF_POPUP)) + { + /* move menu bar selection if no more popups are left */ + + if (!MENU_DoNextMenu(pmt, VK_LEFT, Flags)) + MENU_MoveSelection(pmt->OwnerWnd, pmt->TopMenu, ITEM_PREV); + + if (MenuPrev != MenuTmp || pmt->TrackFlags & TF_SUSPENDPOPUP) + { + /* A sublevel menu was displayed - display the next one + * unless there is another displacement coming up */ + + if (!MENU_SuspendPopup(pmt, WM_KEYDOWN)) + pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, pmt->TopMenu, + TRUE, Flags); + } + } +} + +/*********************************************************************** + * MenuKeyRight + * + * Handle a VK_RIGHT key event in a menu. + */ +static void FASTCALL MENU_KeyRight(MTRACKER *pmt, UINT Flags) +{ + PMENU menutmp; + UINT NextCol; + + TRACE("MenuKeyRight called, cur %p, top %p.\n", + pmt->CurrentMenu, pmt->TopMenu); + + if ((pmt->TopMenu->fFlags & MNF_POPUP) || (pmt->CurrentMenu != pmt->TopMenu)) + { + /* If already displaying a popup, try to display sub-popup */ + + menutmp = pmt->CurrentMenu; + pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, menutmp, TRUE, Flags); + + /* if subpopup was displayed then we are done */ + if (menutmp != pmt->CurrentMenu) return; + } + + /* Check to see if there's another column */ + if ( (NextCol = MENU_GetStartOfNextColumn(pmt->CurrentMenu)) != NO_SELECTED_ITEM) + { + TRACE("Going to %d.\n", NextCol); + MENU_SelectItem(pmt->OwnerWnd, pmt->CurrentMenu, NextCol, TRUE, 0); + return; + } + + if (!(pmt->TopMenu->fFlags & MNF_POPUP)) /* menu bar tracking */ + { + if (pmt->CurrentMenu != pmt->TopMenu) + { + MENU_HideSubPopups(pmt->OwnerWnd, pmt->TopMenu, FALSE, Flags); + menutmp = pmt->CurrentMenu = pmt->TopMenu; + } + else + { + menutmp = NULL; + } + + /* try to move to the next item */ + if ( !MENU_DoNextMenu(pmt, VK_RIGHT, Flags)) + MENU_MoveSelection(pmt->OwnerWnd, pmt->TopMenu, ITEM_NEXT); + + if ( menutmp || pmt->TrackFlags & TF_SUSPENDPOPUP ) + { + if ( !MENU_SuspendPopup(pmt, WM_KEYDOWN) ) + pmt->CurrentMenu = MENU_ShowSubPopup(pmt->OwnerWnd, pmt->TopMenu, TRUE, Flags); + } + } +} + +/*********************************************************************** + * MenuTrackMenu + * + * Menu tracking code. + */ +static INT FASTCALL MENU_TrackMenu(PMENU pmenu, UINT wFlags, INT x, INT y, + PWND pwnd, const RECT *lprect ) +{ + MSG msg; + BOOL fRemove; + INT executedMenuId = -1; + MTRACKER mt; + HWND capture_win; + PMENU pmMouse; + BOOL enterIdleSent = FALSE; + PTHREADINFO pti = PsGetCurrentThreadWin32Thread(); + + if (pti != pwnd->head.pti) + { + ERR("Not the same PTI!!!!\n"); + } + + mt.TrackFlags = 0; + mt.CurrentMenu = pmenu; + mt.TopMenu = pmenu; + mt.OwnerWnd = pwnd; + mt.Pt.x = x; + mt.Pt.y = y; + + TRACE("MTM : hmenu=%p flags=0x%08x (%d,%d) hwnd=%x (%ld,%ld)-(%ld,%ld)\n", + UserHMGetHandle(pmenu), wFlags, x, y, UserHMGetHandle(pwnd), lprect ? lprect->left : 0, lprect ? lprect->top : 0, + lprect ? lprect->right : 0, lprect ? lprect->bottom : 0); + + pti->MessageQueue->QF_flags &= ~QF_ACTIVATIONCHANGE; + + if (wFlags & TPM_BUTTONDOWN) + { + /* Get the result in order to start the tracking or not */ + fRemove = MENU_ButtonDown( &mt, pmenu, wFlags ); + fInsideMenuLoop = fRemove; + } + + if (wFlags & TF_ENDMENU) fInsideMenuLoop = FALSE; + + if (wFlags & TPM_POPUPMENU && pmenu->cItems == 0) // Tracking empty popup menu... + { + MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, NULL); + pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED; + co_UserSetCapture(NULL); /* release the capture */ + return 0; + } + + capture_win = IntGetCapture(); + + while (fInsideMenuLoop) + { + BOOL ErrorExit = FALSE; + if (!VerifyMenu( mt.CurrentMenu )) /* sometimes happens if I do a window manager close */ + break; + + /* we have to keep the message in the queue until it's + * clear that menu loop is not over yet. */ + + for (;;) + { + if (co_IntGetPeekMessage( &msg, 0, 0, 0, PM_NOREMOVE, FALSE )) + { + if (!IntCallMsgFilter( &msg, MSGF_MENU )) break; + /* remove the message from the queue */ + co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE ); + } + else + { + /* ReactOS Checks */ + if (!VerifyWnd(mt.OwnerWnd) || + !ValidateHwndNoErr(mt.CurrentMenu->hWnd) || + pti->MessageQueue->QF_flags & QF_ACTIVATIONCHANGE || + capture_win != IntGetCapture() ) // Should not happen, but this is ReactOS... + { + ErrorExit = TRUE; // Do not wait on dead windows, now win test_capture_4 works. + break; + } + + if (!enterIdleSent) + { + HWND win = mt.CurrentMenu->fFlags & MNF_POPUP ? mt.CurrentMenu->hWnd : NULL; + enterIdleSent = TRUE; + co_IntSendMessage( UserHMGetHandle(mt.OwnerWnd), WM_ENTERIDLE, MSGF_MENU, (LPARAM) win); + } + co_IntWaitMessage(NULL, 0, 0); + } + } + + if (ErrorExit) break; // Gracefully dropout. + + /* check if EndMenu() tried to cancel us, by posting this message */ + if (msg.message == WM_CANCELMODE) + { + /* we are now out of the loop */ + fInsideMenuLoop = FALSE; + + /* remove the message from the queue */ + co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE ); + + /* break out of internal loop, ala ESCAPE */ + break; + } + + IntTranslateKbdMessage(&msg, 0); + mt.Pt = msg.pt; + + if ( (msg.hwnd == mt.CurrentMenu->hWnd) || ((msg.message!=WM_TIMER) || (msg.message!=WM_SYSTIMER)) ) + enterIdleSent=FALSE; + + fRemove = FALSE; + if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST)) + { + /* + * Use the mouse coordinates in lParam instead of those in the MSG + * struct to properly handle synthetic messages. They are already + * in screen coordinates. + */ + mt.Pt.x = (short)LOWORD(msg.lParam); + mt.Pt.y = (short)HIWORD(msg.lParam); + + /* Find a menu for this mouse event */ + pmMouse = MENU_PtMenu( mt.TopMenu, mt.Pt ); + + switch(msg.message) + { + /* no WM_NC... messages in captured state */ + + case WM_RBUTTONDBLCLK: + case WM_RBUTTONDOWN: + if (!(wFlags & TPM_RIGHTBUTTON)) break; + /* fall through */ + case WM_LBUTTONDBLCLK: + case WM_LBUTTONDOWN: + /* If the message belongs to the menu, removes it from the queue */ + /* Else, end menu tracking */ + fRemove = MENU_ButtonDown(&mt, pmMouse, wFlags); + fInsideMenuLoop = fRemove; + if (msg.message == WM_LBUTTONDBLCLK) fInsideMenuLoop = FALSE; // Must exit or loop forever! + break; + + case WM_RBUTTONUP: + if (!(wFlags & TPM_RIGHTBUTTON)) break; + /* fall through */ + case WM_LBUTTONUP: + /* Check if a menu was selected by the mouse */ + if (pmMouse) + { + executedMenuId = MENU_ButtonUp( &mt, pmMouse, wFlags); + + /* End the loop if executedMenuId is an item ID */ + /* or if the job was done (executedMenuId = 0). */ + fRemove = (executedMenuId != -1); + fInsideMenuLoop = !fRemove; + } + /* No menu was selected by the mouse */ + /* if the function was called by TrackPopupMenu, continue + with the menu tracking. If not, stop it */ + else + fInsideMenuLoop = ((wFlags & TPM_POPUPMENU) ? TRUE : FALSE); + + break; + + case WM_MOUSEMOVE: + /* the selected menu item must be changed every time */ + /* the mouse moves. */ + + if (pmMouse) + fInsideMenuLoop |= MENU_MouseMove( &mt, pmMouse, wFlags ); + + } /* switch(msg.message) - mouse */ + } + else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST)) + { + fRemove = TRUE; /* Keyboard messages are always removed */ + switch(msg.message) + { + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + switch(msg.wParam) + { + case VK_MENU: + case VK_F10: + fInsideMenuLoop = FALSE; + break; + + case VK_HOME: + case VK_END: + MENU_SelectItem(mt.OwnerWnd, mt.CurrentMenu, NO_SELECTED_ITEM, FALSE, 0 ); + MENU_MoveSelection(mt.OwnerWnd, mt.CurrentMenu, VK_HOME == msg.wParam ? ITEM_NEXT : ITEM_PREV); + break; + + case VK_UP: + case VK_DOWN: /* If on menu bar, pull-down the menu */ + if (!(mt.CurrentMenu->fFlags & MNF_POPUP)) + mt.CurrentMenu = MENU_ShowSubPopup(mt.OwnerWnd, mt.TopMenu, TRUE, wFlags); + else /* otherwise try to move selection */ + MENU_MoveSelection(mt.OwnerWnd, mt.CurrentMenu, (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT ); + break; + + case VK_LEFT: + MENU_KeyLeft( &mt, wFlags ); + break; + + case VK_RIGHT: + MENU_KeyRight( &mt, wFlags ); + break; + + case VK_ESCAPE: + fInsideMenuLoop = !MENU_KeyEscape(&mt, wFlags); + break; + + case VK_F1: + { + HELPINFO hi; + hi.cbSize = sizeof(HELPINFO); + hi.iContextType = HELPINFO_MENUITEM; + if (mt.CurrentMenu->iItem == NO_SELECTED_ITEM) + hi.iCtrlId = 0; + else + hi.iCtrlId = pmenu->rgItems[mt.CurrentMenu->iItem].wID; + hi.hItemHandle = UserHMGetHandle(mt.CurrentMenu); + hi.dwContextId = pmenu->dwContextHelpId; + hi.MousePos = msg.pt; + co_IntSendMessage( UserHMGetHandle(pwnd), WM_HELP, 0, (LPARAM)&hi); + break; + } + + default: + break; + } + break; /* WM_KEYDOWN */ + + case WM_CHAR: + case WM_SYSCHAR: + { + UINT pos; + BOOL fEndMenu; + + if (msg.wParam == L'\r' || msg.wParam == L' ') + { + executedMenuId = MENU_ExecFocusedItem(&mt, mt.CurrentMenu, wFlags); + fEndMenu = (executedMenuId != -2); + fInsideMenuLoop = !fEndMenu; + break; + } + + /* Hack to avoid control chars. */ + /* We will find a better way real soon... */ + if (msg.wParam < 32) break; + + pos = MENU_FindItemByKey(mt.OwnerWnd, mt.CurrentMenu, LOWORD(msg.wParam), FALSE); + + if (pos == (UINT)-2) fInsideMenuLoop = FALSE; + else if (pos == (UINT)-1) UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_MESSAGE_BEEP, 0); //MessageBeep(0); + else + { + MENU_SelectItem(mt.OwnerWnd, mt.CurrentMenu, pos, TRUE, 0); + executedMenuId = MENU_ExecFocusedItem(&mt, mt.CurrentMenu, wFlags); + fEndMenu = (executedMenuId != -2); + fInsideMenuLoop = !fEndMenu; + } + } + break; + } /* switch(msg.message) - kbd */ + } + else + { + co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE ); + IntDispatchMessage( &msg ); + continue; + } + + if (fInsideMenuLoop) fRemove = TRUE; + + /* finally remove message from the queue */ + + if (fRemove && !(mt.TrackFlags & TF_SKIPREMOVE) ) + co_IntGetPeekMessage( &msg, 0, msg.message, msg.message, PM_REMOVE, FALSE ); + else mt.TrackFlags &= ~TF_SKIPREMOVE; + } + + MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, NULL); + pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED; + co_UserSetCapture(NULL); /* release the capture */ + + /* If dropdown is still painted and the close box is clicked on + then the menu will be destroyed as part of the DispatchMessage above. + This will then invalidate the menu handle in mt.hTopMenu. We should + check for this first. */ + if ( VerifyMenu( mt.TopMenu ) ) + { + if (VerifyWnd(mt.OwnerWnd)) + { + MENU_HideSubPopups(mt.OwnerWnd, mt.TopMenu, FALSE, wFlags); + + if (mt.TopMenu->fFlags & MNF_POPUP) + { + PWND pwndTM = ValidateHwndNoErr(mt.TopMenu->hWnd); + IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPEND, pwndTM, OBJID_CLIENT, CHILDID_SELF, 0); + + co_UserDestroyWindow(pwndTM); + mt.TopMenu->hWnd = NULL; + + if (!(wFlags & TPM_NONOTIFY)) + { + co_IntSendMessage( UserHMGetHandle(mt.OwnerWnd), WM_UNINITMENUPOPUP, (WPARAM)UserHMGetHandle(mt.TopMenu), + MAKELPARAM(0, IS_SYSTEM_MENU(mt.TopMenu)) ); + } + } + MENU_SelectItem( mt.OwnerWnd, mt.TopMenu, NO_SELECTED_ITEM, FALSE, 0 ); + co_IntSendMessage( UserHMGetHandle(mt.OwnerWnd), WM_MENUSELECT, MAKEWPARAM(0, 0xffff), 0 ); + } + + /* Reset the variable for hiding menu */ + mt.TopMenu->TimeToHide = FALSE; + } + + /* The return value is only used by TrackPopupMenu */ + if (!(wFlags & TPM_RETURNCMD)) return TRUE; + if (executedMenuId == -1) executedMenuId = 0; + return executedMenuId; +} + +/*********************************************************************** + * MenuInitTracking + */ +static BOOL FASTCALL MENU_InitTracking(PWND pWnd, PMENU Menu, BOOL bPopup, UINT wFlags) +{ + HWND capture_win; + PTHREADINFO pti = PsGetCurrentThreadWin32Thread(); + + TRACE("hwnd=%p hmenu=%p\n", UserHMGetHandle(pWnd), UserHMGetHandle(Menu)); + + co_UserHideCaret(0); + + /* This makes the menus of applications built with Delphi work. + * It also enables menus to be displayed in more than one window, + * but there are some bugs left that need to be fixed in this case. + */ + if (!bPopup) + { + Menu->hWnd = UserHMGetHandle(pWnd); + } + + if (!top_popup) { + top_popup = Menu->hWnd; + top_popup_hmenu = UserHMGetHandle(Menu); + } + + fInsideMenuLoop = TRUE; + fInEndMenu = FALSE; + + /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */ + if (!(wFlags & TPM_NONOTIFY)) + { + co_IntSendMessage( UserHMGetHandle(pWnd), WM_ENTERMENULOOP, bPopup, 0 ); + } + + // + // Capture is set before calling WM_INITMENU and after WM_ENTERMENULOOP, see msg_menu. + // + capture_win = (wFlags & TPM_POPUPMENU) ? Menu->hWnd : UserHMGetHandle(pWnd); + MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, capture_win); // 1 + co_UserSetCapture(capture_win); // 2 + pti->MessageQueue->QF_flags |= QF_CAPTURELOCKED; // Set the Q bits so noone can change this! + + co_IntSendMessage( UserHMGetHandle(pWnd), WM_SETCURSOR, (WPARAM)UserHMGetHandle(pWnd), HTCAPTION ); + + if (!(wFlags & TPM_NONOTIFY)) + { + co_IntSendMessage( UserHMGetHandle(pWnd), WM_INITMENU, (WPARAM)UserHMGetHandle(Menu), 0 ); + /* If an app changed/recreated menu bar entries in WM_INITMENU + * menu sizes will be recalculated once the menu created/shown. + */ + } + + IntNotifyWinEvent( EVENT_SYSTEM_MENUSTART, + pWnd, + Menu->fFlags & MNF_SYSMENU ? OBJID_SYSMENU : OBJID_MENU, + CHILDID_SELF, 0); + return TRUE; +} + +/*********************************************************************** + * MenuExitTracking + */ +static BOOL FASTCALL MENU_ExitTracking(PWND pWnd, BOOL bPopup, UINT wFlags) +{ + TRACE("Exit Track hwnd=%p bPopup %d\n", UserHMGetHandle(pWnd), bPopup); + + IntNotifyWinEvent( EVENT_SYSTEM_MENUEND, pWnd, OBJID_WINDOW, CHILDID_SELF, 0); + + if (!(wFlags & TPM_NONOTIFY)) + co_IntSendMessage( UserHMGetHandle(pWnd), WM_EXITMENULOOP, bPopup, 0 ); + + co_UserShowCaret(0); + + top_popup = 0; + top_popup_hmenu = NULL; + + return TRUE; +} + +/*********************************************************************** + * MenuTrackMouseMenuBar + * + * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand(). + */ +VOID MENU_TrackMouseMenuBar( PWND pWnd, ULONG ht, POINT pt) +{ + PMENU pMenu = (ht == HTSYSMENU) ? get_win_sys_menu( UserHMGetHandle(pWnd) ) : IntGetMenu( UserHMGetHandle(pWnd) ); + UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON; + + TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", pWnd, ht, pt.x, pt.y); + + if (pWnd->ExStyle & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL; + if (VerifyMenu(pMenu)) + { + /* map point to parent client coordinates */ + PWND Parent = UserGetAncestor(pWnd, GA_PARENT ); + if (Parent != UserGetDesktopWindow()) + { + IntScreenToClient(Parent, &pt); + } + + MENU_InitTracking(pWnd, pMenu, FALSE, wFlags); + /* fetch the window menu again, it may have changed */ + pMenu = (ht == HTSYSMENU) ? get_win_sys_menu( UserHMGetHandle(pWnd) ) : IntGetMenu( UserHMGetHandle(pWnd) ); + MENU_TrackMenu(pMenu, wFlags, pt.x, pt.y, pWnd, NULL); + MENU_ExitTracking(pWnd, FALSE, wFlags); + } +} + +/*********************************************************************** + * MenuTrackKbdMenuBar + * + * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand(). + */ +VOID MENU_TrackKbdMenuBar(PWND pwnd, UINT wParam, WCHAR wChar) +{ + UINT uItem = NO_SELECTED_ITEM; + PMENU TrackMenu; + UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON; + + TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", UserHMGetHandle(pwnd), wParam, wChar); + + /* find window that has a menu */ + + while (!( (pwnd->style & (WS_CHILD | WS_POPUP)) != WS_CHILD ) ) + if (!(pwnd = UserGetAncestor( pwnd, GA_PARENT ))) return; + + /* check if we have to track a system menu */ + + TrackMenu = IntGetMenu( UserHMGetHandle(pwnd) ); + if (!TrackMenu || (pwnd->style & WS_MINIMIZE) != 0 || wChar == ' ' ) + { + if (!(pwnd->style & WS_SYSMENU)) return; + TrackMenu = get_win_sys_menu( UserHMGetHandle(pwnd) ); + uItem = 0; + wParam |= HTSYSMENU; /* prevent item lookup */ + } + + if (!VerifyMenu( TrackMenu )) return; + + MENU_InitTracking( pwnd, TrackMenu, FALSE, wFlags ); + + /* fetch the window menu again, it may have changed */ + TrackMenu = (wParam & HTSYSMENU) ? get_win_sys_menu( UserHMGetHandle(pwnd) ) : IntGetMenu( UserHMGetHandle(pwnd) ); + + if( wChar && wChar != ' ' ) + { + uItem = MENU_FindItemByKey( pwnd, TrackMenu, wChar, (wParam & HTSYSMENU) ); + if ( uItem >= (UINT)(-2) ) + { + if( uItem == (UINT)(-1) ) UserPostMessage(hwndSAS, WM_LOGONNOTIFY, LN_MESSAGE_BEEP, 0); //MessageBeep(0); + /* schedule end of menu tracking */ + wFlags |= TF_ENDMENU; + goto track_menu; + } + } + + MENU_SelectItem( pwnd, TrackMenu, uItem, TRUE, 0 ); + + if (!(wParam & HTSYSMENU) || wChar == ' ') + { + if( uItem == NO_SELECTED_ITEM ) + MENU_MoveSelection( pwnd, TrackMenu, ITEM_NEXT ); + else + UserPostMessage( UserHMGetHandle(pwnd), WM_KEYDOWN, VK_RETURN, 0 ); + } + +track_menu: + MENU_TrackMenu( TrackMenu, wFlags, 0, 0, pwnd, NULL ); + MENU_ExitTracking( pwnd, FALSE, wFlags); +} + +/********************************************************************** + * TrackPopupMenuEx (USER32.@) + */ +BOOL WINAPI IntTrackPopupMenuEx( PMENU menu, UINT wFlags, int x, int y, + PWND pWnd, LPTPMPARAMS lpTpm) +{ + BOOL ret = FALSE; + PTHREADINFO pti = PsGetCurrentThreadWin32Thread(); + + if (pti != pWnd->head.pti) + { + ERR("Must be the same pti!\n"); + return ret; + } + + TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p \n", //rect %s\n", + UserHMGetHandle(menu), wFlags, x, y, UserHMGetHandle(pWnd), lpTpm); //, + //lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" ); + + if (menu->hWnd && IntIsWindow(menu->hWnd)) + { + EngSetLastError( ERROR_POPUP_ALREADY_ACTIVE ); + return FALSE; + } + + if (MENU_InitPopup( pWnd, menu, wFlags )) + { + MENU_InitTracking(pWnd, menu, TRUE, wFlags); + + /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */ + if (!(wFlags & TPM_NONOTIFY)) + { + co_IntSendMessage( UserHMGetHandle(pWnd), WM_INITMENUPOPUP, (WPARAM) UserHMGetHandle(menu), 0); + } + + if (MENU_ShowPopup(pWnd, menu, 0, wFlags, x, y, 0, 0 )) + ret = MENU_TrackMenu( menu, wFlags | TPM_POPUPMENU, 0, 0, pWnd, + lpTpm ? &lpTpm->rcExclude : NULL); + else + { + MsqSetStateWindow(pti, MSQ_STATE_MENUOWNER, NULL); + pti->MessageQueue->QF_flags &= ~QF_CAPTURELOCKED; + co_UserSetCapture(NULL); /* release the capture */ + } + + // + // HACK : Until back trace fault in co_IntUpdateWindows and MENU_TrackMenu. + // + if (EngGetLastError() == ERROR_ACCESS_DENIED) + { + EngSetLastError(NO_ERROR); + } + + MENU_ExitTracking(pWnd, TRUE, wFlags); + + if (menu->hWnd) + { + PWND pwndM = ValidateHwndNoErr( menu->hWnd ); + if (pwndM) // wine hack around this with their destroy function. + co_UserDestroyWindow( pwndM ); // Fix wrong error return. + menu->hWnd = 0; + + if (!(wFlags & TPM_NONOTIFY)) + { + co_IntSendMessage( UserHMGetHandle(pWnd), WM_UNINITMENUPOPUP, (WPARAM)UserHMGetHandle(menu), + MAKELPARAM(0, IS_SYSTEM_MENU(menu)) ); + } + } + } + return ret; +} + +// +// Menu Class Proc. +// +BOOL WINAPI +PopupMenuWndProc( + PWND Wnd, + UINT Message, + WPARAM wParam, + LPARAM lParam, + LRESULT *lResult) +{ + PPOPUPMENU pPopupMenu; + + *lResult = 0; + + TRACE("PMWP : pwnd=%x msg=%d wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam); + + if (Wnd) + { + if (!Wnd->fnid) + { + if (Message != WM_NCCREATE) + { + *lResult = IntDefWindowProc(Wnd, Message, wParam, lParam, FALSE); + return TRUE; + } + Wnd->fnid = FNID_MENU; + pPopupMenu = DesktopHeapAlloc( Wnd->head.rpdesk, sizeof(POPUPMENU) ); + pPopupMenu->posSelectedItem = NO_SELECTED_ITEM; + pPopupMenu->spwndPopupMenu = Wnd; + ((PMENUWND)Wnd)->ppopupmenu = pPopupMenu; + TRACE("Pop Up Menu is Setup! Msg %d\n",Message); + *lResult = 1; + return TRUE; + } + else + { + if (Wnd->fnid != FNID_MENU) + { + ERR("Wrong window class for Menu! fnid %x\n",Wnd->fnid); + return TRUE; + } + pPopupMenu = ((PMENUWND)Wnd)->ppopupmenu; + } + } + + switch(Message) + { + case WM_CREATE: + { + CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam; + pPopupMenu->spmenu = UserGetMenuObject(cs->lpCreateParams); + break; + } + + case WM_MOUSEACTIVATE: /* We don't want to be activated */ + *lResult = MA_NOACTIVATE; + break; + + case WM_PAINT: + { + PAINTSTRUCT ps; + IntBeginPaint(Wnd, &ps); + MENU_DrawPopupMenu(Wnd, ps.hdc, pPopupMenu->spmenu); + IntEndPaint(Wnd, &ps); + break; + } + + case WM_PRINTCLIENT: + { + MENU_DrawPopupMenu( Wnd, (HDC)wParam, pPopupMenu->spmenu); + break; + } + + case WM_ERASEBKGND: + *lResult = 1; + break; + + case WM_DESTROY: + /* zero out global pointer in case resident popup window was destroyed. */ + if (pPopupMenu) + { + if (UserHMGetHandle(Wnd) == top_popup) + { + top_popup = NULL; + top_popup_hmenu = NULL; + } + } + else + { + ERR("No Window Pop Up!\n"); + } + break; + + case WM_NCDESTROY: + { + DesktopHeapFree(Wnd->head.rpdesk, pPopupMenu ); + ((PMENUWND)Wnd)->ppopupmenu = 0; + Wnd->fnid = FNID_DESTROY; + break; + } + + case MM_SETMENUHANDLE: // wine'isms + case MN_SETHMENU: + { + PMENU pmenu = UserGetMenuObject((HMENU)wParam); + if (!pmenu) + { + ERR("Bad Menu Handle\n"); + break; + } + pPopupMenu->spmenu = pmenu; + break; + } + + case MM_GETMENUHANDLE: // wine'isms + case MN_GETHMENU: + *lResult = (LRESULT)(pPopupMenu ? (pPopupMenu->spmenu ? UserHMGetHandle(pPopupMenu->spmenu) : NULL) : NULL); + break; + + default: + if (Message > MN_GETHMENU && Message < MN_GETHMENU+19) + { + ERR("Someone is passing unknown menu messages %d\n",Message); + } + TRACE("PMWP to IDWP %d\n",Message); + *lResult = IntDefWindowProc(Wnd, Message, wParam, lParam, FALSE); + break; + } + + return TRUE; } BOOL FASTCALL -co_IntTrackPopupMenu(PMENU Menu, PWND Window, - UINT Flags, POINT *Pos, UINT MenuPos, RECTL *ExcludeRect) +IntHiliteMenuItem(PWND WindowObject, + PMENU MenuObject, + UINT uItemHilite, + UINT uHilite) { - co_IntInitTracking(Window, Menu, TRUE, Flags); + PITEM MenuItem; + UINT uItem = uItemHilite; - co_IntExitTracking(Window, Menu, TRUE, Flags); - return FALSE; + if (!(MenuItem = MENU_FindItem( &MenuObject, &uItem, uHilite ))) return TRUE; + + if (uHilite & MF_HILITE) + { + MenuItem->fState |= MF_HILITE; + } + else + { + MenuItem->fState &= ~MF_HILITE; + } + if (MenuObject->iItem == uItemHilite) return TRUE; + MENU_HideSubPopups( WindowObject, MenuObject, FALSE, 0 ); + MENU_SelectItem( WindowObject, MenuObject, uItemHilite, TRUE, 0 ); + + return TRUE; // Always returns true!!!! } BOOLEAN APIENTRY @@ -1372,39 +4741,18 @@ HMENU FASTCALL IntGetSubMenu( HMENU hMenu, int nPos) UINT FASTCALL IntFindSubMenu(HMENU *hMenu, HMENU hSubTarget ) { - PMENU menu; - HMENU hSubMenu; - UINT i; - PITEM item; - + PMENU menu, pSubTarget; + UINT Pos; if (((*hMenu)==(HMENU)0xffff) ||(!(menu = UserGetMenuObject(*hMenu)))) return NO_SELECTED_ITEM; - item = menu->rgItems; - for (i = 0; i < menu->cItems; i++, item++) - { - if (!item->spSubMenu) - continue; - else - { - hSubMenu = UserHMGetHandle(item->spSubMenu); - if (hSubMenu == hSubTarget) - { - return i; - } - else - { - HMENU hsubmenu = hSubMenu; - UINT pos = IntFindSubMenu( &hsubmenu, hSubTarget ); - if (pos != NO_SELECTED_ITEM) - { - *hMenu = hsubmenu; - return pos; - } - } - } - } - return NO_SELECTED_ITEM; + pSubTarget = UserGetMenuObject(hSubTarget); + + Pos = MENU_FindSubMenu(&menu, pSubTarget ); + + *hMenu = (menu ? UserHMGetHandle(menu) : NULL); + + return Pos; } @@ -1500,9 +4848,9 @@ UserMenuItemInfo( SetLastNtError(Status); return( FALSE); } - if (sizeof(MENUITEMINFOW) != Size - && FIELD_OFFSET(MENUITEMINFOW, hbmpItem) != Size - && sizeof(ROSMENUITEMINFO) != Size) + if ( Size != sizeof(MENUITEMINFOW) && + Size != FIELD_OFFSET(MENUITEMINFOW, hbmpItem) && + Size != sizeof(ROSMENUITEMINFO) ) { EngSetLastError(ERROR_INVALID_PARAMETER); return( FALSE); @@ -1566,7 +4914,7 @@ UserMenuInfo( SetLastNtError(Status); return( FALSE); } - if(Size < sizeof(MENUINFO) || sizeof(ROSMENUINFO) < Size) + if ( Size < sizeof(MENUINFO) || Size > sizeof(ROSMENUINFO) ) { EngSetLastError(ERROR_INVALID_PARAMETER); return( FALSE); @@ -1601,21 +4949,6 @@ UserMenuInfo( return( Res); } -VOID FASTCALL -MENU_AdjustMenuItemRect(PMENU menu, PRECTL rect) -{ - if (menu->dwArrowsOn) - { - UINT arrow_bitmap_height; - //BITMAP bmp; - //GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp); - arrow_bitmap_height = gpsi->oembmi[65].cy; ///// Menu up arrow! OBM_UPARROW DFCS_MENUARROWUP - //arrow_bitmap_height = bmp.bmHeight; - rect->top += arrow_bitmap_height - menu->iTop; - rect->bottom += arrow_bitmap_height - menu->iTop; - } -} - BOOL FASTCALL IntGetMenuItemRect( PWND pWnd, @@ -1625,14 +4958,9 @@ IntGetMenuItemRect( { LONG XMove, YMove; PITEM MenuItem; + UINT I = uItem; - if (!pWnd) - { - HWND hWnd = Menu->hWnd; - if (!(pWnd = UserGetWindowObject(hWnd))) return FALSE; - } - - if ((MenuItem = MENU_FindItem (&Menu, &uItem, MF_BYPOSITION))) + if ((MenuItem = MENU_FindItem (&Menu, &I, MF_BYPOSITION))) { Rect->left = MenuItem->xItem; Rect->top = MenuItem->yItem; @@ -1645,6 +4973,12 @@ IntGetMenuItemRect( return FALSE; } + if (!pWnd) + { + HWND hWnd = Menu->hWnd; + if (!(pWnd = UserGetWindowObject(hWnd))) return FALSE; + } + if (Menu->fFlags & MNF_POPUP) { XMove = pWnd->rcClient.left; @@ -1685,7 +5019,7 @@ PMENU FASTCALL MENU_GetSystemMenu(PWND Window, PMENU Popup) } SysMenu->fFlags |= MNF_SYSMENU; - SysMenu->hWnd = Window->head.h; + SysMenu->hWnd = UserHMGetHandle(Window); if (!Popup) { @@ -1850,12 +5184,12 @@ IntSetMenu( if ((Wnd->style & (WS_CHILD | WS_POPUP)) == WS_CHILD) { - ERR("SetMenu: Invalid handle 0x%p!\n",UserHMGetHandle(Wnd)); + ERR("SetMenu: Window is a Child 0x%p!\n",UserHMGetHandle(Wnd)); EngSetLastError(ERROR_INVALID_WINDOW_HANDLE); return FALSE; } - *Changed = (Wnd->IDMenu != (UINT) Menu); + *Changed = (UlongToHandle(Wnd->IDMenu) != Menu); if (! *Changed) { return TRUE; @@ -1863,8 +5197,8 @@ IntSetMenu( if (Wnd->IDMenu) { - OldMenu = IntGetMenuObject((HMENU) Wnd->IDMenu); - ASSERT(NULL == OldMenu || OldMenu->hWnd == Wnd->head.h); + OldMenu = IntGetMenuObject(UlongToHandle(Wnd->IDMenu)); + ASSERT(NULL == OldMenu || OldMenu->hWnd == UserHMGetHandle(Wnd)); } else { @@ -1899,7 +5233,7 @@ IntSetMenu( Wnd->IDMenu = (UINT) Menu; if (NULL != NewMenu) { - NewMenu->hWnd = Wnd->head.h; + NewMenu->hWnd = UserHMGetHandle(Wnd); IntReleaseMenuObject(NewMenu); } if (NULL != OldMenu) @@ -2217,6 +5551,36 @@ CLEANUP: END_CLEANUP; } +/* + * @implemented + */ +BOOL APIENTRY +NtUserEndMenu(VOID) +{ + //PWND pWnd; + TRACE("Enter NtUserEndMenu\n"); + UserEnterExclusive(); + /* if ( gptiCurrent->pMenuState && + gptiCurrent->pMenuState->pGlobalPopupMenu ) + { + pWnd = IntGetMSWND(gptiCurrent->pMenuState); + if (pWnd) + { + UserPostMessage( UserHMGetHandle(pWnd), WM_CANCELMODE, 0, 0); + } + else + gptiCurrent->pMenuState->fInsideMenuLoop = FALSE; + }*/ + if (fInsideMenuLoop && top_popup) + { + fInsideMenuLoop = FALSE; + UserPostMessage( top_popup, WM_CANCELMODE, 0, 0); + } + UserLeave(); + TRACE("Leave NtUserEndMenu\n"); + return TRUE; +} + /* * @implemented */ @@ -2232,6 +5596,7 @@ NtUserGetMenuBarInfo( MENUBARINFO kmbi; BOOL Ret; PPOPUPMENU pPopupMenu; + USER_REFERENCE_ENTRY Ref; NTSTATUS Status = STATUS_SUCCESS; PMENU Menu = NULL; DECLARE_RETURN(BOOL); @@ -2245,6 +5610,14 @@ NtUserGetMenuBarInfo( RETURN(FALSE); } + UserRefObjectCo(pWnd, &Ref); + + RECTL_vSetEmptyRect(&kmbi.rcBar); + kmbi.hMenu = NULL; + kmbi.hwndMenu = NULL; + kmbi.fBarFocused = FALSE; + kmbi.fFocused = FALSE; + switch (idObject) { case OBJID_CLIENT: @@ -2258,14 +5631,24 @@ NtUserGetMenuBarInfo( } // Windows does this! Wine checks for Atom and uses GetWindowLongPtrW. hMenu = (HMENU)co_IntSendMessage(hwnd, MN_GETHMENU, 0, 0); + pPopupMenu = ((PMENUWND)pWnd)->ppopupmenu; + if (pPopupMenu && pPopupMenu->spmenu) + { + if (UserHMGetHandle(pPopupMenu->spmenu) != hMenu) + { + ERR("Window Pop Up hMenu %p not the same as Get hMenu %p!\n",pPopupMenu->spmenu->head.h,hMenu); + } + } break; case OBJID_MENU: + if (pWnd->style & WS_CHILD) RETURN(FALSE); hMenu = UlongToHandle(pWnd->IDMenu); + TRACE("GMBI: OBJID_MENU hMenu %p\n",hMenu); break; case OBJID_SYSMENU: if (!(pWnd->style & WS_SYSMENU)) RETURN(FALSE); Menu = IntGetSystemMenu(pWnd, FALSE); - hMenu = Menu->head.h; + hMenu = UserHMGetHandle(Menu); break; default: RETURN(FALSE); @@ -2276,6 +5659,7 @@ NtUserGetMenuBarInfo( _SEH2_TRY { + ProbeForRead(pmbi, sizeof(MENUBARINFO), 1); kmbi.cbSize = pmbi->cbSize; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) @@ -2297,32 +5681,22 @@ NtUserGetMenuBarInfo( if ((idItem < 0) || ((ULONG)idItem > Menu->cItems)) RETURN(FALSE); - RECTL_vSetEmptyRect(&kmbi.rcBar); - if (idItem == 0) { Ret = IntGetMenuItemRect(pWnd, Menu, 0, &kmbi.rcBar); kmbi.rcBar.right = kmbi.rcBar.left + Menu->cxMenu; kmbi.rcBar.bottom = kmbi.rcBar.top + Menu->cyMenu; - TRACE("idItem 0 %d\n",Ret); + TRACE("idItem a 0 %d\n",Ret); } else { Ret = IntGetMenuItemRect(pWnd, Menu, idItem-1, &kmbi.rcBar); - TRACE("idItem X %d\n", Ret); + TRACE("idItem b %d %d\n", idItem-1, Ret); } kmbi.hMenu = hMenu; - kmbi.hwndMenu = NULL; - kmbi.fBarFocused = FALSE; - kmbi.fFocused = FALSE; - - pPopupMenu = ((PMENUWND)pWnd)->ppopupmenu; - if (pPopupMenu) - { - //kmbi.fBarFocused = pPopupMenu->spmenu == Menu; - } - + kmbi.fBarFocused = top_popup_hmenu == hMenu; + TRACE("GMBI: top p hm %p hMenu %p\n",top_popup_hmenu, hMenu); if (idItem) { kmbi.fFocused = Menu->iItem == idItem-1; @@ -2331,13 +5705,14 @@ NtUserGetMenuBarInfo( kmbi.hwndMenu = Menu->rgItems[idItem - 1].spSubMenu->hWnd; } } -/* else + else { kmbi.fFocused = kmbi.fBarFocused; - }*/ + } _SEH2_TRY { + ProbeForWrite(pmbi, sizeof(MENUBARINFO), 1); RtlCopyMemory(pmbi, &kmbi, sizeof(MENUBARINFO)); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) @@ -2355,6 +5730,7 @@ NtUserGetMenuBarInfo( RETURN(TRUE); CLEANUP: + if (pWnd) UserDerefObjectCo(pWnd); TRACE("Leave NtUserGetMenuBarInfo, ret=%i\n",_ret_); UserLeave(); END_CLEANUP; @@ -2515,6 +5891,63 @@ CLEANUP: END_CLEANUP; } +/* + * @implemented + */ +DWORD +APIENTRY +NtUserDrawMenuBarTemp( + HWND hWnd, + HDC hDC, + PRECT pRect, + HMENU hMenu, + HFONT hFont) +{ + PMENU Menu; + PWND Window; + RECT Rect; + NTSTATUS Status = STATUS_SUCCESS; + DECLARE_RETURN(DWORD); + + ERR("Enter NtUserDrawMenuBarTemp\n"); + UserEnterExclusive(); + + if(!(Window = UserGetWindowObject(hWnd))) + { + EngSetLastError(ERROR_INVALID_WINDOW_HANDLE); + RETURN(0); + } + + if(!(Menu = UserGetMenuObject(hMenu))) + { + EngSetLastError(ERROR_INVALID_MENU_HANDLE); + RETURN(0); + } + + _SEH2_TRY + { + ProbeForRead(pRect, sizeof(RECT), sizeof(ULONG)); + RtlCopyMemory(&Rect, pRect, sizeof(RECT)); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + Status = _SEH2_GetExceptionCode(); + } + _SEH2_END; + + if (Status != STATUS_SUCCESS) + { + SetLastNtError(Status); + RETURN(0); + } + + RETURN( IntDrawMenuBarTemp(Window, hDC, &Rect, Menu, hFont)); + +CLEANUP: + ERR("Leave NtUserDrawMenuBarTemp, ret=%u\n",_ret_); + UserLeave(); + END_CLEANUP; +} /* * @implemented @@ -2552,11 +5985,14 @@ NtUserMenuItemFromPoint( for (i = 0; i < Menu->cItems; i++, mi++) { RECTL Rect; + Rect.left = mi->xItem; Rect.top = mi->yItem; - Rect.right = mi->cxItem; // Do this for now...... + Rect.right = mi->cxItem; Rect.bottom = mi->cyItem; - //MENU_AdjustMenuItemRect(Menu, &Rect); Need gpsi OBMI via callback! + + MENU_AdjustMenuItemRect(Menu, &Rect); + if (RECTL_bPointInRect(&Rect, X, Y)) { break; @@ -2622,7 +6058,7 @@ NtUserSetMenu( RETURN( FALSE); } - if (! IntSetMenu(Window, Menu, &Changed)) + if (!IntSetMenu(Window, Menu, &Changed)) { RETURN( FALSE); } @@ -2807,4 +6243,61 @@ CLEANUP: END_CLEANUP; } +/* + * @implemented + */ +BOOL APIENTRY +NtUserTrackPopupMenuEx( + HMENU hMenu, + UINT fuFlags, + int x, + int y, + HWND hWnd, + LPTPMPARAMS lptpm) +{ + PMENU menu; + PWND pWnd; + TPMPARAMS tpm; + BOOL Ret = FALSE; + USER_REFERENCE_ENTRY Ref; + + TRACE("Enter NtUserTrackPopupMenuEx\n"); + UserEnterExclusive(); + /* Parameter check */ + if (!(menu = UserGetMenuObject( hMenu ))) + { + ERR("TPME : Invalid Menu handle.\n"); + EngSetLastError( ERROR_INVALID_MENU_HANDLE ); + goto Exit; + } + + if (!(pWnd = UserGetWindowObject(hWnd))) + { + ERR("TPME : Invalid Window handle.\n"); + goto Exit; + } + + if (lptpm) + { + _SEH2_TRY + { + ProbeForRead(lptpm, sizeof(TPMPARAMS), sizeof(ULONG)); + tpm = *lptpm; + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + _SEH2_YIELD(goto Exit); + } + _SEH2_END + } + UserRefObjectCo(pWnd, &Ref); + Ret = IntTrackPopupMenuEx(menu, fuFlags, x, y, pWnd, lptpm ? &tpm : NULL); + UserDerefObjectCo(pWnd); + +Exit: + TRACE("Leave NtUserTrackPopupMenuEx, ret=%i\n",Ret); + UserLeave(); + return Ret; +} + /* EOF */ diff --git a/reactos/win32ss/user/ntuser/menu.h b/reactos/win32ss/user/ntuser/menu.h index ca01889a79b..77eff6565c4 100644 --- a/reactos/win32ss/user/ntuser/menu.h +++ b/reactos/win32ss/user/ntuser/menu.h @@ -66,6 +66,62 @@ typedef struct _SETMENUITEMRECT RECTL rcRect; } SETMENUITEMRECT, *PSETMENUITEMRECT; + +// +// Legacy ReactOS Menu transfer structures. +// +typedef struct tagROSMENUINFO +{ + /* ----------- MENUINFO ----------- */ + DWORD cbSize; + DWORD fMask; + DWORD dwStyle; + UINT cyMax; + HBRUSH hbrBack; + DWORD dwContextHelpID; + ULONG_PTR dwMenuData; + /* ----------- Extra ----------- */ + ULONG fFlags; /* Menu flags (MF_POPUP, MF_SYSMENU) */ + UINT iItem; /* Currently focused item */ + UINT cItems; /* Number of items in the menu */ + WORD cxMenu; /* Width of the whole menu */ + WORD cyMenu; /* Height of the whole menu */ + ULONG cxTextAlign; + PWND spwndNotify; /* window receiving the messages for ownerdraw */ + INT iTop; + INT iMaxTop; + DWORD dwArrowsOn:2; + + HMENU Self; /* Handle of this menu */ + HWND Wnd; /* Window containing the menu */ + BOOL TimeToHide; /* Request hiding when receiving a second click in the top-level menu item */ +} ROSMENUINFO, *PROSMENUINFO; + +typedef struct tagROSMENUITEMINFO +{ + /* ----------- MENUITEMINFOW ----------- */ + UINT cbSize; + UINT fMask; + UINT fType; + UINT fState; + UINT wID; + HMENU hSubMenu; + HBITMAP hbmpChecked; + HBITMAP hbmpUnchecked; + DWORD dwItemData; + LPWSTR dwTypeData; + UINT cch; + HBITMAP hbmpItem; + /* ----------- Extra ----------- */ + RECT Rect; /* Item area (relative to menu window) */ + UINT dxTab; /* X position of text after Tab */ + LPWSTR lpstr; /* Copy of the text pointer in MenuItem->Text */ + SIZE maxBmpSize; /* Maximum size of the bitmap items in MIIM_BITMAP state */ +} ROSMENUITEMINFO, *PROSMENUITEMINFO; +// +// +// + PMENU FASTCALL IntGetMenuObject(HMENU hMenu); @@ -101,3 +157,14 @@ BOOL FASTCALL IntRemoveMenuItem(PMENU Menu, UINT uPosition, UINT uFlags, BOOL bR PITEM FASTCALL MENU_FindItem( PMENU *pmenu, UINT *nPos, UINT wFlags ); BOOL FASTCALL IntMenuItemInfo(PMENU Menu, UINT Item, BOOL ByPosition, PROSMENUITEMINFO UnsafeItemInfo, BOOL SetOrGet, PUNICODE_STRING lpstr); BOOL FASTCALL IntSetMenu(PWND Wnd,HMENU Menu,BOOL *Changed); +UINT MENU_DrawMenuBar( HDC hDC, LPRECT lprect, PWND pWnd, BOOL suppress_draw ); +BOOL MenuInit(VOID); +VOID MENU_TrackKbdMenuBar(PWND pwnd, UINT wParam, WCHAR wChar); +VOID MENU_TrackMouseMenuBar( PWND pWnd, ULONG ht, POINT pt); +BOOL WINAPI PopupMenuWndProc(PWND Wnd,UINT Message,WPARAM wParam,LPARAM lParam,LRESULT *lResult); +BOOL FASTCALL IntSetMenuItemInfo(PMENU, PITEM, PROSMENUITEMINFO, PUNICODE_STRING); +PWND MENU_IsMenuActive(VOID); +void MENU_EndMenu( PWND pwnd ); +void FASTCALL MENU_InitSysMenuPopup(PMENU menu, DWORD style, DWORD clsStyle, LONG HitTest ); +INT FASTCALL IntMenuItemFromPoint(PWND pWnd, HMENU hMenu, POINT ptScreen); +BOOL WINAPI IntTrackPopupMenuEx( PMENU menu, UINT wFlags, int x, int y, PWND pWnd, LPTPMPARAMS lpTpm); diff --git a/reactos/win32ss/user/ntuser/message.c b/reactos/win32ss/user/ntuser/message.c index 6b21310525b..262ca9db528 100644 --- a/reactos/win32ss/user/ntuser/message.c +++ b/reactos/win32ss/user/ntuser/message.c @@ -136,6 +136,10 @@ static MSGMEMORY g_MsgMemory[] = { WM_WINDOWPOSCHANGING, sizeof(WINDOWPOS), MMS_FLAG_READWRITE }, { WM_SIZING, sizeof(RECT), MMS_FLAG_READWRITE }, { WM_MOVING, sizeof(RECT), MMS_FLAG_READWRITE }, + { WM_MEASUREITEM, sizeof(MEASUREITEMSTRUCT), MMS_FLAG_READWRITE }, + { WM_DRAWITEM, sizeof(DRAWITEMSTRUCT), MMS_FLAG_READWRITE }, + { WM_HELP, sizeof(HELPINFO), MMS_FLAG_READWRITE }, + { WM_NEXTMENU, sizeof(MDINEXTMENU), MMS_FLAG_READWRITE }, }; static PMSGMEMORY FASTCALL @@ -745,7 +749,13 @@ IntDispatchMessage(PMSG pMsg) pMsg->message, pMsg->wParam, pMsg->lParam, - &retval); + &retval); + case FNID_MENU: + DoCallBack = !PopupMenuWndProc( Window, + pMsg->message, + pMsg->wParam, + pMsg->lParam, + &retval); break; } } @@ -949,7 +959,7 @@ co_IntPeekMessage( PMSG Msg, return TRUE; } -static BOOL FASTCALL +BOOL FASTCALL co_IntWaitMessage( PWND Window, UINT MsgFilterMin, UINT MsgFilterMax ) @@ -1353,6 +1363,9 @@ co_IntSendMessageTimeoutSingle( HWND hWnd, case FNID_MESSAGEWND: DoCallBack = !UserMessageWindowProc(Window, Msg, wParam, lParam,(LRESULT*)&Result); break; + case FNID_MENU: + DoCallBack = !PopupMenuWndProc( Window, Msg, wParam, lParam,(LRESULT*)&Result); + break; } if (!DoCallBack) { @@ -1644,6 +1657,9 @@ co_IntSendMessageWithCallBack( HWND hWnd, case FNID_MESSAGEWND: DoCallBack = !UserMessageWindowProc(Window, Msg, wParam, lParam,(LRESULT*)&Result); break; + case FNID_MENU: + DoCallBack = !PopupMenuWndProc( Window, Msg, wParam, lParam,(LRESULT*)&Result); + break; } } @@ -2333,13 +2349,21 @@ NtUserMessageCall( HWND hWnd, } break; } - + case FNID_MENU: + { + Window = UserGetWindowObject(hWnd); + if (Window) + { + Ret = PopupMenuWndProc( Window, Msg, wParam, lParam, &lResult); + } + break; + } case FNID_MESSAGEWND: { Window = UserGetWindowObject(hWnd); if (Window) { - Ret = !UserMessageWindowProc(Window, Msg, wParam, lParam,&lResult); + Ret = !UserMessageWindowProc(Window, Msg, wParam, lParam, &lResult); } break; } @@ -2832,6 +2856,7 @@ NtUserMessageCall( HWND hWnd, case FNID_CALLWNDPROCRET: case FNID_SCROLLBAR: case FNID_DESKTOP: + case FNID_MENU: if (ResultInfo) { _SEH2_TRY diff --git a/reactos/win32ss/user/ntuser/misc.c b/reactos/win32ss/user/ntuser/misc.c index ba7db3140f6..87baf1d7769 100644 --- a/reactos/win32ss/user/ntuser/misc.c +++ b/reactos/win32ss/user/ntuser/misc.c @@ -397,6 +397,28 @@ NtUserGetGUIThreadInfo( CaretInfo = MsgQueue->CaretInfo; SafeGui.flags = (CaretInfo->Visible ? GUI_CARETBLINKING : 0); +/* + if (W32Thread->pMenuState->pGlobalPopupMenu) + { + SafeGui.flags |= GUI_INMENUMODE; + + if (W32Thread->pMenuState->pGlobalPopupMenu->spwndNotify) + SafeGui.hwndMenuOwner = UserHMGetHandle(W32Thread->pMenuState->pGlobalPopupMenu->spwndNotify); + + if (W32Thread->pMenuState->pGlobalPopupMenu->fHasMenuBar) + { + if (W32Thread->pMenuState->pGlobalPopupMenu->fIsSysMenu) + { + SafeGui.flags |= GUI_SYSTEMMENUMODE; + } + } + else + { + SafeGui.flags |= GUI_POPUPMENUMODE; + } + } + */ + SafeGui.hwndMenuOwner = MsgQueue->MenuOwner; if (MsgQueue->MenuOwner) SafeGui.flags |= GUI_INMENUMODE | MsgQueue->MenuState; @@ -409,7 +431,6 @@ NtUserGetGUIThreadInfo( SafeGui.hwndActive = MsgQueue->spwndActive ? UserHMGetHandle(MsgQueue->spwndActive) : 0; SafeGui.hwndFocus = MsgQueue->spwndFocus ? UserHMGetHandle(MsgQueue->spwndFocus) : 0; SafeGui.hwndCapture = MsgQueue->spwndCapture ? UserHMGetHandle(MsgQueue->spwndCapture) : 0; - SafeGui.hwndMenuOwner = MsgQueue->MenuOwner; SafeGui.hwndMoveSize = MsgQueue->MoveSize; SafeGui.hwndCaret = CaretInfo->hWnd; diff --git a/reactos/win32ss/user/ntuser/msgqueue.c b/reactos/win32ss/user/ntuser/msgqueue.c index 911c32aa239..3a7fe54f10c 100644 --- a/reactos/win32ss/user/ntuser/msgqueue.c +++ b/reactos/win32ss/user/ntuser/msgqueue.c @@ -2005,8 +2005,28 @@ NTSTATUS FASTCALL co_MsqWaitForNewMessages(PTHREADINFO pti, PWND WndFilter, UINT MsgFilterMin, UINT MsgFilterMax) { - NTSTATUS ret; + NTSTATUS ret = STATUS_SUCCESS; + + // Post mouse moves before waiting for messages. + if (pti->MessageQueue->QF_flags & QF_MOUSEMOVED) + { + IntCoalesceMouseMove(pti); + } + + if ( pti->nCntsQBits[QSRosMouseButton] != 0 || + pti->nCntsQBits[QSRosMouseMove] != 0 || + pti->nCntsQBits[QSRosKey] != 0 || + pti->nCntsQBits[QSRosSendMessage] != 0 || + pti->nCntsQBits[QSRosPostMessage] != 0 ) + { + TRACE("No time to wait!\n"); + return ret; + } + UserLeaveCo(); + + ZwYieldExecution(); // Let someone else run! + ret = KeWaitForSingleObject( pti->pEventQueueServer, UserRequest, UserMode, diff --git a/reactos/win32ss/user/ntuser/msgqueue.h b/reactos/win32ss/user/ntuser/msgqueue.h index c552acc27aa..f7b92b48381 100644 --- a/reactos/win32ss/user/ntuser/msgqueue.h +++ b/reactos/win32ss/user/ntuser/msgqueue.h @@ -274,5 +274,9 @@ UserPostThreadMessage( PTHREADINFO pti, UINT Msg, WPARAM wParam, LPARAM lParam ); +BOOL FASTCALL +co_IntWaitMessage( PWND Window, + UINT MsgFilterMin, + UINT MsgFilterMax ); /* EOF */ diff --git a/reactos/win32ss/user/ntuser/nonclient.c b/reactos/win32ss/user/ntuser/nonclient.c index 6a10bab5512..dd2dafdd6f9 100644 --- a/reactos/win32ss/user/ntuser/nonclient.c +++ b/reactos/win32ss/user/ntuser/nonclient.c @@ -31,6 +31,12 @@ DBG_DEFAULT_CHANNEL(UserDefwnd); #define ON_BOTTOM_BORDER(hit) \ (((hit) == HTBOTTOM) || ((hit) == HTBOTTOMLEFT) || ((hit) == HTBOTTOMRIGHT)) +#define HASSIZEGRIP(Style, ExStyle, ParentStyle, WindowRect, ParentClientRect) \ + ((!(Style & WS_CHILD) && (Style & WS_THICKFRAME) && !(Style & WS_MAXIMIZE)) || \ + ((Style & WS_CHILD) && (ParentStyle & WS_THICKFRAME) && !(ParentStyle & WS_MAXIMIZE) && \ + (WindowRect.right - WindowRect.left == ParentClientRect.right) && \ + (WindowRect.bottom - WindowRect.top == ParentClientRect.bottom))) + VOID FASTCALL UserDrawWindowFrame(HDC hdc, @@ -109,6 +115,32 @@ NC_GetInsideRect(PWND Wnd, RECT *rect) } } +/*********************************************************************** + * NC_GetSysPopupPos + */ +void FASTCALL +NC_GetSysPopupPos(PWND Wnd, RECT *Rect) +{ + RECT WindowRect; + + if ((Wnd->style & WS_MINIMIZE) != 0) + { + IntGetWindowRect(Wnd, Rect); + } + else + { + NC_GetInsideRect(Wnd, Rect); + IntGetWindowRect(Wnd, &WindowRect); + RECTL_vOffsetRect(Rect, WindowRect.left, WindowRect.top); + if (Wnd->style & WS_CHILD) + { + IntClientToScreen(IntGetParent(Wnd), (POINT *) Rect); + } + Rect->right = Rect->left + UserGetSystemMetrics(SM_CYCAPTION) - 1; + Rect->bottom = Rect->top + UserGetSystemMetrics(SM_CYCAPTION) - 1; + } +} + LONG FASTCALL DefWndStartSizeMove(PWND Wnd, WPARAM wParam, POINT *capturePoint) { @@ -598,6 +630,1073 @@ PCURICON_OBJECT FASTCALL NC_IconForWindow( PWND pWnd ) return pIcon; } +BOOL +UserDrawSysMenuButton(PWND pWnd, HDC hDC, LPRECT Rect, BOOL Down) +{ + PCURICON_OBJECT WindowIcon; + BOOL Ret = FALSE; + + if ((WindowIcon = NC_IconForWindow(pWnd))) + { + UserReferenceObject(WindowIcon); + + Ret = UserDrawIconEx( hDC, + Rect->left + 2, + Rect->top + 2, + WindowIcon, + UserGetSystemMetrics(SM_CXSMICON), + UserGetSystemMetrics(SM_CYSMICON), + 0, NULL, DI_NORMAL); + + UserDereferenceObject(WindowIcon); + } + return Ret; +} + +void +UserGetInsideRectNC(PWND Wnd, RECT *rect) +{ + ULONG Style; + ULONG ExStyle; + + Style = Wnd->style; + ExStyle = Wnd->ExStyle; + + rect->top = rect->left = 0; + rect->right = Wnd->rcWindow.right - Wnd->rcWindow.left; + rect->bottom = Wnd->rcWindow.bottom - Wnd->rcWindow.top; + + if (Style & WS_ICONIC) + { + return; + } + + /* Remove frame from rectangle */ + if (UserHasThickFrameStyle(Style, ExStyle )) + { + RECTL_vInflateRect(rect, -UserGetSystemMetrics(SM_CXFRAME), -UserGetSystemMetrics(SM_CYFRAME)); + } + else + { + if (UserHasDlgFrameStyle(Style, ExStyle )) + { + RECTL_vInflateRect(rect, -UserGetSystemMetrics(SM_CXDLGFRAME), -UserGetSystemMetrics(SM_CYDLGFRAME)); + /* FIXME: this isn't in NC_AdjustRect? why not? */ + if (ExStyle & WS_EX_DLGMODALFRAME) + RECTL_vInflateRect( rect, -1, 0 ); + } + else + { + if (UserHasThinFrameStyle(Style, ExStyle)) + { + RECTL_vInflateRect(rect, -UserGetSystemMetrics(SM_CXBORDER), -UserGetSystemMetrics(SM_CYBORDER)); + } + } + } + /* We have additional border information if the window + * is a child (but not an MDI child) */ + if ((Style & WS_CHILD) && !(ExStyle & WS_EX_MDICHILD)) + { + if (ExStyle & WS_EX_CLIENTEDGE) + RECTL_vInflateRect (rect, -UserGetSystemMetrics(SM_CXEDGE), -UserGetSystemMetrics(SM_CYEDGE)); + if (ExStyle & WS_EX_STATICEDGE) + RECTL_vInflateRect (rect, -UserGetSystemMetrics(SM_CXBORDER), -UserGetSystemMetrics(SM_CYBORDER)); + } +} + +BOOL +IntIsScrollBarVisible(PWND pWnd, INT hBar) +{ + SCROLLBARINFO sbi; + sbi.cbSize = sizeof(SCROLLBARINFO); + + if(!co_IntGetScrollBarInfo(pWnd, hBar, &sbi)) + return FALSE; + + return !(sbi.rgstate[0] & STATE_SYSTEM_OFFSCREEN); +} + +BOOL +UserHasMenu(PWND pWnd, ULONG Style) +{ + return (!(Style & WS_CHILD) && UlongToHandle(pWnd->IDMenu) != 0); +} + +/* + * FIXME: + * - Cache bitmaps, then just bitblt instead of calling DFC() (and + * wasting precious CPU cycles) every time + * - Center the buttons verticaly in the rect + */ +VOID +UserDrawCaptionButton(PWND pWnd, LPRECT Rect, DWORD Style, DWORD ExStyle, HDC hDC, BOOL bDown, ULONG Type) +{ + RECT TempRect; + + if (!(Style & WS_SYSMENU)) + { + return; + } + + TempRect = *Rect; + + switch (Type) + { + case DFCS_CAPTIONMIN: + { + if (ExStyle & WS_EX_TOOLWINDOW) + return; /* ToolWindows don't have min/max buttons */ + + if (Style & WS_SYSMENU) + TempRect.right -= UserGetSystemMetrics(SM_CXSIZE) + 1; + + if (Style & (WS_MAXIMIZEBOX | WS_MINIMIZEBOX)) + TempRect.right -= UserGetSystemMetrics(SM_CXSIZE) - 2; + + TempRect.left = TempRect.right - UserGetSystemMetrics(SM_CXSIZE) + 1; + TempRect.bottom = TempRect.top + UserGetSystemMetrics(SM_CYSIZE) - 2; + TempRect.top += 2; + TempRect.right -= 1; + + DrawFrameControl(hDC, &TempRect, DFC_CAPTION, + ((Style & WS_MINIMIZE) ? DFCS_CAPTIONRESTORE : DFCS_CAPTIONMIN) | + (bDown ? DFCS_PUSHED : 0) | + ((Style & WS_MINIMIZEBOX) ? 0 : DFCS_INACTIVE)); + break; + } + case DFCS_CAPTIONMAX: + { + if (ExStyle & WS_EX_TOOLWINDOW) + return; /* ToolWindows don't have min/max buttons */ + + if (Style & WS_SYSMENU) + TempRect.right -= UserGetSystemMetrics(SM_CXSIZE) + 1; + + TempRect.left = TempRect.right - UserGetSystemMetrics(SM_CXSIZE) + 1; + TempRect.bottom = TempRect.top + UserGetSystemMetrics(SM_CYSIZE) - 2; + TempRect.top += 2; + TempRect.right -= 1; + + DrawFrameControl(hDC, &TempRect, DFC_CAPTION, + ((Style & WS_MAXIMIZE) ? DFCS_CAPTIONRESTORE : DFCS_CAPTIONMAX) | + (bDown ? DFCS_PUSHED : 0) | + ((Style & WS_MAXIMIZEBOX) ? 0 : DFCS_INACTIVE)); + break; + } + case DFCS_CAPTIONCLOSE: + { + PMENU pSysMenu = IntGetSystemMenu(pWnd, FALSE); + UINT MenuState = IntGetMenuState(UserHMGetHandle(pSysMenu), SC_CLOSE, MF_BYCOMMAND); /* in case of error MenuState==0xFFFFFFFF */ + + /* FIXME: A tool window has a smaller Close button */ + + if (ExStyle & WS_EX_TOOLWINDOW) + { + TempRect.left = TempRect.right - UserGetSystemMetrics(SM_CXSMSIZE); + TempRect.bottom = TempRect.top + UserGetSystemMetrics(SM_CYSMSIZE) - 2; + } + else + { + TempRect.left = TempRect.right - UserGetSystemMetrics(SM_CXSIZE); + TempRect.bottom = TempRect.top + UserGetSystemMetrics(SM_CYSIZE) - 2; + } + TempRect.top += 2; + TempRect.right -= 2; + + DrawFrameControl(hDC, &TempRect, DFC_CAPTION, + (DFCS_CAPTIONCLOSE | (bDown ? DFCS_PUSHED : 0) | + ((!(MenuState & (MF_GRAYED|MF_DISABLED)) && !(pWnd->pcls->style & CS_NOCLOSE)) ? 0 : DFCS_INACTIVE))); + break; + } + } +} + +VOID +UserDrawCaptionButtonWnd(PWND pWnd, HDC hDC, BOOL bDown, ULONG Type) +{ + RECT WindowRect; + SIZE WindowBorder; + + IntGetWindowRect(pWnd, &WindowRect); + + WindowRect.right -= WindowRect.left; + WindowRect.bottom -= WindowRect.top; + WindowRect.left = WindowRect.top = 0; + + UserGetWindowBorders(pWnd->style, pWnd->ExStyle, &WindowBorder, FALSE); + + RECTL_vInflateRect(&WindowRect, -WindowBorder.cx, -WindowBorder.cy); + + UserDrawCaptionButton(pWnd, &WindowRect, pWnd->style, pWnd->ExStyle, hDC, bDown, Type); +} + +VOID +NC_DrawFrame( HDC hDC, RECT *CurrentRect, BOOL Active, DWORD Style, DWORD ExStyle) +{ + /* Firstly the "thick" frame */ + if ((Style & WS_THICKFRAME) && !(Style & WS_MINIMIZE)) + { + LONG Width = + (UserGetSystemMetrics(SM_CXFRAME) - UserGetSystemMetrics(SM_CXDLGFRAME)) * + UserGetSystemMetrics(SM_CXBORDER); + + LONG Height = + (UserGetSystemMetrics(SM_CYFRAME) - UserGetSystemMetrics(SM_CYDLGFRAME)) * + UserGetSystemMetrics(SM_CYBORDER); + + NtGdiSelectBrush(hDC, IntGetSysColorBrush(Active ? COLOR_ACTIVEBORDER : COLOR_INACTIVEBORDER)); + + /* Draw frame */ + NtGdiPatBlt(hDC, CurrentRect->left, CurrentRect->top, CurrentRect->right - CurrentRect->left, Height, PATCOPY); + NtGdiPatBlt(hDC, CurrentRect->left, CurrentRect->top, Width, CurrentRect->bottom - CurrentRect->top, PATCOPY); + NtGdiPatBlt(hDC, CurrentRect->left, CurrentRect->bottom - 1, CurrentRect->right - CurrentRect->left, -Height, PATCOPY); + NtGdiPatBlt(hDC, CurrentRect->right - 1, CurrentRect->top, -Width, CurrentRect->bottom - CurrentRect->top, PATCOPY); + + RECTL_vInflateRect(CurrentRect, -Width, -Height); + } + + /* Now the other bit of the frame */ + if (Style & (WS_DLGFRAME | WS_BORDER) || ExStyle & WS_EX_DLGMODALFRAME) + { + DWORD Width = UserGetSystemMetrics(SM_CXBORDER); + DWORD Height = UserGetSystemMetrics(SM_CYBORDER); + + NtGdiSelectBrush(hDC, IntGetSysColorBrush( + (ExStyle & (WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE)) ? COLOR_3DFACE : + (ExStyle & WS_EX_STATICEDGE) ? COLOR_WINDOWFRAME : + (Style & (WS_DLGFRAME | WS_THICKFRAME)) ? COLOR_3DFACE : + COLOR_WINDOWFRAME)); + + /* Draw frame */ + NtGdiPatBlt(hDC, CurrentRect->left, CurrentRect->top, CurrentRect->right - CurrentRect->left, Height, PATCOPY); + NtGdiPatBlt(hDC, CurrentRect->left, CurrentRect->top, Width, CurrentRect->bottom - CurrentRect->top, PATCOPY); + NtGdiPatBlt(hDC, CurrentRect->left, CurrentRect->bottom - 1, CurrentRect->right - CurrentRect->left, -Height, PATCOPY); + NtGdiPatBlt(hDC, CurrentRect->right - 1, CurrentRect->top, -Width, CurrentRect->bottom - CurrentRect->top, PATCOPY); + + RECTL_vInflateRect(CurrentRect, -Width, -Height); + } +} + +VOID UserDrawCaptionBar( + PWND pWnd, + HDC hDC, + INT Flags) +{ + DWORD Style, ExStyle; + RECT WindowRect, CurrentRect, TempRect; + HPEN PreviousPen; + BOOL Gradient = FALSE; + PCURICON_OBJECT pIcon = NULL; + + if (!(Flags & DC_NOVISIBLE) && !IntIsWindowVisible(pWnd)) return; + + TRACE("UserDrawCaptionBar: pWnd %p, hDc %p, Flags 0x%x.\n", pWnd, hDC, Flags); + + Style = pWnd->style; + ExStyle = pWnd->ExStyle; + + IntGetWindowRect(pWnd, &WindowRect); + + CurrentRect.top = CurrentRect.left = 0; + CurrentRect.right = WindowRect.right - WindowRect.left; + CurrentRect.bottom = WindowRect.bottom - WindowRect.top; + + /* Draw outer edge */ + if (UserHasWindowEdge(Style, ExStyle)) + { + DrawEdge(hDC, &CurrentRect, EDGE_RAISED, BF_RECT | BF_ADJUST); + } + else if (ExStyle & WS_EX_STATICEDGE) + { +#if 0 + DrawEdge(hDC, &CurrentRect, BDR_SUNKENINNER, BF_RECT | BF_ADJUST | BF_FLAT); +#else + NtGdiSelectBrush(hDC, IntGetSysColorBrush(COLOR_BTNSHADOW)); + NtGdiPatBlt(hDC, CurrentRect.left, CurrentRect.top, CurrentRect.right - CurrentRect.left, 1, PATCOPY); + NtGdiPatBlt(hDC, CurrentRect.left, CurrentRect.top, 1, CurrentRect.bottom - CurrentRect.top, PATCOPY); + + NtGdiSelectBrush(hDC, IntGetSysColorBrush(COLOR_BTNHIGHLIGHT)); + NtGdiPatBlt(hDC, CurrentRect.left, CurrentRect.bottom - 1, CurrentRect.right - CurrentRect.left, 1, PATCOPY); + NtGdiPatBlt(hDC, CurrentRect.right - 1, CurrentRect.top, 1, CurrentRect.bottom - CurrentRect.top, PATCOPY); + + RECTL_vInflateRect(&CurrentRect, -1, -1); +#endif + } + + if (Flags & DC_FRAME) NC_DrawFrame(hDC, &CurrentRect, (Flags & DC_ACTIVE), Style, ExStyle); + + /* Draw caption */ + if ((Style & WS_CAPTION) == WS_CAPTION) + { + TempRect = CurrentRect; + + if (UserSystemParametersInfo(SPI_GETGRADIENTCAPTIONS, 0, &Gradient, 0) && Gradient) + { + Flags |= DC_GRADIENT; + } + + if (ExStyle & WS_EX_TOOLWINDOW) + { + Flags |= DC_SMALLCAP; + TempRect.bottom = TempRect.top + UserGetSystemMetrics(SM_CYSMCAPTION) - 1; + CurrentRect.top += UserGetSystemMetrics(SM_CYSMCAPTION); + } + else + { + TempRect.bottom = TempRect.top + UserGetSystemMetrics(SM_CYCAPTION) - 1; + CurrentRect.top += UserGetSystemMetrics(SM_CYCAPTION); + } + + if (!(Flags & DC_ICON) && + (Style & WS_SYSMENU) && + !(Flags & DC_SMALLCAP) && + !(ExStyle & WS_EX_DLGMODALFRAME) && + !(ExStyle & WS_EX_TOOLWINDOW) ) + { + pIcon = NC_IconForWindow(pWnd); // Force redraw of caption with icon if DC_ICON not flaged.... + } + UserDrawCaption(pWnd, hDC, &TempRect, NULL, pIcon ? UserHMGetHandle(pIcon) : NULL, NULL, Flags); + + /* Draw buttons */ + if (Style & WS_SYSMENU) + { + UserDrawCaptionButton(pWnd, &TempRect, Style, ExStyle, hDC, FALSE, DFCS_CAPTIONCLOSE); + if ((Style & (WS_MAXIMIZEBOX | WS_MINIMIZEBOX)) && !(ExStyle & WS_EX_TOOLWINDOW)) + { + UserDrawCaptionButton(pWnd, &TempRect, Style, ExStyle, hDC, FALSE, DFCS_CAPTIONMIN); + UserDrawCaptionButton(pWnd, &TempRect, Style, ExStyle, hDC, FALSE, DFCS_CAPTIONMAX); + } + } + + if (!(Style & WS_MINIMIZE)) + { + /* Line under caption */ + PreviousPen = NtGdiSelectPen(hDC, NtGdiGetStockObject(DC_PEN)); + + IntSetDCPenColor( hDC, IntGetSysColor(((ExStyle & (WS_EX_STATICEDGE|WS_EX_CLIENTEDGE|WS_EX_DLGMODALFRAME)) == WS_EX_STATICEDGE) ? + COLOR_WINDOWFRAME : COLOR_3DFACE)); + + GreMoveTo(hDC, TempRect.left, TempRect.bottom, NULL); + + NtGdiLineTo(hDC, TempRect.right, TempRect.bottom); + + NtGdiSelectPen(hDC, PreviousPen); + } + } + + if (!(Style & WS_MINIMIZE)) + { + if (ExStyle & WS_EX_CLIENTEDGE) + { + DrawEdge(hDC, &CurrentRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST); + } + } +} + +// Note from Wine: +/* MSDN docs are pretty idiotic here, they say app CAN use clipRgn in + the call to GetDCEx implying that it is allowed not to use it either. + However, the suggested GetDCEx( , DCX_WINDOW | DCX_INTERSECTRGN) + will cause clipRgn to be deleted after ReleaseDC(). + Now, how is the "system" supposed to tell what happened? + */ +/* + * FIXME: + * - Drawing of WS_BORDER after scrollbars + * - Correct drawing of size-box + */ +LRESULT +NC_DoNCPaint(PWND pWnd, HDC hDC, INT Flags) +{ + DWORD Style, ExStyle; + PWND Parent; + RECT WindowRect, CurrentRect, TempRect; + BOOL Active = FALSE; + PCURICON_OBJECT pIcon = NULL; + + if (!IntIsWindowVisible(pWnd) || + (pWnd->state & WNDS_NONCPAINT && !(pWnd->state & WNDS_FORCEMENUDRAW)) || + IntEqualRect(&pWnd->rcWindow, &pWnd->rcClient) ) + return 0; + + Style = pWnd->style; + + TRACE("DefWndNCPaint: pWnd %p, hDc %p, Active %s.\n", pWnd, hDC, Flags & DC_ACTIVE ? "TRUE" : "FALSE"); + + Parent = IntGetParent(pWnd); + ExStyle = pWnd->ExStyle; + + if (Flags == -1) // NC paint mode. + { + if (ExStyle & WS_EX_MDICHILD) + { + Active = IntIsChildWindow(gpqForeground->spwndActive, pWnd); + + if (Active) + Active = (UserHMGetHandle(pWnd) == (HWND)co_IntSendMessage(UserHMGetHandle(Parent), WM_MDIGETACTIVE, 0, 0)); + } + else + { + Active = (gpqForeground == pWnd->head.pti->MessageQueue); + } + Flags = DC_NC; + } + + IntGetWindowRect(pWnd, &WindowRect); + + CurrentRect.top = CurrentRect.left = 0; + CurrentRect.right = WindowRect.right - WindowRect.left; + CurrentRect.bottom = WindowRect.bottom - WindowRect.top; + + /* Draw outer edge */ + if (UserHasWindowEdge(pWnd->style, pWnd->ExStyle)) + { + DrawEdge(hDC, &CurrentRect, EDGE_RAISED, BF_RECT | BF_ADJUST); + } + else if (pWnd->ExStyle & WS_EX_STATICEDGE) + { +#if 0 + DrawEdge(hDC, &CurrentRect, BDR_SUNKENINNER, BF_RECT | BF_ADJUST | BF_FLAT); +#else + NtGdiSelectBrush(hDC, IntGetSysColorBrush(COLOR_BTNSHADOW)); + NtGdiPatBlt(hDC, CurrentRect.left, CurrentRect.top, CurrentRect.right - CurrentRect.left, 1, PATCOPY); + NtGdiPatBlt(hDC, CurrentRect.left, CurrentRect.top, 1, CurrentRect.bottom - CurrentRect.top, PATCOPY); + + NtGdiSelectBrush(hDC, IntGetSysColorBrush(COLOR_BTNHIGHLIGHT)); + NtGdiPatBlt(hDC, CurrentRect.left, CurrentRect.bottom - 1, CurrentRect.right - CurrentRect.left, 1, PATCOPY); + NtGdiPatBlt(hDC, CurrentRect.right - 1, CurrentRect.top, 1, CurrentRect.bottom - CurrentRect.top, PATCOPY); + + RECTL_vInflateRect(&CurrentRect, -1, -1); +#endif + } + + if (Flags & DC_FRAME) NC_DrawFrame(hDC, &CurrentRect, Active ? Active : (Flags & DC_ACTIVE), Style, ExStyle); + + /* Draw caption */ + if ((Style & WS_CAPTION) == WS_CAPTION) + { + HPEN PreviousPen; + BOOL Gradient = FALSE; + + if (Flags & DC_REDRAWHUNGWND) + { + Flags &= ~DC_REDRAWHUNGWND; + Flags |= DC_NOSENDMSG; + } + + if (UserSystemParametersInfo(SPI_GETGRADIENTCAPTIONS, 0, &Gradient, 0) && Gradient) + { + Flags |= DC_GRADIENT; + } + + if (Active) + { + if (!(pWnd->state & WNDS_ACTIVEFRAME)) + { + ERR("Wnd is active and not set active!\n"); + } + Flags |= DC_ACTIVE; + } + + TempRect = CurrentRect; + + if (ExStyle & WS_EX_TOOLWINDOW) + { + Flags |= DC_SMALLCAP; + TempRect.bottom = TempRect.top + UserGetSystemMetrics(SM_CYSMCAPTION) - 1; + CurrentRect.top += UserGetSystemMetrics(SM_CYSMCAPTION); + } + else + { + TempRect.bottom = TempRect.top + UserGetSystemMetrics(SM_CYCAPTION) - 1; + CurrentRect.top += UserGetSystemMetrics(SM_CYCAPTION); + } + + if (!(Flags & DC_ICON) && + (Style & WS_SYSMENU) && + !(Flags & DC_SMALLCAP) && + !(ExStyle & WS_EX_DLGMODALFRAME) && + !(ExStyle & WS_EX_TOOLWINDOW) ) + { + pIcon = NC_IconForWindow(pWnd); // Force redraw of caption with icon if DC_ICON not flaged.... + } + UserDrawCaption(pWnd, hDC, &TempRect, NULL, pIcon ? UserHMGetHandle(pIcon) : NULL, NULL, Flags); + + /* Draw buttons */ + if (Style & WS_SYSMENU) + { + UserDrawCaptionButton(pWnd, &TempRect, Style, ExStyle, hDC, FALSE, DFCS_CAPTIONCLOSE); + if ((Style & (WS_MAXIMIZEBOX | WS_MINIMIZEBOX)) && !(ExStyle & WS_EX_TOOLWINDOW)) + { + UserDrawCaptionButton(pWnd, &TempRect, Style, ExStyle, hDC, FALSE, DFCS_CAPTIONMIN); + UserDrawCaptionButton(pWnd, &TempRect, Style, ExStyle, hDC, FALSE, DFCS_CAPTIONMAX); + } + } + if (!(Style & WS_MINIMIZE)) + { + /* Line under caption */ + PreviousPen = NtGdiSelectPen(hDC, NtGdiGetStockObject(DC_PEN)); + + IntSetDCPenColor( hDC, IntGetSysColor( + ((ExStyle & (WS_EX_STATICEDGE | WS_EX_CLIENTEDGE | WS_EX_DLGMODALFRAME)) == WS_EX_STATICEDGE) ? + COLOR_WINDOWFRAME : COLOR_3DFACE)); + + GreMoveTo(hDC, TempRect.left, TempRect.bottom, NULL); + + NtGdiLineTo(hDC, TempRect.right, TempRect.bottom); + + NtGdiSelectPen(hDC, PreviousPen); + } + } + + if (!(Style & WS_MINIMIZE)) + { + PMENU menu = UserGetMenuObject(UlongToHandle(pWnd->IDMenu)); + /* Draw menu bar */ + if (menu && !(Style & WS_CHILD)) + { + TempRect = CurrentRect; + TempRect.bottom = TempRect.top + menu->cyMenu; + CurrentRect.top += MENU_DrawMenuBar(hDC, &TempRect, pWnd, FALSE); + } + + if (ExStyle & WS_EX_CLIENTEDGE) + { + DrawEdge(hDC, &CurrentRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST); + } + + /* Draw the scrollbars */ + if ((Style & WS_VSCROLL) && (Style & WS_HSCROLL) && + IntIsScrollBarVisible(pWnd, OBJID_VSCROLL) && IntIsScrollBarVisible(pWnd, OBJID_HSCROLL)) + { + RECT ParentClientRect; + + TempRect = CurrentRect; + + if (ExStyle & WS_EX_LEFTSCROLLBAR) + TempRect.right = TempRect.left + UserGetSystemMetrics(SM_CXVSCROLL); + else + TempRect.left = TempRect.right - UserGetSystemMetrics(SM_CXVSCROLL); + + TempRect.top = TempRect.bottom - UserGetSystemMetrics(SM_CYHSCROLL); + + FillRect(hDC, &TempRect, IntGetSysColorBrush(COLOR_BTNFACE)); + + if (Parent) + IntGetClientRect(Parent, &ParentClientRect); + + if (HASSIZEGRIP(Style, ExStyle, Parent->style, WindowRect, ParentClientRect)) + { + DrawFrameControl(hDC, &TempRect, DFC_SCROLL, DFCS_SCROLLSIZEGRIP); + } + + IntDrawScrollBar(pWnd, hDC, SB_VERT); + IntDrawScrollBar(pWnd, hDC, SB_HORZ); + } + else + { + if (Style & WS_VSCROLL && IntIsScrollBarVisible(pWnd, OBJID_VSCROLL)) + { + IntDrawScrollBar(pWnd, hDC, SB_VERT); + } + else if (Style & WS_HSCROLL && IntIsScrollBarVisible(pWnd, OBJID_HSCROLL)) + { + IntDrawScrollBar(pWnd, hDC, SB_HORZ); + } + } + } + return 0; // For WM_NCPAINT message, return 0. +} + +LRESULT NC_HandleNCCalcSize( PWND Wnd, WPARAM wparam, RECTL *Rect ) +{ + LRESULT Result = 0; + SIZE WindowBorders; + RECT OrigRect; + DWORD Style = Wnd->style; + + if (Rect == NULL) + { + return Result; + } + OrigRect = *Rect; + + Wnd->state &= ~WNDS_HASCAPTION; + + if (wparam) + { + if (Wnd->pcls->style & CS_VREDRAW) + { + Result |= WVR_VREDRAW; + } + if (Wnd->pcls->style & CS_HREDRAW) + { + Result |= WVR_HREDRAW; + } + Result |= WVR_VALIDRECTS; + } + + if (!(Wnd->style & WS_MINIMIZE)) + { + if (UserHasWindowEdge(Wnd->style, Wnd->ExStyle)) + { + UserGetWindowBorders(Wnd->style, Wnd->ExStyle, &WindowBorders, FALSE); + RECTL_vInflateRect(Rect, -WindowBorders.cx, -WindowBorders.cy); + } + else if ((Wnd->ExStyle & WS_EX_STATICEDGE) || (Wnd->style & WS_BORDER)) + { + RECTL_vInflateRect(Rect, -1, -1); + } + + if ((Wnd->style & WS_CAPTION) == WS_CAPTION) + { + Wnd->state |= WNDS_HASCAPTION; + + if (Wnd->ExStyle & WS_EX_TOOLWINDOW) + Rect->top += UserGetSystemMetrics(SM_CYSMCAPTION); + else + Rect->top += UserGetSystemMetrics(SM_CYCAPTION); + } + + if (Wnd->IDMenu && ((Wnd->style & (WS_CHILD | WS_POPUP)) != WS_CHILD)) + { + HDC hDC = UserGetDCEx(Wnd, 0, DCX_USESTYLE | DCX_WINDOW); + + Wnd->state |= WNDS_HASMENU; + + if (hDC) + { + RECT CliRect = *Rect; + CliRect.bottom -= OrigRect.top; + CliRect.right -= OrigRect.left; + CliRect.left -= OrigRect.left; + CliRect.top -= OrigRect.top; + Rect->top += MENU_DrawMenuBar(hDC, &CliRect, Wnd, TRUE); + UserReleaseDC(Wnd, hDC, FALSE); + } + } + + if (Wnd->ExStyle & WS_EX_CLIENTEDGE) + { + RECTL_vInflateRect(Rect, -2 * UserGetSystemMetrics(SM_CXBORDER), -2 * UserGetSystemMetrics(SM_CYBORDER)); + } + + if (Wnd->style & (WS_VSCROLL | WS_HSCROLL)) + { + SCROLLBARINFO sbi; + SETSCROLLBARINFO ssbi; + + sbi.cbSize = sizeof(SCROLLBARINFO); + if ((Style & WS_VSCROLL) && co_IntGetScrollBarInfo(Wnd, OBJID_VSCROLL, &sbi)) + { + int i; + LONG sx = Rect->right; + + Wnd->state |= WNDS_HASVERTICALSCROOLLBAR; + + sx -= UserGetSystemMetrics(SM_CXVSCROLL); + + for (i = 0; i <= CCHILDREN_SCROLLBAR; i++) + ssbi.rgstate[i] = sbi.rgstate[i]; + + if (sx <= Rect->left) + ssbi.rgstate[0] |= STATE_SYSTEM_OFFSCREEN; + else + ssbi.rgstate[0] &= ~STATE_SYSTEM_OFFSCREEN; + + co_IntSetScrollBarInfo(Wnd, OBJID_VSCROLL, &ssbi); + + if (ssbi.rgstate[0] & STATE_SYSTEM_OFFSCREEN) + Style &= ~WS_VSCROLL; + } + else + Style &= ~WS_VSCROLL; + + if ((Style & WS_HSCROLL) && co_IntGetScrollBarInfo(Wnd, OBJID_HSCROLL, &sbi)) + { + int i; + LONG sy = Rect->bottom; + + Wnd->state |= WNDS_HASHORIZONTALSCROLLBAR; + + sy -= UserGetSystemMetrics(SM_CYHSCROLL); + + for (i = 0; i <= CCHILDREN_SCROLLBAR; i++) + ssbi.rgstate[i] = sbi.rgstate[i]; + + if (sy <= Rect->top) + ssbi.rgstate[0] |= STATE_SYSTEM_OFFSCREEN; + else + ssbi.rgstate[0] &= ~STATE_SYSTEM_OFFSCREEN; + + co_IntSetScrollBarInfo(Wnd, OBJID_HSCROLL, &ssbi); + + if (ssbi.rgstate[0] & STATE_SYSTEM_OFFSCREEN) + Style &= ~WS_HSCROLL; + } + else + Style &= ~WS_HSCROLL; + } + + if ((Style & WS_VSCROLL) && (Style & WS_HSCROLL)) + { + if ((Wnd->ExStyle & WS_EX_LEFTSCROLLBAR) != 0) + Rect->left += UserGetSystemMetrics(SM_CXVSCROLL); + else + Rect->right -= UserGetSystemMetrics(SM_CXVSCROLL); + + Rect->bottom -= UserGetSystemMetrics(SM_CYHSCROLL); + } + else + { + if (Style & WS_VSCROLL) + { + if ((Wnd->ExStyle & WS_EX_LEFTSCROLLBAR) != 0) + Rect->left += UserGetSystemMetrics(SM_CXVSCROLL); + else + Rect->right -= UserGetSystemMetrics(SM_CXVSCROLL); + } + else if (Style & WS_HSCROLL) + Rect->bottom -= UserGetSystemMetrics(SM_CYHSCROLL); + } + + if (Rect->top > Rect->bottom) + Rect->bottom = Rect->top; + + if (Rect->left > Rect->right) + Rect->right = Rect->left; + } + else + { + Rect->right = Rect->left; + Rect->bottom = Rect->top; + } + + return Result; +} + +static +INT NC_DoNCActive(PWND Wnd) +{ + INT Ret = 0; + + if ( IntGetSysColor(COLOR_CAPTIONTEXT) != IntGetSysColor(COLOR_INACTIVECAPTIONTEXT) || + IntGetSysColor(COLOR_ACTIVECAPTION) != IntGetSysColor(COLOR_INACTIVECAPTION) ) + Ret = DC_CAPTION; + + if (!(Wnd->style & WS_MINIMIZED) && UserHasThickFrameStyle(Wnd->style, Wnd->ExStyle)) + { + //if (IntGetSysColor(COLOR_ACTIVEBORDER) != IntGetSysColor(COLOR_INACTIVEBORDER)) // Why are these the same? + { + Ret = DC_FRAME; + } + } + return Ret; +} + +LRESULT NC_HandleNCActivate( PWND Wnd, WPARAM wParam, LPARAM lParam ) +{ + INT Flags; + /* Lotus Notes draws menu descriptions in the caption of its main + * window. When it wants to restore original "system" view, it just + * sends WM_NCACTIVATE message to itself. Any optimizations here in + * attempt to minimize redrawings lead to a not restored caption. + */ + if (wParam & DC_ACTIVE) + { + Wnd->state |= WNDS_ACTIVEFRAME|WNDS_HASCAPTION; + wParam = DC_CAPTION|DC_ACTIVE; + } + else + { + Wnd->state &= ~(WNDS_ACTIVEFRAME|WNDS_HASCAPTION); + wParam = DC_CAPTION; + } + + if (Wnd->state & WNDS_NONCPAINT || !(Wnd->style & WS_VISIBLE)) + return 0; + + /* This isn't documented but is reproducible in at least XP SP2 and + * Outlook 2007 depends on it + */ + // MSDN: + // If this parameter is set to -1, DefWindowProc does not repaint the + // nonclient area to reflect the state change. + if ( lParam != -1 && + ( Flags = NC_DoNCActive(Wnd)) != 0 ) + { + HDC hDC; + HRGN hRgnTemp = NULL, hRgn = (HRGN)lParam; + + if (GreIsHandleValid(hRgn)) + { + hRgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0); + if (NtGdiCombineRgn(hRgnTemp, hRgn, 0, RGN_COPY) == ERROR) + { + GreDeleteObject(hRgnTemp); + hRgnTemp = NULL; + } + } + + if ((hDC = UserGetDCEx(Wnd, hRgnTemp, DCX_WINDOW|DCX_USESTYLE))) + { + NC_DoNCPaint(Wnd, hDC, wParam | Flags); // Redraw MENUs. + UserReleaseDC(Wnd, hDC, FALSE); + } + else + GreDeleteObject(hRgnTemp); + } + + return TRUE; +} + +VOID +NC_DoButton(PWND pWnd, WPARAM wParam, LPARAM lParam) +{ + MSG Msg; + HDC WindowDC; + BOOL Pressed = TRUE, OldState; + WPARAM SCMsg; + PMENU SysMenu; + ULONG ButtonType; + DWORD Style; + UINT MenuState; + + Style = pWnd->style; + switch (wParam) + { + case HTCLOSE: + SysMenu = IntGetSystemMenu(pWnd, FALSE); + MenuState = IntGetMenuState(UserHMGetHandle(SysMenu), SC_CLOSE, MF_BYCOMMAND); /* in case of error MenuState==0xFFFFFFFF */ + if (!(Style & WS_SYSMENU) || (MenuState & (MF_GRAYED|MF_DISABLED)) || (pWnd->style & CS_NOCLOSE)) + return; + ButtonType = DFCS_CAPTIONCLOSE; + SCMsg = SC_CLOSE; + break; + case HTMINBUTTON: + if (!(Style & WS_MINIMIZEBOX)) + return; + ButtonType = DFCS_CAPTIONMIN; + SCMsg = ((Style & WS_MINIMIZE) ? SC_RESTORE : SC_MINIMIZE); + break; + case HTMAXBUTTON: + if (!(Style & WS_MAXIMIZEBOX)) + return; + ButtonType = DFCS_CAPTIONMAX; + SCMsg = ((Style & WS_MAXIMIZE) ? SC_RESTORE : SC_MAXIMIZE); + break; + + default: + ASSERT(FALSE); + return; + } + + /* + * FIXME: Not sure where to do this, but we must flush the pending + * window updates when someone clicks on the close button and at + * the same time the window is overlapped with another one. This + * looks like a good place for now... + */ + co_IntUpdateWindows(pWnd, RDW_ALLCHILDREN, FALSE); + + WindowDC = UserGetWindowDC(pWnd); + UserDrawCaptionButtonWnd(pWnd, WindowDC, TRUE, ButtonType); + + co_UserSetCapture(UserHMGetHandle(pWnd)); + + for (;;) + { + if (co_IntGetPeekMessage(&Msg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE, TRUE) <= 0) + break; + if (IntCallMsgFilter( &Msg, MSGF_MAX )) continue; + + if (Msg.message == WM_LBUTTONUP) + break; + + if (Msg.message != WM_MOUSEMOVE) + continue; + + OldState = Pressed; + Pressed = (GetNCHitEx(pWnd, Msg.pt) == wParam); + if (Pressed != OldState) + UserDrawCaptionButtonWnd(pWnd, WindowDC, Pressed, ButtonType); + } + + if (Pressed) + UserDrawCaptionButtonWnd(pWnd, WindowDC, FALSE, ButtonType); + IntReleaseCapture(); + UserReleaseDC(pWnd, WindowDC, FALSE); + if (Pressed) + co_IntSendMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, SCMsg, SCMsg == SC_CLOSE ? lParam : MAKELONG(Msg.pt.x,Msg.pt.y)); +} + + +LRESULT +NC_HandleNCLButtonDown(PWND pWnd, WPARAM wParam, LPARAM lParam) +{ + switch (wParam) + { + case HTCAPTION: + { + PWND TopWnd = pWnd, parent; + while(1) + { + if ((TopWnd->style & (WS_POPUP|WS_CHILD)) != WS_CHILD) + break; + parent = UserGetAncestor( TopWnd, GA_PARENT ); + if (!parent || parent == UserGetDesktopWindow()) break; + TopWnd = parent; + } + + if ( co_IntSetForegroundWindowMouse(TopWnd) || + //NtUserCallHwndLock(hTopWnd, HWNDLOCK_ROUTINE_SETFOREGROUNDWINDOWMOUSE) || + UserGetActiveWindow() == UserHMGetHandle(TopWnd)) + { + co_IntSendMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, SC_MOVE + HTCAPTION, lParam); + } + break; + } + case HTSYSMENU: + { + LONG style = pWnd->style; + if (style & WS_SYSMENU) + { + if(!(style & WS_MINIMIZE) ) + { + RECT rect; + HDC hDC = UserGetWindowDC(pWnd); + UserGetInsideRectNC(pWnd, &rect); + UserDrawSysMenuButton(pWnd, hDC, &rect, TRUE); + UserReleaseDC( pWnd, hDC, FALSE ); + } + co_IntSendMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, SC_MOUSEMENU + HTSYSMENU, lParam); + } + break; + } + case HTMENU: + { + co_IntSendMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, SC_MOUSEMENU + HTMENU, lParam); + break; + } + case HTHSCROLL: + { + co_IntSendMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, SC_HSCROLL + HTHSCROLL, lParam); + break; + } + case HTVSCROLL: + { + co_IntSendMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, SC_VSCROLL + HTVSCROLL, lParam); + break; + } + case HTMINBUTTON: + case HTMAXBUTTON: + case HTCLOSE: + { + NC_DoButton(pWnd, wParam, lParam); + break; + } + case HTLEFT: + case HTRIGHT: + case HTTOP: + case HTBOTTOM: + case HTTOPLEFT: + case HTTOPRIGHT: + case HTBOTTOMLEFT: + case HTBOTTOMRIGHT: + { + /* Old comment: + * "make sure hittest fits into 0xf and doesn't overlap with HTSYSMENU" + * This was previously done by setting wParam=SC_SIZE + wParam - 2 + */ + /* But that is not what WinNT does. Instead it sends this. This + * is easy to differentiate from HTSYSMENU, because HTSYSMENU adds + * SC_MOUSEMENU into wParam. + */ + co_IntSendMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, SC_SIZE + wParam - (HTLEFT - WMSZ_LEFT), lParam); + break; + } + case HTBORDER: + break; + } + return(0); +} + + +LRESULT +NC_HandleNCLButtonDblClk(PWND pWnd, WPARAM wParam, LPARAM lParam) +{ + ULONG Style; + + Style = pWnd->style; + switch(wParam) + { + case HTCAPTION: + { + /* Maximize/Restore the window */ + if((Style & WS_CAPTION) == WS_CAPTION && (Style & WS_MAXIMIZEBOX)) + { + co_IntSendMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, ((Style & (WS_MINIMIZE | WS_MAXIMIZE)) ? SC_RESTORE : SC_MAXIMIZE), 0); + } + break; + } + case HTSYSMENU: + { + PMENU SysMenu = IntGetSystemMenu(pWnd, FALSE); + UINT state = IntGetMenuState(UserHMGetHandle(SysMenu), SC_CLOSE, MF_BYCOMMAND); + + /* If the close item of the sysmenu is disabled or not present do nothing */ + if ((state & (MF_DISABLED | MF_GRAYED)) || (state == 0xFFFFFFFF)) + break; + + co_IntSendMessage(UserHMGetHandle(pWnd), WM_SYSCOMMAND, SC_CLOSE, lParam); + break; + } + default: + return NC_HandleNCLButtonDown(pWnd, wParam, lParam); + } + return(0); +} + +/*********************************************************************** + * NC_HandleNCRButtonDown + * + * Handle a WM_NCRBUTTONDOWN message. Called from DefWindowProc(). + */ +LRESULT NC_HandleNCRButtonDown( PWND pwnd, WPARAM wParam, LPARAM lParam ) +{ + MSG msg; + INT hittest = wParam; + + switch (hittest) + { + case HTCAPTION: + case HTSYSMENU: + if (!IntGetSystemMenu( pwnd, FALSE )) break; + + co_UserSetCapture( UserHMGetHandle(pwnd) ); + for (;;) + { + if (!co_IntGetPeekMessage(&msg, 0, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE, TRUE)) break; + if (IntCallMsgFilter( &msg, MSGF_MAX )) continue; + if (msg.message == WM_RBUTTONUP) + { + hittest = GetNCHitEx( pwnd, msg.pt ); + break; + } + if (UserHMGetHandle(pwnd) != IntGetCapture()) return 0; + } + IntReleaseCapture(); + if (hittest == HTCAPTION || hittest == HTSYSMENU || hittest == HTHSCROLL || hittest == HTVSCROLL) + { + TRACE("Msg pt %x and Msg.lParam %x and lParam %x\n",MAKELONG(msg.pt.x,msg.pt.y),msg.lParam,lParam); + co_IntSendMessage( UserHMGetHandle(pwnd), WM_CONTEXTMENU, (WPARAM)UserHMGetHandle(pwnd), MAKELONG(msg.pt.x,msg.pt.y)); + } + break; + } + return 0; +} + + DWORD FASTCALL GetNCHitEx(PWND pWnd, POINT pt) { diff --git a/reactos/win32ss/user/ntuser/ntstubs.c b/reactos/win32ss/user/ntuser/ntstubs.c index 4338a89dae4..ab31fb8a4bc 100644 --- a/reactos/win32ss/user/ntuser/ntstubs.c +++ b/reactos/win32ss/user/ntuser/ntstubs.c @@ -921,23 +921,6 @@ NtUserCtxDisplayIOCtl( return 0; } -/* - * @unimplemented - */ -DWORD -APIENTRY -NtUserDrawMenuBarTemp( - HWND hWnd, - HDC hDC, - PRECT hRect, - HMENU hMenu, - HFONT hFont) -{ - /* We'll use this function just for caching the menu bar */ - STUB - return 0; -} - /* * FillWindow: Called from User; Dialog, Edit and ListBox procs during a WM_ERASEBKGND. */ @@ -1013,35 +996,6 @@ NtUserUpdateLayeredWindow( return 0; } -/* - * @unimplemented - */ -BOOL APIENTRY -NtUserEndMenu(VOID) -{ - STUB - - return 0; -} - -/* - * @implemented - */ -/* NOTE: unused function */ -BOOL APIENTRY -NtUserTrackPopupMenuEx( - HMENU hMenu, - UINT fuFlags, - int x, - int y, - HWND hWnd, - LPTPMPARAMS lptpm) -{ - STUB - - return FALSE; -} - DWORD APIENTRY NtUserQuerySendMessage(DWORD Unknown0) { diff --git a/reactos/win32ss/user/ntuser/painting.c b/reactos/win32ss/user/ntuser/painting.c index cf90a7ca9f6..1a8d63bfac2 100644 --- a/reactos/win32ss/user/ntuser/painting.c +++ b/reactos/win32ss/user/ntuser/painting.c @@ -1865,8 +1865,6 @@ CLEANUP: END_CLEANUP; } -static const WCHAR ELLIPSISW[] = {'.','.','.', 0}; - BOOL UserDrawCaptionText( PWND pWnd, @@ -1884,7 +1882,6 @@ UserDrawCaptionText( SIZE Size; BOOL Ret = TRUE; ULONG fit = 0, Length; - WCHAR szText[128]; RECTL r = *lpRc; TRACE("UserDrawCaptionText: %wZ\n", Text); @@ -1936,26 +1933,33 @@ UserDrawCaptionText( GreGetTextExtentExW(hDc, Text->Buffer, Text->Length/sizeof(WCHAR), r.right - r.left, &fit, 0, &Size, 0); Length = (Text->Length/sizeof(WCHAR) == fit ? fit : fit+1); - - RtlZeroMemory(&szText, sizeof(szText)); - RtlCopyMemory(&szText, Text->Buffer, Text->Length); - if (Text->Length/sizeof(WCHAR) > Length && Length > 3) + if (Text->Length/sizeof(WCHAR) > Length) { - RtlCopyMemory(&szText[Length-3], ELLIPSISW, sizeof(ELLIPSISW)); Ret = FALSE; } - GreExtTextOutW( hDc, - lpRc->left, - lpRc->top + (lpRc->bottom - lpRc->top) / 2 - Size.cy / 2, // DT_SINGLELINE && DT_VCENTER - ETO_CLIPPED, - (RECTL *)lpRc, - (LPWSTR)&szText, - Length, - NULL, - 0 ); - + if (Ret) + { // Faster while in setup. + GreExtTextOutW( hDc, + lpRc->left, + lpRc->top + (lpRc->bottom - lpRc->top) / 2 - Size.cy / 2, // DT_SINGLELINE && DT_VCENTER + ETO_CLIPPED, + (RECTL *)lpRc, + Text->Buffer, + Length, + NULL, + 0 ); + } + else + { + DrawTextW( hDc, + Text->Buffer, + Text->Length/sizeof(WCHAR), + (RECTL *)&r, + DT_END_ELLIPSIS|DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX|DT_LEFT); + } + IntGdiSetTextColor(hDc, OldTextColor); if (hOldFont) @@ -2209,8 +2213,26 @@ NtUserDrawCaptionTemp( if (str != NULL) Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, &SafeStr, uFlags); else + { + if ( RECTL_bIsEmptyRect(&SafeRect) && hFont == 0 && hIcon == 0 ) + { + Ret = TRUE; + if (uFlags & DC_DRAWCAPTIONMD) + { + ERR("NC Caption Mode\n"); + UserDrawCaptionBar(pWnd, hDC, uFlags); + goto Exit; + } + else if (uFlags & DC_DRAWFRAMEMD) + { + ERR("NC Paint Mode\n"); + NC_DoNCPaint(pWnd, hDC, uFlags); // Update Menus too! + goto Exit; + } + } Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, NULL, uFlags); - + } +Exit: UserLeave(); return Ret; } @@ -2222,7 +2244,7 @@ NtUserDrawCaption(HWND hWnd, LPCRECT lpRc, UINT uFlags) { - return NtUserDrawCaptionTemp(hWnd, hDC, lpRc, 0, 0, NULL, uFlags); + return NtUserDrawCaptionTemp(hWnd, hDC, lpRc, 0, 0, NULL, uFlags); } BOOL diff --git a/reactos/win32ss/user/ntuser/painting.h b/reactos/win32ss/user/ntuser/painting.h index 307ddbda5c8..e70183b6e6e 100644 --- a/reactos/win32ss/user/ntuser/painting.h +++ b/reactos/win32ss/user/ntuser/painting.h @@ -33,4 +33,6 @@ HDC FASTCALL IntBeginPaint(PWND,PPAINTSTRUCT); PCURICON_OBJECT FASTCALL NC_IconForWindow( PWND ); BOOL FASTCALL IntFlashWindowEx(PWND,PFLASHWINFO); BOOL FASTCALL IntIntersectWithParents(PWND, RECTL *); -BOOL FASTCALL IntIsWindowDrawable(PWND); \ No newline at end of file +BOOL FASTCALL IntIsWindowDrawable(PWND); +BOOL UserDrawCaption(PWND,HDC,RECTL*,HFONT,HICON,const PUNICODE_STRING,UINT); + diff --git a/reactos/win32ss/user/ntuser/scroll.h b/reactos/win32ss/user/ntuser/scroll.h index 57423c43136..6fe9c5d05da 100644 --- a/reactos/win32ss/user/ntuser/scroll.h +++ b/reactos/win32ss/user/ntuser/scroll.h @@ -39,5 +39,11 @@ typedef struct _SBINFOEX #define SBOBJ_TO_SBID(Obj) ((Obj) - OBJID_HSCROLL) #define SBID_IS_VALID(id) (id == SB_HORZ || id == SB_VERT || id == SB_CTL) -BOOL FASTCALL co_IntCreateScrollBars(PWND Window); -BOOL FASTCALL IntDestroyScrollBars(PWND Window); +BOOL FASTCALL co_IntCreateScrollBars(PWND); +BOOL FASTCALL IntDestroyScrollBars(PWND); +DWORD FASTCALL co_UserShowScrollBar(PWND,int,BOOL,BOOL); +BOOL FASTCALL co_IntGetScrollBarInfo(PWND,LONG,PSCROLLBARINFO); +BOOL FASTCALL co_IntSetScrollBarInfo(PWND,LONG,PSETSCROLLBARINFO); +void IntDrawScrollBar(PWND,HDC,INT); +BOOL FASTCALL IntScrollWindow(PWND,int,int,CONST RECT*,CONST RECT*); +DWORD FASTCALL IntScrollWindowEx(PWND,INT,INT,const RECT*,const RECT*,HRGN,LPRECT,UINT); diff --git a/reactos/win32ss/user/ntuser/scrollbar.c b/reactos/win32ss/user/ntuser/scrollbar.c index 1cdff4ad0f5..462690b2945 100644 --- a/reactos/win32ss/user/ntuser/scrollbar.c +++ b/reactos/win32ss/user/ntuser/scrollbar.c @@ -10,6 +10,32 @@ #include DBG_DEFAULT_CHANNEL(UserScrollbar); +/* Definitions for scrollbar hit testing [See SCROLLBARINFO in MSDN] */ +#define SCROLL_NOWHERE 0x00 /* Outside the scroll bar */ +#define SCROLL_TOP_ARROW 0x01 /* Top or left arrow */ +#define SCROLL_TOP_RECT 0x02 /* Rectangle between the top arrow and the thumb */ +#define SCROLL_THUMB 0x03 /* Thumb rectangle */ +#define SCROLL_BOTTOM_RECT 0x04 /* Rectangle between the thumb and the bottom arrow */ +#define SCROLL_BOTTOM_ARROW 0x05 /* Bottom or right arrow */ + +#define SCROLL_FIRST_DELAY 200 /* Delay (in ms) before first repetition when + holding the button down */ +#define SCROLL_REPEAT_DELAY 50 /* Delay (in ms) between scroll repetitions */ + +#define SCROLL_TIMER 0 /* Scroll timer id */ + + /* Minimum size of the rectangle between the arrows */ +#define SCROLL_MIN_RECT 4 + + /* Minimum size of the thumb in pixels */ +#define SCROLL_MIN_THUMB 6 + + /* Overlap between arrows and thumb */ +#define SCROLL_ARROW_THUMB_OVERLAP 0 + +// +// +// #define MINTRACKTHUMB 8 /* Minimum size of the rectangle between the arrows */ /* What to do after SetScrollInfo() */ @@ -554,6 +580,39 @@ co_IntGetScrollBarInfo(PWND Window, LONG idObject, PSCROLLBARINFO psbi) return TRUE; } +BOOL FASTCALL +co_IntSetScrollBarInfo(PWND Window, LONG idObject, PSETSCROLLBARINFO psbi) +{ + INT Bar; + PSCROLLBARINFO sbi; + LPSCROLLINFO psi; + ASSERT_REFS_CO(Window); + + Bar = SBOBJ_TO_SBID(idObject); + + if(!SBID_IS_VALID(Bar)) + { + EngSetLastError(ERROR_INVALID_PARAMETER); + ERR("Trying to get scrollinfo for unknown scrollbar type %d\n", Bar); + return FALSE; + } + + if(!co_IntCreateScrollBars(Window)) + { + ERR("Failed to create scrollbars for window.\n"); + return FALSE; + } + + sbi = IntGetScrollbarInfoFromWindow(Window, Bar); + psi = IntGetScrollInfoFromWindow(Window, Bar); + + psi->nTrackPos = psbi->nTrackPos; + sbi->reserved = psbi->reserved; + RtlCopyMemory(&sbi->rgstate, &psbi->rgstate, sizeof(psbi->rgstate)); + + return TRUE; +} + BOOL FASTCALL co_IntCreateScrollBars(PWND Window) { @@ -709,6 +768,248 @@ co_UserShowScrollBar(PWND Wnd, int nBar, BOOL fShowH, BOOL fShowV) return FALSE; } +static void +IntDrawScrollInterior(PWND pWnd, HDC hDC, INT nBar, BOOL Vertical, PSCROLLBARINFO ScrollBarInfo) +{ + INT ThumbSize = ScrollBarInfo->xyThumbBottom - ScrollBarInfo->xyThumbTop; + INT ThumbTop = ScrollBarInfo->xyThumbTop; + RECT Rect; + HBRUSH hSaveBrush, hBrush; + BOOL TopSelected = FALSE, BottomSelected = FALSE; + + if (ScrollBarInfo->rgstate[SCROLL_TOP_RECT] & STATE_SYSTEM_PRESSED) + TopSelected = TRUE; + if (ScrollBarInfo->rgstate[SCROLL_BOTTOM_RECT] & STATE_SYSTEM_PRESSED) + BottomSelected = TRUE; + + /* + * Only scrollbar controls send WM_CTLCOLORSCROLLBAR. + * The window-owned scrollbars need to call DefWndControlColor + * to correctly setup default scrollbar colors + */ + if (nBar == SB_CTL) + { + hBrush = GetControlBrush( pWnd, hDC, WM_CTLCOLORSCROLLBAR); + if (!hBrush) + hBrush = IntGetSysColorBrush(COLOR_SCROLLBAR); + } + else + { + hBrush = DefWndControlColor(hDC, CTLCOLOR_SCROLLBAR); + } + + hSaveBrush = NtGdiSelectBrush(hDC, hBrush); + + /* Calculate the scroll rectangle */ + if (Vertical) + { + Rect.top = ScrollBarInfo->rcScrollBar.top + ScrollBarInfo->dxyLineButton; + Rect.bottom = ScrollBarInfo->rcScrollBar.bottom - ScrollBarInfo->dxyLineButton; + Rect.left = ScrollBarInfo->rcScrollBar.left; + Rect.right = ScrollBarInfo->rcScrollBar.right; + } + else + { + Rect.top = ScrollBarInfo->rcScrollBar.top; + Rect.bottom = ScrollBarInfo->rcScrollBar.bottom; + Rect.left = ScrollBarInfo->rcScrollBar.left + ScrollBarInfo->dxyLineButton; + Rect.right = ScrollBarInfo->rcScrollBar.right - ScrollBarInfo->dxyLineButton; + } + + /* Draw the scroll rectangles and thumb */ + if (!ScrollBarInfo->xyThumbBottom) + { + NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right - Rect.left, + Rect.bottom - Rect.top, PATCOPY); + + /* Cleanup and return */ + NtGdiSelectBrush(hDC, hSaveBrush); + return; + } + + ThumbTop -= ScrollBarInfo->dxyLineButton; + + if (ScrollBarInfo->dxyLineButton) + { + if (Vertical) + { + if (ThumbSize) + { + NtGdiPatBlt(hDC, Rect.left, Rect.top, Rect.right - Rect.left, + ThumbTop, TopSelected ? BLACKNESS : PATCOPY); + Rect.top += ThumbTop; + NtGdiPatBlt(hDC, Rect.left, Rect.top + ThumbSize, Rect.right - Rect.left, + Rect.bottom - Rect.top - ThumbSize, BottomSelected ? BLACKNESS : PATCOPY); + Rect.bottom = Rect.top + ThumbSize; + } + else + { + if (ThumbTop) + { + NtGdiPatBlt(hDC, Rect.left, ScrollBarInfo->dxyLineButton, + Rect.right - Rect.left, Rect.bottom - Rect.top, PATCOPY); + } + } + } + else + { + if (ThumbSize) + { + NtGdiPatBlt(hDC, Rect.left, Rect.top, ThumbTop, + Rect.bottom - Rect.top, TopSelected ? BLACKNESS : PATCOPY); + Rect.left += ThumbTop; + NtGdiPatBlt(hDC, Rect.left + ThumbSize, Rect.top, + Rect.right - Rect.left - ThumbSize, Rect.bottom - Rect.top, + BottomSelected ? BLACKNESS : PATCOPY); + Rect.right = Rect.left + ThumbSize; + } + else + { + if (ThumbTop) + { + NtGdiPatBlt(hDC, ScrollBarInfo->dxyLineButton, Rect.top, + Rect.right - Rect.left, Rect.bottom - Rect.top, PATCOPY); + } + } + } + } + + /* Draw the thumb */ + if (ThumbSize) + DrawEdge(hDC, &Rect, EDGE_RAISED, BF_RECT | BF_MIDDLE); + + /* Cleanup */ + NtGdiSelectBrush(hDC, hSaveBrush); +} + + +static VOID FASTCALL +IntDrawScrollArrows(HDC hDC, PSCROLLBARINFO ScrollBarInfo, BOOL Vertical) +{ + RECT RectLT, RectRB; + INT ScrollDirFlagLT, ScrollDirFlagRB; + + RectLT = RectRB = ScrollBarInfo->rcScrollBar; + if (Vertical) + { + ScrollDirFlagLT = DFCS_SCROLLUP; + ScrollDirFlagRB = DFCS_SCROLLDOWN; + RectLT.bottom = RectLT.top + ScrollBarInfo->dxyLineButton; + RectRB.top = RectRB.bottom - ScrollBarInfo->dxyLineButton; + } + else + { + ScrollDirFlagLT = DFCS_SCROLLLEFT; + ScrollDirFlagRB = DFCS_SCROLLRIGHT; + RectLT.right = RectLT.left + ScrollBarInfo->dxyLineButton; + RectRB.left = RectRB.right - ScrollBarInfo->dxyLineButton; + } + + if (ScrollBarInfo->rgstate[SCROLL_TOP_ARROW] & STATE_SYSTEM_PRESSED) + { + ScrollDirFlagLT |= DFCS_PUSHED | DFCS_FLAT; + } + if (ScrollBarInfo->rgstate[SCROLL_TOP_ARROW] & STATE_SYSTEM_UNAVAILABLE) + { + ScrollDirFlagLT |= DFCS_INACTIVE; + } + if (ScrollBarInfo->rgstate[SCROLL_BOTTOM_ARROW] & STATE_SYSTEM_PRESSED) + { + ScrollDirFlagRB |= DFCS_PUSHED | DFCS_FLAT; + } + if (ScrollBarInfo->rgstate[SCROLL_BOTTOM_ARROW] & STATE_SYSTEM_UNAVAILABLE) + { + ScrollDirFlagRB |= DFCS_INACTIVE; + } + + DrawFrameControl(hDC, &RectLT, DFC_SCROLL, ScrollDirFlagLT); + DrawFrameControl(hDC, &RectRB, DFC_SCROLL, ScrollDirFlagRB); +} + +static LONG FASTCALL +IntScrollGetObjectId(INT SBType) +{ + if (SBType == SB_VERT) + return OBJID_VSCROLL; + if (SBType == SB_HORZ) + return OBJID_HSCROLL; + return OBJID_CLIENT; +} + +void +IntDrawScrollBar(PWND Wnd, HDC DC, INT Bar) +{ + //PSBWND pSBWnd; + //INT ThumbSize; + PTHREADINFO pti; + SCROLLBARINFO Info; + BOOL Vertical; + + pti = PsGetCurrentThreadWin32Thread(); + + /* + * Get scroll bar info. + */ + switch (Bar) + { + case SB_HORZ: + Vertical = FALSE; + break; + + case SB_VERT: + Vertical = TRUE; + break; + + case SB_CTL: + Vertical = (Wnd->style & SBS_VERT) != 0; + break; + + default: + return; + } + + if (!co_IntGetScrollBarInfo(Wnd, IntScrollGetObjectId(Bar), &Info)) + { + return; + } + + if (RECTL_bIsEmptyRect(&Info.rcScrollBar)) + { + return; + } + + //ThumbSize = pSBWnd->pSBCalc->pxThumbBottom - pSBWnd->pSBCalc->pxThumbTop; + + /* + * Draw the arrows. + */ + if (Info.dxyLineButton) + { + IntDrawScrollArrows(DC, &Info, Vertical); + } + + /* + * Draw the interior. + */ + IntDrawScrollInterior(Wnd, DC, Bar, Vertical, &Info); + + /* + * If scroll bar has focus, reposition the caret. + */ + if ( Wnd == pti->MessageQueue->spwndFocus && Bar == SB_CTL ) + { + if (Vertical) + { + co_IntSetCaretPos(Info.rcScrollBar.top + 1, Info.dxyLineButton + 1); + } + else + { + co_IntSetCaretPos(Info.dxyLineButton + 1, Info.rcScrollBar.top + 1); + } + } +} + + LRESULT APIENTRY ScrollBarWndProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) { diff --git a/reactos/win32ss/user/ntuser/scrollex.c b/reactos/win32ss/user/ntuser/scrollex.c index e0cd6359c69..64576ec113a 100644 --- a/reactos/win32ss/user/ntuser/scrollex.c +++ b/reactos/win32ss/user/ntuser/scrollex.c @@ -226,6 +226,241 @@ UserScrollDC( return Result; } +DWORD +FASTCALL +IntScrollWindowEx( + PWND Window, + INT dx, + INT dy, + const RECT *prcScroll, + const RECT *prcClip, + HRGN hrgnUpdate, + LPRECT prcUpdate, + UINT flags) +{ + RECTL rcScroll, rcClip, rcCaret; + INT Result; + PWND CaretWnd; + HDC hDC; + PREGION RgnUpdate = NULL, RgnTemp, RgnWinupd = NULL; + HWND hwndCaret; + DWORD dcxflags = 0; + int rdw_flags; + USER_REFERENCE_ENTRY CaretRef; + + IntGetClientRect(Window, &rcClip); + + if (prcScroll) + { + RECTL_bIntersectRect(&rcScroll, &rcClip, prcScroll); + } + else + rcScroll = rcClip; + + if (prcClip) + { + RECTL_bIntersectRect(&rcClip, &rcClip, prcClip); + } + + if (rcClip.right <= rcClip.left || rcClip.bottom <= rcClip.top || + (dx == 0 && dy == 0)) + { + return NULLREGION; + } + + /* We must use a copy of the region, as we can't hold an exclusive lock + * on it while doing callouts to user-mode */ + RgnUpdate = IntSysCreateRectpRgn(0, 0, 0, 0); + if(!RgnUpdate) + { + EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); + return ERROR; + } + + if (hrgnUpdate) + { + RgnTemp = REGION_LockRgn(hrgnUpdate); + if (!RgnTemp) + { + EngSetLastError(ERROR_INVALID_HANDLE); + return ERROR; + } + IntGdiCombineRgn(RgnUpdate, RgnTemp, NULL, RGN_COPY); + REGION_UnlockRgn(RgnTemp); + } + + /* ScrollWindow uses the window DC, ScrollWindowEx doesn't */ + if (flags & SW_SCROLLWNDDCE) + { + dcxflags = DCX_USESTYLE; + + if (!(Window->pcls->style & (CS_OWNDC|CS_CLASSDC))) + dcxflags |= DCX_CACHE; // AH??? wine~ If not Powned or with Class go Cheap! + + if (flags & SW_SCROLLCHILDREN && Window->style & WS_CLIPCHILDREN) + dcxflags |= DCX_CACHE|DCX_NOCLIPCHILDREN; + } + else + { + /* So in this case ScrollWindowEx uses Cache DC. */ + dcxflags = DCX_CACHE|DCX_USESTYLE; + if (flags & SW_SCROLLCHILDREN) dcxflags |= DCX_NOCLIPCHILDREN; + } + + hDC = UserGetDCEx(Window, 0, dcxflags); + if (!hDC) + { + /* FIXME: SetLastError? */ + return ERROR; + } + + rdw_flags = (flags & SW_ERASE) && (flags & SW_INVALIDATE) ? RDW_INVALIDATE | RDW_ERASE : RDW_INVALIDATE ; + + rcCaret = rcScroll; + hwndCaret = co_IntFixCaret(Window, &rcCaret, flags); + + Result = UserScrollDC( hDC, + dx, + dy, + &rcScroll, + &rcClip, + NULL, + RgnUpdate, + prcUpdate); + + UserReleaseDC(Window, hDC, FALSE); + + /* + * Take into account the fact that some damage may have occurred during + * the scroll. Keep a copy in hrgnWinupd to be added to hrngUpdate at the end. + */ + + RgnTemp = IntSysCreateRectpRgn(0, 0, 0, 0); + if (!RgnTemp) + { + EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); + return ERROR; + } + + if (co_IntGetUpdateRgn(Window, RgnTemp, FALSE) != NULLREGION) + { + PREGION RgnClip = IntSysCreateRectpRgnIndirect(&rcClip); + if (RgnClip) + { + if (hrgnUpdate) + { + RgnWinupd = IntSysCreateRectpRgn( 0, 0, 0, 0); + IntGdiCombineRgn( RgnWinupd, RgnTemp, 0, RGN_COPY); + } + + REGION_bOffsetRgn(RgnTemp, dx, dy); + + IntGdiCombineRgn(RgnTemp, RgnTemp, RgnClip, RGN_AND); + + if (hrgnUpdate) + IntGdiCombineRgn( RgnWinupd, RgnWinupd, RgnTemp, RGN_OR ); + + co_UserRedrawWindow(Window, NULL, RgnTemp, rdw_flags ); + + REGION_Delete(RgnClip); + } + } + REGION_Delete(RgnTemp); + + if (flags & SW_SCROLLCHILDREN) + { + PWND Child; + RECTL rcChild; + POINT ClientOrigin; + USER_REFERENCE_ENTRY WndRef; + RECTL rcDummy; + LPARAM lParam; + + IntGetClientOrigin(Window, &ClientOrigin); + + for (Child = Window->spwndChild; Child; Child = Child->spwndNext) + { + rcChild = Child->rcWindow; + rcChild.left -= ClientOrigin.x; + rcChild.top -= ClientOrigin.y; + rcChild.right -= ClientOrigin.x; + rcChild.bottom -= ClientOrigin.y; + + if (!prcScroll || RECTL_bIntersectRect(&rcDummy, &rcChild, &rcScroll)) + { + UserRefObjectCo(Child, &WndRef); + + if (Window->spwndParent == UserGetDesktopWindow()) // Window->spwndParent->fnid == FNID_DESKTOP ) + lParam = MAKELONG(Child->rcClient.left, Child->rcClient.top); + else + lParam = MAKELONG(rcChild.left + dx, rcChild.top + dy); + + /* wine sends WM_POSCHANGING, WM_POSCHANGED messages */ + /* windows sometimes a WM_MOVE */ + co_IntSendMessage(UserHMGetHandle(Child), WM_MOVE, 0, lParam); + + UserDerefObjectCo(Child); + } + } + } + + if (flags & (SW_INVALIDATE | SW_ERASE)) + { + co_UserRedrawWindow( Window, + NULL, + RgnUpdate, + rdw_flags | /* HACK */ + ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : RDW_NOCHILDREN) ); + } + + if (hwndCaret && (CaretWnd = UserGetWindowObject(hwndCaret))) + { + UserRefObjectCo(CaretWnd, &CaretRef); + + co_IntSetCaretPos(rcCaret.left + dx, rcCaret.top + dy); + co_UserShowCaret(CaretWnd); + + UserDerefObjectCo(CaretWnd); + } + + if (hrgnUpdate && (Result != ERROR)) + { + /* Give everything back to the caller */ + RgnTemp = REGION_LockRgn(hrgnUpdate); + /* The handle should still be valid */ + ASSERT(RgnTemp); + if (RgnWinupd) + IntGdiCombineRgn(RgnTemp, RgnUpdate, RgnWinupd, RGN_OR); + else + IntGdiCombineRgn(RgnTemp, RgnUpdate, NULL, RGN_COPY); + REGION_UnlockRgn(RgnTemp); + } + + if (RgnWinupd) + { + REGION_Delete(RgnWinupd); + } + + if (RgnUpdate) + { + REGION_Delete(RgnUpdate); + } + + return Result; +} + + +BOOL FASTCALL +IntScrollWindow(PWND pWnd, + int dx, + int dy, + CONST RECT *lpRect, + CONST RECT *prcClip) +{ + return IntScrollWindowEx( pWnd, dx, dy, lpRect, prcClip, 0, NULL, + (lpRect ? 0 : SW_SCROLLCHILDREN) | (SW_ERASE|SW_INVALIDATE|SW_SCROLLWNDDCE)) != ERROR; +} + /* * NtUserScrollDC * diff --git a/reactos/win32ss/user/ntuser/userfuncs.h b/reactos/win32ss/user/ntuser/userfuncs.h index 2f0e3869a7a..2addad1176b 100644 --- a/reactos/win32ss/user/ntuser/userfuncs.h +++ b/reactos/win32ss/user/ntuser/userfuncs.h @@ -2,7 +2,12 @@ FORCEINLINE PMENU UserGetMenuObject(HMENU hMenu) { - return UserGetObject(gHandleTable, hMenu, TYPE_MENU); + PMENU pMenu = UserGetObject(gHandleTable, hMenu, TYPE_MENU); + if (!pMenu) + { + EngSetLastError(ERROR_INVALID_MENU_HANDLE); + } + return pMenu; } #define ASSERT_REFS_CO(_obj_) \ @@ -97,14 +102,11 @@ UserSystemParametersInfo( VOID FASTCALL IntSetWindowState(PWND, UINT); VOID FASTCALL IntClearWindowState(PWND, UINT); PTHREADINFO FASTCALL IntTID2PTI(HANDLE); +HBRUSH FASTCALL GetControlBrush(PWND pwnd,HDC hdc,UINT ctlType); /*************** MESSAGE.C ***************/ -BOOL FASTCALL -UserPostMessage(HWND Wnd, - UINT Msg, - WPARAM wParam, - LPARAM lParam); +BOOL FASTCALL UserPostMessage(HWND Wnd,UINT Msg, WPARAM wParam, LPARAM lParam); /*************** WINDOW.C ***************/ @@ -114,6 +116,8 @@ HWND FASTCALL UserGetShellWindow(VOID); HDC FASTCALL UserGetDCEx(PWND Window OPTIONAL, HANDLE ClipRegion, ULONG Flags); BOOLEAN co_UserDestroyWindow(PVOID Object); PWND FASTCALL UserGetAncestor(PWND Wnd, UINT Type); +BOOL APIENTRY DefSetText(PWND Wnd, PCWSTR WindowText); +DWORD FASTCALL IntGetWindowContextHelpId( PWND pWnd ); /*************** MENU.C ***************/ @@ -121,12 +125,24 @@ HMENU FASTCALL UserCreateMenu(PDESKTOP Desktop, BOOL PopupMenu); BOOL FASTCALL UserSetMenuDefaultItem(PMENU Menu, UINT uItem, UINT fByPos); BOOL FASTCALL UserDestroyMenu(HMENU hMenu); -/*************** SCROLLBAR.C ***************/ - -DWORD FASTCALL co_UserShowScrollBar(PWND Wnd, int nBar, BOOL fShowH, BOOL fShowV); - /************** NONCLIENT **************/ VOID FASTCALL DefWndDoSizeMove(PWND pwnd, WORD wParam); +LRESULT NC_DoNCPaint(PWND,HDC,INT); +void FASTCALL NC_GetSysPopupPos(PWND, RECT *); +LRESULT NC_HandleNCActivate( PWND Wnd, WPARAM wParam, LPARAM lParam ); +LRESULT NC_HandleNCCalcSize( PWND wnd, WPARAM wparam, RECTL *winRect ); +VOID NC_DrawFrame( HDC hDC, RECT *CurrentRect, BOOL Active, DWORD Style, DWORD ExStyle); +VOID UserDrawCaptionBar( PWND pWnd, HDC hDC, INT Flags); +void UserGetInsideRectNC(PWND Wnd, RECT *rect); +LRESULT NC_HandleNCLButtonDown(PWND Wnd, WPARAM wParam, LPARAM lParam); +LRESULT NC_HandleNCLButtonDblClk(PWND Wnd, WPARAM wParam, LPARAM lParam); +LRESULT NC_HandleNCRButtonDown( PWND wnd, WPARAM wParam, LPARAM lParam ); + +/************** DEFWND **************/ + +HBRUSH FASTCALL DefWndControlColor(HDC hDC,UINT ctlType); +BOOL UserDrawSysMenuButton(PWND pWnd, HDC hDC, LPRECT Rect, BOOL Down); +BOOL UserPaintCaption(PWND pWnd, INT Flags); /* EOF */ diff --git a/reactos/win32ss/user/ntuser/win32.h b/reactos/win32ss/user/ntuser/win32.h index 2dad834bbaa..ac5053cc4b0 100644 --- a/reactos/win32ss/user/ntuser/win32.h +++ b/reactos/win32ss/user/ntuser/win32.h @@ -52,8 +52,6 @@ extern HANDLE hModuleWin; // This Win32k Instance. extern struct _CLS *SystemClassList; extern BOOL RegisteredSysClasses; -typedef struct tagMENUSTATE MENUSTATE, *PMENUSTATE; - #include // FIXME: Move to ntuser.h typedef struct _TL @@ -110,7 +108,7 @@ typedef struct _THREADINFO HDESK hdesk; UINT cPaintsReady; /* Count of paints pending. */ UINT cTimersReady; /* Count of timers pending. */ - PMENUSTATE pMenuState; + struct tagMENUSTATE* pMenuState; DWORD dwExpWinVer; DWORD dwCompatFlags; DWORD dwCompatFlags2; diff --git a/reactos/win32ss/user/ntuser/window.c b/reactos/win32ss/user/ntuser/window.c index 6de81b44b17..d63ec08c17a 100644 --- a/reactos/win32ss/user/ntuser/window.c +++ b/reactos/win32ss/user/ntuser/window.c @@ -65,9 +65,12 @@ PWND FASTCALL VerifyWnd(PWND pWnd) { HWND hWnd; UINT State, State2; + ULONG Error; if (!pWnd) return NULL; + Error = EngGetLastError(); + _SEH2_TRY { hWnd = UserHMGetHandle(pWnd); @@ -76,6 +79,7 @@ PWND FASTCALL VerifyWnd(PWND pWnd) } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { + EngSetLastError(Error); _SEH2_YIELD(return NULL); } _SEH2_END @@ -83,8 +87,9 @@ PWND FASTCALL VerifyWnd(PWND pWnd) if ( UserObjectInDestroy(hWnd) || State & WNDS_DESTROYED || State2 & WNDS2_INDESTROY ) - return NULL; + pWnd = NULL; + EngSetLastError(Error); return pWnd; } @@ -362,6 +367,20 @@ IntGetWindow(HWND hWnd, return Ret; } +DWORD FASTCALL IntGetWindowContextHelpId( PWND pWnd ) +{ + PPROPERTY HelpId; + + do + { + HelpId = IntGetProp(pWnd, gpsi->atomContextHelpIdProp); + if (!HelpId) break; + pWnd = IntGetParent(pWnd); + } + while (pWnd && pWnd->fnid != FNID_DESKTOP); + return (DWORD) (HelpId ? HelpId->Data : 0 ); +} + /*********************************************************************** * IntSendDestroyMsg */ @@ -593,6 +612,7 @@ LRESULT co_UserFreeWindow(PWND Window, Window->IDMenu && (Menu = UserGetMenuObject((HMENU)Window->IDMenu))) { + TRACE("UFW: IDMenu %p\n",Window->IDMenu); IntDestroyMenuObject(Menu, TRUE); Window->IDMenu = 0; } @@ -3497,7 +3517,7 @@ co_IntSetWindowLong(HWND hWnd, DWORD Index, LONG NewValue, BOOL Ansi, BOOL bAlte Ansi); if (!OldValue) return 0; } -*/ + */ *((LONG *)((PCHAR)(Window + 1) + Index)) = NewValue; } else @@ -3913,6 +3933,78 @@ CLEANUP: END_CLEANUP; } +BOOL APIENTRY +DefSetText(PWND Wnd, PCWSTR WindowText) +{ + UNICODE_STRING UnicodeString; + BOOL Ret = FALSE; + + RtlInitUnicodeString(&UnicodeString, WindowText); + + if (UnicodeString.Length != 0) + { + if (Wnd->strName.MaximumLength > 0 && + UnicodeString.Length <= Wnd->strName.MaximumLength - sizeof(UNICODE_NULL)) + { + ASSERT(Wnd->strName.Buffer != NULL); + + Wnd->strName.Length = UnicodeString.Length; + Wnd->strName.Buffer[UnicodeString.Length / sizeof(WCHAR)] = L'\0'; + RtlCopyMemory(Wnd->strName.Buffer, + UnicodeString.Buffer, + UnicodeString.Length); + } + else + { + PWCHAR buf; + Wnd->strName.MaximumLength = Wnd->strName.Length = 0; + buf = Wnd->strName.Buffer; + Wnd->strName.Buffer = NULL; + if (buf != NULL) + { + DesktopHeapFree(Wnd->head.rpdesk, buf); + } + + Wnd->strName.Buffer = DesktopHeapAlloc(Wnd->head.rpdesk, + UnicodeString.Length + sizeof(UNICODE_NULL)); + if (Wnd->strName.Buffer != NULL) + { + Wnd->strName.Buffer[UnicodeString.Length / sizeof(WCHAR)] = L'\0'; + RtlCopyMemory(Wnd->strName.Buffer, + UnicodeString.Buffer, + UnicodeString.Length); + Wnd->strName.MaximumLength = UnicodeString.Length + sizeof(UNICODE_NULL); + Wnd->strName.Length = UnicodeString.Length; + } + else + { + EngSetLastError(ERROR_NOT_ENOUGH_MEMORY); + goto Exit; + } + } + } + else + { + Wnd->strName.Length = 0; + if (Wnd->strName.Buffer != NULL) + Wnd->strName.Buffer[0] = L'\0'; + } + + // FIXME: HAX! Windows does not do this in here! + // In User32, these are called after: NotifyWinEvent EVENT_OBJECT_NAMECHANGE than + // RepaintButton, StaticRepaint, NtUserCallHwndLock HWNDLOCK_ROUTINE_REDRAWFRAMEANDHOOK, etc. + /* Send shell notifications */ + if (!Wnd->spwndOwner && !IntGetParent(Wnd)) + { + co_IntShellHookNotify(HSHELL_REDRAW, (WPARAM) UserHMGetHandle(Wnd), FALSE); // FIXME Flashing? + } + + Ret = TRUE; +Exit: + if (UnicodeString.Buffer) RtlFreeUnicodeString(&UnicodeString); + return Ret; +} + /* * NtUserDefSetText * diff --git a/reactos/win32ss/user/ntuser/window.h b/reactos/win32ss/user/ntuser/window.h index 843f6e8506b..d3772060273 100644 --- a/reactos/win32ss/user/ntuser/window.h +++ b/reactos/win32ss/user/ntuser/window.h @@ -55,6 +55,10 @@ PWND FASTCALL IntCreateWindow(CREATESTRUCTW* Cs, PWND OwnerWindow, PVOID acbiBuffer, PDESKTOP pdeskCreated); +PWND FASTCALL co_UserCreateWindowEx(CREATESTRUCTW* Cs, + PUNICODE_STRING ClassName, + PLARGE_STRING WindowName, + PVOID acbiBuffer); BOOL FASTCALL IntEnableWindow(HWND,BOOL); BOOL FASTCALL IntIsWindowVisible(PWND); DWORD FASTCALL GetNCHitEx(PWND,POINT); diff --git a/reactos/win32ss/user/ntuser/winpos.c b/reactos/win32ss/user/ntuser/winpos.c index faf67901d0c..8ba06b38ce3 100644 --- a/reactos/win32ss/user/ntuser/winpos.c +++ b/reactos/win32ss/user/ntuser/winpos.c @@ -2280,7 +2280,7 @@ co_WinPosShowWindow(PWND Wnd, INT Cmd) Cmd = SW_SHOWDEFAULT; } FirstTime = TRUE; - ERR("co_WPSW FT 1\n"); + TRACE("co_WPSW FT 1\n"); } } } @@ -2292,7 +2292,7 @@ co_WinPosShowWindow(PWND Wnd, INT Cmd) { Cmd = pti->ppi->usi.wShowWindow; FirstTime = TRUE; - ERR("co_WPSW FT 2\n"); + TRACE("co_WPSW FT 2\n"); } } diff --git a/reactos/win32ss/user/ntuser/winpos.h b/reactos/win32ss/user/ntuser/winpos.h index 42694eb3bb9..03e1cd8923b 100644 --- a/reactos/win32ss/user/ntuser/winpos.h +++ b/reactos/win32ss/user/ntuser/winpos.h @@ -69,3 +69,5 @@ PWND FASTCALL IntRealChildWindowFromPoint(PWND,LONG,LONG); BOOL FASTCALL IntScreenToClient(PWND,LPPOINT); BOOL FASTCALL IntClientToScreen(PWND,LPPOINT); BOOL FASTCALL IntGetWindowRect(PWND,RECTL*); +BOOL UserHasWindowEdge(DWORD,DWORD); +VOID UserGetWindowBorders(DWORD,DWORD,SIZE*,BOOL); diff --git a/reactos/win32ss/user/ntuser/winsta.c b/reactos/win32ss/user/ntuser/winsta.c index 64b37cd389f..76ffa6d988f 100644 --- a/reactos/win32ss/user/ntuser/winsta.c +++ b/reactos/win32ss/user/ntuser/winsta.c @@ -297,6 +297,9 @@ co_IntInitializeDesktopGraphics(VOID) /* Setup the icons */ co_IntSetWndIcons(); + /* Setup Menu */ + MenuInit(); + /* Show the desktop */ pdesk = IntGetActiveDesktop(); ASSERT(pdesk); diff --git a/reactos/win32ss/user/rtl/text.c b/reactos/win32ss/user/rtl/text.c new file mode 100644 index 00000000000..457a089486f --- /dev/null +++ b/reactos/win32ss/user/rtl/text.c @@ -0,0 +1,1365 @@ +/* + * ReactOS kernel + * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* + * PROJECT: ReactOS user32.dll + * FILE: win32ss/user/rtl/text.c + * PURPOSE: Draw Text + * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net) + * UPDATE HISTORY: + * 09-05-2001 CSH Created + */ + +/* INCLUDES ******************************************************************/ + +#ifdef _WIN32K_ +#include +DBG_DEFAULT_CHANNEL(UserMenu); +#else +#include +#include +WINE_DEFAULT_DEBUG_CHANNEL(text); +#endif + +/* FUNCTIONS *****************************************************************/ + +#ifndef NDEBUG + +#ifdef assert +#undef assert +#endif + +#define assert(e) ((e) ? (void)0 : _font_assert(#e, __FILE__, __LINE__)) + +#else +#include + +#endif + +void _font_assert(const char *msg, const char *file, int line) +{ + /* Assertion failed at foo.c line 45: x 0) + { + RECT r; + INT x0; + x0 = x; + r.left = x0; + /* chop the string into substrings of 0 or more + * possibly followed by 1 or more normal characters */ + for (i = 0; i < count; i++) + if (lpstr[i] != '\t') break; + for (j = i; j < count; j++) + if (lpstr[j] == '\t') break; + /* get the extent of the normal character part */ +#ifdef _WIN32K_ + GreGetTextExtentW( hdc, (LPWSTR)lpstr + i, j - i , &extent, 1 ); +#else + GetTextExtentPointW( hdc, lpstr + i, j - i , &extent ); +#endif + /* and if there is a , calculate its position */ + if( i) { + /* get x coordinate for the drawing of this string */ + for (; cTabStops > i; lpTabPos++, cTabStops--) + { + if( nTabOrg + abs( *lpTabPos) > x) { + if( lpTabPos[ i - 1] >= 0) { + /* a left aligned tab */ + x = nTabOrg + lpTabPos[ i-1] + extent.cx; + break; + } + else + { + /* if tab pos is negative then text is right-aligned + * to tab stop meaning that the string extends to the + * left, so we must subtract the width of the string */ + if (nTabOrg - lpTabPos[ i - 1] - extent.cx > x) + { + x = nTabOrg - lpTabPos[ i - 1]; + x0 = x - extent.cx; + break; + } + } + } + } + /* if we have run out of tab stops and we have a valid default tab + * stop width then round x up to that width */ + if ((cTabStops <= i) && (defWidth > 0)) { + x0 = nTabOrg + ((x - nTabOrg) / defWidth + i) * defWidth; + x = x0 + extent.cx; + } else if ((cTabStops <= i) && (defWidth < 0)) { + x = nTabOrg + ((x - nTabOrg + extent.cx) / -defWidth + i) + * -defWidth; + x0 = x - extent.cx; + } + } else + x += extent.cx; + + if (fDisplayText) + { + r.top = y; + r.right = x; + r.bottom = y + extent.cy; +#ifdef _WIN32K_ + GreExtTextOutW( hdc, x0, y, GreGetBkMode(hdc) == OPAQUE ? ETO_OPAQUE : 0, + &r, (LPWSTR)lpstr + i, j - i, NULL, 0 ); +#else + ExtTextOutW( hdc, x0, y, GetBkMode(hdc) == OPAQUE ? ETO_OPAQUE : 0, + &r, lpstr + i, j - i, NULL ); +#endif + } + count -= j; + lpstr += j; + } + return MAKELONG(x - start, extent.cy); +} + +/********************************************************************* + * + * DrawText functions + * + * Copied from Wine. + * Copyright 1993, 1994 Alexandre Julliard + * Copyright 2002 Bill Medland + * + * Design issues + * How many buffers to use + * While processing in DrawText there are potentially three different forms + * of the text that need to be held. How are they best held? + * 1. The original text is needed, of course, to see what to display. + * 2. The text that will be returned to the user if the DT_MODIFYSTRING is + * in effect. + * 3. The buffered text that is about to be displayed e.g. the current line. + * Typically this will exclude the ampersands used for prefixing etc. + * + * Complications. + * a. If the buffered text to be displayed includes the ampersands then + * we will need special measurement and draw functions that will ignore + * the ampersands (e.g. by copying to a buffer without the prefix and + * then using the normal forms). This may involve less space but may + * require more processing. e.g. since a line containing tabs may + * contain several underlined characters either we need to carry around + * a list of prefix locations or we may need to locate them several + * times. + * b. If we actually directly modify the "original text" as we go then we + * will need some special "caching" to handle the fact that when we + * ellipsify the text the ellipsis may modify the next line of text, + * which we have not yet processed. (e.g. ellipsification of a W at the + * end of a line will overwrite the W, the \n and the first character of + * the next line, and a \0 will overwrite the second. Try it!!) + * + * Option 1. Three separate storages. (To be implemented) + * If DT_MODIFYSTRING is in effect then allocate an extra buffer to hold + * the edited string in some form, either as the string itself or as some + * sort of "edit list" to be applied just before returning. + * Use a buffer that holds the ellipsified current line sans ampersands + * and accept the need occasionally to recalculate the prefixes (if + * DT_EXPANDTABS and not DT_NOPREFIX and not DT_HIDEPREFIX) + */ + +#define TAB 9 +#define LF 10 +#define CR 13 +#define SPACE 32 +#define PREFIX 38 +#define ALPHA_PREFIX 30 /* Win16: Alphabet prefix */ +#define KANA_PREFIX 31 /* Win16: Katakana prefix */ + +#define FORWARD_SLASH '/' +#define BACK_SLASH '\\' + +static const WCHAR ELLIPSISW[] = {'.','.','.', 0}; + +typedef struct tag_ellipsis_data +{ + int before; + int len; + int under; + int after; +} ellipsis_data; + +/********************************************************************* + * TEXT_Ellipsify (static) + * + * Add an ellipsis to the end of the given string whilst ensuring it fits. + * + * If the ellipsis alone doesn't fit then it will be returned anyway. + * + * See Also TEXT_PathEllipsify + * + * Arguments + * hdc [in] The handle to the DC that defines the font. + * str [in/out] The string that needs to be modified. + * max_str [in] The dimension of str (number of WCHAR). + * len_str [in/out] The number of characters in str + * width [in] The maximum width permitted (in logical coordinates) + * size [out] The dimensions of the text + * modstr [out] The modified form of the string, to be returned to the + * calling program. It is assumed that the caller has + * made sufficient space available so we don't need to + * know the size of the space. This pointer may be NULL if + * the modified string is not required. + * len_before [out] The number of characters before the ellipsis. + * len_ellip [out] The number of characters in the ellipsis. + * + * See for example Microsoft article Q249678. + * + * For now we will simply use three dots rather than worrying about whether + * the font contains an explicit ellipsis character. + */ +static void TEXT_Ellipsify (HDC hdc, WCHAR *str, unsigned int max_len, + unsigned int *len_str, int width, SIZE *size, + WCHAR *modstr, + int *len_before, int *len_ellip) +{ + unsigned int len_ellipsis; + unsigned int lo, mid, hi; +#ifdef _WIN32K_ + len_ellipsis = wcslen (ELLIPSISW); +#else + len_ellipsis = strlenW (ELLIPSISW); +#endif + if (len_ellipsis > max_len) len_ellipsis = max_len; + if (*len_str > max_len - len_ellipsis) + *len_str = max_len - len_ellipsis; + + /* First do a quick binary search to get an upper bound for *len_str. */ + if (*len_str > 0 && +#ifdef _WIN32K_ + GreGetTextExtentExW(hdc, str, *len_str, width, NULL, NULL, size, 0) && +#else + GetTextExtentExPointW(hdc, str, *len_str, width, NULL, NULL, size) && +#endif + size->cx > width) + { + for (lo = 0, hi = *len_str; lo < hi; ) + { + mid = (lo + hi) / 2; +#ifdef _WIN32K_ + if (!GreGetTextExtentExW(hdc, str, mid, width, NULL, NULL, size, 0)) +#else + if (!GetTextExtentExPointW(hdc, str, mid, width, NULL, NULL, size)) +#endif + break; + if (size->cx > width) + hi = mid; + else + lo = mid + 1; + } + *len_str = hi; + } + /* Now this should take only a couple iterations at most. */ + for ( ; ; ) + { + memcpy(str + *len_str, ELLIPSISW, len_ellipsis*sizeof(WCHAR)); +#ifdef _WIN32K_ + if (!GreGetTextExtentExW (hdc, str, *len_str + len_ellipsis, width, + NULL, NULL, size, 0)) break; +#else + if (!GetTextExtentExPointW (hdc, str, *len_str + len_ellipsis, width, + NULL, NULL, size)) break; +#endif + if (!*len_str || size->cx <= width) break; + + (*len_str)--; + } + *len_ellip = len_ellipsis; + *len_before = *len_str; + *len_str += len_ellipsis; + + if (modstr) + { + memcpy (modstr, str, *len_str * sizeof(WCHAR)); + modstr[*len_str] = '\0'; + } +} + +/********************************************************************* + * TEXT_PathEllipsify (static) + * + * Add an ellipsis to the provided string in order to make it fit within + * the width. The ellipsis is added as specified for the DT_PATH_ELLIPSIS + * flag. + * + * See Also TEXT_Ellipsify + * + * Arguments + * hdc [in] The handle to the DC that defines the font. + * str [in/out] The string that needs to be modified + * max_str [in] The dimension of str (number of WCHAR). + * len_str [in/out] The number of characters in str + * width [in] The maximum width permitted (in logical coordinates) + * size [out] The dimensions of the text + * modstr [out] The modified form of the string, to be returned to the + * calling program. It is assumed that the caller has + * made sufficient space available so we don't need to + * know the size of the space. This pointer may be NULL if + * the modified string is not required. + * pellip [out] The ellipsification results + * + * For now we will simply use three dots rather than worrying about whether + * the font contains an explicit ellipsis character. + * + * The following applies, I think to Win95. We will need to extend it for + * Win98 which can have both path and end ellipsis at the same time (e.g. + * C:\MyLongFileName.Txt becomes ...\MyLongFileN...) + * + * The resulting string consists of as much as possible of the following: + * 1. The ellipsis itself + * 2. The last \ or / of the string (if any) + * 3. Everything after the last \ or / of the string (if any) or the whole + * string if there is no / or \. I believe that under Win95 this would + * include everything even though some might be clipped off the end whereas + * under Win98 that might be ellipsified too. + * Yet to be investigated is whether this would include wordbreaking if the + * filename is more than 1 word and splitting if DT_EDITCONTROL was in + * effect. (If DT_EDITCONTROL is in effect then on occasions text will be + * broken within words). + * 4. All the stuff before the / or \, which is placed before the ellipsis. + */ +static void TEXT_PathEllipsify (HDC hdc, WCHAR *str, unsigned int max_len, + unsigned int *len_str, int width, SIZE *size, + WCHAR *modstr, ellipsis_data *pellip) +{ + int len_ellipsis; + int len_trailing; + int len_under; + WCHAR *lastBkSlash, *lastFwdSlash, *lastSlash; +#ifdef _WIN32K_ + len_ellipsis = wcslen (ELLIPSISW); +#else + len_ellipsis = strlenW (ELLIPSISW); +#endif + if (!max_len) return; + if (len_ellipsis >= max_len) len_ellipsis = max_len - 1; + if (*len_str + len_ellipsis >= max_len) + *len_str = max_len - len_ellipsis-1; + /* Hopefully this will never happen, otherwise it would probably lose + * the wrong character + */ + str[*len_str] = '\0'; /* to simplify things */ +#ifdef _WIN32K_ + lastBkSlash = wcsrchr (str, BACK_SLASH); + lastFwdSlash = wcsrchr (str, FORWARD_SLASH); +#else + lastBkSlash = strrchrW (str, BACK_SLASH); + lastFwdSlash = strrchrW (str, FORWARD_SLASH); +#endif + lastSlash = lastBkSlash > lastFwdSlash ? lastBkSlash : lastFwdSlash; + if (!lastSlash) lastSlash = str; + len_trailing = *len_str - (lastSlash - str); + + /* overlap-safe movement to the right */ + memmove (lastSlash+len_ellipsis, lastSlash, len_trailing * sizeof(WCHAR)); + memcpy (lastSlash, ELLIPSISW, len_ellipsis*sizeof(WCHAR)); + len_trailing += len_ellipsis; + /* From this point on lastSlash actually points to the ellipsis in front + * of the last slash and len_trailing includes the ellipsis + */ + + len_under = 0; + for ( ; ; ) + { +#ifdef _WIN32K_ + if (!GreGetTextExtentExW (hdc, str, *len_str + len_ellipsis, width, + NULL, NULL, size, 0)) break; +#else + if (!GetTextExtentExPointW (hdc, str, *len_str + len_ellipsis, width, + NULL, NULL, size)) break; +#endif + if (lastSlash == str || size->cx <= width) break; + + /* overlap-safe movement to the left */ + memmove (lastSlash-1, lastSlash, len_trailing * sizeof(WCHAR)); + lastSlash--; + len_under++; + + assert (*len_str); + (*len_str)--; + } + pellip->before = lastSlash-str; + pellip->len = len_ellipsis; + pellip->under = len_under; + pellip->after = len_trailing - len_ellipsis; + *len_str += len_ellipsis; + + if (modstr) + { + memcpy(modstr, str, *len_str * sizeof(WCHAR)); + modstr[*len_str] = '\0'; + } +} + +/* Check the character is Chinese, Japanese, Korean and/or Thai */ +inline BOOL IsCJKT(WCHAR wch) +{ + if (0x0E00 <= wch && wch <= 0x0E7F) + return TRUE; /* Thai */ + + if (0x3000 <= wch && wch <= 0x9FFF) + return TRUE; /* CJK */ + + if (0xAC00 <= wch && wch <= 0xD7FF) + return TRUE; /* Korean */ + + if (0xFF00 <= wch && wch <= 0xFFEF) + return TRUE; /* CJK */ + + return FALSE; +} + +/* See http://en.wikipedia.org/wiki/Kinsoku_shori */ +static const WCHAR KinsokuClassA[] = +{ + 0x2010, 0x2013, 0x2019, 0x201D, 0x203C, 0x2047, 0x2048, 0x2049, 0x3001, + 0x3002, 0x3005, 0x3009, 0x300B, 0x300D, 0x300F, 0x3011, 0x3015, 0x3017, + 0x3019, 0x301C, 0x301F, 0x303B, 0x3041, 0x3043, 0x3045, 0x3047, 0x3049, + 0x3063, 0x3083, 0x3085, 0x3087, 0x308E, 0x3095, 0x3096, 0x30A0, 0x30A1, + 0x30A3, 0x30A5, 0x30A7, 0x30A9, 0x30C3, 0x30E3, 0x30E5, 0x30E7, 0x30EE, + 0x30F5, 0x30F6, 0x30FB, 0x30FC, 0x30FD, 0x30FE, 0x31F0, 0x31F1, 0x31F2, + 0x31F3, 0x31F4, 0x31F5, 0x31F6, 0x31F7, 0x31F8, 0x31F9, 0x31FA, 0x31FB, + 0x31FC, 0x31FD, 0x31FE, 0x31FF, 0xFF01, 0xFF09, 0xFF0C, 0xFF0E, 0xFF1A, + 0xFF1B, 0xFF1F, 0xFF3D, 0xFF5D, 0xFF60, 0 +}; + +/********************************************************************* + * TEXT_WordBreak (static) + * + * Perform wordbreak processing on the given string + * + * Assumes that DT_WORDBREAK has been specified and not all the characters + * fit. Note that this function should even be called when the first character + * that doesn't fit is known to be a space or tab, so that it can swallow them. + * + * Note that the Windows processing has some strange properties. + * 1. If the text is left-justified and there is room for some of the spaces + * that follow the last word on the line then those that fit are included on + * the line. + * 2. If the text is centred or right-justified and there is room for some of + * the spaces that follow the last word on the line then all but one of those + * that fit are included on the line. + * 3. (Reasonable behaviour) If the word breaking causes a space to be the first + * character of a new line it will be skipped. + * + * Arguments + * hdc [in] The handle to the DC that defines the font. + * str [in/out] The string that needs to be broken. + * max_str [in] The dimension of str (number of WCHAR). + * len_str [in/out] The number of characters in str + * width [in] The maximum width permitted + * format [in] The format flags in effect + * chars_fit [in] The maximum number of characters of str that are already + * known to fit; chars_fit+1 is known not to fit. + * chars_used [out] The number of characters of str that have been "used" and + * do not need to be included in later text. For example this will + * include any spaces that have been discarded from the start of + * the next line. + * size [out] The size of the returned text in logical coordinates + * + * Pedantic assumption - Assumes that the text length is monotonically + * increasing with number of characters (i.e. no weird kernings) + * + * Algorithm + * + * Work back from the last character that did fit to either a space or the last + * character of a word, whichever is met first. + * If there was one or the first character didn't fit then + * If the text is centred or right justified and that one character was a + * space then break the line before that character + * Otherwise break the line after that character + * and if the next character is a space then discard it. + * Suppose there was none (and the first character did fit). + * If Break Within Word is permitted + * break the word after the last character that fits (there must be + * at least one; none is caught earlier). + * Otherwise + * discard any trailing space. + * include the whole word; it may be ellipsified later + * + * Break Within Word is permitted under a set of circumstances that are not + * totally clear yet. Currently our best guess is: + * If DT_EDITCONTROL is in effect and neither DT_WORD_ELLIPSIS nor + * DT_PATH_ELLIPSIS is + */ + +static void TEXT_WordBreak (HDC hdc, WCHAR *str, unsigned int max_str, + unsigned int *len_str, + int width, int format, unsigned int chars_fit, + unsigned int *chars_used, SIZE *size) +{ + WCHAR *p; + int word_fits; + assert (format & DT_WORDBREAK); + assert (chars_fit < *len_str); + + /* Work back from the last character that did fit to either a space or the + * last character of a word, whichever is met first. + */ + p = str + chars_fit; /* The character that doesn't fit */ + word_fits = TRUE; + if (!chars_fit) + word_fits = FALSE; + else if (*p == SPACE) /* chars_fit < *len_str so this is valid */ + p--; /* the word just fitted */ + else + { + while (p > str && *(--p) != SPACE && (!IsCJKT(p[1]) || + p[1] == L'\0' || wcschr(KinsokuClassA, p[1]) != NULL)) + ; + word_fits = (p != str || *p == SPACE || IsCJKT(p[1])); + } + /* If there was one. */ + if (word_fits) + { + int next_is_space; + /* break the line before/after that character */ + if (!(format & (DT_RIGHT | DT_CENTER)) || *p != SPACE) + p++; + next_is_space = (unsigned int) (p - str) < *len_str && *p == SPACE; + *len_str = p - str; + /* and if the next character is a space then discard it. */ + *chars_used = *len_str; + if (next_is_space) + (*chars_used)++; + } + /* Suppose there was none. */ + else + { + if ((format & (DT_EDITCONTROL | DT_WORD_ELLIPSIS | DT_PATH_ELLIPSIS)) == + DT_EDITCONTROL) + { + /* break the word after the last character that fits (there must be + * at least one). */ + if (!chars_fit) + ++chars_fit; + *len_str = chars_fit; + *chars_used = chars_fit; + + /* FIXME - possible error. Since the next character is now removed + * this could make the text longer so that it no longer fits, and + * so we need a loop to test and shrink. + */ + } + /* Otherwise */ + else + { + /* discard any trailing space. */ + const WCHAR *e = str + *len_str; + p = str + chars_fit; + while (p < e && *p != SPACE) + p++; + *chars_used = p - str; + if (p < e) /* i.e. loop failed because *p == SPACE */ + (*chars_used)++; + + /* include the whole word; it may be ellipsified later */ + *len_str = p - str; + /* Possible optimisation; if DT_WORD_ELLIPSIS only use chars_fit+1 + * so that it will be too long + */ + } + } + /* Remeasure the string */ +#ifdef _WIN32K_ + GreGetTextExtentExW (hdc, str, *len_str, 0, NULL, NULL, size, 0); +#else + GetTextExtentExPointW (hdc, str, *len_str, 0, NULL, NULL, size); +#endif +} + +/********************************************************************* + * TEXT_SkipChars + * + * Skip over the given number of characters, bearing in mind prefix + * substitution and the fact that a character may take more than one + * WCHAR (Unicode surrogates are two words long) (and there may have been + * a trailing &) + * + * Parameters + * new_count [out] The updated count + * new_str [out] The updated pointer + * start_count [in] The count of remaining characters corresponding to the + * start of the string + * start_str [in] The starting point of the string + * max [in] The number of characters actually in this segment of the + * string (the & counts) + * n [in] The number of characters to skip (if prefix then + * &c counts as one) + * prefix [in] Apply prefix substitution + * + * Return Values + * none + * + * Remarks + * There must be at least n characters in the string + * We need max because the "line" may have ended with a & followed by a tab + * or newline etc. which we don't want to swallow + */ + +static void TEXT_SkipChars (int *new_count, const WCHAR **new_str, + int start_count, const WCHAR *start_str, + int max, int n, int prefix) +{ + /* This is specific to wide characters, MSDN doesn't say anything much + * about Unicode surrogates yet and it isn't clear if _wcsinc will + * correctly handle them so we'll just do this the easy way for now + */ + + if (prefix) + { + const WCHAR *str_on_entry = start_str; + assert (max >= n); + max -= n; + while (n--) + { + if ((*start_str == PREFIX || *start_str == ALPHA_PREFIX) && max--) + start_str++; + start_str++; + } + start_count -= (start_str - str_on_entry); + } + else + { + start_str += n; + start_count -= n; + } + *new_str = start_str; + *new_count = start_count; +} + +/********************************************************************* + * TEXT_Reprefix + * + * Reanalyse the text to find the prefixed character. This is called when + * wordbreaking or ellipsification has shortened the string such that the + * previously noted prefixed character is no longer visible. + * + * Parameters + * str [in] The original string segment (including all characters) + * ns [in] The number of characters in str (including prefixes) + * pe [in] The ellipsification data + * + * Return Values + * The prefix offset within the new string segment (the one that contains the + * ellipses and does not contain the prefix characters) (-1 if none) + */ + +static int TEXT_Reprefix (const WCHAR *str, unsigned int ns, + const ellipsis_data *pe) +{ + int result = -1; + unsigned int i; + unsigned int n = pe->before + pe->under + pe->after; + assert (n <= ns); + for (i = 0; i < n; i++, str++) + { + if (i == (unsigned int) pe->before) + { + /* Reached the path ellipsis; jump over it */ + if (ns < (unsigned int) pe->under) break; + str += pe->under; + ns -= pe->under; + i += pe->under; + if (!pe->after) break; /* Nothing after the path ellipsis */ + } + if (!ns) break; + ns--; + if (*str++ == PREFIX || *str == ALPHA_PREFIX) + { + str++; + if (!ns) break; + if (*str != PREFIX) + result = (i < (unsigned int) pe->before || pe->under == 0) ? i : i - pe->under + pe->len; + /* pe->len may be non-zero while pe_under is zero */ + ns--; + } + } + return result; +} + +/********************************************************************* + * Returns true if and only if the remainder of the line is a single + * newline representation or nothing + */ + +static int remainder_is_none_or_newline (int num_chars, const WCHAR *str) +{ + if (!num_chars) return TRUE; + if (*str != LF && *str != CR) return FALSE; + if (!--num_chars) return TRUE; + if (*str == *(str+1)) return FALSE; + str++; + if (*str != CR && *str != LF) return FALSE; + if (--num_chars) return FALSE; + return TRUE; +} + +/********************************************************************* + * Return next line of text from a string. + * + * hdc - handle to DC. + * str - string to parse into lines. + * count - length of str. + * dest - destination in which to return line. + * len - dest buffer size in chars on input, copied length into dest on output. + * width - maximum width of line in pixels. + * format - format type passed to DrawText. + * retsize - returned size of the line in pixels. + * last_line - TRUE if is the last line that will be processed + * p_retstr - If DT_MODIFYSTRING this points to a cursor in the buffer in which + * the return string is built. + * tabwidth - The width of a tab in logical coordinates + * pprefix_offset - Here is where we return the offset within dest of the first + * prefixed (underlined) character. -1 is returned if there + * are none. Note that there may be more; the calling code + * will need to use TEXT_Reprefix to find any later ones. + * pellip - Here is where we return the information about any ellipsification + * that was carried out. Note that if tabs are being expanded then + * this data will correspond to the last text segment actually + * returned in dest; by definition there would not have been any + * ellipsification in earlier text segments of the line. + * + * Returns pointer to next char in str after end of the line + * or NULL if end of str reached. + */ +static const WCHAR *TEXT_NextLineW( HDC hdc, const WCHAR *str, int *count, + WCHAR *dest, int *len, int width, DWORD format, + SIZE *retsize, int last_line, WCHAR **p_retstr, + int tabwidth, int *pprefix_offset, + ellipsis_data *pellip) +{ + int i = 0, j = 0; + int plen = 0; + SIZE size = {0, 0}; + int maxl = *len; + int seg_i, seg_count, seg_j; + int max_seg_width; + int num_fit; + int word_broken; + int line_fits; + unsigned int j_in_seg; + int ellipsified; + *pprefix_offset = -1; + + /* For each text segment in the line */ + + retsize->cy = 0; + while (*count) + { + + /* Skip any leading tabs */ + + if (str[i] == TAB && (format & DT_EXPANDTABS)) + { + plen = ((plen/tabwidth)+1)*tabwidth; + (*count)--; if (j < maxl) dest[j++] = str[i++]; else i++; + while (*count && str[i] == TAB) + { + plen += tabwidth; + (*count)--; if (j < maxl) dest[j++] = str[i++]; else i++; + } + } + + + /* Now copy as far as the next tab or cr/lf or eos */ + + seg_i = i; + seg_count = *count; + seg_j = j; + + while (*count && + (str[i] != TAB || !(format & DT_EXPANDTABS)) && + ((str[i] != CR && str[i] != LF) || (format & DT_SINGLELINE))) + { + if ((format & DT_NOPREFIX) || *count <= 1) + { + (*count)--; if (j < maxl) dest[j++] = str[i++]; else i++; + continue; + } + + if (str[i] == PREFIX || str[i] == ALPHA_PREFIX) { + (*count)--, i++; /* Throw away the prefix itself */ + if (str[i] == PREFIX) + { + /* Swallow it before we see it again */ + (*count)--; if (j < maxl) dest[j++] = str[i++]; else i++; + } + else if (*pprefix_offset == -1 || *pprefix_offset >= seg_j) + { + *pprefix_offset = j; + } + /* else the previous prefix was in an earlier segment of the + * line; we will leave it to the drawing code to catch this + * one. + */ + } + else if (str[i] == KANA_PREFIX) + { + /* Throw away katakana access keys */ + (*count)--, i++; /* skip the prefix */ + (*count)--, i++; /* skip the letter */ + } + else + { + (*count)--; if (j < maxl) dest[j++] = str[i++]; else i++; + } + } + + + /* Measure the whole text segment and possibly WordBreak and + * ellipsify it + */ + + j_in_seg = j - seg_j; + max_seg_width = width - plen; +#ifdef _WIN32K_ + GreGetTextExtentExW (hdc, dest + seg_j, j_in_seg, max_seg_width, (PULONG)&num_fit, NULL, &size, 0); +#else + GetTextExtentExPointW (hdc, dest + seg_j, j_in_seg, max_seg_width, &num_fit, NULL, &size); +#endif + + /* The Microsoft handling of various combinations of formats is weird. + * The following may very easily be incorrect if several formats are + * combined, and may differ between versions (to say nothing of the + * several bugs in the Microsoft versions). + */ + word_broken = 0; + line_fits = (num_fit >= j_in_seg); + if (!line_fits && (format & DT_WORDBREAK)) + { + const WCHAR *s; + unsigned int chars_used; + TEXT_WordBreak (hdc, dest+seg_j, maxl-seg_j, &j_in_seg, + max_seg_width, format, num_fit, &chars_used, &size); + line_fits = (size.cx <= max_seg_width); + /* and correct the counts */ + TEXT_SkipChars (count, &s, seg_count, str+seg_i, i-seg_i, + chars_used, !(format & DT_NOPREFIX)); + i = s - str; + word_broken = 1; + } + pellip->before = j_in_seg; + pellip->under = 0; + pellip->after = 0; + pellip->len = 0; + ellipsified = 0; + if (!line_fits && (format & DT_PATH_ELLIPSIS)) + { + TEXT_PathEllipsify (hdc, dest + seg_j, maxl-seg_j, &j_in_seg, + max_seg_width, &size, *p_retstr, pellip); + line_fits = (size.cx <= max_seg_width); + ellipsified = 1; + } + /* NB we may end up ellipsifying a word-broken or path_ellipsified + * string */ + if ((!line_fits && (format & DT_WORD_ELLIPSIS)) || + ((format & DT_END_ELLIPSIS) && + ((last_line && *count) || + (remainder_is_none_or_newline (*count, &str[i]) && !line_fits)))) + { + int before, len_ellipsis; + TEXT_Ellipsify (hdc, dest + seg_j, maxl-seg_j, &j_in_seg, + max_seg_width, &size, *p_retstr, &before, &len_ellipsis); + if (before > pellip->before) + { + /* We must have done a path ellipsis too */ + pellip->after = before - pellip->before - pellip->len; + /* Leave the len as the length of the first ellipsis */ + } + else + { + /* If we are here after a path ellipsification it must be + * because even the ellipsis itself didn't fit. + */ + assert (pellip->under == 0 && pellip->after == 0); + pellip->before = before; + pellip->len = len_ellipsis; + /* pellip->after remains as zero as does + * pellip->under + */ + } + line_fits = (size.cx <= max_seg_width); + ellipsified = 1; + } + /* As an optimisation if we have ellipsified and we are expanding + * tabs and we haven't reached the end of the line we can skip to it + * now rather than going around the loop again. + */ + if ((format & DT_EXPANDTABS) && ellipsified) + { + if (format & DT_SINGLELINE) + *count = 0; + else + { + while ((*count) && str[i] != CR && str[i] != LF) + { + (*count)--, i++; + } + } + } + + j = seg_j + j_in_seg; + if (*pprefix_offset >= seg_j + pellip->before) + { + *pprefix_offset = TEXT_Reprefix (str + seg_i, i - seg_i, pellip); + if (*pprefix_offset != -1) + *pprefix_offset += seg_j; + } + + plen += size.cx; + if (size.cy > retsize->cy) + retsize->cy = size.cy; + + if (word_broken) + break; + else if (!*count) + break; + else if (str[i] == CR || str[i] == LF) + { + (*count)--, i++; + if (*count && (str[i] == CR || str[i] == LF) && str[i] != str[i-1]) + { + (*count)--, i++; + } + break; + } + /* else it was a Tab and we go around again */ + } + + retsize->cx = plen; + *len = j; + if (*count) + return (&str[i]); + else + return NULL; +} + + +/*********************************************************************** + * TEXT_DrawUnderscore + * + * Draw the underline under the prefixed character + * + * Parameters + * hdc [in] The handle of the DC for drawing + * x [in] The x location of the line segment (logical coordinates) + * y [in] The y location of where the underscore should appear + * (logical coordinates) + * str [in] The text of the line segment + * offset [in] The offset of the underscored character within str + * rect [in] Clipping rectangle (if not NULL) + */ +/* Synced with wine 1.1.32 */ +static void TEXT_DrawUnderscore (HDC hdc, int x, int y, const WCHAR *str, int offset, const RECT *rect) +{ + int prefix_x; + int prefix_end; + SIZE size; + HPEN hpen; + HPEN oldPen; +#ifdef _WIN32K_ + GreGetTextExtentW (hdc, (LPWSTR)str, offset, &size, 1); +#else + GetTextExtentPointW (hdc, str, offset, &size); +#endif + prefix_x = x + size.cx; +#ifdef _WIN32K_ + GreGetTextExtentW (hdc, (LPWSTR)str, offset+1, &size, 1); +#else + GetTextExtentPointW (hdc, str, offset+1, &size); +#endif + prefix_end = x + size.cx - 1; + /* The above method may eventually be slightly wrong due to kerning etc. */ + + /* Check for clipping */ + if (rect) + { + if (prefix_x > rect->right || prefix_end < rect->left || + y < rect->top || y > rect->bottom) + return; /* Completely outside */ + /* Partially outside */ + if (prefix_x < rect->left ) prefix_x = rect->left; + if (prefix_end > rect->right) prefix_end = rect->right; + } +#ifdef _WIN32K_ + hpen = NtGdiCreatePen (PS_SOLID, 1, GreGetTextColor (hdc), NULL); + oldPen = NtGdiSelectPen (hdc, hpen); + GreMoveTo (hdc, prefix_x, y, NULL); + NtGdiLineTo (hdc, prefix_end, y); + NtGdiSelectPen (hdc, oldPen); + GreDeleteObject (hpen); +#else + hpen = CreatePen (PS_SOLID, 1, GetTextColor (hdc)); + oldPen = SelectObject (hdc, hpen); + MoveToEx (hdc, prefix_x, y, NULL); + LineTo (hdc, prefix_end, y); + SelectObject (hdc, oldPen); + DeleteObject (hpen); +#endif +} + +/*********************************************************************** + * DrawTextExW (USER32.@) + * + * The documentation on the extra space required for DT_MODIFYSTRING at MSDN + * is not quite complete, especially with regard to \0. We will assume that + * the returned string could have a length of up to i_count+3 and also have + * a trailing \0 (which would be 4 more than a not-null-terminated string but + * 3 more than a null-terminated string). If this is not so then increase + * the allowance in DrawTextExA. + */ +#define MAX_BUFFER 1024 +/* + * DrawTextExW + * + * Synced with Wine Staging 1.7.37 + */ +INT WINAPI DrawTextExWorker( HDC hdc, + LPWSTR str, + INT i_count, + LPRECT rect, + UINT flags, + LPDRAWTEXTPARAMS dtp ) +{ + SIZE size; + const WCHAR *strPtr; + WCHAR *retstr, *p_retstr; + size_t size_retstr; + WCHAR line[MAX_BUFFER]; + int len, lh, count=i_count; + TEXTMETRICW tm; + int lmargin = 0, rmargin = 0; + int x = rect->left, y = rect->top; + int width = rect->right - rect->left; + int max_width = 0; + int last_line; + int tabwidth /* to keep gcc happy */ = 0; + int prefix_offset; + ellipsis_data ellip; + BOOL invert_y=FALSE; +#ifdef _WIN32K_ + TRACE("%S, %d, %08x\n", str, count, flags); +#else + TRACE("%s, %d, [%s] %08x\n", debugstr_wn (str, count), count, + wine_dbgstr_rect(rect), flags); +#endif + if (dtp) TRACE("Params: iTabLength=%d, iLeftMargin=%d, iRightMargin=%d\n", + dtp->iTabLength, dtp->iLeftMargin, dtp->iRightMargin); + + if (!str) return 0; + + strPtr = str; + + if (flags & DT_SINGLELINE) + flags &= ~DT_WORDBREAK; +#ifdef _WIN32K_ + GreGetTextMetricsW(hdc, &tm); +#else + GetTextMetricsW(hdc, &tm); +#endif + if (flags & DT_EXTERNALLEADING) + lh = tm.tmHeight + tm.tmExternalLeading; + else + lh = tm.tmHeight; + + if (str[0] && count == 0) + return lh; + + if (dtp && dtp->cbSize != sizeof(DRAWTEXTPARAMS)) + return 0; +#ifdef _WIN32K_ + if (GreGetGraphicsMode(hdc) == GM_COMPATIBLE) + { + SIZE window_ext, viewport_ext; + GreGetWindowExtEx(hdc, &window_ext); + GreGetViewportExtEx(hdc, &viewport_ext); + if ((window_ext.cy > 0) != (viewport_ext.cy > 0)) + invert_y = TRUE; + } +#else + if (GetGraphicsMode(hdc) == GM_COMPATIBLE) + { + SIZE window_ext, viewport_ext; + GetWindowExtEx(hdc, &window_ext); + GetViewportExtEx(hdc, &viewport_ext); + if ((window_ext.cy > 0) != (viewport_ext.cy > 0)) + invert_y = TRUE; + } +#endif + if (count == -1) + { +#ifdef _WIN32K_ + count = wcslen(str); +#else + count = strlenW(str); +#endif + if (count == 0) + { + if( flags & DT_CALCRECT) + { + rect->right = rect->left; + if( flags & DT_SINGLELINE) + rect->bottom = rect->top + (invert_y ? -lh : lh); + else + rect->bottom = rect->top; + } + return lh; + } + } + + if (dtp) + { + lmargin = dtp->iLeftMargin; + rmargin = dtp->iRightMargin; + if (!(flags & (DT_CENTER | DT_RIGHT))) + x += lmargin; + dtp->uiLengthDrawn = 0; /* This param RECEIVES number of chars processed */ + } + + if (flags & DT_EXPANDTABS) + { + int tabstop = ((flags & DT_TABSTOP) && dtp) ? dtp->iTabLength : 8; + tabwidth = tm.tmAveCharWidth * tabstop; + } + + if (flags & DT_CALCRECT) flags |= DT_NOCLIP; + + if (flags & DT_MODIFYSTRING) + { + size_retstr = (count + 4) * sizeof (WCHAR); +#ifdef _WIN32K_ + retstr = ExAllocatePoolWithTag(PagedPool, size_retstr, USERTAG_RTL); +#else + retstr = HeapAlloc(GetProcessHeap(), 0, size_retstr); +#endif + if (!retstr) return 0; + memcpy (retstr, str, size_retstr); + } + else + { + size_retstr = 0; + retstr = NULL; + } + p_retstr = retstr; + + do + { + len = sizeof(line)/sizeof(line[0]); + if (invert_y) + last_line = !(flags & DT_NOCLIP) && y - ((flags & DT_EDITCONTROL) ? 2*lh-1 : lh) < rect->bottom; + else + last_line = !(flags & DT_NOCLIP) && y + ((flags & DT_EDITCONTROL) ? 2*lh-1 : lh) > rect->bottom; + strPtr = TEXT_NextLineW(hdc, strPtr, &count, line, &len, width, flags, &size, last_line, &p_retstr, tabwidth, &prefix_offset, &ellip); + +#ifdef __REACTOS__ + if (flags & DT_CENTER) + { + if (((rect->right - rect->left) < size.cx) && (flags & DT_CALCRECT)) + { + x = rect->left + size.cx; + } + else + { + x = (rect->left + rect->right - size.cx) / 2; + } + } +#else + if (flags & DT_CENTER) x = (rect->left + rect->right - + size.cx) / 2; +#endif + else if (flags & DT_RIGHT) x = rect->right - size.cx; + + if (flags & DT_SINGLELINE) + { + if (flags & DT_VCENTER) +#ifdef __REACTOS__ + { + if (((rect->bottom - rect->top) < (invert_y ? -size.cy : size.cy)) && (flags & DT_CALCRECT)) + { + y = rect->top + (invert_y ? -size.cy : size.cy); + } + else + { +#endif + y = rect->top + (rect->bottom - rect->top) / 2 + (invert_y ? (size.cy / 2) : (-size.cy / 2)); +#ifdef __REACTOS__ + } + } +#endif + else if (flags & DT_BOTTOM) + y = rect->bottom + (invert_y ? 0 : -size.cy); + } + + if (!(flags & DT_CALCRECT)) + { + const WCHAR *str = line; + int xseg = x; + while (len) + { + int len_seg; + SIZE size; + if ((flags & DT_EXPANDTABS)) + { + const WCHAR *p; + p = str; while (p < str+len && *p != TAB) p++; + len_seg = p - str; + if (len_seg != len && +#ifdef _WIN32K_ + !GreGetTextExtentW(hdc, (LPWSTR)str, len_seg, &size, 1)) +#else + !GetTextExtentPointW(hdc, str, len_seg, &size)) +#endif + { +#ifdef _WIN32K_ + ExFreePoolWithTag(retstr, USERTAG_RTL); +#else + HeapFree (GetProcessHeap(), 0, retstr); +#endif + return 0; + } + } + else + len_seg = len; +#ifdef _WIN32K_ + if (!GreExtTextOutW( hdc, xseg, y, + ((flags & DT_NOCLIP) ? 0 : ETO_CLIPPED) | + ((flags & DT_RTLREADING) ? ETO_RTLREADING : 0), + rect, (LPWSTR)str, len_seg, NULL, 0 )) +#else + if (!ExtTextOutW( hdc, xseg, y, + ((flags & DT_NOCLIP) ? 0 : ETO_CLIPPED) | + ((flags & DT_RTLREADING) ? ETO_RTLREADING : 0), + rect, str, len_seg, NULL )) +#endif + { +#ifdef _WIN32K_ + ExFreePoolWithTag(retstr, USERTAG_RTL); +#else + HeapFree (GetProcessHeap(), 0, retstr); +#endif + return 0; + } + if (prefix_offset != -1 && prefix_offset < len_seg) + { + TEXT_DrawUnderscore (hdc, xseg, y + tm.tmAscent + 1, str, prefix_offset, (flags & DT_NOCLIP) ? NULL : rect); + } + len -= len_seg; + str += len_seg; + if (len) + { + assert ((flags & DT_EXPANDTABS) && *str == TAB); + len--; str++; + xseg += ((size.cx/tabwidth)+1)*tabwidth; + if (prefix_offset != -1) + { + if (prefix_offset < len_seg) + { + /* We have just drawn an underscore; we ought to + * figure out where the next one is. I am going + * to leave it for now until I have a better model + * for the line, which will make reprefixing easier. + * This is where ellip would be used. + */ + prefix_offset = -1; + } + else + prefix_offset -= len_seg; + } + } + } + } + else if (size.cx > max_width) + max_width = size.cx; + + y += invert_y ? -lh : lh; + if (dtp) + dtp->uiLengthDrawn += len; + } + while (strPtr && !last_line); + + if (flags & DT_CALCRECT) + { + rect->right = rect->left + max_width; + rect->bottom = y; + if (dtp) + rect->right += lmargin + rmargin; + } + if (retstr) + { + memcpy (str, retstr, size_retstr); +#ifdef _WIN32K_ + ExFreePoolWithTag(retstr, USERTAG_RTL); +#else + HeapFree (GetProcessHeap(), 0, retstr); +#endif + } + return y - rect->top; +} + diff --git a/reactos/win32ss/user/user32/CMakeLists.txt b/reactos/win32ss/user/user32/CMakeLists.txt index 409d325255a..4cdc70e58b7 100644 --- a/reactos/win32ss/user/user32/CMakeLists.txt +++ b/reactos/win32ss/user/user32/CMakeLists.txt @@ -60,6 +60,7 @@ list(APPEND SOURCE windows/text.c windows/window.c windows/winpos.c + ${REACTOS_SOURCE_DIR}/win32ss/user/rtl/text.c ${CMAKE_CURRENT_BINARY_DIR}/user32_stubs.c include/user32.h) diff --git a/reactos/win32ss/user/user32/misc/usrapihk.c b/reactos/win32ss/user/user32/misc/usrapihk.c index b0df579cfa5..1d0ea631831 100644 --- a/reactos/win32ss/user/user32/misc/usrapihk.c +++ b/reactos/win32ss/user/user32/misc/usrapihk.c @@ -34,7 +34,7 @@ LRESULT WINAPI RealUserDrawCaption(HWND hWnd, HDC hDC, LPCRECT lpRc, UINT uFlags DWORD gcLoadUserApiHook = 0; LONG gcCallUserApiHook = 0; -DWORD gfUserApiHook; +DWORD gfUserApiHook = 0; HINSTANCE ghmodUserApiHook = NULL; USERAPIHOOKPROC gpfnInitUserApi; RTL_CRITICAL_SECTION gcsUserApiHook; diff --git a/reactos/win32ss/user/user32/user32.spec b/reactos/win32ss/user/user32/user32.spec index 5b58121f510..49d8cda99da 100644 --- a/reactos/win32ss/user/user32/user32.spec +++ b/reactos/win32ss/user/user32/user32.spec @@ -184,7 +184,7 @@ @ stdcall DrawIcon(long long long long) @ stdcall DrawIconEx(long long long long long long long long long) @ stdcall DrawMenuBar(long) -@ stdcall DrawMenuBarTemp(long long long long long) +@ stdcall DrawMenuBarTemp(long long long long long) NtUserDrawMenuBarTemp @ stdcall DrawStateA(long long ptr long long long long long long long) @ stdcall DrawStateW(long long ptr long long long long long long long) @ stdcall DrawTextA(long str long ptr long) @@ -198,7 +198,7 @@ @ stdcall EnableWindow(long long) @ stdcall EndDeferWindowPos(long) @ stdcall EndDialog(long long) -@ stdcall EndMenu() +@ stdcall EndMenu() NtUserEndMenu @ stdcall EndPaint(long ptr) NtUserEndPaint @ stdcall EndTask(ptr long long) @ stdcall EnterReaderModeHelper(ptr) @@ -302,7 +302,7 @@ @ stdcall GetLayeredWindowAttributes(long ptr ptr ptr) NtUserGetLayeredWindowAttributes @ stdcall GetListBoxInfo(long) NtUserGetListBoxInfo @ stdcall GetMenu(long) -@ stdcall GetMenuBarInfo(long long long ptr) ; tempo haxzo NtUserGetMenuBarInfo +@ stdcall GetMenuBarInfo(long long long ptr) NtUserGetMenuBarInfo @ stdcall GetMenuCheckMarkDimensions() @ stdcall GetMenuContextHelpId(long) @ stdcall GetMenuDefaultItem(long long long) @@ -349,7 +349,7 @@ @ stdcall GetSubMenu(long long) @ stdcall GetSysColor(long) @ stdcall GetSysColorBrush(long) -@ stdcall GetSystemMenu(long long) ; Direct call NtUserGetSystemMenu +@ stdcall GetSystemMenu(long long) ; NtUserGetSystemMenu @ stdcall GetSystemMetrics(long) @ stdcall GetTabbedTextExtentA(long str long long ptr) @ stdcall GetTabbedTextExtentW(long wstr long long ptr) @@ -387,7 +387,7 @@ @ stdcall GrayStringA(long long ptr long long long long long long) @ stdcall GrayStringW(long long ptr long long long long long long) @ stdcall HideCaret(long) NtUserHideCaret -@ stdcall HiliteMenuItem(long long long long) ; Use both ReactOS and wine NtUserHiliteMenuItem +@ stdcall HiliteMenuItem(long long long long) NtUserHiliteMenuItem @ stdcall IMPGetIMEA(long ptr) @ stdcall IMPGetIMEW(long ptr) @ stdcall IMPQueryIMEA(ptr) @@ -478,7 +478,7 @@ @ stdcall MapVirtualKeyExW(long long long) @ stdcall MapVirtualKeyW(long long) @ stdcall MapWindowPoints(long long ptr long) -@ stdcall MenuItemFromPoint(long long double) ; Direct call NtUserMenuItemFromPoint +@ stdcall MenuItemFromPoint(long long double) NtUserMenuItemFromPoint @ stdcall MenuWindowProcA (long ptr long long long) @ stdcall MenuWindowProcW (long ptr long long long) @ stdcall MessageBeep(long) @@ -635,7 +635,7 @@ @ stdcall SetSysColors(long ptr ptr) @ stdcall SetSysColorsTemp(ptr ptr long) @ stdcall SetSystemCursor(long long) -@ stdcall SetSystemMenu(long long) ; Direct call NtUserSetSystemMenu +@ stdcall SetSystemMenu(long long) ; NtUserSetSystemMenu @ stdcall SetSystemTimer(long long long ptr) NtUserSetSystemTimer @ stdcall SetTaskmanWindow (long) @ stdcall SetThreadDesktop(long) NtUserSetThreadDesktop @@ -684,7 +684,7 @@ @ stdcall ToUnicodeEx(long long ptr ptr long long long) @ stdcall TrackMouseEvent(ptr) NtUserTrackMouseEvent @ stdcall TrackPopupMenu(long long long long long long ptr) -@ stdcall TrackPopupMenuEx(long long long long long ptr) ; Direct call NtUserTrackPopupMenuEx +@ stdcall TrackPopupMenuEx(long long long long long ptr) NtUserTrackPopupMenuEx @ stdcall TranslateAccelerator(long long ptr) TranslateAcceleratorA @ stdcall TranslateAcceleratorA(long long ptr) @ stdcall TranslateAcceleratorW(long long ptr) diff --git a/reactos/win32ss/user/user32/windows/defwnd.c b/reactos/win32ss/user/user32/windows/defwnd.c index 20c51698f26..368043f2fa1 100644 --- a/reactos/win32ss/user/user32/windows/defwnd.c +++ b/reactos/win32ss/user/user32/windows/defwnd.c @@ -16,21 +16,10 @@ #include WINE_DEFAULT_DEBUG_CHANNEL(user32); -LRESULT DefWndNCPaint(HWND hWnd, HRGN hRgn, BOOL Active); -LRESULT DefWndNCCalcSize(HWND hWnd, BOOL CalcSizeStruct, RECT *Rect); -LRESULT DefWndNCActivate(HWND hWnd, WPARAM wParam, LPARAM lParam); LRESULT DefWndNCHitTest(HWND hWnd, POINT Point); -LRESULT DefWndNCLButtonDown(HWND hWnd, WPARAM wParam, LPARAM lParam); -LRESULT DefWndNCLButtonDblClk(HWND hWnd, WPARAM wParam, LPARAM lParam); -LRESULT NC_HandleNCRButtonDown( HWND hwnd, WPARAM wParam, LPARAM lParam ); -void FASTCALL MenuInitSysMenuPopup(HMENU Menu, DWORD Style, DWORD ClsStyle, LONG HitTest ); -void MENU_EndMenu( HWND ); /* GLOBALS *******************************************************************/ -static short iF10Key = 0; -static short iMenuSysKey = 0; - /* FUNCTIONS *****************************************************************/ /* @@ -219,6 +208,21 @@ DefWndHandleSysCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) if (!IsWindowEnabled( hWnd )) return 0; + switch (wParam & 0xfff0) + { + case SC_MOVE: + case SC_SIZE: +// case SC_DEFAULT: + case SC_MOUSEMENU: + case SC_KEYMENU: + case SC_SCREENSAVE: + NtUserMessageCall( hWnd, WM_SYSCOMMAND, wParam, lParam, (ULONG_PTR)&lResult, FNID_DEFWINDOWPROC, FALSE); + return 0; + + default: + break; + } + if (ISITHOOKED(WH_CBT)) { NtUserMessageCall( hWnd, WM_SYSCOMMAND, wParam, lParam, (ULONG_PTR)&lResult, FNID_DEFWINDOWPROC, FALSE); @@ -227,10 +231,6 @@ DefWndHandleSysCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) switch (wParam & 0xfff0) { - case SC_MOVE: - case SC_SIZE: - NtUserMessageCall( hWnd, WM_SYSCOMMAND, wParam, lParam, (ULONG_PTR)&lResult, FNID_DEFWINDOWPROC, FALSE); - break; case SC_MINIMIZE: if (hWnd == GetActiveWindow()) @@ -253,17 +253,6 @@ DefWndHandleSysCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) case SC_CLOSE: return SendMessageW(hWnd, WM_CLOSE, 0, 0); -// case SC_DEFAULT: - case SC_MOUSEMENU: - { - Pt.x = (short)LOWORD(lParam); - Pt.y = (short)HIWORD(lParam); - MenuTrackMouseMenuBar(hWnd, wParam & 0x000f, Pt); - } - break; - case SC_KEYMENU: - MenuTrackKbdMenuBar(hWnd, wParam, (WCHAR)lParam); - break; case SC_VSCROLL: case SC_HSCROLL: { @@ -277,9 +266,6 @@ DefWndHandleSysCommand(HWND hWnd, WPARAM wParam, LPARAM lParam) WinExec( "taskman.exe", SW_SHOWNORMAL ); break; - case SC_SCREENSAVE: - NtUserMessageCall( hWnd, WM_SYSCOMMAND, wParam, lParam, (ULONG_PTR)&lResult, FNID_DEFWINDOWPROC, FALSE); - break; case SC_NEXTWINDOW: case SC_PREVWINDOW: @@ -359,38 +345,6 @@ DefWndControlColor(HDC hDC, UINT ctlType) return GetSysColorBrush(COLOR_WINDOW); } -static void DefWndPrint( HWND hwnd, HDC hdc, ULONG uFlags) -{ - /* - * Visibility flag. - */ - if ( (uFlags & PRF_CHECKVISIBLE) && - !IsWindowVisible(hwnd) ) - return; - - /* - * Unimplemented flags. - */ - if ( (uFlags & PRF_CHILDREN) || - (uFlags & PRF_OWNED) || - (uFlags & PRF_NONCLIENT) ) - { - FIXME("WM_PRINT message with unsupported flags\n"); - } - - /* - * Background - */ - if ( uFlags & PRF_ERASEBKGND) - SendMessageW(hwnd, WM_ERASEBKGND, (WPARAM)hdc, 0); - - /* - * Client area - */ - if ( uFlags & PRF_CLIENT) - SendMessageW(hwnd, WM_PRINTCLIENT, (WPARAM)hdc, uFlags); -} - static BOOL CALLBACK UserSendUiUpdateMsg(HWND hwnd, LPARAM lParam) { @@ -398,21 +352,13 @@ UserSendUiUpdateMsg(HWND hwnd, LPARAM lParam) return TRUE; } -/* - RealUserDrawCaption: This function is passed through RegisterUserApiHook to uxtheme - to call it when the classic caption is needed to be drawn. - */ -LRESULT WINAPI -RealUserDrawCaption(HWND hWnd, HDC hDC, LPCRECT lpRc, UINT uFlags) -{ - return DefWndNCPaint(hWnd, HRGN_WINDOW, -1); -} - static void -UserPaintCaption(HWND hwnd) +UserPaintCaption(PWND pwnd, INT Flags) { - /* FIXME: this is not 100% correct */ - + if ( pwnd->style & WS_VISIBLE && (pwnd->style & WS_CAPTION) == WS_CAPTION ) + { + if (pwnd->state & WNDS_HASCAPTION && NtUserQueryWindow(UserHMGetHandle(pwnd), QUERY_WINDOW_FOREGROUND)) + Flags |= DC_ACTIVE; /* * When themes are not enabled we can go on and paint the non client area. * However if we do that with themes enabled we will draw a classic frame. @@ -426,51 +372,16 @@ UserPaintCaption(HWND hwnd) * RealUserDrawCaption in order to draw the classic caption when themes * are disabled but the themes service is enabled */ - SendMessage(hwnd, WM_NCUAHDRAWCAPTION,0,0); + SendMessageW(UserHMGetHandle(pwnd), WM_NCUAHDRAWCAPTION, Flags, 0); } else { - DefWndNCPaint(hwnd, HRGN_WINDOW, -1); + RECT rc = {0,0,0,0}; + HDC hDC = GetDCEx(UserHMGetHandle(pwnd), NULL, DCX_WINDOW|DCX_USESTYLE); + NtUserDrawCaption(UserHMGetHandle(pwnd), hDC, &rc, DC_DRAWCAPTIONMD|Flags); + ReleaseDC(UserHMGetHandle(pwnd), hDC); } -} - -// WM_SETICON -LRESULT FASTCALL -DefWndSetIcon(PWND pWnd, WPARAM wParam, LPARAM lParam) -{ - HICON hIcon, hIconSmall, hIconOld; - - if ( wParam > ICON_SMALL2 ) - { - SetLastError(ERROR_INVALID_PARAMETER); - return 0; - } - hIconSmall = UserGetProp(UserHMGetHandle(pWnd), gpsi->atomIconSmProp); - hIcon = UserGetProp(UserHMGetHandle(pWnd), gpsi->atomIconProp); - - hIconOld = wParam == ICON_BIG ? hIcon : hIconSmall; - - switch(wParam) - { - case ICON_BIG: - hIcon = (HICON)lParam; - break; - case ICON_SMALL: - hIconSmall = (HICON)lParam; - break; - case ICON_SMALL2: - ERR("FIXME: Set ICON_SMALL2 support!\n"); - default: - break; - } - - NtUserSetProp(UserHMGetHandle(pWnd), gpsi->atomIconProp, hIcon); - NtUserSetProp(UserHMGetHandle(pWnd), gpsi->atomIconSmProp, hIconSmall); - - if ((pWnd->style & WS_CAPTION ) == WS_CAPTION) - UserPaintCaption(UserHMGetHandle(pWnd)); /* Repaint caption */ - - return (LRESULT)hIconOld; + } } LRESULT FASTCALL @@ -497,41 +408,6 @@ DefWndGetIcon(PWND pWnd, WPARAM wParam, LPARAM lParam) return (LRESULT)hIconRet; } -VOID FASTCALL -DefWndScreenshot(HWND hWnd) -{ - RECT rect; - HDC hdc; - INT w; - INT h; - HBITMAP hbitmap; - HDC hdc2; - - OpenClipboard(hWnd); - EmptyClipboard(); - - hdc = GetWindowDC(hWnd); - GetWindowRect(hWnd, &rect); - w = rect.right - rect.left; - h = rect.bottom - rect.top; - - hbitmap = CreateCompatibleBitmap(hdc, w, h); - hdc2 = CreateCompatibleDC(hdc); - SelectObject(hdc2, hbitmap); - - BitBlt(hdc2, 0, 0, w, h, - hdc, 0, 0, - SRCCOPY); - - SetClipboardData(CF_BITMAP, hbitmap); - - ReleaseDC(hWnd, hdc); - ReleaseDC(hWnd, hdc2); - - CloseClipboard(); -} - - LRESULT WINAPI User32DefWindowProc(HWND hWnd, @@ -549,33 +425,18 @@ User32DefWindowProc(HWND hWnd, switch (Msg) { - case WM_NCPAINT: - { - return DefWndNCPaint(hWnd, (HRGN)wParam, -1); - } - - case WM_NCCALCSIZE: - { - return DefWndNCCalcSize(hWnd, (BOOL)wParam, (RECT*)lParam); - } - case WM_POPUPSYSTEMMENU: { /* This is an undocumented message used by the windows taskbar to display the system menu of windows that belong to other processes. */ HMENU menu = GetSystemMenu(hWnd, FALSE); - + ERR("WM_POPUPSYSTEMMENU\n"); if (menu) - TrackPopupMenu(menu, TPM_LEFTBUTTON|TPM_RIGHTBUTTON, + TrackPopupMenu(menu, TPM_LEFTBUTTON|TPM_RIGHTBUTTON|TPM_SYSTEM_MENU, LOWORD(lParam), HIWORD(lParam), 0, hWnd, NULL); return 0; } - case WM_NCACTIVATE: - { - return DefWndNCActivate(hWnd, wParam, lParam); - } - case WM_NCHITTEST: { POINT Point; @@ -584,32 +445,9 @@ User32DefWindowProc(HWND hWnd, return (DefWndNCHitTest(hWnd, Point)); } - case WM_LBUTTONDOWN: - case WM_RBUTTONDOWN: - case WM_MBUTTONDOWN: - iF10Key = iMenuSysKey = 0; - break; - - case WM_NCLBUTTONDOWN: - { - return (DefWndNCLButtonDown(hWnd, wParam, lParam)); - } - - case WM_LBUTTONDBLCLK: - return (DefWndNCLButtonDblClk(hWnd, HTCLIENT, lParam)); - - case WM_NCLBUTTONDBLCLK: - { - return (DefWndNCLButtonDblClk(hWnd, wParam, lParam)); - } - - case WM_NCRBUTTONDOWN: - return NC_HandleNCRButtonDown( hWnd, wParam, lParam ); - case WM_RBUTTONUP: { POINT Pt; - Pt.x = GET_X_LPARAM(lParam); Pt.y = GET_Y_LPARAM(lParam); ClientToScreen(hWnd, &Pt); @@ -632,6 +470,7 @@ User32DefWindowProc(HWND hWnd, * "If it is appropriate to do so, the system sends the WM_SYSCOMMAND * message to the window". When is it appropriate? */ + ERR("WM_NCRBUTTONUP\n"); break; case WM_CONTEXTMENU: @@ -649,96 +488,11 @@ User32DefWindowProc(HWND hWnd, } else { - POINT Pt; - LONG_PTR Style; - LONG HitCode; - - Style = GetWindowLongPtrW(hWnd, GWL_STYLE); - - Pt.x = GET_X_LPARAM(lParam); - Pt.y = GET_Y_LPARAM(lParam); - if (Style & WS_CHILD) - { - ScreenToClient(GetParent(hWnd), &Pt); - } - - HitCode = DefWndNCHitTest(hWnd, Pt); - - if (HitCode == HTCAPTION || HitCode == HTSYSMENU) - { - HMENU SystemMenu; - UINT Flags; - - if((SystemMenu = GetSystemMenu(hWnd, FALSE))) - { - MenuInitSysMenuPopup(SystemMenu, GetWindowLongPtrW(hWnd, GWL_STYLE), - GetClassLongPtrW(hWnd, GCL_STYLE), HitCode); - - if(HitCode == HTCAPTION) - Flags = TPM_LEFTBUTTON | TPM_RIGHTBUTTON; - else - Flags = TPM_LEFTBUTTON; - - TrackPopupMenu(SystemMenu, Flags, - Pt.x, Pt.y, 0, hWnd, NULL); - } - } - } + goto GoSS; + } break; } - case WM_PRINT: - { - DefWndPrint(hWnd, (HDC)wParam, lParam); - return (0); - } - - case WM_SYSCOLORCHANGE: - { - /* force to redraw non-client area */ - DefWndNCPaint(hWnd, HRGN_WINDOW, -1); - /* Use InvalidateRect to redraw client area, enable - * erase to redraw all subcontrols otherwise send the - * WM_SYSCOLORCHANGE to child windows/controls is required - */ - InvalidateRect(hWnd,NULL,TRUE); - return (0); - } - - case WM_PAINTICON: - case WM_PAINT: - { - PAINTSTRUCT Ps; - HDC hDC; - - /* If already in Paint and Client area is not empty just return. */ - if (pWnd->state2 & WNDS2_STARTPAINT && !IsRectEmpty(&pWnd->rcClient)) - { - ERR("In Paint and Client area is not empty!\n"); - return 0; - } - - hDC = BeginPaint(hWnd, &Ps); - if (hDC) - { - HICON hIcon; - - if (IsIconic(hWnd) && ((hIcon = (HICON)GetClassLongPtrW( hWnd, GCLP_HICON)))) - { - RECT ClientRect; - INT x, y; - GetClientRect(hWnd, &ClientRect); - x = (ClientRect.right - ClientRect.left - - GetSystemMetrics(SM_CXICON)) / 2; - y = (ClientRect.bottom - ClientRect.top - - GetSystemMetrics(SM_CYICON)) / 2; - DrawIcon(hDC, x, y, hIcon); - } - EndPaint(hWnd, &Ps); - } - return (0); - } - case WM_CLOSE: DestroyWindow(hWnd); return (0); @@ -806,109 +560,6 @@ User32DefWindowProc(HWND hWnd, case WM_SYSCOMMAND: return (DefWndHandleSysCommand(hWnd, wParam, lParam)); - case WM_KEYDOWN: - if(wParam == VK_F10) iF10Key = VK_F10; - break; - - case WM_SYSKEYDOWN: - { - if (HIWORD(lParam) & KF_ALTDOWN) - { /* Previous state, if the key was down before this message, - this is a cheap way to ignore autorepeat keys. */ - if ( !(HIWORD(lParam) & KF_REPEAT) ) - { - if ( ( wParam == VK_MENU || - wParam == VK_LMENU || - wParam == VK_RMENU ) && !iMenuSysKey ) - iMenuSysKey = 1; - else - iMenuSysKey = 0; - } - - iF10Key = 0; - - if (wParam == VK_F4) /* Try to close the window */ - { - HWND top = GetAncestor(hWnd, GA_ROOT); - if (!(GetClassLongPtrW(top, GCL_STYLE) & CS_NOCLOSE)) - PostMessageW(top, WM_SYSCOMMAND, SC_CLOSE, 0); - } - else if (wParam == VK_SNAPSHOT) // Alt-VK_SNAPSHOT? - { - HWND hwnd = hWnd; - while (GetParent(hwnd) != NULL) - { - hwnd = GetParent(hwnd); - } - DefWndScreenshot(hwnd); - } - else if ( wParam == VK_ESCAPE || wParam == VK_TAB ) // Alt-Tab/ESC Alt-Shift-Tab/ESC - { - WPARAM wParamTmp; - HWND Active = GetActiveWindow(); // Noticed MDI problem. - if (!Active) - { - FIXME("WM_SYSKEYDOWN VK_ESCAPE no active\n"); - break; - } - wParamTmp = GetKeyState(VK_SHIFT) & 0x8000 ? SC_PREVWINDOW : SC_NEXTWINDOW; - SendMessageW( Active, WM_SYSCOMMAND, wParamTmp, wParam ); - } - } - else if( wParam == VK_F10 ) - { - if (GetKeyState(VK_SHIFT) & 0x8000) - SendMessageW( hWnd, WM_CONTEXTMENU, (WPARAM)hWnd, MAKELPARAM(-1, -1) ); - iF10Key = 1; - } - break; - } - - case WM_KEYUP: - case WM_SYSKEYUP: - { - /* Press and release F10 or ALT */ - if (((wParam == VK_MENU || wParam == VK_LMENU || wParam == VK_RMENU) - && iMenuSysKey) || ((wParam == VK_F10) && iF10Key)) - SendMessageW( GetAncestor( hWnd, GA_ROOT ), WM_SYSCOMMAND, SC_KEYMENU, 0L ); - iMenuSysKey = iF10Key = 0; - break; - } - - case WM_SYSCHAR: - { - iMenuSysKey = 0; - if (wParam == VK_RETURN && IsIconic(hWnd)) - { - PostMessageW( hWnd, WM_SYSCOMMAND, SC_RESTORE, 0L ); - break; - } - if ((HIWORD(lParam) & KF_ALTDOWN) && wParam) - { - if (wParam == VK_TAB || wParam == VK_ESCAPE) break; - if (wParam == VK_SPACE && (GetWindowLongPtrW( hWnd, GWL_STYLE ) & WS_CHILD)) - SendMessageW( GetParent(hWnd), Msg, wParam, lParam ); - else - SendMessageW( hWnd, WM_SYSCOMMAND, SC_KEYMENU, wParam ); - } - else /* check for Ctrl-Esc */ - if (wParam != VK_ESCAPE) MessageBeep(0); - break; - } - - case WM_CANCELMODE: - { - iMenuSysKey = 0; - /* FIXME: Check for a desktop. */ - //if (!(GetWindowLongPtrW( hWnd, GWL_STYLE ) & WS_CHILD)) EndMenu(); - MENU_EndMenu( hWnd ); - if (GetCapture() == hWnd) - { - ReleaseCapture(); - } - break; - } - case WM_VKEYTOITEM: case WM_CHARTOITEM: return (-1); @@ -959,11 +610,6 @@ User32DefWindowProc(HWND hWnd, break; } - case WM_SETICON: - { - return DefWndSetIcon(pWnd, wParam, lParam); - } - case WM_GETICON: { return DefWndGetIcon(pWnd, wParam, lParam); @@ -1201,6 +847,30 @@ User32DefWindowProc(HWND hWnd, /* Move to Win32k !*/ case WM_SHOWWINDOW: if (!lParam) break; // Call when it is necessary. + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_NCLBUTTONDOWN: + case WM_LBUTTONDBLCLK: + case WM_NCLBUTTONDBLCLK: + case WM_NCRBUTTONDOWN: + case WM_KEYF1: + case WM_KEYUP: + case WM_SYSKEYUP: + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + case WM_SYSCHAR: + case WM_CANCELMODE: + case WM_PAINTICON: + case WM_PAINT: + case WM_PRINT: + case WM_SETICON: + case WM_SYSCOLORCHANGE: + case WM_NCUAHDRAWCAPTION: + case WM_NCUAHDRAWFRAME: + case WM_NCPAINT: + case WM_NCACTIVATE: + case WM_NCCALCSIZE: case WM_SYNCPAINT: case WM_SETREDRAW: case WM_CLIENTSHUTDOWN: @@ -1210,6 +880,7 @@ User32DefWindowProc(HWND hWnd, case WM_WINDOWPOSCHANGED: case WM_APPCOMMAND: case WM_SETCURSOR: +GoSS: { LRESULT lResult; NtUserMessageCall( hWnd, Msg, wParam, lParam, (ULONG_PTR)&lResult, FNID_DEFWINDOWPROC, !bUnicode); @@ -1399,7 +1070,7 @@ RealDefWindowProcA(HWND hWnd, if ((GetWindowLongPtrW(hWnd, GWL_STYLE) & WS_CAPTION) == WS_CAPTION) { - UserPaintCaption(hWnd); + UserPaintCaption(Wnd, DC_TEXT); IntNotifyWinEvent(EVENT_OBJECT_NAMECHANGE, hWnd, OBJID_WINDOW, CHILDID_SELF, 0); } Result = 1; @@ -1564,7 +1235,7 @@ RealDefWindowProcW(HWND hWnd, DefSetText(hWnd, (PCWSTR)lParam, FALSE); if ((GetWindowLongPtrW(hWnd, GWL_STYLE) & WS_CAPTION) == WS_CAPTION) - UserPaintCaption(hWnd); + UserPaintCaption(Wnd, DC_TEXT); Result = 1; break; } diff --git a/reactos/win32ss/user/user32/windows/draw.c b/reactos/win32ss/user/user32/windows/draw.c index d7f07fc7650..585b0c79651 100644 --- a/reactos/win32ss/user/user32/windows/draw.c +++ b/reactos/win32ss/user/user32/windows/draw.c @@ -979,6 +979,14 @@ static BOOL UITOOLS95_DrawFrameMenu(HDC dc, LPRECT r, UINT uFlags) TCHAR Symbol; switch(uFlags & 0xff) { + case DFCS_MENUARROWUP: + Symbol = '5'; + break; + + case DFCS_MENUARROWDOWN: + Symbol = '6'; + break; + case DFCS_MENUARROW: Symbol = '8'; break; diff --git a/reactos/win32ss/user/user32/windows/font.c b/reactos/win32ss/user/user32/windows/font.c index 56fec12ea68..953d776bacd 100644 --- a/reactos/win32ss/user/user32/windows/font.c +++ b/reactos/win32ss/user/user32/windows/font.c @@ -18,8 +18,8 @@ */ /* * PROJECT: ReactOS user32.dll - * FILE: lib/user32/windows/input.c - * PURPOSE: Input + * FILE: win32ss/user/user32/windows/font.c + * PURPOSE: Draw Text * PROGRAMMER: Casper S. Hornstrup (chorns@users.sourceforge.net) * UPDATE HISTORY: * 09-05-2001 CSH Created @@ -35,30 +35,15 @@ WINE_DEFAULT_DEBUG_CHANNEL(text); DWORD WINAPI GdiGetCodePage(HDC hdc); +INT WINAPI DrawTextExWorker( HDC hdc, LPWSTR str, INT i_count, + LPRECT rect, UINT flags, LPDRAWTEXTPARAMS dtp ); + +LONG TEXT_TabbedTextOut( HDC hdc, INT x, INT y, LPCWSTR lpstr, + INT count, INT cTabStops, const INT *lpTabPos, INT nTabOrg, + BOOL fDisplayText ); /* FUNCTIONS *****************************************************************/ -#ifndef NDEBUG - -#ifdef assert -#undef assert -#endif - -#define assert(e) ((e) ? (void)0 : _font_assert(#e, __FILE__, __LINE__)) - -#else -#include - -#endif - -void _font_assert(const char *msg, const char *file, int line) -{ - /* Assertion failed at foo.c line 45: x 0) - { - RECT r; - INT x0; - x0 = x; - r.left = x0; - /* chop the string into substrings of 0 or more - * possibly followed by 1 or more normal characters */ - for (i = 0; i < count; i++) - if (lpstr[i] != '\t') break; - for (j = i; j < count; j++) - if (lpstr[j] == '\t') break; - /* get the extent of the normal character part */ - GetTextExtentPointW( hdc, lpstr + i, j - i , &extent ); - /* and if there is a , calculate its position */ - if( i) { - /* get x coordinate for the drawing of this string */ - for (; cTabStops > i; lpTabPos++, cTabStops--) - { - if( nTabOrg + abs( *lpTabPos) > x) { - if( lpTabPos[ i - 1] >= 0) { - /* a left aligned tab */ - x = nTabOrg + lpTabPos[ i-1] + extent.cx; - break; - } - else - { - /* if tab pos is negative then text is right-aligned - * to tab stop meaning that the string extends to the - * left, so we must subtract the width of the string */ - if (nTabOrg - lpTabPos[ i - 1] - extent.cx > x) - { - x = nTabOrg - lpTabPos[ i - 1]; - x0 = x - extent.cx; - break; - } - } - } - } - /* if we have run out of tab stops and we have a valid default tab - * stop width then round x up to that width */ - if ((cTabStops <= i) && (defWidth > 0)) { - x0 = nTabOrg + ((x - nTabOrg) / defWidth + i) * defWidth; - x = x0 + extent.cx; - } else if ((cTabStops <= i) && (defWidth < 0)) { - x = nTabOrg + ((x - nTabOrg + extent.cx) / -defWidth + i) - * -defWidth; - x0 = x - extent.cx; - } - } else - x += extent.cx; - - if (fDisplayText) - { - r.top = y; - r.right = x; - r.bottom = y + extent.cy; - ExtTextOutW( hdc, x0, y, GetBkMode(hdc) == OPAQUE ? ETO_OPAQUE : 0, - &r, lpstr + i, j - i, NULL ); - } - count -= j; - lpstr += j; - } - return MAKELONG(x - start, extent.cy); -} - /* * @implemented */ @@ -249,841 +135,6 @@ GetTabbedTextExtentW( return TEXT_TabbedTextOut(hDC, 0, 0, lpString, nCount, nTabPositions, lpnTabStopPositions, 0, FALSE); } -/********************************************************************* - * - * DrawText functions - * - * Copied from Wine. - * Copyright 1993, 1994 Alexandre Julliard - * Copyright 2002 Bill Medland - * - * Design issues - * How many buffers to use - * While processing in DrawText there are potentially three different forms - * of the text that need to be held. How are they best held? - * 1. The original text is needed, of course, to see what to display. - * 2. The text that will be returned to the user if the DT_MODIFYSTRING is - * in effect. - * 3. The buffered text that is about to be displayed e.g. the current line. - * Typically this will exclude the ampersands used for prefixing etc. - * - * Complications. - * a. If the buffered text to be displayed includes the ampersands then - * we will need special measurement and draw functions that will ignore - * the ampersands (e.g. by copying to a buffer without the prefix and - * then using the normal forms). This may involve less space but may - * require more processing. e.g. since a line containing tabs may - * contain several underlined characters either we need to carry around - * a list of prefix locations or we may need to locate them several - * times. - * b. If we actually directly modify the "original text" as we go then we - * will need some special "caching" to handle the fact that when we - * ellipsify the text the ellipsis may modify the next line of text, - * which we have not yet processed. (e.g. ellipsification of a W at the - * end of a line will overwrite the W, the \n and the first character of - * the next line, and a \0 will overwrite the second. Try it!!) - * - * Option 1. Three separate storages. (To be implemented) - * If DT_MODIFYSTRING is in effect then allocate an extra buffer to hold - * the edited string in some form, either as the string itself or as some - * sort of "edit list" to be applied just before returning. - * Use a buffer that holds the ellipsified current line sans ampersands - * and accept the need occasionally to recalculate the prefixes (if - * DT_EXPANDTABS and not DT_NOPREFIX and not DT_HIDEPREFIX) - */ - -#define TAB 9 -#define LF 10 -#define CR 13 -#define SPACE 32 -#define PREFIX 38 -#define ALPHA_PREFIX 30 /* Win16: Alphabet prefix */ -#define KANA_PREFIX 31 /* Win16: Katakana prefix */ - -#define FORWARD_SLASH '/' -#define BACK_SLASH '\\' - -static const WCHAR ELLIPSISW[] = {'.','.','.', 0}; - -typedef struct tag_ellipsis_data -{ - int before; - int len; - int under; - int after; -} ellipsis_data; - -/********************************************************************* - * TEXT_Ellipsify (static) - * - * Add an ellipsis to the end of the given string whilst ensuring it fits. - * - * If the ellipsis alone doesn't fit then it will be returned anyway. - * - * See Also TEXT_PathEllipsify - * - * Arguments - * hdc [in] The handle to the DC that defines the font. - * str [in/out] The string that needs to be modified. - * max_str [in] The dimension of str (number of WCHAR). - * len_str [in/out] The number of characters in str - * width [in] The maximum width permitted (in logical coordinates) - * size [out] The dimensions of the text - * modstr [out] The modified form of the string, to be returned to the - * calling program. It is assumed that the caller has - * made sufficient space available so we don't need to - * know the size of the space. This pointer may be NULL if - * the modified string is not required. - * len_before [out] The number of characters before the ellipsis. - * len_ellip [out] The number of characters in the ellipsis. - * - * See for example Microsoft article Q249678. - * - * For now we will simply use three dots rather than worrying about whether - * the font contains an explicit ellipsis character. - */ -static void TEXT_Ellipsify (HDC hdc, WCHAR *str, unsigned int max_len, - unsigned int *len_str, int width, SIZE *size, - WCHAR *modstr, - int *len_before, int *len_ellip) -{ - unsigned int len_ellipsis; - unsigned int lo, mid, hi; - - len_ellipsis = strlenW (ELLIPSISW); - if (len_ellipsis > max_len) len_ellipsis = max_len; - if (*len_str > max_len - len_ellipsis) - *len_str = max_len - len_ellipsis; - - /* First do a quick binary search to get an upper bound for *len_str. */ - if (*len_str > 0 && - GetTextExtentExPointW(hdc, str, *len_str, width, NULL, NULL, size) && - size->cx > width) - { - for (lo = 0, hi = *len_str; lo < hi; ) - { - mid = (lo + hi) / 2; - if (!GetTextExtentExPointW(hdc, str, mid, width, NULL, NULL, size)) - break; - if (size->cx > width) - hi = mid; - else - lo = mid + 1; - } - *len_str = hi; - } - /* Now this should take only a couple iterations at most. */ - for ( ; ; ) - { - memcpy(str + *len_str, ELLIPSISW, len_ellipsis*sizeof(WCHAR)); - - if (!GetTextExtentExPointW (hdc, str, *len_str + len_ellipsis, width, - NULL, NULL, size)) break; - - if (!*len_str || size->cx <= width) break; - - (*len_str)--; - } - *len_ellip = len_ellipsis; - *len_before = *len_str; - *len_str += len_ellipsis; - - if (modstr) - { - memcpy (modstr, str, *len_str * sizeof(WCHAR)); - modstr[*len_str] = '\0'; - } -} - -/********************************************************************* - * TEXT_PathEllipsify (static) - * - * Add an ellipsis to the provided string in order to make it fit within - * the width. The ellipsis is added as specified for the DT_PATH_ELLIPSIS - * flag. - * - * See Also TEXT_Ellipsify - * - * Arguments - * hdc [in] The handle to the DC that defines the font. - * str [in/out] The string that needs to be modified - * max_str [in] The dimension of str (number of WCHAR). - * len_str [in/out] The number of characters in str - * width [in] The maximum width permitted (in logical coordinates) - * size [out] The dimensions of the text - * modstr [out] The modified form of the string, to be returned to the - * calling program. It is assumed that the caller has - * made sufficient space available so we don't need to - * know the size of the space. This pointer may be NULL if - * the modified string is not required. - * pellip [out] The ellipsification results - * - * For now we will simply use three dots rather than worrying about whether - * the font contains an explicit ellipsis character. - * - * The following applies, I think to Win95. We will need to extend it for - * Win98 which can have both path and end ellipsis at the same time (e.g. - * C:\MyLongFileName.Txt becomes ...\MyLongFileN...) - * - * The resulting string consists of as much as possible of the following: - * 1. The ellipsis itself - * 2. The last \ or / of the string (if any) - * 3. Everything after the last \ or / of the string (if any) or the whole - * string if there is no / or \. I believe that under Win95 this would - * include everything even though some might be clipped off the end whereas - * under Win98 that might be ellipsified too. - * Yet to be investigated is whether this would include wordbreaking if the - * filename is more than 1 word and splitting if DT_EDITCONTROL was in - * effect. (If DT_EDITCONTROL is in effect then on occasions text will be - * broken within words). - * 4. All the stuff before the / or \, which is placed before the ellipsis. - */ -static void TEXT_PathEllipsify (HDC hdc, WCHAR *str, unsigned int max_len, - unsigned int *len_str, int width, SIZE *size, - WCHAR *modstr, ellipsis_data *pellip) -{ - int len_ellipsis; - int len_trailing; - int len_under; - WCHAR *lastBkSlash, *lastFwdSlash, *lastSlash; - - len_ellipsis = strlenW (ELLIPSISW); - if (!max_len) return; - if (len_ellipsis >= max_len) len_ellipsis = max_len - 1; - if (*len_str + len_ellipsis >= max_len) - *len_str = max_len - len_ellipsis-1; - /* Hopefully this will never happen, otherwise it would probably lose - * the wrong character - */ - str[*len_str] = '\0'; /* to simplify things */ - - lastBkSlash = strrchrW (str, BACK_SLASH); - lastFwdSlash = strrchrW (str, FORWARD_SLASH); - lastSlash = lastBkSlash > lastFwdSlash ? lastBkSlash : lastFwdSlash; - if (!lastSlash) lastSlash = str; - len_trailing = *len_str - (lastSlash - str); - - /* overlap-safe movement to the right */ - memmove (lastSlash+len_ellipsis, lastSlash, len_trailing * sizeof(WCHAR)); - memcpy (lastSlash, ELLIPSISW, len_ellipsis*sizeof(WCHAR)); - len_trailing += len_ellipsis; - /* From this point on lastSlash actually points to the ellipsis in front - * of the last slash and len_trailing includes the ellipsis - */ - - len_under = 0; - for ( ; ; ) - { - if (!GetTextExtentExPointW (hdc, str, *len_str + len_ellipsis, width, - NULL, NULL, size)) break; - - if (lastSlash == str || size->cx <= width) break; - - /* overlap-safe movement to the left */ - memmove (lastSlash-1, lastSlash, len_trailing * sizeof(WCHAR)); - lastSlash--; - len_under++; - - assert (*len_str); - (*len_str)--; - } - pellip->before = lastSlash-str; - pellip->len = len_ellipsis; - pellip->under = len_under; - pellip->after = len_trailing - len_ellipsis; - *len_str += len_ellipsis; - - if (modstr) - { - memcpy(modstr, str, *len_str * sizeof(WCHAR)); - modstr[*len_str] = '\0'; - } -} - -/* Check the character is Chinese, Japanese, Korean and/or Thai */ -inline BOOL IsCJKT(WCHAR wch) -{ - if (0x0E00 <= wch && wch <= 0x0E7F) - return TRUE; /* Thai */ - - if (0x3000 <= wch && wch <= 0x9FFF) - return TRUE; /* CJK */ - - if (0xAC00 <= wch && wch <= 0xD7FF) - return TRUE; /* Korean */ - - if (0xFF00 <= wch && wch <= 0xFFEF) - return TRUE; /* CJK */ - - return FALSE; -} - -/* See http://en.wikipedia.org/wiki/Kinsoku_shori */ -static const WCHAR KinsokuClassA[] = -{ - 0x2010, 0x2013, 0x2019, 0x201D, 0x203C, 0x2047, 0x2048, 0x2049, 0x3001, - 0x3002, 0x3005, 0x3009, 0x300B, 0x300D, 0x300F, 0x3011, 0x3015, 0x3017, - 0x3019, 0x301C, 0x301F, 0x303B, 0x3041, 0x3043, 0x3045, 0x3047, 0x3049, - 0x3063, 0x3083, 0x3085, 0x3087, 0x308E, 0x3095, 0x3096, 0x30A0, 0x30A1, - 0x30A3, 0x30A5, 0x30A7, 0x30A9, 0x30C3, 0x30E3, 0x30E5, 0x30E7, 0x30EE, - 0x30F5, 0x30F6, 0x30FB, 0x30FC, 0x30FD, 0x30FE, 0x31F0, 0x31F1, 0x31F2, - 0x31F3, 0x31F4, 0x31F5, 0x31F6, 0x31F7, 0x31F8, 0x31F9, 0x31FA, 0x31FB, - 0x31FC, 0x31FD, 0x31FE, 0x31FF, 0xFF01, 0xFF09, 0xFF0C, 0xFF0E, 0xFF1A, - 0xFF1B, 0xFF1F, 0xFF3D, 0xFF5D, 0xFF60, 0 -}; - -/********************************************************************* - * TEXT_WordBreak (static) - * - * Perform wordbreak processing on the given string - * - * Assumes that DT_WORDBREAK has been specified and not all the characters - * fit. Note that this function should even be called when the first character - * that doesn't fit is known to be a space or tab, so that it can swallow them. - * - * Note that the Windows processing has some strange properties. - * 1. If the text is left-justified and there is room for some of the spaces - * that follow the last word on the line then those that fit are included on - * the line. - * 2. If the text is centred or right-justified and there is room for some of - * the spaces that follow the last word on the line then all but one of those - * that fit are included on the line. - * 3. (Reasonable behaviour) If the word breaking causes a space to be the first - * character of a new line it will be skipped. - * - * Arguments - * hdc [in] The handle to the DC that defines the font. - * str [in/out] The string that needs to be broken. - * max_str [in] The dimension of str (number of WCHAR). - * len_str [in/out] The number of characters in str - * width [in] The maximum width permitted - * format [in] The format flags in effect - * chars_fit [in] The maximum number of characters of str that are already - * known to fit; chars_fit+1 is known not to fit. - * chars_used [out] The number of characters of str that have been "used" and - * do not need to be included in later text. For example this will - * include any spaces that have been discarded from the start of - * the next line. - * size [out] The size of the returned text in logical coordinates - * - * Pedantic assumption - Assumes that the text length is monotonically - * increasing with number of characters (i.e. no weird kernings) - * - * Algorithm - * - * Work back from the last character that did fit to either a space or the last - * character of a word, whichever is met first. - * If there was one or the first character didn't fit then - * If the text is centred or right justified and that one character was a - * space then break the line before that character - * Otherwise break the line after that character - * and if the next character is a space then discard it. - * Suppose there was none (and the first character did fit). - * If Break Within Word is permitted - * break the word after the last character that fits (there must be - * at least one; none is caught earlier). - * Otherwise - * discard any trailing space. - * include the whole word; it may be ellipsified later - * - * Break Within Word is permitted under a set of circumstances that are not - * totally clear yet. Currently our best guess is: - * If DT_EDITCONTROL is in effect and neither DT_WORD_ELLIPSIS nor - * DT_PATH_ELLIPSIS is - */ - -static void TEXT_WordBreak (HDC hdc, WCHAR *str, unsigned int max_str, - unsigned int *len_str, - int width, int format, unsigned int chars_fit, - unsigned int *chars_used, SIZE *size) -{ - WCHAR *p; - int word_fits; - assert (format & DT_WORDBREAK); - assert (chars_fit < *len_str); - - /* Work back from the last character that did fit to either a space or the - * last character of a word, whichever is met first. - */ - p = str + chars_fit; /* The character that doesn't fit */ - word_fits = TRUE; - if (!chars_fit) - word_fits = FALSE; - else if (*p == SPACE) /* chars_fit < *len_str so this is valid */ - p--; /* the word just fitted */ - else - { - while (p > str && *(--p) != SPACE && (!IsCJKT(p[1]) || - p[1] == L'\0' || wcschr(KinsokuClassA, p[1]) != NULL)) - ; - word_fits = (p != str || *p == SPACE || IsCJKT(p[1])); - } - /* If there was one. */ - if (word_fits) - { - int next_is_space; - /* break the line before/after that character */ - if (!(format & (DT_RIGHT | DT_CENTER)) || *p != SPACE) - p++; - next_is_space = (unsigned int) (p - str) < *len_str && *p == SPACE; - *len_str = p - str; - /* and if the next character is a space then discard it. */ - *chars_used = *len_str; - if (next_is_space) - (*chars_used)++; - } - /* Suppose there was none. */ - else - { - if ((format & (DT_EDITCONTROL | DT_WORD_ELLIPSIS | DT_PATH_ELLIPSIS)) == - DT_EDITCONTROL) - { - /* break the word after the last character that fits (there must be - * at least one). */ - if (!chars_fit) - ++chars_fit; - *len_str = chars_fit; - *chars_used = chars_fit; - - /* FIXME - possible error. Since the next character is now removed - * this could make the text longer so that it no longer fits, and - * so we need a loop to test and shrink. - */ - } - /* Otherwise */ - else - { - /* discard any trailing space. */ - const WCHAR *e = str + *len_str; - p = str + chars_fit; - while (p < e && *p != SPACE) - p++; - *chars_used = p - str; - if (p < e) /* i.e. loop failed because *p == SPACE */ - (*chars_used)++; - - /* include the whole word; it may be ellipsified later */ - *len_str = p - str; - /* Possible optimisation; if DT_WORD_ELLIPSIS only use chars_fit+1 - * so that it will be too long - */ - } - } - /* Remeasure the string */ - GetTextExtentExPointW (hdc, str, *len_str, 0, NULL, NULL, size); -} - -/********************************************************************* - * TEXT_SkipChars - * - * Skip over the given number of characters, bearing in mind prefix - * substitution and the fact that a character may take more than one - * WCHAR (Unicode surrogates are two words long) (and there may have been - * a trailing &) - * - * Parameters - * new_count [out] The updated count - * new_str [out] The updated pointer - * start_count [in] The count of remaining characters corresponding to the - * start of the string - * start_str [in] The starting point of the string - * max [in] The number of characters actually in this segment of the - * string (the & counts) - * n [in] The number of characters to skip (if prefix then - * &c counts as one) - * prefix [in] Apply prefix substitution - * - * Return Values - * none - * - * Remarks - * There must be at least n characters in the string - * We need max because the "line" may have ended with a & followed by a tab - * or newline etc. which we don't want to swallow - */ - -static void TEXT_SkipChars (int *new_count, const WCHAR **new_str, - int start_count, const WCHAR *start_str, - int max, int n, int prefix) -{ - /* This is specific to wide characters, MSDN doesn't say anything much - * about Unicode surrogates yet and it isn't clear if _wcsinc will - * correctly handle them so we'll just do this the easy way for now - */ - - if (prefix) - { - const WCHAR *str_on_entry = start_str; - assert (max >= n); - max -= n; - while (n--) - { - if ((*start_str == PREFIX || *start_str == ALPHA_PREFIX) && max--) - start_str++; - start_str++; - } - start_count -= (start_str - str_on_entry); - } - else - { - start_str += n; - start_count -= n; - } - *new_str = start_str; - *new_count = start_count; -} - -/********************************************************************* - * TEXT_Reprefix - * - * Reanalyse the text to find the prefixed character. This is called when - * wordbreaking or ellipsification has shortened the string such that the - * previously noted prefixed character is no longer visible. - * - * Parameters - * str [in] The original string segment (including all characters) - * ns [in] The number of characters in str (including prefixes) - * pe [in] The ellipsification data - * - * Return Values - * The prefix offset within the new string segment (the one that contains the - * ellipses and does not contain the prefix characters) (-1 if none) - */ - -static int TEXT_Reprefix (const WCHAR *str, unsigned int ns, - const ellipsis_data *pe) -{ - int result = -1; - unsigned int i; - unsigned int n = pe->before + pe->under + pe->after; - assert (n <= ns); - for (i = 0; i < n; i++, str++) - { - if (i == (unsigned int) pe->before) - { - /* Reached the path ellipsis; jump over it */ - if (ns < (unsigned int) pe->under) break; - str += pe->under; - ns -= pe->under; - i += pe->under; - if (!pe->after) break; /* Nothing after the path ellipsis */ - } - if (!ns) break; - ns--; - if (*str++ == PREFIX || *str == ALPHA_PREFIX) - { - str++; - if (!ns) break; - if (*str != PREFIX) - result = (i < (unsigned int) pe->before || pe->under == 0) ? i : i - pe->under + pe->len; - /* pe->len may be non-zero while pe_under is zero */ - ns--; - } - } - return result; -} - -/********************************************************************* - * Returns true if and only if the remainder of the line is a single - * newline representation or nothing - */ - -static int remainder_is_none_or_newline (int num_chars, const WCHAR *str) -{ - if (!num_chars) return TRUE; - if (*str != LF && *str != CR) return FALSE; - if (!--num_chars) return TRUE; - if (*str == *(str+1)) return FALSE; - str++; - if (*str != CR && *str != LF) return FALSE; - if (--num_chars) return FALSE; - return TRUE; -} - -/********************************************************************* - * Return next line of text from a string. - * - * hdc - handle to DC. - * str - string to parse into lines. - * count - length of str. - * dest - destination in which to return line. - * len - dest buffer size in chars on input, copied length into dest on output. - * width - maximum width of line in pixels. - * format - format type passed to DrawText. - * retsize - returned size of the line in pixels. - * last_line - TRUE if is the last line that will be processed - * p_retstr - If DT_MODIFYSTRING this points to a cursor in the buffer in which - * the return string is built. - * tabwidth - The width of a tab in logical coordinates - * pprefix_offset - Here is where we return the offset within dest of the first - * prefixed (underlined) character. -1 is returned if there - * are none. Note that there may be more; the calling code - * will need to use TEXT_Reprefix to find any later ones. - * pellip - Here is where we return the information about any ellipsification - * that was carried out. Note that if tabs are being expanded then - * this data will correspond to the last text segment actually - * returned in dest; by definition there would not have been any - * ellipsification in earlier text segments of the line. - * - * Returns pointer to next char in str after end of the line - * or NULL if end of str reached. - */ -static const WCHAR *TEXT_NextLineW( HDC hdc, const WCHAR *str, int *count, - WCHAR *dest, int *len, int width, DWORD format, - SIZE *retsize, int last_line, WCHAR **p_retstr, - int tabwidth, int *pprefix_offset, - ellipsis_data *pellip) -{ - int i = 0, j = 0; - int plen = 0; - SIZE size = {0, 0}; - int maxl = *len; - int seg_i, seg_count, seg_j; - int max_seg_width; - int num_fit; - int word_broken; - int line_fits; - unsigned int j_in_seg; - int ellipsified; - *pprefix_offset = -1; - - /* For each text segment in the line */ - - retsize->cy = 0; - while (*count) - { - - /* Skip any leading tabs */ - - if (str[i] == TAB && (format & DT_EXPANDTABS)) - { - plen = ((plen/tabwidth)+1)*tabwidth; - (*count)--; if (j < maxl) dest[j++] = str[i++]; else i++; - while (*count && str[i] == TAB) - { - plen += tabwidth; - (*count)--; if (j < maxl) dest[j++] = str[i++]; else i++; - } - } - - - /* Now copy as far as the next tab or cr/lf or eos */ - - seg_i = i; - seg_count = *count; - seg_j = j; - - while (*count && - (str[i] != TAB || !(format & DT_EXPANDTABS)) && - ((str[i] != CR && str[i] != LF) || (format & DT_SINGLELINE))) - { - if ((format & DT_NOPREFIX) || *count <= 1) - { - (*count)--; if (j < maxl) dest[j++] = str[i++]; else i++; - continue; - } - - if (str[i] == PREFIX || str[i] == ALPHA_PREFIX) { - (*count)--, i++; /* Throw away the prefix itself */ - if (str[i] == PREFIX) - { - /* Swallow it before we see it again */ - (*count)--; if (j < maxl) dest[j++] = str[i++]; else i++; - } - else if (*pprefix_offset == -1 || *pprefix_offset >= seg_j) - { - *pprefix_offset = j; - } - /* else the previous prefix was in an earlier segment of the - * line; we will leave it to the drawing code to catch this - * one. - */ - } - else if (str[i] == KANA_PREFIX) - { - /* Throw away katakana access keys */ - (*count)--, i++; /* skip the prefix */ - (*count)--, i++; /* skip the letter */ - } - else - { - (*count)--; if (j < maxl) dest[j++] = str[i++]; else i++; - } - } - - - /* Measure the whole text segment and possibly WordBreak and - * ellipsify it - */ - - j_in_seg = j - seg_j; - max_seg_width = width - plen; - GetTextExtentExPointW (hdc, dest + seg_j, j_in_seg, max_seg_width, &num_fit, NULL, &size); - - /* The Microsoft handling of various combinations of formats is weird. - * The following may very easily be incorrect if several formats are - * combined, and may differ between versions (to say nothing of the - * several bugs in the Microsoft versions). - */ - word_broken = 0; - line_fits = (num_fit >= j_in_seg); - if (!line_fits && (format & DT_WORDBREAK)) - { - const WCHAR *s; - unsigned int chars_used; - TEXT_WordBreak (hdc, dest+seg_j, maxl-seg_j, &j_in_seg, - max_seg_width, format, num_fit, &chars_used, &size); - line_fits = (size.cx <= max_seg_width); - /* and correct the counts */ - TEXT_SkipChars (count, &s, seg_count, str+seg_i, i-seg_i, - chars_used, !(format & DT_NOPREFIX)); - i = s - str; - word_broken = 1; - } - pellip->before = j_in_seg; - pellip->under = 0; - pellip->after = 0; - pellip->len = 0; - ellipsified = 0; - if (!line_fits && (format & DT_PATH_ELLIPSIS)) - { - TEXT_PathEllipsify (hdc, dest + seg_j, maxl-seg_j, &j_in_seg, - max_seg_width, &size, *p_retstr, pellip); - line_fits = (size.cx <= max_seg_width); - ellipsified = 1; - } - /* NB we may end up ellipsifying a word-broken or path_ellipsified - * string */ - if ((!line_fits && (format & DT_WORD_ELLIPSIS)) || - ((format & DT_END_ELLIPSIS) && - ((last_line && *count) || - (remainder_is_none_or_newline (*count, &str[i]) && !line_fits)))) - { - int before, len_ellipsis; - TEXT_Ellipsify (hdc, dest + seg_j, maxl-seg_j, &j_in_seg, - max_seg_width, &size, *p_retstr, &before, &len_ellipsis); - if (before > pellip->before) - { - /* We must have done a path ellipsis too */ - pellip->after = before - pellip->before - pellip->len; - /* Leave the len as the length of the first ellipsis */ - } - else - { - /* If we are here after a path ellipsification it must be - * because even the ellipsis itself didn't fit. - */ - assert (pellip->under == 0 && pellip->after == 0); - pellip->before = before; - pellip->len = len_ellipsis; - /* pellip->after remains as zero as does - * pellip->under - */ - } - line_fits = (size.cx <= max_seg_width); - ellipsified = 1; - } - /* As an optimisation if we have ellipsified and we are expanding - * tabs and we haven't reached the end of the line we can skip to it - * now rather than going around the loop again. - */ - if ((format & DT_EXPANDTABS) && ellipsified) - { - if (format & DT_SINGLELINE) - *count = 0; - else - { - while ((*count) && str[i] != CR && str[i] != LF) - { - (*count)--, i++; - } - } - } - - j = seg_j + j_in_seg; - if (*pprefix_offset >= seg_j + pellip->before) - { - *pprefix_offset = TEXT_Reprefix (str + seg_i, i - seg_i, pellip); - if (*pprefix_offset != -1) - *pprefix_offset += seg_j; - } - - plen += size.cx; - if (size.cy > retsize->cy) - retsize->cy = size.cy; - - if (word_broken) - break; - else if (!*count) - break; - else if (str[i] == CR || str[i] == LF) - { - (*count)--, i++; - if (*count && (str[i] == CR || str[i] == LF) && str[i] != str[i-1]) - { - (*count)--, i++; - } - break; - } - /* else it was a Tab and we go around again */ - } - - retsize->cx = plen; - *len = j; - if (*count) - return (&str[i]); - else - return NULL; -} - - -/*********************************************************************** - * TEXT_DrawUnderscore - * - * Draw the underline under the prefixed character - * - * Parameters - * hdc [in] The handle of the DC for drawing - * x [in] The x location of the line segment (logical coordinates) - * y [in] The y location of where the underscore should appear - * (logical coordinates) - * str [in] The text of the line segment - * offset [in] The offset of the underscored character within str - * rect [in] Clipping rectangle (if not NULL) - */ -/* Synced with wine 1.1.32 */ -static void TEXT_DrawUnderscore (HDC hdc, int x, int y, const WCHAR *str, int offset, const RECT *rect) -{ - int prefix_x; - int prefix_end; - SIZE size; - HPEN hpen; - HPEN oldPen; - - GetTextExtentPointW (hdc, str, offset, &size); - prefix_x = x + size.cx; - GetTextExtentPointW (hdc, str, offset+1, &size); - prefix_end = x + size.cx - 1; - /* The above method may eventually be slightly wrong due to kerning etc. */ - - /* Check for clipping */ - if (rect) - { - if (prefix_x > rect->right || prefix_end < rect->left || - y < rect->top || y > rect->bottom) - return; /* Completely outside */ - /* Partially outside */ - if (prefix_x < rect->left ) prefix_x = rect->left; - if (prefix_end > rect->right) prefix_end = rect->right; - } - - hpen = CreatePen (PS_SOLID, 1, GetTextColor (hdc)); - oldPen = SelectObject (hdc, hpen); - MoveToEx (hdc, prefix_x, y, NULL); - LineTo (hdc, prefix_end, y); - SelectObject (hdc, oldPen); - DeleteObject (hpen); -} /*********************************************************************** * DrawTextExW (USER32.@) @@ -1104,234 +155,7 @@ static void TEXT_DrawUnderscore (HDC hdc, int x, int y, const WCHAR *str, int of INT WINAPI DrawTextExW( HDC hdc, LPWSTR str, INT i_count, LPRECT rect, UINT flags, LPDRAWTEXTPARAMS dtp ) { - SIZE size; - const WCHAR *strPtr; - WCHAR *retstr, *p_retstr; - size_t size_retstr; - WCHAR line[MAX_BUFFER]; - int len, lh, count=i_count; - TEXTMETRICW tm; - int lmargin = 0, rmargin = 0; - int x = rect->left, y = rect->top; - int width = rect->right - rect->left; - int max_width = 0; - int last_line; - int tabwidth /* to keep gcc happy */ = 0; - int prefix_offset; - ellipsis_data ellip; - BOOL invert_y=FALSE; - - TRACE("%s, %d, [%s] %08x\n", debugstr_wn (str, count), count, - wine_dbgstr_rect(rect), flags); - - if (dtp) TRACE("Params: iTabLength=%d, iLeftMargin=%d, iRightMargin=%d\n", - dtp->iTabLength, dtp->iLeftMargin, dtp->iRightMargin); - - if (!str) return 0; - - strPtr = str; - - if (flags & DT_SINGLELINE) - flags &= ~DT_WORDBREAK; - - GetTextMetricsW(hdc, &tm); - if (flags & DT_EXTERNALLEADING) - lh = tm.tmHeight + tm.tmExternalLeading; - else - lh = tm.tmHeight; - - if (str[0] && count == 0) - return lh; - - if (dtp && dtp->cbSize != sizeof(DRAWTEXTPARAMS)) - return 0; - - if (GetGraphicsMode(hdc) == GM_COMPATIBLE) - { - SIZE window_ext, viewport_ext; - GetWindowExtEx(hdc, &window_ext); - GetViewportExtEx(hdc, &viewport_ext); - if ((window_ext.cy > 0) != (viewport_ext.cy > 0)) - invert_y = TRUE; - } - - if (count == -1) - { - count = strlenW(str); - if (count == 0) - { - if( flags & DT_CALCRECT) - { - rect->right = rect->left; - if( flags & DT_SINGLELINE) - rect->bottom = rect->top + (invert_y ? -lh : lh); - else - rect->bottom = rect->top; - } - return lh; - } - } - - if (dtp) - { - lmargin = dtp->iLeftMargin; - rmargin = dtp->iRightMargin; - if (!(flags & (DT_CENTER | DT_RIGHT))) - x += lmargin; - dtp->uiLengthDrawn = 0; /* This param RECEIVES number of chars processed */ - } - - if (flags & DT_EXPANDTABS) - { - int tabstop = ((flags & DT_TABSTOP) && dtp) ? dtp->iTabLength : 8; - tabwidth = tm.tmAveCharWidth * tabstop; - } - - if (flags & DT_CALCRECT) flags |= DT_NOCLIP; - - if (flags & DT_MODIFYSTRING) - { - size_retstr = (count + 4) * sizeof (WCHAR); - retstr = HeapAlloc(GetProcessHeap(), 0, size_retstr); - if (!retstr) return 0; - memcpy (retstr, str, size_retstr); - } - else - { - size_retstr = 0; - retstr = NULL; - } - p_retstr = retstr; - - do - { - len = sizeof(line)/sizeof(line[0]); - if (invert_y) - last_line = !(flags & DT_NOCLIP) && y - ((flags & DT_EDITCONTROL) ? 2*lh-1 : lh) < rect->bottom; - else - last_line = !(flags & DT_NOCLIP) && y + ((flags & DT_EDITCONTROL) ? 2*lh-1 : lh) > rect->bottom; - strPtr = TEXT_NextLineW(hdc, strPtr, &count, line, &len, width, flags, &size, last_line, &p_retstr, tabwidth, &prefix_offset, &ellip); - -#ifdef __REACTOS__ - if (flags & DT_CENTER) - { - if (((rect->right - rect->left) < size.cx) && (flags & DT_CALCRECT)) - { - x = rect->left + size.cx; - } - else - { - x = (rect->left + rect->right - size.cx) / 2; - } - } -#else - if (flags & DT_CENTER) x = (rect->left + rect->right - - size.cx) / 2; -#endif - else if (flags & DT_RIGHT) x = rect->right - size.cx; - - if (flags & DT_SINGLELINE) - { - if (flags & DT_VCENTER) -#ifdef __REACTOS__ - { - if (((rect->bottom - rect->top) < (invert_y ? -size.cy : size.cy)) && (flags & DT_CALCRECT)) - { - y = rect->top + (invert_y ? -size.cy : size.cy); - } - else - { -#endif - y = rect->top + (rect->bottom - rect->top) / 2 + (invert_y ? (size.cy / 2) : (-size.cy / 2)); -#ifdef __REACTOS__ - } - } -#endif - else if (flags & DT_BOTTOM) - y = rect->bottom + (invert_y ? 0 : -size.cy); - } - - if (!(flags & DT_CALCRECT)) - { - const WCHAR *str = line; - int xseg = x; - while (len) - { - int len_seg; - SIZE size; - if ((flags & DT_EXPANDTABS)) - { - const WCHAR *p; - p = str; while (p < str+len && *p != TAB) p++; - len_seg = p - str; - if (len_seg != len && !GetTextExtentPointW(hdc, str, len_seg, &size)) - { - HeapFree (GetProcessHeap(), 0, retstr); - return 0; - } - } - else - len_seg = len; - - if (!ExtTextOutW( hdc, xseg, y, - ((flags & DT_NOCLIP) ? 0 : ETO_CLIPPED) | - ((flags & DT_RTLREADING) ? ETO_RTLREADING : 0), - rect, str, len_seg, NULL )) - { - HeapFree (GetProcessHeap(), 0, retstr); - return 0; - } - if (prefix_offset != -1 && prefix_offset < len_seg) - { - TEXT_DrawUnderscore (hdc, xseg, y + tm.tmAscent + 1, str, prefix_offset, (flags & DT_NOCLIP) ? NULL : rect); - } - len -= len_seg; - str += len_seg; - if (len) - { - assert ((flags & DT_EXPANDTABS) && *str == TAB); - len--; str++; - xseg += ((size.cx/tabwidth)+1)*tabwidth; - if (prefix_offset != -1) - { - if (prefix_offset < len_seg) - { - /* We have just drawn an underscore; we ought to - * figure out where the next one is. I am going - * to leave it for now until I have a better model - * for the line, which will make reprefixing easier. - * This is where ellip would be used. - */ - prefix_offset = -1; - } - else - prefix_offset -= len_seg; - } - } - } - } - else if (size.cx > max_width) - max_width = size.cx; - - y += invert_y ? -lh : lh; - if (dtp) - dtp->uiLengthDrawn += len; - } - while (strPtr && !last_line); - - if (flags & DT_CALCRECT) - { - rect->right = rect->left + max_width; - rect->bottom = y; - if (dtp) - rect->right += lmargin + rmargin; - } - if (retstr) - { - memcpy (str, retstr, size_retstr); - HeapFree (GetProcessHeap(), 0, retstr); - } - return y - rect->top; + return DrawTextExWorker( hdc, str, i_count, rect, flags, dtp ); } /*********************************************************************** diff --git a/reactos/win32ss/user/user32/windows/menu.c b/reactos/win32ss/user/user32/windows/menu.c index b902958f732..ce65ec6def1 100644 --- a/reactos/win32ss/user/user32/windows/menu.c +++ b/reactos/win32ss/user/user32/windows/menu.c @@ -13,10 +13,7 @@ #include #include -LRESULT DefWndNCPaint(HWND hWnd, HRGN hRgn, BOOL Active); BOOL WINAPI GdiValidateHandle(HGDIOBJ hobj); -LRESULT DefWndNCHitTest(HWND hWnd, POINT Point); -void FASTCALL NcGetSysPopupPos(HWND Wnd, RECT *Rect); WINE_DEFAULT_DEBUG_CHANNEL(menu); @@ -25,27 +22,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(menu); #define MM_SETMENUHANDLE (WM_USER + 0) #define MM_GETMENUHANDLE (WM_USER + 1) -/* internal flags for menu tracking */ - -#define TF_ENDMENU 0x10000 -#define TF_SUSPENDPOPUP 0x20000 -#define TF_SKIPREMOVE 0x40000 - -#define ITEM_PREV -1 -#define ITEM_NEXT 1 - -/* Internal MenuTrackMenu() flags */ -#define TPM_INTERNAL 0xF0000000 -#define TPM_BUTTONDOWN 0x40000000 /* menu was clicked before tracking */ -#define TPM_POPUPMENU 0x20000000 /* menu is a popup menu */ - - /* Space between 2 columns */ -#define MENU_COL_SPACE 4 - -/* top and bottom margins for popup menus */ -#define MENU_TOP_MARGIN 2 //3 -#define MENU_BOTTOM_MARGIN 2 - #define MENU_TYPE_MASK (MF_STRING | MF_BITMAP | MF_OWNERDRAW | MF_SEPARATOR) #define MENU_ITEM_TYPE(flags) ((flags) & MENU_TYPE_MASK) @@ -77,29 +53,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(menu); #define IS_BITMAP_ITEM(flags) (MF_BITMAP == MENU_ITEM_TYPE(flags)) -/* Use global popup window because there's no way 2 menus can - * be tracked at the same time. */ -static HWND top_popup; -static HMENU top_popup_hmenu; - -/* Flag set by EndMenu() to force an exit from menu tracking */ -static BOOL fEndMenu = FALSE; - -#define MENU_ITEM_HBMP_SPACE (5) -#define MENU_BAR_ITEMS_SPACE (12) -#define SEPARATOR_HEIGHT (5) -#define MENU_TAB_SPACE (8) - -typedef struct -{ - UINT TrackFlags; - HMENU CurrentMenu; /* current submenu (can be equal to hTopMenu)*/ - HMENU TopMenu; /* initial menu */ - HWND OwnerWnd; /* where notifications are sent */ - POINT Pt; -} MTRACKER; - - /********************************************************************* * PopupMenu class descriptor */ @@ -121,14 +74,6 @@ const struct builtin_class_descr POPUPMENU_builtin_class = #define GET_DWORD(ptr) (*(DWORD *)(ptr)) #endif -HFONT hMenuFont = NULL; -HFONT hMenuFontBold = NULL; - -/* Dimension of the menu bitmaps */ -static HBITMAP BmpSysMenu = NULL; - -static SIZE MenuCharSize; - /*********************************************************************** * MENU_GetMenu @@ -140,22 +85,6 @@ FORCEINLINE PMENU MENU_GetMenu(HMENU hMenu) return ValidateHandleNoErr(hMenu, TYPE_MENU); } -/*********************************************************************** - * get_win_sys_menu - * - * Get the system menu of a window - */ -static HMENU get_win_sys_menu( HWND hwnd ) -{ - HMENU ret = 0; - WND *win = ValidateHwnd( hwnd ); - if (win) - { - ret = win->SystemMenu; - } - return ret; -} - /*********************************************************************** * MENU_FindItem * @@ -214,7 +143,6 @@ ITEM *MENU_FindItem( HMENU *hmenu, UINT *nPos, UINT wFlags ) return fallback; } -#define MAX_GOINTOSUBMENU (0x10) UINT FASTCALL IntGetMenuDefaultItem(PMENU Menu, BOOL fByPos, UINT gmdiFlags, DWORD *gismc) { @@ -371,2119 +299,17 @@ static BOOL GetMenuItemInfo_common ( HMENU hmenu, return TRUE; } -/*********************************************************************** - * MenuGetRosMenuInfo - * - * Get full information about menu - */ -static BOOL FASTCALL -MenuGetRosMenuInfo(PROSMENUINFO MenuInfo, HMENU Menu) -{ - PMENU pMenu; - if (!(pMenu = ValidateHandleNoErr(Menu, TYPE_MENU))) return FALSE; - MenuInfo->hbrBack = pMenu->hbrBack; - MenuInfo->dwContextHelpID = pMenu->dwContextHelpId; - MenuInfo->cyMax = pMenu->cyMax; - MenuInfo->dwMenuData = pMenu->dwMenuData; - MenuInfo->dwStyle = pMenu->fFlags & MNS_STYLE_MASK; - - MenuInfo->cItems = pMenu->cItems; - - MenuInfo->iItem = pMenu->iItem; - MenuInfo->cxMenu = pMenu->cxMenu; - MenuInfo->cyMenu = pMenu->cyMenu; - MenuInfo->spwndNotify = pMenu->spwndNotify; - MenuInfo->cxTextAlign = pMenu->cxTextAlign; - MenuInfo->iTop = pMenu->iTop; - MenuInfo->iMaxTop = pMenu->iMaxTop; - MenuInfo->dwArrowsOn = pMenu->dwArrowsOn; - - MenuInfo->fFlags = pMenu->fFlags; - MenuInfo->Self = pMenu->head.h; - MenuInfo->TimeToHide = pMenu->TimeToHide; - MenuInfo->Wnd = pMenu->hWnd; - return TRUE; -} - -/*********************************************************************** - * MenuSetRosMenuInfo - * - * Set full information about menu - */ -static BOOL FASTCALL -MenuSetRosMenuInfo(PROSMENUINFO MenuInfo) -{ - MenuInfo->cbSize = sizeof(ROSMENUINFO); - MenuInfo->fMask = MIM_BACKGROUND | MIM_HELPID | MIM_MAXHEIGHT | MIM_MENUDATA | MIM_STYLE; - - return NtUserThunkedMenuInfo(MenuInfo->Self, (LPCMENUINFO)MenuInfo); -} - -/*********************************************************************** - * MenuInitRosMenuItemInfo - * - * Initialize a buffer for use with MenuGet/SetRosMenuItemInfo - */ -static VOID FASTCALL -MenuInitRosMenuItemInfo(PROSMENUITEMINFO ItemInfo) -{ - ZeroMemory(ItemInfo, sizeof(ROSMENUITEMINFO)); - ItemInfo->cbSize = sizeof(ROSMENUITEMINFO); -} - -/*********************************************************************** - * MenuGetRosMenuItemInfo - * - * Get full information about a menu item - */ -static BOOL FASTCALL -MenuGetRosMenuItemInfo(HMENU Menu, UINT Index, PROSMENUITEMINFO ItemInfo) -{ - PITEM pItem; - UINT Save_Mask = ItemInfo->fMask; /* Save the org mask bits. */ - - if (ItemInfo->dwTypeData != NULL) - { - HeapFree(GetProcessHeap(), 0, ItemInfo->dwTypeData); - } - - ItemInfo->dwTypeData = NULL; - - if (!(pItem = MENU_FindItem(&Menu, &Index, MF_BYPOSITION))) - { - ItemInfo->fType = 0; - return FALSE; - } - - ItemInfo->fType = pItem->fType; - ItemInfo->hbmpItem = pItem->hbmp; - ItemInfo->hbmpChecked = pItem->hbmpChecked; - ItemInfo->hbmpUnchecked = pItem->hbmpUnchecked; - ItemInfo->dwItemData = pItem->dwItemData; - ItemInfo->wID = pItem->wID; - ItemInfo->fState = pItem->fState; - - if (pItem->spSubMenu) - { - PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu); - HMENU hSubMenu = UserHMGetHandle(pSubMenu); - ItemInfo->hSubMenu = hSubMenu; - } - else - ItemInfo->hSubMenu = NULL; - - if (MENU_ITEM_TYPE(ItemInfo->fType) == MF_STRING) - { - LPWSTR lpstr = pItem->lpstr.Buffer ? DesktopPtrToUser(pItem->lpstr.Buffer) : NULL; - if (lpstr) - { - ItemInfo->cch = pItem->lpstr.Length / sizeof(WCHAR); - ItemInfo->cch++; - ItemInfo->dwTypeData = HeapAlloc(GetProcessHeap(), 0, ItemInfo->cch * sizeof(WCHAR)); - if (ItemInfo->dwTypeData == NULL) - { - return FALSE; - } - RtlCopyMemory(ItemInfo->dwTypeData, lpstr, min(ItemInfo->cch * sizeof(WCHAR), pItem->lpstr.MaximumLength)); - } - else - { - ItemInfo->cch = 0; - } - } - - ItemInfo->Rect.left = pItem->xItem; - ItemInfo->Rect.top = pItem->yItem; - ItemInfo->Rect.right = pItem->cxItem; // Do this for now...... - ItemInfo->Rect.bottom = pItem->cyItem; - ItemInfo->dxTab = pItem->dxTab; - ItemInfo->lpstr = pItem->lpstr.Buffer ? DesktopPtrToUser(pItem->lpstr.Buffer) : NULL; - ItemInfo->maxBmpSize.cx = pItem->cxBmp; - ItemInfo->maxBmpSize.cy = pItem->cyBmp; - - ItemInfo->fMask = Save_Mask; - return TRUE; -} - -/*********************************************************************** - * MenuSetRosMenuItemInfo - * - * Set selected information about a menu item, need to set the mask bits. - */ -static BOOL FASTCALL -MenuSetRosMenuItemInfo(HMENU Menu, UINT Index, PROSMENUITEMINFO ItemInfo) -{ - BOOL Ret; - - if (MENU_ITEM_TYPE(ItemInfo->fType) == MF_STRING && - ItemInfo->dwTypeData != NULL) - { - ItemInfo->cch = strlenW(ItemInfo->dwTypeData); - } - if (ItemInfo->hSubMenu) - { - if (!IsMenu(ItemInfo->hSubMenu)) ItemInfo->hSubMenu = NULL; - } - Ret = NtUserThunkedMenuItemInfo(Menu, Index, TRUE, FALSE, (LPMENUITEMINFOW)ItemInfo, NULL); - return Ret; -} - -/*********************************************************************** - * MenuCleanupRosMenuItemInfo - * - * Cleanup after use of MenuGet/SetRosMenuItemInfo - */ -static VOID FASTCALL -MenuCleanupRosMenuItemInfo(PROSMENUITEMINFO ItemInfo) -{ - if (ItemInfo->dwTypeData != NULL) - { - HeapFree(GetProcessHeap(), 0, ItemInfo->dwTypeData); - ItemInfo->dwTypeData = NULL; - } -} - -/*********************************************************************** - * MenuInitSysMenuPopup - * - * Grey the appropriate items in System menu. - */ -void FASTCALL MenuInitSysMenuPopup(HMENU hmenu, DWORD style, DWORD clsStyle, LONG HitTest ) -{ - BOOL gray; - UINT DefItem; - #if 0 - MENUITEMINFOW mii; - #endif - - gray = !(style & WS_THICKFRAME) || (style & (WS_MAXIMIZE | WS_MINIMIZE)); - EnableMenuItem( hmenu, SC_SIZE, (gray ? MF_GRAYED : MF_ENABLED) ); - gray = ((style & WS_MAXIMIZE) != 0); - EnableMenuItem( hmenu, SC_MOVE, (gray ? MF_GRAYED : MF_ENABLED) ); - gray = !(style & WS_MINIMIZEBOX) || (style & WS_MINIMIZE); - EnableMenuItem( hmenu, SC_MINIMIZE, (gray ? MF_GRAYED : MF_ENABLED) ); - gray = !(style & WS_MAXIMIZEBOX) || (style & WS_MAXIMIZE); - EnableMenuItem( hmenu, SC_MAXIMIZE, (gray ? MF_GRAYED : MF_ENABLED) ); - gray = !(style & (WS_MAXIMIZE | WS_MINIMIZE)); - EnableMenuItem( hmenu, SC_RESTORE, (gray ? MF_GRAYED : MF_ENABLED) ); - gray = (clsStyle & CS_NOCLOSE) != 0; - - /* The menu item must keep its state if it's disabled */ - if(gray) - EnableMenuItem( hmenu, SC_CLOSE, MF_GRAYED); - - /* Set default menu item */ - if(style & WS_MINIMIZE) DefItem = SC_RESTORE; - else if(HitTest == HTCAPTION) DefItem = ((style & (WS_MAXIMIZE | WS_MINIMIZE)) ? SC_RESTORE : SC_MAXIMIZE); - else DefItem = SC_CLOSE; -#if 0 - mii.cbSize = sizeof(MENUITEMINFOW); - mii.fMask |= MIIM_STATE; - if((DefItem != SC_CLOSE) && GetMenuItemInfoW(hmenu, DefItem, FALSE, &mii) && - (mii.fState & (MFS_GRAYED | MFS_DISABLED))) DefItem = SC_CLOSE; -#endif - SetMenuDefaultItem(hmenu, DefItem, MF_BYCOMMAND); -} - -/****************************************************************************** - * - * UINT MenuGetStartOfNextColumn( - * PROSMENUINFO MenuInfo) - * - *****************************************************************************/ - -static UINT MENU_GetStartOfNextColumn( - HMENU hMenu ) -{ - MENU *menu = MENU_GetMenu(hMenu); - PITEM pItem; - UINT i; - - if(!menu) - return NO_SELECTED_ITEM; - - i = menu->iItem + 1; - if( i == NO_SELECTED_ITEM ) - return i; - - pItem = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL; - if (!pItem) return NO_SELECTED_ITEM; - for( ; i < menu->cItems; ++i ) { - if (pItem[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK)) - return i; - } - - return NO_SELECTED_ITEM; -} - -/****************************************************************************** - * - * UINT MenuGetStartOfPrevColumn( - * PROSMENUINFO MenuInfo) - * - *****************************************************************************/ -static UINT MENU_GetStartOfPrevColumn( - HMENU hMenu ) -{ - MENU *menu = MENU_GetMenu(hMenu); - UINT i; - PITEM pItem; - - if( !menu ) - return NO_SELECTED_ITEM; - - if( menu->iItem == 0 || menu->iItem == NO_SELECTED_ITEM ) - return NO_SELECTED_ITEM; - - pItem = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL; - if (!pItem) return NO_SELECTED_ITEM; - - /* Find the start of the column */ - - for(i = menu->iItem; i != 0 && - !(pItem[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK)); - --i); /* empty */ - - if(i == 0) - return NO_SELECTED_ITEM; - - for(--i; i != 0; --i) { - if (pItem[i].fType & (MF_MENUBREAK | MF_MENUBARBREAK)) - break; - } - - TRACE("ret %d.\n", i ); - - return i; -} - -/*********************************************************************** - * MenuLoadBitmaps - * - * Load the arrow bitmap. We can't do this from MenuInit since user32 - * can also be used (and thus initialized) from text-mode. - */ -static void FASTCALL -MenuLoadBitmaps(VOID) -{ - /* Load system buttons bitmaps */ - if (BmpSysMenu == NULL) - { - BmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE)); - } -} -/////////// Make gpsi OBMI via callback ////////////// -/*********************************************************************** - * get_arrow_bitmap - */ -HBITMAP get_arrow_bitmap(void) -{ - static HBITMAP arrow_bitmap; - - if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_MNARROW)); - return arrow_bitmap; -} - -/*********************************************************************** - * get_down_arrow_bitmap DFCS_MENUARROWDOWN - */ -HBITMAP get_down_arrow_bitmap(void) -{ - static HBITMAP arrow_bitmap; - - if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROW)); - return arrow_bitmap; -} - -/*********************************************************************** - * get_down_arrow_inactive_bitmap DFCS_MENUARROWDOWN | DFCS_INACTIVE - */ -HBITMAP get_down_arrow_inactive_bitmap(void) -{ - static HBITMAP arrow_bitmap; - - if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_DNARROWI)); - return arrow_bitmap; -} - -/*********************************************************************** - * get_up_arrow_bitmap DFCS_MENUARROWUP - */ -HBITMAP get_up_arrow_bitmap(void) -{ - static HBITMAP arrow_bitmap; - - if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROW)); - return arrow_bitmap; -} - -/*********************************************************************** - * get_up_arrow_inactive_bitmap DFCS_MENUARROWUP | DFCS_INACTIVE - */ -static HBITMAP get_up_arrow_inactive_bitmap(void) -{ - static HBITMAP arrow_bitmap; - - if (!arrow_bitmap) arrow_bitmap = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_UPARROWI)); - return arrow_bitmap; -} -//////////////// -/*********************************************************************** - * MenuFindSubMenu - * - * Find a Sub menu. Return the position of the submenu, and modifies - * *hmenu in case it is found in another sub-menu. - * If the submenu cannot be found, NO_SELECTED_ITEM is returned. - */ -static UINT FASTCALL MenuFindSubMenu(HMENU *hmenu, HMENU hSubTarget ) -{ - PMENU menu, pSubMenu; - HMENU hSubMenu; - UINT i; - PITEM item; - - if (((*hmenu)==(HMENU)0xffff) ||(!(menu = MENU_GetMenu(*hmenu)))) - return NO_SELECTED_ITEM; - - item = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL; - for (i = 0; i < menu->cItems; i++, item++) - { - if (!item->spSubMenu) - continue; - else - { - pSubMenu = DesktopPtrToUser(item->spSubMenu); - hSubMenu = UserHMGetHandle(pSubMenu); - if (hSubMenu == hSubTarget) - { - return i; - } - else - { - HMENU hsubmenu = hSubMenu; - UINT pos = MenuFindSubMenu( &hsubmenu, hSubTarget ); - if (pos != NO_SELECTED_ITEM) - { - *hmenu = hsubmenu; - return pos; - } - } - } - } - return NO_SELECTED_ITEM; -} - -/*********************************************************************** - * MENU_AdjustMenuItemRect - * - * Adjust menu item rectangle according to scrolling state. - */ -static void -MENU_AdjustMenuItemRect(PROSMENUINFO menu, LPRECT rect) -{ - if (menu->dwArrowsOn) - { - UINT arrow_bitmap_height; - BITMAP bmp; - - GetObjectW(get_up_arrow_bitmap(), sizeof(bmp), &bmp); - arrow_bitmap_height = bmp.bmHeight; - rect->top += arrow_bitmap_height - menu->iTop; - rect->bottom += arrow_bitmap_height - menu->iTop; - } -} - -/*********************************************************************** - * MenuDrawPopupGlyph - * - * Draws popup magic glyphs (can be found in system menu). - */ -static void FASTCALL -MenuDrawPopupGlyph(HDC dc, LPRECT r, INT_PTR popupMagic, BOOL inactive, BOOL hilite) -{ - LOGFONTW lf; - HFONT hFont, hOldFont; - COLORREF clrsave; - INT bkmode; - TCHAR symbol; - switch (popupMagic) - { - case (INT_PTR) HBMMENU_POPUP_RESTORE: - symbol = '2'; - break; - case (INT_PTR) HBMMENU_POPUP_MINIMIZE: - symbol = '0'; - break; - case (INT_PTR) HBMMENU_POPUP_MAXIMIZE: - symbol = '1'; - break; - case (INT_PTR) HBMMENU_POPUP_CLOSE: - symbol = 'r'; - break; - default: - ERR("Invalid popup magic bitmap %d\n", (int)popupMagic); - return; - } - ZeroMemory(&lf, sizeof(LOGFONTW)); - InflateRect(r, -2, -2); - lf.lfHeight = r->bottom - r->top; - lf.lfWidth = 0; - lf.lfWeight = FW_NORMAL; - lf.lfCharSet = DEFAULT_CHARSET; - lstrcpy(lf.lfFaceName, TEXT("Marlett")); - hFont = CreateFontIndirect(&lf); - /* save font and text color */ - hOldFont = SelectObject(dc, hFont); - clrsave = GetTextColor(dc); - bkmode = GetBkMode(dc); - /* set color and drawing mode */ - SetBkMode(dc, TRANSPARENT); - if (inactive) - { - /* draw shadow */ - if (!hilite) - { - SetTextColor(dc, GetSysColor(COLOR_HIGHLIGHTTEXT)); - TextOut(dc, r->left + 1, r->top + 1, &symbol, 1); - } - } - SetTextColor(dc, GetSysColor(inactive ? COLOR_GRAYTEXT : (hilite ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT))); - /* draw selected symbol */ - TextOut(dc, r->left, r->top, &symbol, 1); - /* restore previous settings */ - SetTextColor(dc, clrsave); - SelectObject(dc, hOldFont); - SetBkMode(dc, bkmode); - DeleteObject(hFont); -} - -/*********************************************************************** - * MenuFindItemByKey - * - * Find the menu item selected by a key press. - * Return item id, -1 if none, -2 if we should close the menu. - */ -static UINT FASTCALL MENU_FindItemByKey(HWND WndOwner, HMENU hmenu, - WCHAR Key, BOOL ForceMenuChar) -{ - LRESULT MenuChar; - WORD Flags = 0; - - TRACE("\tlooking for '%c' (0x%02x) in [%p]\n", (char)Key, Key, hmenu ); - - if (!IsMenu( hmenu )) hmenu = GetSubMenu( get_win_sys_menu(WndOwner), 0); - if (hmenu) - { - MENU *menu = MENU_GetMenu( hmenu ); - ITEM *item = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL; - - if ( !ForceMenuChar ) - { - UINT i; - BOOL cjk = GetSystemMetrics( SM_DBCSENABLED ); - - for (i = 0; i < menu->cItems; i++, item++) - { - LPWSTR text = item->Xlpstr ? DesktopPtrToUser(item->Xlpstr) : NULL; - if( text) - { - const WCHAR *p = text - 2; - do - { - const WCHAR *q = p + 2; - p = strchrW (q, '&'); - if (!p && cjk) p = strchrW (q, '\036'); /* Japanese Win16 */ - } - while (p != NULL && p [1] == '&'); - if (p && (toupperW(p[1]) == toupperW(Key))) return i; - } - } - } - - Flags |= menu->fFlags & MNF_POPUP ? MF_POPUP : 0; - Flags |= menu->fFlags & MNF_SYSMENU ? MF_SYSMENU : 0; - - MenuChar = SendMessageW( WndOwner, WM_MENUCHAR, - MAKEWPARAM(Key, Flags), (LPARAM) hmenu); - if (HIWORD(MenuChar) == MNC_EXECUTE) return LOWORD(MenuChar); - if (HIWORD(MenuChar) == MNC_CLOSE) return (UINT)(-2); - } - return (UINT)(-1); -} - -/*********************************************************************** - * MenuGetBitmapItemSize - * - * Get the size of a bitmap item. - */ -static void FASTCALL MenuGetBitmapItemSize(PROSMENUITEMINFO lpitem, SIZE *size, HWND WndOwner) -{ - BITMAP bm; - HBITMAP bmp = lpitem->hbmpItem; - - size->cx = size->cy = 0; - - /* check if there is a magic menu item associated with this item */ - if (IS_MAGIC_BITMAP(bmp)) - { - switch((INT_PTR) bmp) - { - case (INT_PTR)HBMMENU_CALLBACK: - { - MEASUREITEMSTRUCT measItem; - measItem.CtlType = ODT_MENU; - measItem.CtlID = 0; - measItem.itemID = lpitem->wID; - measItem.itemWidth = lpitem->Rect.right - lpitem->Rect.left; - measItem.itemHeight = lpitem->Rect.bottom - lpitem->Rect.top; - measItem.itemData = lpitem->dwItemData; - SendMessageW( WndOwner, WM_MEASUREITEM, lpitem->wID, (LPARAM)&measItem); - size->cx = measItem.itemWidth; - size->cy = measItem.itemHeight; - return; - } - break; - - case (INT_PTR) HBMMENU_SYSTEM: - if (0 != lpitem->dwItemData) - { - bmp = (HBITMAP) lpitem->dwItemData; - break; - } - /* fall through */ - case (INT_PTR) HBMMENU_MBAR_RESTORE: - case (INT_PTR) HBMMENU_MBAR_MINIMIZE: - case (INT_PTR) HBMMENU_MBAR_CLOSE: - case (INT_PTR) HBMMENU_MBAR_MINIMIZE_D: - case (INT_PTR) HBMMENU_MBAR_CLOSE_D: - case (INT_PTR) HBMMENU_POPUP_CLOSE: - case (INT_PTR) HBMMENU_POPUP_RESTORE: - case (INT_PTR) HBMMENU_POPUP_MAXIMIZE: - case (INT_PTR) HBMMENU_POPUP_MINIMIZE: - /* FIXME: Why we need to subtract these magic values? */ - /* to make them smaller than the menu bar? */ - size->cx = GetSystemMetrics(SM_CXSIZE) - 2; - size->cy = GetSystemMetrics(SM_CYSIZE) - 4; - return; - } - } - - if (GetObjectW(bmp, sizeof(BITMAP), &bm)) - { - size->cx = bm.bmWidth; - size->cy = bm.bmHeight; - } -} - -/*********************************************************************** - * MenuDrawBitmapItem - * - * Draw a bitmap item. - */ -static void FASTCALL MenuDrawBitmapItem(HDC hdc, PROSMENUITEMINFO lpitem, const RECT *rect, - PROSMENUINFO MenuInfo, HWND WndOwner, UINT odaction, BOOL MenuBar) -{ - BITMAP bm; - DWORD rop; - HDC hdcMem; - HBITMAP bmp; - int w = rect->right - rect->left; - int h = rect->bottom - rect->top; - int bmp_xoffset = 0; - int left, top; - HBITMAP hbmToDraw = lpitem->hbmpItem; - bmp = hbmToDraw; - - /* Check if there is a magic menu item associated with this item */ - if (IS_MAGIC_BITMAP(hbmToDraw)) - { - UINT flags = 0; - RECT r; - - r = *rect; - switch ((INT_PTR)hbmToDraw) - { - case (INT_PTR)HBMMENU_SYSTEM: - if (lpitem->dwItemData) - { - bmp = (HBITMAP)lpitem->dwItemData; - if (!GetObjectW( bmp, sizeof(bm), &bm )) return; - } - else - { - if (!BmpSysMenu) BmpSysMenu = LoadBitmapW(0, MAKEINTRESOURCEW(OBM_CLOSE)); - bmp = BmpSysMenu; - if (! GetObjectW(bmp, sizeof(bm), &bm)) return; - /* only use right half of the bitmap */ - bmp_xoffset = bm.bmWidth / 2; - bm.bmWidth -= bmp_xoffset; - } - goto got_bitmap; - case (INT_PTR)HBMMENU_MBAR_RESTORE: - flags = DFCS_CAPTIONRESTORE; - break; - case (INT_PTR)HBMMENU_MBAR_MINIMIZE: - r.right += 1; - flags = DFCS_CAPTIONMIN; - break; - case (INT_PTR)HBMMENU_MBAR_MINIMIZE_D: - r.right += 1; - flags = DFCS_CAPTIONMIN | DFCS_INACTIVE; - break; - case (INT_PTR)HBMMENU_MBAR_CLOSE: - flags = DFCS_CAPTIONCLOSE; - break; - case (INT_PTR)HBMMENU_MBAR_CLOSE_D: - flags = DFCS_CAPTIONCLOSE | DFCS_INACTIVE; - break; - case (INT_PTR)HBMMENU_CALLBACK: - { - DRAWITEMSTRUCT drawItem; - POINT origorg; - drawItem.CtlType = ODT_MENU; - drawItem.CtlID = 0; - drawItem.itemID = lpitem->wID; - drawItem.itemAction = odaction; - drawItem.itemState = (lpitem->fState & MF_CHECKED)?ODS_CHECKED:0; - drawItem.itemState |= (lpitem->fState & MF_DEFAULT)?ODS_DEFAULT:0; - drawItem.itemState |= (lpitem->fState & MF_DISABLED)?ODS_DISABLED:0; - drawItem.itemState |= (lpitem->fState & MF_GRAYED)?ODS_GRAYED|ODS_DISABLED:0; - drawItem.itemState |= (lpitem->fState & MF_HILITE)?ODS_SELECTED:0; - //drawItem.itemState |= (!(MenuInfo->fFlags & MNF_UNDERLINE))?ODS_NOACCEL:0; - //drawItem.itemState |= (MenuInfo->fFlags & MNF_INACTIVE)?ODS_INACTIVE:0; - drawItem.hwndItem = (HWND)MenuInfo->Self; - drawItem.hDC = hdc; - drawItem.rcItem = *rect; - drawItem.itemData = lpitem->dwItemData; - /* some applications make this assumption on the DC's origin */ - SetViewportOrgEx( hdc, lpitem->Rect.left, lpitem->Rect.top, &origorg); - OffsetRect( &drawItem.rcItem, - lpitem->Rect.left, - lpitem->Rect.top); - SendMessageW( WndOwner, WM_DRAWITEM, 0, (LPARAM)&drawItem); - SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL); - return; - } - break; - - case (INT_PTR) HBMMENU_POPUP_CLOSE: - case (INT_PTR) HBMMENU_POPUP_RESTORE: - case (INT_PTR) HBMMENU_POPUP_MAXIMIZE: - case (INT_PTR) HBMMENU_POPUP_MINIMIZE: - MenuDrawPopupGlyph(hdc, &r, (INT_PTR)hbmToDraw, lpitem->fState & MF_GRAYED, lpitem->fState & MF_HILITE); - return; - } - InflateRect(&r, -1, -1); - if (0 != (lpitem->fState & MF_HILITE)) - { - flags |= DFCS_PUSHED; - } - DrawFrameControl(hdc, &r, DFC_CAPTION, flags); - return; - } - - if (!bmp || !GetObjectW( bmp, sizeof(bm), &bm )) return; - - got_bitmap: - hdcMem = CreateCompatibleDC( hdc ); - SelectObject( hdcMem, bmp ); - - /* handle fontsize > bitmap_height */ - top = (h>bm.bmHeight) ? rect->top+(h-bm.bmHeight)/2 : rect->top; - left=rect->left; - rop=((lpitem->fState & MF_HILITE) && !IS_MAGIC_BITMAP(hbmToDraw)) ? NOTSRCCOPY : SRCCOPY; - if ((lpitem->fState & MF_HILITE) && lpitem->hbmpItem) - SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT)); - BitBlt( hdc, left, top, w, h, hdcMem, bmp_xoffset, 0, rop ); - DeleteDC( hdcMem ); -} - -/*********************************************************************** - * MenuCalcItemSize - * - * Calculate the size of the menu item and store it in lpitem->rect. - */ -static void FASTCALL MenuCalcItemSize( HDC hdc, PROSMENUITEMINFO lpitem, PROSMENUINFO MenuInfo, HWND hwndOwner, - INT orgX, INT orgY, BOOL menuBar, BOOL textandbmp) -{ - WCHAR *p; - UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK ); - UINT arrow_bitmap_width; - BITMAP bm; - INT itemheight = 0; - - TRACE("dc=%x owner=%x (%d,%d)\n", hdc, hwndOwner, orgX, orgY); - - GetObjectW( get_arrow_bitmap(), sizeof(bm), &bm ); - arrow_bitmap_width = bm.bmWidth; - - MenuCharSize.cx = GdiGetCharDimensions( hdc, NULL, &MenuCharSize.cy ); - - SetRect( &lpitem->Rect, orgX, orgY, orgX, orgY ); - - if (lpitem->fType & MF_OWNERDRAW) - { - MEASUREITEMSTRUCT mis; - mis.CtlType = ODT_MENU; - mis.CtlID = 0; - mis.itemID = lpitem->wID; - mis.itemData = lpitem->dwItemData; - mis.itemHeight = HIWORD( GetDialogBaseUnits()); - mis.itemWidth = 0; - SendMessageW( hwndOwner, WM_MEASUREITEM, 0, (LPARAM)&mis ); - /* Tests reveal that Windows ( Win95 thru WinXP) adds twice the average - * width of a menufont character to the width of an owner-drawn menu. - */ - lpitem->Rect.right += mis.itemWidth + 2 * MenuCharSize.cx; - if (menuBar) { - /* under at least win95 you seem to be given a standard - height for the menu and the height value is ignored */ - lpitem->Rect.bottom += GetSystemMetrics(SM_CYMENUSIZE); - } else - lpitem->Rect.bottom += mis.itemHeight; - // Or this, - //Item->cxBmp = mis.itemWidth; - //Item->cyBmp = mis.itemHeight; - TRACE("id=%04lx size=%dx%d\n", - lpitem->wID, lpitem->Rect.right-lpitem->Rect.left, - lpitem->Rect.bottom-lpitem->Rect.top); - return; - } - - if (lpitem->fType & MF_SEPARATOR) - { - lpitem->Rect.bottom += GetSystemMetrics( SM_CYMENUSIZE)/2;//SEPARATOR_HEIGHT; - if( !menuBar) - lpitem->Rect.right += arrow_bitmap_width/*check_bitmap_width*/ + MenuCharSize.cx; - return; - } - - lpitem->dxTab = 0; - - if (lpitem->hbmpItem) - { - SIZE size; - - if (!menuBar) { - MenuGetBitmapItemSize(lpitem, &size, hwndOwner ); - /* Keep the size of the bitmap in callback mode to be able - * to draw it correctly */ - lpitem->maxBmpSize = size; - MenuInfo->cxTextAlign = max(MenuInfo->cxTextAlign, size.cx); - MenuSetRosMenuInfo(MenuInfo); - lpitem->Rect.right += size.cx + 2; - itemheight = size.cy + 2; - - if( !(MenuInfo->dwStyle & MNS_NOCHECK)) - lpitem->Rect.right += 2 * check_bitmap_width; - lpitem->Rect.right += 4 + MenuCharSize.cx; - lpitem->dxTab = lpitem->Rect.right; - lpitem->Rect.right += arrow_bitmap_width;//check_bitmap_width; - } else /* hbmpItem & MenuBar */ { - MenuGetBitmapItemSize(lpitem, &size, hwndOwner ); - lpitem->Rect.right += size.cx; - if( lpitem->lpstr) lpitem->Rect.right += 2; - itemheight = size.cy; - - /* Special case: Minimize button doesn't have a space behind it. */ - if (lpitem->hbmpItem == (HBITMAP)HBMMENU_MBAR_MINIMIZE || - lpitem->hbmpItem == (HBITMAP)HBMMENU_MBAR_MINIMIZE_D) - lpitem->Rect.right -= 1; - } - } - else if (!menuBar) { - if( !(MenuInfo->dwStyle & MNS_NOCHECK)) - lpitem->Rect.right += check_bitmap_width; - lpitem->Rect.right += 4 + MenuCharSize.cx; - lpitem->dxTab = lpitem->Rect.right; - lpitem->Rect.right += check_bitmap_width; - } - - /* it must be a text item - unless it's the system menu */ - if (!(lpitem->fType & MF_SYSMENU) && lpitem->lpstr) { - HFONT hfontOld = NULL; - RECT rc = lpitem->Rect; - LONG txtheight, txtwidth; - - if ( lpitem->fState & MFS_DEFAULT ) { - hfontOld = SelectObject( hdc, hMenuFontBold ); - } - if (menuBar) { - txtheight = DrawTextW( hdc, lpitem->lpstr, -1, &rc, - DT_SINGLELINE|DT_CALCRECT); - lpitem->Rect.right += rc.right - rc.left; - itemheight = max( max( itemheight, txtheight), - GetSystemMetrics( SM_CYMENU) - 1); - lpitem->Rect.right += 2 * MenuCharSize.cx; - } else { - if ((p = strchrW( lpitem->lpstr, '\t' )) != NULL) { - RECT tmprc = rc; - LONG tmpheight; - int n = (int)( p - lpitem->lpstr); - /* Item contains a tab (only meaningful in popup menus) */ - /* get text size before the tab */ - txtheight = DrawTextW( hdc, lpitem->lpstr, n, &rc, - DT_SINGLELINE|DT_CALCRECT); - txtwidth = rc.right - rc.left; - p += 1; /* advance past the Tab */ - /* get text size after the tab */ - tmpheight = DrawTextW( hdc, p, -1, &tmprc, - DT_SINGLELINE|DT_CALCRECT); - lpitem->dxTab += txtwidth; - txtheight = max( txtheight, tmpheight); - txtwidth += MenuCharSize.cx + /* space for the tab */ - tmprc.right - tmprc.left; /* space for the short cut */ - } else { - txtheight = DrawTextW( hdc, lpitem->lpstr, -1, &rc, - DT_SINGLELINE|DT_CALCRECT); - txtwidth = rc.right - rc.left; - lpitem->dxTab += txtwidth; - } - lpitem->Rect.right += 2 + txtwidth; - itemheight = max( itemheight, - max( txtheight + 2, MenuCharSize.cy + 4)); - } - if (hfontOld) SelectObject (hdc, hfontOld); - } else if( menuBar) { - itemheight = max( itemheight, GetSystemMetrics(SM_CYMENU)-1); - } - lpitem->Rect.bottom += itemheight; - TRACE("(%ld,%ld)-(%ld,%ld)\n", lpitem->Rect.left, lpitem->Rect.top, lpitem->Rect.right, lpitem->Rect.bottom); -} - -/*********************************************************************** - * MENU_GetMaxPopupHeight - */ -static UINT -MENU_GetMaxPopupHeight(PROSMENUINFO lppop) -{ - if (lppop->cyMax) - { - //ERR("MGMaxPH cyMax %d\n",lppop->cyMax); - return lppop->cyMax; - } - //ERR("MGMaxPH SyMax %d\n",GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER)); - return GetSystemMetrics(SM_CYSCREEN) - GetSystemMetrics(SM_CYBORDER); -} - -/*********************************************************************** - * MenuPopupMenuCalcSize - * - * Calculate the size of a popup menu. - */ -static void FASTCALL MenuPopupMenuCalcSize(PROSMENUINFO MenuInfo, HWND WndOwner) -{ - ROSMENUITEMINFO lpitem; - HDC hdc; - int start, i; - int orgX, orgY, maxX, maxTab, maxTabWidth, maxHeight; - BOOL textandbmp = FALSE; - - MenuInfo->cxMenu = MenuInfo->cyMenu = 0; - if (MenuInfo->cItems == 0) - { - MenuSetRosMenuInfo(MenuInfo); - return; - } - - hdc = GetDC(NULL); - SelectObject( hdc, hMenuFont ); - - start = 0; - maxX = 2 + 1; - - MenuInfo->cxTextAlign = 0; - - MenuInitRosMenuItemInfo(&lpitem); - //MenuGetRosMenuItemInfo(MenuInfo->Self, start, &lpitem); - while (start < MenuInfo->cItems) - { - //lpitem = &lppop->items[start]; - orgX = maxX; - //if( lpitem.fType & (MF_MENUBREAK | MF_MENUBARBREAK)) - // orgX += MENU_COL_SPACE; - orgY = MENU_TOP_MARGIN; - - maxTab = maxTabWidth = 0; - /* Parse items until column break or end of menu */ - for (i = start; i < MenuInfo->cItems; i++)//, lpitem++) - { - if (! MenuGetRosMenuItemInfo(MenuInfo->Self, i, &lpitem)) - { - MenuCleanupRosMenuItemInfo(&lpitem); - MenuSetRosMenuInfo(MenuInfo); - return; - } - if (i != start && - (lpitem.fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break; - - - MenuCalcItemSize(hdc, &lpitem, MenuInfo, WndOwner, orgX, orgY, FALSE, textandbmp); - if (! MenuSetRosMenuItemInfo(MenuInfo->Self, i, &lpitem)) - { - MenuCleanupRosMenuItemInfo(&lpitem); - MenuSetRosMenuInfo(MenuInfo); - return; - } - - maxX = max(maxX, lpitem.Rect.right); - orgY = lpitem.Rect.bottom; - if (IS_STRING_ITEM(lpitem.fType) && lpitem.dxTab ) - { - maxTab = max( maxTab, lpitem.dxTab ); - maxTabWidth = max(maxTabWidth, lpitem.Rect.right - lpitem.dxTab); - } - if( lpitem.lpstr && lpitem.hbmpItem) textandbmp = TRUE; - } - - /* Finish the column (set all items to the largest width found) */ - maxX = max( maxX, maxTab + maxTabWidth ); - while (start < i) - { - if (MenuGetRosMenuItemInfo(MenuInfo->Self, start, &lpitem)) - { - lpitem.Rect.right = maxX; - if (IS_STRING_ITEM(lpitem.fType) && lpitem.dxTab) - { - lpitem.dxTab = maxTab; - } - MenuSetRosMenuItemInfo(MenuInfo->Self, start, &lpitem); - } - start++; - } - MenuInfo->cyMenu = max(MenuInfo->cyMenu, orgY); - } - - MenuInfo->cxMenu = maxX; - /* if none of the items have both text and bitmap then - * the text and bitmaps are all aligned on the left. If there is at - * least one item with both text and bitmap then bitmaps are - * on the left and texts left aligned with the right hand side - * of the bitmaps */ - if( !textandbmp) MenuInfo->cxTextAlign = 0; - - /* space for 3d border */ - MenuInfo->cyMenu += MENU_BOTTOM_MARGIN; - MenuInfo->cxMenu += 2; - - /* Adjust popup height if it exceeds maximum */ - maxHeight = MENU_GetMaxPopupHeight(MenuInfo); - MenuInfo->iMaxTop = MenuInfo->cyMenu - MENU_TOP_MARGIN; - if (MenuInfo->cyMenu >= maxHeight) - { - MenuInfo->cyMenu = maxHeight; - MenuInfo->dwArrowsOn = 1; - } - else - { - MenuInfo->dwArrowsOn = 0; - } - - MenuCleanupRosMenuItemInfo(&lpitem); - MenuSetRosMenuInfo(MenuInfo); - ReleaseDC( 0, hdc ); -} - -/*********************************************************************** - * MenuMenuBarCalcSize - * - * FIXME: Word 6 implements its own MDI and its own 'close window' bitmap - * height is off by 1 pixel which causes lengthy window relocations when - * active document window is maximized/restored. - * - * Calculate the size of the menu bar. - */ -static void FASTCALL MenuMenuBarCalcSize( HDC hdc, LPRECT lprect, - PROSMENUINFO MenuInfo, HWND hwndOwner ) -{ - ROSMENUITEMINFO ItemInfo; - int start, i, orgX, orgY, maxY, helpPos; - - if ((lprect == NULL) || (MenuInfo == NULL)) return; - if (MenuInfo->cItems == 0) return; - TRACE("lprect %p %s\n", lprect, wine_dbgstr_rect( lprect)); - MenuInfo->cxMenu = lprect->right - lprect->left; - MenuInfo->cyMenu = 0; - maxY = lprect->top + 1; - start = 0; - helpPos = -1; - - MenuInfo->cxTextAlign = 0; - - MenuInitRosMenuItemInfo(&ItemInfo); - while (start < MenuInfo->cItems) - { - if (! MenuGetRosMenuItemInfo(MenuInfo->Self, start, &ItemInfo)) - { - MenuCleanupRosMenuItemInfo(&ItemInfo); - return; - } - orgX = lprect->left; - orgY = maxY; - - /* Parse items until line break or end of menu */ - for (i = start; i < MenuInfo->cItems; i++) - { - if ((helpPos == -1) && (ItemInfo.fType & MF_RIGHTJUSTIFY)) helpPos = i; - if ((i != start) && - (ItemInfo.fType & (MF_MENUBREAK | MF_MENUBARBREAK))) break; - - TRACE("calling MENU_CalcItemSize org=(%d, %d)\n", orgX, orgY); - MenuCalcItemSize(hdc, &ItemInfo, MenuInfo, hwndOwner, orgX, orgY, TRUE, FALSE); - if (! MenuSetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo)) - { - MenuCleanupRosMenuItemInfo(&ItemInfo); - return; - } - - if (ItemInfo.Rect.right > lprect->right) - { - if (i != start) break; - else ItemInfo.Rect.right = lprect->right; - } - maxY = max( maxY, ItemInfo.Rect.bottom ); - orgX = ItemInfo.Rect.right; - if (i + 1 < MenuInfo->cItems) - { - if (! MenuGetRosMenuItemInfo(MenuInfo->Self, i + 1, &ItemInfo)) - { - MenuCleanupRosMenuItemInfo(&ItemInfo); - return; - } - } - } - -/* FIXME: Is this really needed? */ /*NO! it is not needed, why make the - HBMMENU_MBAR_CLOSE, MINIMIZE & RESTORE, look the same size as the menu bar! */ -#if 0 - /* Finish the line (set all items to the largest height found) */ - while (start < i) - { - if (MenuGetRosMenuItemInfo(MenuInfo->Self, start, &ItemInfo)) - { - ItemInfo.Rect.bottom = maxY; - MenuSetRosMenuItemInfo(MenuInfo->Self, start, &ItemInfo); - } - start++; - } -#else - start = i; /* This works! */ -#endif - } - - lprect->bottom = maxY; - MenuInfo->cyMenu = lprect->bottom - lprect->top; - MenuSetRosMenuInfo(MenuInfo); - - if (helpPos != -1) - { - /* Flush right all items between the MF_RIGHTJUSTIFY and */ - /* the last item (if several lines, only move the last line) */ - if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->cItems - 1, &ItemInfo)) - { - MenuCleanupRosMenuItemInfo(&ItemInfo); - return; - } - orgY = ItemInfo.Rect.top; - orgX = lprect->right; - for (i = MenuInfo->cItems - 1; helpPos <= i; i--) - { - if (i < helpPos) break; /* done */ - if (ItemInfo.Rect.top != orgY) break; /* Other line */ - if (orgX <= ItemInfo.Rect.right) break; /* Too far right already */ - ItemInfo.Rect.left += orgX - ItemInfo.Rect.right; - ItemInfo.Rect.right = orgX; - orgX = ItemInfo.Rect.left; - MenuSetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo); - if (helpPos + 1 <= i && - ! MenuGetRosMenuItemInfo(MenuInfo->Self, i - 1, &ItemInfo)) - { - MenuCleanupRosMenuItemInfo(&ItemInfo); - return; - } - } - } - - MenuCleanupRosMenuItemInfo(&ItemInfo); -} - -/*********************************************************************** - * MENU_DrawScrollArrows - * - * Draw scroll arrows. - */ -static void -MENU_DrawScrollArrows(PROSMENUINFO lppop, HDC hdc) -{ - HDC hdcMem = CreateCompatibleDC(hdc); - HBITMAP hOrigBitmap; - UINT arrow_bitmap_width, arrow_bitmap_height; - BITMAP bmp; - RECT rect; - - GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp); - arrow_bitmap_width = bmp.bmWidth; - arrow_bitmap_height = bmp.bmHeight; - - if (lppop->iTop) - hOrigBitmap = SelectObject(hdcMem, get_up_arrow_bitmap()); - else - hOrigBitmap = SelectObject(hdcMem, get_up_arrow_inactive_bitmap()); - rect.left = 0; - rect.top = 0; - rect.right = lppop->cxMenu; - rect.bottom = arrow_bitmap_height; - FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU)); - BitBlt(hdc, (lppop->cxMenu - arrow_bitmap_width) / 2, 0, - arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY); - rect.top = lppop->cyMenu - arrow_bitmap_height; - rect.bottom = lppop->cyMenu; - FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENU)); - if (lppop->iTop < lppop->iMaxTop - (MENU_GetMaxPopupHeight(lppop) - 2 * arrow_bitmap_height)) - SelectObject(hdcMem, get_down_arrow_bitmap()); - else - SelectObject(hdcMem, get_down_arrow_inactive_bitmap()); - BitBlt(hdc, (lppop->cxMenu - arrow_bitmap_width) / 2, - lppop->cyMenu - arrow_bitmap_height, - arrow_bitmap_width, arrow_bitmap_height, hdcMem, 0, 0, SRCCOPY); - SelectObject(hdcMem, hOrigBitmap); - DeleteDC(hdcMem); -} - -/*********************************************************************** - * draw_popup_arrow - * - * Draws the popup-menu arrow. - */ -static void draw_popup_arrow( HDC hdc, RECT rect, UINT arrow_bitmap_width, - UINT arrow_bitmap_height) -{ - HDC hdcMem = CreateCompatibleDC( hdc ); - HBITMAP hOrigBitmap; - - hOrigBitmap = SelectObject( hdcMem, get_arrow_bitmap() ); - BitBlt( hdc, rect.right - arrow_bitmap_width - 1, - (rect.top + rect.bottom - arrow_bitmap_height) / 2, - arrow_bitmap_width, arrow_bitmap_height, - hdcMem, 0, 0, SRCCOPY ); - SelectObject( hdcMem, hOrigBitmap ); - DeleteDC( hdcMem ); -} - -/*********************************************************************** - * MenuDrawMenuItem - * - * Draw a single menu item. - */ -static void FASTCALL MenuDrawMenuItem(HWND hWnd, PROSMENUINFO MenuInfo, HWND WndOwner, HDC hdc, - PROSMENUITEMINFO lpitem, UINT Height, BOOL menuBar, UINT odaction) -{ - RECT rect; - PWCHAR Text; - BOOL flat_menu = FALSE; - int bkgnd; - UINT arrow_bitmap_width = 0, arrow_bitmap_height = 0; - PWND Wnd = ValidateHwndNoErr(hWnd); - - if (!Wnd) - return; - - if (!menuBar) { - BITMAP bmp; - GetObjectW( get_arrow_bitmap(), sizeof(bmp), &bmp ); - arrow_bitmap_width = bmp.bmWidth; - arrow_bitmap_height = bmp.bmHeight; - } - - if (lpitem->fType & MF_SYSMENU) - { - if ( (Wnd->style & WS_MINIMIZE)) - { - UserGetInsideRectNC(Wnd, &rect); - UserDrawSysMenuButton(hWnd, hdc, &rect, lpitem->fState & (MF_HILITE | MF_MOUSESELECT)); - } - return; - } - - SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0); - bkgnd = (menuBar && flat_menu) ? COLOR_MENUBAR : COLOR_MENU; - - /* Setup colors */ - - if (lpitem->fState & MF_HILITE) - { - if(menuBar && !flat_menu) { - SetTextColor(hdc, GetSysColor(COLOR_MENUTEXT)); - SetBkColor(hdc, GetSysColor(COLOR_MENU)); - } else { - if (lpitem->fState & MF_GRAYED) - SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT)); - else - SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT)); - SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT)); - } - } - else - { - if (lpitem->fState & MF_GRAYED) - SetTextColor( hdc, GetSysColor( COLOR_GRAYTEXT ) ); - else - SetTextColor( hdc, GetSysColor( COLOR_MENUTEXT ) ); - SetBkColor( hdc, GetSysColor( bkgnd ) ); - } - - TRACE("rect=%s\n", wine_dbgstr_rect( &lpitem->Rect)); - rect = lpitem->Rect; - MENU_AdjustMenuItemRect(MenuInfo, &rect); - - if (lpitem->fType & MF_OWNERDRAW) - { - /* - ** Experimentation under Windows reveals that an owner-drawn - ** menu is given the rectangle which includes the space it requested - ** in its response to WM_MEASUREITEM _plus_ width for a checkmark - ** and a popup-menu arrow. This is the value of lpitem->rect. - ** Windows will leave all drawing to the application except for - ** the popup-menu arrow. Windows always draws that itself, after - ** the menu owner has finished drawing. - */ - DRAWITEMSTRUCT dis; - - dis.CtlType = ODT_MENU; - dis.CtlID = 0; - dis.itemID = lpitem->wID; - dis.itemData = (DWORD)lpitem->dwItemData; - dis.itemState = 0; - if (lpitem->fState & MF_CHECKED) dis.itemState |= ODS_CHECKED; - if (lpitem->fState & MF_DEFAULT) dis.itemState |= ODS_DEFAULT; - if (lpitem->fState & MF_DISABLED) dis.itemState |= ODS_DISABLED; - if (lpitem->fState & MF_GRAYED) dis.itemState |= ODS_GRAYED | ODS_DISABLED; - if (lpitem->fState & MF_HILITE) dis.itemState |= ODS_SELECTED; - //if (!(MenuInfo->fFlags & MNF_UNDERLINE)) dis.itemState |= ODS_NOACCEL; - //if (MenuInfo->fFlags & MNF_INACTIVE) dis.itemState |= ODS_INACTIVE; - dis.itemAction = odaction; /* ODA_DRAWENTIRE | ODA_SELECT | ODA_FOCUS; */ - dis.hwndItem = (HWND) MenuInfo->Self; - dis.hDC = hdc; - dis.rcItem = rect; - TRACE("Ownerdraw: owner=%p itemID=%d, itemState=%d, itemAction=%d, " - "hwndItem=%p, hdc=%p, rcItem={%ld,%ld,%ld,%ld}\n", hWnd, - dis.itemID, dis.itemState, dis.itemAction, dis.hwndItem, - dis.hDC, dis.rcItem.left, dis.rcItem.top, dis.rcItem.right, - dis.rcItem.bottom); - SendMessageW(WndOwner, WM_DRAWITEM, 0, (LPARAM) &dis); - /* Draw the popup-menu arrow */ - if (lpitem->hSubMenu) - { - /* RECT rectTemp; - CopyRect(&rectTemp, &rect); - rectTemp.left = rectTemp.right - GetSystemMetrics(SM_CXMENUCHECK); - DrawFrameControl(hdc, &rectTemp, DFC_MENU, DFCS_MENUARROW); - */ - draw_popup_arrow( hdc, rect, arrow_bitmap_width, arrow_bitmap_height); - } - return; - } - - if (menuBar && (lpitem->fType & MF_SEPARATOR)) return; - - if (lpitem->fState & MF_HILITE) - { - if (flat_menu) - { - InflateRect (&rect, -1, -1); - FillRect(hdc, &rect, GetSysColorBrush(COLOR_MENUHILIGHT)); - InflateRect (&rect, 1, 1); - FrameRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT)); - } - else - { - if(menuBar) - DrawEdge(hdc, &rect, BDR_SUNKENOUTER, BF_RECT); - else - FillRect(hdc, &rect, GetSysColorBrush(COLOR_HIGHLIGHT)); - } - } - else - FillRect( hdc, &rect, GetSysColorBrush(bkgnd) ); - - SetBkMode( hdc, TRANSPARENT ); - - /* vertical separator */ - if (!menuBar && (lpitem->fType & MF_MENUBARBREAK)) - { - HPEN oldPen; - RECT rc = rect; - - rc.left -= 3;//MENU_COL_SPACE / 2 + 1; - rc.top = 3; - rc.bottom = Height - 3; - if (flat_menu) - { - oldPen = SelectObject( hdc, GetStockObject(DC_PEN) ); - SetDCPenColor(hdc, GetSysColor(COLOR_BTNSHADOW)); - MoveToEx( hdc, rc.left, rc.top, NULL ); - LineTo( hdc, rc.left, rc.bottom ); - SelectObject( hdc, oldPen ); - } - else - DrawEdge (hdc, &rc, EDGE_ETCHED, BF_LEFT); - } - - /* horizontal separator */ - if (lpitem->fType & MF_SEPARATOR) - { - HPEN oldPen; - RECT rc = rect; - - rc.left++; - rc.right--; - rc.top += SEPARATOR_HEIGHT / 2; - if (flat_menu) - { - oldPen = SelectObject( hdc, GetStockObject(DC_PEN) ); - SetDCPenColor( hdc, GetSysColor(COLOR_BTNSHADOW)); - MoveToEx( hdc, rc.left, rc.top, NULL ); - LineTo( hdc, rc.right, rc.top ); - SelectObject( hdc, oldPen ); - } - else - DrawEdge (hdc, &rc, EDGE_ETCHED, BF_TOP); - return; - } - -#if 0 - /* helper lines for debugging */ - /* This is a very good test tool when hacking menus! (JT) 07/16/2006 */ - FrameRect(hdc, &rect, GetStockObject(BLACK_BRUSH)); - SelectObject(hdc, GetStockObject(DC_PEN)); - SetDCPenColor(hdc, GetSysColor(COLOR_WINDOWFRAME)); - MoveToEx(hdc, rect.left, (rect.top + rect.bottom) / 2, NULL); - LineTo(hdc, rect.right, (rect.top + rect.bottom) / 2); -#endif - - if (!menuBar) - { - HBITMAP bm; - INT y = rect.top + rect.bottom; - RECT rc = rect; - BOOL checked = FALSE; - UINT check_bitmap_width = GetSystemMetrics( SM_CXMENUCHECK ); - UINT check_bitmap_height = GetSystemMetrics( SM_CYMENUCHECK ); - /* Draw the check mark - * - * FIXME: - * Custom checkmark bitmaps are monochrome but not always 1bpp. - */ - if( !(MenuInfo->dwStyle & MNS_NOCHECK)) { - bm = (lpitem->fState & MF_CHECKED) ? lpitem->hbmpChecked : - lpitem->hbmpUnchecked; - if (bm) /* we have a custom bitmap */ - { - HDC hdcMem = CreateCompatibleDC( hdc ); - - SelectObject( hdcMem, bm ); - BitBlt( hdc, rc.left, (y - check_bitmap_height) / 2, - check_bitmap_width, check_bitmap_height, - hdcMem, 0, 0, SRCCOPY ); - DeleteDC( hdcMem ); - checked = TRUE; - } - else if (lpitem->fState & MF_CHECKED) /* standard bitmaps */ - { - RECT r; - //HBITMAP bm = CreateBitmap( check_bitmap_width, check_bitmap_height, 1, 1, NULL ); - //HDC hdcMem = CreateCompatibleDC( hdc ); - //SelectObject( hdcMem, bm ); - //SetRect( &r, 0, 0, check_bitmap_width, check_bitmap_height); - CopyRect(&r, &rect); - r.right = r.left + GetSystemMetrics(SM_CXMENUCHECK); - DrawFrameControl( hdc, &r, DFC_MENU, - (lpitem->fType & MFT_RADIOCHECK) ? - DFCS_MENUBULLET : DFCS_MENUCHECK); - //BitBlt( hdc, rc.left, (y - r.bottom) / 2, r.right, r.bottom, hdcMem, 0, 0, SRCCOPY ); - //DeleteDC( hdcMem ); - //DeleteObject( bm ); - checked = TRUE; - } - } - if ( lpitem->hbmpItem )//&& !( checked && (MenuInfo->dwStyle & MNS_CHECKORBMP))) - { - RECT bmpRect; - CopyRect(&bmpRect, &rect); - if (!(MenuInfo->dwStyle & MNS_CHECKORBMP) && !(MenuInfo->dwStyle & MNS_NOCHECK)) - bmpRect.left += check_bitmap_width + 2; - if (!(checked && (MenuInfo->dwStyle & MNS_CHECKORBMP))) - { - //POINT origorg; - bmpRect.right = bmpRect.left + lpitem->maxBmpSize.cx; - /* some applications make this assumption on the DC's origin */ - //SetViewportOrgEx( hdc, rect.left, rect.top, &origorg); - MenuDrawBitmapItem(hdc, lpitem, &bmpRect, MenuInfo, WndOwner, odaction, menuBar); - //SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL); - } - } - /* Draw the popup-menu arrow */ - if (lpitem->hSubMenu) - { - /* RECT rectTemp; - CopyRect(&rectTemp, &rect); - rectTemp.left = rectTemp.right - GetSystemMetrics(SM_CXMENUCHECK); - DrawFrameControl(hdc, &rectTemp, DFC_MENU, DFCS_MENUARROW); - */ - draw_popup_arrow( hdc, rect, arrow_bitmap_width, arrow_bitmap_height); - } - rect.left += 4; - if( !(MenuInfo->dwStyle & MNS_NOCHECK)) - rect.left += check_bitmap_width; - rect.right -= arrow_bitmap_width;//check_bitmap_width; - } - else if( lpitem->hbmpItem) - { /* Draw the bitmap */ - //POINT origorg; - - //SetViewportOrgEx( hdc, rect.left, rect.top, &origorg); - MenuDrawBitmapItem(hdc, lpitem, &rect, MenuInfo, WndOwner, odaction, menuBar); - //SetViewportOrgEx( hdc, origorg.x, origorg.y, NULL); - } - - /* process text if present */ - if (lpitem->lpstr) - { - int i = 0; - HFONT hfontOld = 0; - - UINT uFormat = menuBar ? - DT_CENTER | DT_VCENTER | DT_SINGLELINE : - DT_LEFT | DT_VCENTER | DT_SINGLELINE; - - if ((MenuInfo->dwStyle & MNS_CHECKORBMP)) - rect.left += max(0, (int)(MenuInfo->cxTextAlign - GetSystemMetrics(SM_CXMENUCHECK))); - else - rect.left += MenuInfo->cxTextAlign; - - if ( lpitem->fState & MFS_DEFAULT ) - { - hfontOld = SelectObject(hdc, hMenuFontBold); - } - - if (menuBar) { - if( lpitem->hbmpItem) - rect.left += lpitem->maxBmpSize.cx; - if( !(lpitem->hbmpItem == HBMMENU_CALLBACK)) - rect.left += MenuCharSize.cx; - rect.right -= MenuCharSize.cx; - //rect.left += MENU_BAR_ITEMS_SPACE / 2; - //rect.right -= MENU_BAR_ITEMS_SPACE / 2; - } - - Text = lpitem->lpstr; - if(Text) - { - for (i = 0; L'\0' != Text[i]; i++) - if (Text[i] == L'\t' || Text[i] == L'\b') - break; - } - - if(lpitem->fState & MF_GRAYED) - { - if (!(lpitem->fState & MF_HILITE) ) - { - ++rect.left; ++rect.top; ++rect.right; ++rect.bottom; - SetTextColor(hdc, RGB(0xff, 0xff, 0xff)); - DrawTextW( hdc, Text, i, &rect, uFormat ); - --rect.left; --rect.top; --rect.right; --rect.bottom; - } - SetTextColor(hdc, RGB(0x80, 0x80, 0x80)); - } - - DrawTextW( hdc, Text, i, &rect, uFormat); - - /* paint the shortcut text */ - if (!menuBar && L'\0' != Text[i]) /* There's a tab or flush-right char */ - { - if (L'\t' == Text[i]) - { - rect.left = lpitem->dxTab; - uFormat = DT_LEFT | DT_VCENTER | DT_SINGLELINE; - } - else - { - rect.right = lpitem->dxTab; - uFormat = DT_RIGHT | DT_VCENTER | DT_SINGLELINE; - } - - if (lpitem->fState & MF_GRAYED) - { - if (!(lpitem->fState & MF_HILITE) ) - { - ++rect.left; ++rect.top; ++rect.right; ++rect.bottom; - SetTextColor(hdc, RGB(0xff, 0xff, 0xff)); - DrawTextW( hdc, Text + i + 1, -1, &rect, uFormat); - --rect.left; --rect.top; --rect.right; --rect.bottom; - } - SetTextColor(hdc, RGB(0x80, 0x80, 0x80)); - } - DrawTextW( hdc, Text + i + 1, -1, &rect, uFormat ); - } - - if (hfontOld) - SelectObject (hdc, hfontOld); - } -} - -/*********************************************************************** - * MenuDrawPopupMenu - * - * Paint a popup menu. - */ -static void FASTCALL MenuDrawPopupMenu(HWND hwnd, HDC hdc, HMENU hmenu ) -{ - HBRUSH hPrevBrush = 0; - RECT rect; - - TRACE("wnd=%p dc=%p menu=%p\n", hwnd, hdc, hmenu); - - GetClientRect( hwnd, &rect ); - - if((hPrevBrush = SelectObject( hdc, GetSysColorBrush(COLOR_MENU) )) - && (SelectObject( hdc, hMenuFont))) - { - HPEN hPrevPen; - - Rectangle( hdc, rect.left, rect.top, rect.right, rect.bottom ); - - hPrevPen = SelectObject( hdc, GetStockObject( NULL_PEN ) ); - if ( hPrevPen ) - { - BOOL flat_menu = FALSE; - ROSMENUINFO MenuInfo; - ROSMENUITEMINFO ItemInfo; - - SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0); - if (flat_menu) - FrameRect(hdc, &rect, GetSysColorBrush(COLOR_BTNSHADOW)); - else - DrawEdge (hdc, &rect, EDGE_RAISED, BF_RECT); - - //TRACE("hmenu %p Style %08x\n", hmenu, menu->dwStyle); - /* draw menu items */ - if (MenuGetRosMenuInfo(&MenuInfo, hmenu) && MenuInfo.cItems) - { - UINT u; - MenuInitRosMenuItemInfo(&ItemInfo); - - //for (u = MenuInfo.cItems; u > 0; u--, item++) - for (u = 0; u < MenuInfo.cItems; u++) - { - if (MenuGetRosMenuItemInfo(MenuInfo.Self, u, &ItemInfo)) - { - HWND WndOwner = MenuInfo.spwndNotify ? MenuInfo.spwndNotify->head.h : NULL; - MenuDrawMenuItem(hwnd, &MenuInfo, WndOwner, hdc, &ItemInfo, - MenuInfo.cyMenu, FALSE, ODA_DRAWENTIRE); - } - } - /* draw scroll arrows */ - if (MenuInfo.dwArrowsOn) - { - MENU_DrawScrollArrows(&MenuInfo, hdc); - } - - MenuSetRosMenuInfo(&MenuInfo); - MenuCleanupRosMenuItemInfo(&ItemInfo); - } - } else - { - SelectObject( hdc, hPrevBrush ); - } - } -} - -/*********************************************************************** - * MenuDrawMenuBar - * - * Paint a menu bar. Returns the height of the menu bar. - * called from [windows/nonclient.c] - */ -UINT MenuDrawMenuBar( HDC hDC, LPRECT lprect, HWND hwnd, - BOOL suppress_draw) -{ - ROSMENUINFO lppop; - HFONT hfontOld = 0; - HMENU hMenu = GetMenu(hwnd); - - if (! MenuGetRosMenuInfo(&lppop, hMenu) || lprect == NULL) - { - return GetSystemMetrics(SM_CYMENU); - } - - if (suppress_draw) - { - hfontOld = SelectObject(hDC, hMenuFont); - - MenuMenuBarCalcSize(hDC, lprect, &lppop, hwnd); - - lprect->bottom = lprect->top + lppop.cyMenu; - - if (hfontOld) SelectObject( hDC, hfontOld); - return lppop.cyMenu; - } - else - return DrawMenuBarTemp(hwnd, hDC, lprect, hMenu, NULL); -} - -/*********************************************************************** - * MENU_InitPopup - * - * Popup menu initialization before WM_ENTERMENULOOP. - */ -static BOOL MENU_InitPopup( HWND hwndOwner, HMENU hmenu, UINT flags ) -{ - MENU *menu; - DWORD ex_style = WS_EX_TOOLWINDOW; - ROSMENUINFO MenuInfo; - - TRACE("owner=%p hmenu=%p\n", hwndOwner, hmenu); - - if (!(menu = MENU_GetMenu( hmenu ))) return FALSE; - - /* store the owner for DrawItem */ - if (!IsWindow( hwndOwner )) - { - SetLastError( ERROR_INVALID_WINDOW_HANDLE ); - return FALSE; - } - MenuGetRosMenuInfo(&MenuInfo, menu->head.h); - //menu->hwndOwner = hwndOwner; - MenuInfo.spwndNotify = ValidateHwndNoErr( hwndOwner ); - - if (flags & TPM_LAYOUTRTL) - ex_style = WS_EX_LAYOUTRTL; - - /* NOTE: In Windows, top menu popup is not owned. */ - //menu->hWnd = CreateWindowExW( ex_style, WC_MENU, NULL, - MenuInfo.Wnd = CreateWindowExW( ex_style, WC_MENU, NULL, - WS_POPUP, 0, 0, 0, 0, - hwndOwner, 0, (HINSTANCE)GetWindowLongPtrW(hwndOwner, GWLP_HINSTANCE), - (LPVOID)hmenu ); - MenuSetRosMenuInfo(&MenuInfo); - if( !menu->hWnd ) return FALSE; - return TRUE; -} - -/*********************************************************************** - * MenuShowPopup - * - * Display a popup menu. - */ -static BOOL FASTCALL MenuShowPopup(HWND hwndOwner, HMENU hmenu, UINT id, UINT flags, - INT x, INT y, INT xanchor, INT yanchor ) -{ - ROSMENUINFO MenuInfo; - ROSMENUITEMINFO ItemInfo; - UINT width, height; - POINT pt; - HMONITOR monitor; - MONITORINFO info; - - TRACE("owner=%p hmenu=%p id=0x%04x x=0x%04x y=0x%04x xa=0x%04x ya=0x%04x\n", - hwndOwner, hmenu, id, x, y, xanchor, yanchor); - - if (! MenuGetRosMenuInfo(&MenuInfo, hmenu)) return FALSE; - if (MenuInfo.iItem != NO_SELECTED_ITEM) - { - MenuInitRosMenuItemInfo(&ItemInfo); - if (MenuGetRosMenuItemInfo(MenuInfo.Self, MenuInfo.iItem, &ItemInfo)) - { - ItemInfo.fMask |= MIIM_STATE; - ItemInfo.fState &= ~(MF_HILITE|MF_MOUSESELECT); - MenuSetRosMenuItemInfo(MenuInfo.Self, MenuInfo.iItem, &ItemInfo); - } - MenuCleanupRosMenuItemInfo(&ItemInfo); - MenuInfo.iItem = NO_SELECTED_ITEM; - } - - //menu->dwArrowsOn = 0; - MenuInfo.dwArrowsOn = 0; - MenuSetRosMenuInfo(&MenuInfo); - MenuPopupMenuCalcSize(&MenuInfo, hwndOwner); - - /* adjust popup menu pos so that it fits within the desktop */ - - width = MenuInfo.cxMenu + GetSystemMetrics(SM_CXBORDER); - height = MenuInfo.cyMenu + GetSystemMetrics(SM_CYBORDER); - - /* FIXME: should use item rect */ - pt.x = x; - pt.y = y; - monitor = MonitorFromPoint( pt, MONITOR_DEFAULTTONEAREST ); - info.cbSize = sizeof(info); - GetMonitorInfoW( monitor, &info ); - - if (flags & TPM_LAYOUTRTL) - flags ^= TPM_RIGHTALIGN; - - if( flags & TPM_RIGHTALIGN ) x -= width; - if( flags & TPM_CENTERALIGN ) x -= width / 2; - - if( flags & TPM_BOTTOMALIGN ) y -= height; - if( flags & TPM_VCENTERALIGN ) y -= height / 2; - - if( x + width > info.rcMonitor.right) - { - if( xanchor && x >= width - xanchor ) - x -= width - xanchor; - - if( x + width > info.rcMonitor.right) - x = info.rcMonitor.right - width; - } - if( x < info.rcMonitor.left ) x = info.rcMonitor.left; - - if( y + height > info.rcMonitor.bottom) - { - if( yanchor && y >= height + yanchor ) - y -= height + yanchor; - - if( y + height > info.rcMonitor.bottom) - y = info.rcMonitor.bottom - height; - } - if( y < info.rcMonitor.top ) y = info.rcMonitor.top; - - if (!top_popup) { - top_popup = MenuInfo.Wnd; - top_popup_hmenu = hmenu; - } - /* Display the window */ - - SetWindowPos( MenuInfo.Wnd, HWND_TOPMOST, x, y, width, height, - SWP_SHOWWINDOW | SWP_NOACTIVATE); - UpdateWindow( MenuInfo.Wnd ); - - IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPSTART, MenuInfo.Wnd, OBJID_CLIENT, CHILDID_SELF, 0); - - return TRUE; -} - -/*********************************************************************** - * MENU_EnsureMenuItemVisible - */ -void -MENU_EnsureMenuItemVisible(PROSMENUINFO lppop, PROSMENUITEMINFO item, HDC hdc) -{ - if (lppop->dwArrowsOn) - { - //ITEM *item = &lppop->items[wIndex]; - UINT nMaxHeight = MENU_GetMaxPopupHeight(lppop); - UINT nOldPos = lppop->iTop; - RECT rc; - UINT arrow_bitmap_height; - BITMAP bmp; - - GetClientRect(lppop->Wnd, &rc); - - GetObjectW(get_down_arrow_bitmap(), sizeof(bmp), &bmp); - arrow_bitmap_height = bmp.bmHeight; - - rc.top += arrow_bitmap_height; - rc.bottom -= arrow_bitmap_height + MENU_BOTTOM_MARGIN; - - nMaxHeight -= GetSystemMetrics(SM_CYBORDER) + 2 * arrow_bitmap_height; - if (item->Rect.bottom > lppop->iTop + nMaxHeight) - { - lppop->iTop = item->Rect.bottom - nMaxHeight; - ScrollWindow(lppop->Wnd, 0, nOldPos - lppop->iTop, &rc, &rc); - MENU_DrawScrollArrows(lppop, hdc); - //ERR("Scroll Down iTop %d iMaxTop %d nMaxHeight %d\n",lppop->iTop,lppop->iMaxTop,nMaxHeight); - } - else if (item->Rect.top - MENU_TOP_MARGIN < lppop->iTop) - { - lppop->iTop = item->Rect.top - MENU_TOP_MARGIN; - ScrollWindow(lppop->Wnd, 0, nOldPos - lppop->iTop, &rc, &rc); - MENU_DrawScrollArrows(lppop, hdc); - //ERR("Scroll Up iTop %d iMaxTop %d nMaxHeight %d\n",lppop->iTop,lppop->iMaxTop,nMaxHeight); - } - MenuSetRosMenuInfo(lppop); - } -} - -/*********************************************************************** - * MenuSelectItem - */ -static void FASTCALL MenuSelectItem(HWND hwndOwner, PROSMENUINFO hmenu, UINT wIndex, - BOOL sendMenuSelect, HMENU topmenu) -{ - ROSMENUITEMINFO ItemInfo; - ROSMENUINFO TopMenuInfo; - HDC hdc; - - TRACE("owner=%p menu=%p index=0x%04x select=0x%04x\n", hwndOwner, hmenu, wIndex, sendMenuSelect); - - if (!hmenu || !hmenu->cItems || !hmenu->Wnd) return; - if (hmenu->iItem == wIndex) return; - if (hmenu->fFlags & MNF_POPUP) hdc = GetDC(hmenu->Wnd); - else hdc = GetDCEx(hmenu->Wnd, 0, DCX_CACHE | DCX_WINDOW); - if (!top_popup) { - top_popup = hmenu->Wnd; - top_popup_hmenu = hmenu->Self; - } - - SelectObject( hdc, hMenuFont ); - - MenuInitRosMenuItemInfo(&ItemInfo); - - /* Clear previous highlighted item */ - if (hmenu->iItem != NO_SELECTED_ITEM) - { - if (MenuGetRosMenuItemInfo(hmenu->Self, hmenu->iItem, &ItemInfo)) - { - ItemInfo.fMask |= MIIM_STATE; - ItemInfo.fState &= ~(MF_HILITE|MF_MOUSESELECT); - MenuSetRosMenuItemInfo(hmenu->Self, hmenu->iItem, &ItemInfo); - } - MenuDrawMenuItem(hmenu->Wnd, hmenu, hwndOwner, hdc, &ItemInfo, - hmenu->cyMenu, !(hmenu->fFlags & MNF_POPUP), - ODA_SELECT); - } - - /* Highlight new item (if any) */ - hmenu->iItem = wIndex; - MenuSetRosMenuInfo(hmenu); - if (hmenu->iItem != NO_SELECTED_ITEM) - { - if (MenuGetRosMenuItemInfo(hmenu->Self, hmenu->iItem, &ItemInfo)) - { - if (!(ItemInfo.fType & MF_SEPARATOR)) - { - ItemInfo.fMask |= MIIM_STATE; - ItemInfo.fState |= MF_HILITE; - MenuSetRosMenuItemInfo(hmenu->Self, hmenu->iItem, &ItemInfo); - MENU_EnsureMenuItemVisible(hmenu, &ItemInfo, hdc); - MenuDrawMenuItem(hmenu->Wnd, hmenu, hwndOwner, hdc, - &ItemInfo, hmenu->cyMenu, !(hmenu->fFlags & MNF_POPUP), ODA_SELECT); - } - if (sendMenuSelect) - { - WPARAM wParam = MAKEWPARAM( ItemInfo.hSubMenu ? wIndex : ItemInfo.wID, - ItemInfo.fType | ItemInfo.fState | - (ItemInfo.hSubMenu ? MF_POPUP : 0) | - (hmenu->fFlags & MNF_SYSMENU ? MF_SYSMENU : 0 ) ); - - SendMessageW(hwndOwner, WM_MENUSELECT, wParam, (LPARAM) hmenu->Self); - } - } - } - else if (sendMenuSelect) - { - if(topmenu) - { - int pos; - pos = MenuFindSubMenu(&topmenu, hmenu->Self); - if (pos != NO_SELECTED_ITEM) - { - if (MenuGetRosMenuInfo(&TopMenuInfo, topmenu) - && MenuGetRosMenuItemInfo(topmenu, pos, &ItemInfo)) - { - WPARAM wParam = MAKEWPARAM( Pos, ItemInfo.fType | ItemInfo.fState | - (ItemInfo.hSubMenu ? MF_POPUP : 0) | - (TopMenuInfo.fFlags & MNF_SYSMENU ? MF_SYSMENU : 0 ) ); - - SendMessageW(hwndOwner, WM_MENUSELECT, wParam, (LPARAM) topmenu); - } - } - } - } - MenuCleanupRosMenuItemInfo(&ItemInfo); - ReleaseDC(hmenu->Wnd, hdc); -} - -/*********************************************************************** - * MenuMoveSelection - * - * Moves currently selected item according to the Offset parameter. - * If there is no selection then it should select the last item if - * Offset is ITEM_PREV or the first item if Offset is ITEM_NEXT. - */ -static void FASTCALL -MenuMoveSelection(HWND WndOwner, PROSMENUINFO MenuInfo, INT Offset) -{ - INT i; - ROSMENUITEMINFO ItemInfo; - INT OrigPos; - - TRACE("hwnd=%x menu=%x off=0x%04x\n", WndOwner, MenuInfo, Offset); - - /* Prevent looping */ - if (0 == MenuInfo->cItems || 0 == Offset) - return; - else if (Offset < -1) - Offset = -1; - else if (Offset > 1) - Offset = 1; - - MenuInitRosMenuItemInfo(&ItemInfo); - - OrigPos = MenuInfo->iItem; - if (OrigPos == NO_SELECTED_ITEM) /* NO_SELECTED_ITEM is not -1 ! */ - { - OrigPos = 0; - i = -1; - } - else - { - i = MenuInfo->iItem; - } - - do - { - /* Step */ - i += Offset; - /* Clip and wrap around */ - if (i < 0) - { - i = MenuInfo->cItems - 1; - } - else if (i >= MenuInfo->cItems) - { - i = 0; - } - /* If this is a good candidate; */ - if (MenuGetRosMenuItemInfo(MenuInfo->Self, i, &ItemInfo) && - 0 == (ItemInfo.fType & MF_SEPARATOR)) - { - MenuSelectItem(WndOwner, MenuInfo, i, TRUE, NULL); - MenuCleanupRosMenuItemInfo(&ItemInfo); - return; - } - } while (i != OrigPos); - - /* Not found */ - MenuCleanupRosMenuItemInfo(&ItemInfo); -} - -#if 0 +// +// User side Menu Class Proc. +// LRESULT WINAPI PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam) { -#ifdef __REACTOS__ // Do this now, remove after Server side is fixed. + LRESULT lResult; PWND pWnd; - PPOPUPMENU pPopupMenu; - pWnd = ValidateHwndNoErr(Wnd); - if (pWnd) - { - if (!pWnd->fnid) - { - if (Message != WM_NCCREATE) - { - return DefWindowProcW(Wnd, Message, wParam, lParam); - } - NtUserSetWindowFNID(Wnd, FNID_MENU); - pPopupMenu = HeapAlloc( GetProcessHeap(), 0, sizeof(POPUPMENU) ); - pPopupMenu->spwndPopupMenu = pWnd; - SetWindowLongPtrW(Wnd, 0, (LONG_PTR)pPopupMenu); - } - else - { - if (pWnd->fnid != FNID_MENU) - { - ERR("Wrong window class for Menu!\n"); - return 0; - } - pPopupMenu = ((PMENUWND)pWnd)->ppopupmenu; - } - } -#endif - - TRACE("hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam); - - switch(Message) - { - case WM_CREATE: - { - CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam; - pPopupMenu->spmenu = ValidateHandle(cs->lpCreateParams, TYPE_MENU); - return 0; - } - - case WM_MOUSEACTIVATE: /* We don't want to be activated */ - return MA_NOACTIVATE; - - case WM_PAINT: - { - PAINTSTRUCT ps; - BeginPaint(Wnd, &ps); - MenuDrawPopupMenu(Wnd, ps.hdc, pPopupMenu->spmenu->head.h); - EndPaint(Wnd, &ps); - return 0; - } - - case WM_PRINTCLIENT: - { - MenuDrawPopupMenu( Wnd, (HDC)wParam, pPopupMenu->spmenu->head.h); - return 0; - } - - case WM_ERASEBKGND: - return 1; - - case WM_DESTROY: - /* zero out global pointer in case resident popup window was destroyed. */ - if (Wnd == top_popup) - { - top_popup = NULL; - top_popup_hmenu = NULL; - } - break; - - case WM_NCDESTROY: - { - HeapFree( GetProcessHeap(), 0, pPopupMenu ); - SetWindowLongPtrW(Wnd, 0, 0); - NtUserSetWindowFNID(Wnd, FNID_DESTROY); - break; - } - - case WM_SHOWWINDOW: - if (0 != wParam) - { - if (!pPopupMenu || !pPopupMenu->spmenu) - { - OutputDebugStringA("no menu to display\n"); - } - } - /*else - { - pPopupMenu->spmenu = NULL; ///// WTF? - }*/ - break; - - case MM_SETMENUHANDLE: - { - PMENU pmenu = ValidateHandle((HMENU)wParam, TYPE_MENU); - if (!pmenu) - { - ERR("Bad Menu Handle\n"); - break; - } - pPopupMenu->spmenu = pmenu; - break; - } - - case MM_GETMENUHANDLE: - case MN_GETHMENU: - return (LRESULT)(pPopupMenu ? (pPopupMenu->spmenu ? pPopupMenu->spmenu->head.h : NULL) : NULL); - - default: - return DefWindowProcW(Wnd, Message, wParam, lParam); - } - - return 0; -} -#endif - -LRESULT WINAPI -PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam) -{ -#ifdef __REACTOS__ // Do this now, remove after Server side is fixed. - PWND pWnd; + TRACE("PMWPW : hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam); pWnd = ValidateHwnd(Wnd); if (pWnd) @@ -2494,7 +320,6 @@ PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam) { return DefWindowProcW(Wnd, Message, wParam, lParam); } - NtUserSetWindowFNID(Wnd, FNID_MENU); } else { @@ -2505,57 +330,31 @@ PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam) } } } -#endif - - TRACE("hwnd=%x msg=0x%04x wp=0x%04lx lp=0x%08lx\n", Wnd, Message, wParam, lParam); switch(Message) { + case WM_DESTROY: + case WM_NCDESTROY: + case WM_NCCREATE: case WM_CREATE: - { - CREATESTRUCTW *cs = (CREATESTRUCTW *) lParam; - SetWindowLongPtrW(Wnd, 0, (LONG_PTR)cs->lpCreateParams); - return 0; - } - - case WM_MOUSEACTIVATE: /* We don't want to be activated */ - return MA_NOACTIVATE; - + case MM_SETMENUHANDLE: + case MM_GETMENUHANDLE: + case MN_SETHMENU: + case MN_GETHMENU: case WM_PAINT: - { - PAINTSTRUCT ps; - BeginPaint(Wnd, &ps); - MenuDrawPopupMenu(Wnd, ps.hdc, (HMENU)GetWindowLongPtrW(Wnd, 0)); - EndPaint(Wnd, &ps); - return 0; - } - case WM_PRINTCLIENT: { - MenuDrawPopupMenu( Wnd, (HDC)wParam, - (HMENU)GetWindowLongPtrW( Wnd, 0 ) ); - return 0; + TRACE("Menu Class ProcW\n"); + NtUserMessageCall( Wnd, Message, wParam, lParam, (ULONG_PTR)&lResult, FNID_MENU, FALSE); + return lResult; } + case WM_MOUSEACTIVATE: /* We don't want to be activated */ + return MA_NOACTIVATE; case WM_ERASEBKGND: return 1; - case WM_DESTROY: - /* zero out global pointer in case resident popup window was destroyed. */ - if (Wnd == top_popup) - { - top_popup = NULL; - top_popup_hmenu = NULL; - } - break; - -#ifdef __REACTOS__ - case WM_NCDESTROY: - NtUserSetWindowFNID(Wnd, FNID_DESTROY); - break; -#endif - - case WM_SHOWWINDOW: + case WM_SHOWWINDOW: // Not sure what this does.... if (0 != wParam) { if (0 == GetWindowLongPtrW(Wnd, 0)) @@ -2565,18 +364,10 @@ PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam) } else { - SetWindowLongPtrW(Wnd, 0, 0); + //SetWindowLongPtrW(Wnd, 0, 0); } break; - case MM_SETMENUHANDLE: - SetWindowLongPtrW(Wnd, 0, wParam); - break; - - case MM_GETMENUHANDLE: - case MN_GETHMENU: - return GetWindowLongPtrW(Wnd, 0); - default: return DefWindowProcW(Wnd, Message, wParam, lParam); } @@ -2584,9 +375,6 @@ PopupMenuWndProcW(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam) return 0; } -// -// This breaks some test results. Should handle A2U if called! -// LRESULT WINAPI PopupMenuWndProcA(HWND Wnd, UINT Message, WPARAM wParam, LPARAM lParam) { PWND pWnd; @@ -2611,6 +399,7 @@ LRESULT WINAPI PopupMenuWndProcA(HWND Wnd, UINT Message, WPARAM wParam, LPARAM l case WM_SHOWWINDOW: case MM_SETMENUHANDLE: case MM_GETMENUHANDLE: + case MN_SETHMENU: case MN_GETHMENU: return PopupMenuWndProcW(Wnd, Message, wParam, lParam); @@ -2732,1563 +521,6 @@ static LPCSTR MENUEX_ParseResource(LPCSTR res, HMENU hMenu) } -/*********************************************************************** - * DrawMenuBarTemp (USER32.@) - * - * UNDOCUMENTED !! - * - * called by W98SE desk.cpl Control Panel Applet - * - * Not 100% sure about the param names, but close. - * - * @implemented - */ -DWORD WINAPI -DrawMenuBarTemp(HWND Wnd, HDC DC, LPRECT Rect, HMENU Menu, HFONT Font) -{ - ROSMENUINFO MenuInfo; - ROSMENUITEMINFO ItemInfo; - UINT i; - HFONT FontOld = NULL; - BOOL flat_menu = FALSE; - - SystemParametersInfoW (SPI_GETFLATMENU, 0, &flat_menu, 0); - - if (NULL == Menu) - { - Menu = GetMenu(Wnd); - } - - if (NULL == Font) - { - Font = hMenuFont; - } - - if (NULL == Rect || ! MenuGetRosMenuInfo(&MenuInfo, Menu)) - { - return GetSystemMetrics(SM_CYMENU); - } - - TRACE("(%x, %x, %p, %x, %x)\n", Wnd, DC, Rect, Menu, Font); - - FontOld = SelectObject(DC, Font); - - if (0 == MenuInfo.cyMenu) - { - MenuMenuBarCalcSize(DC, Rect, &MenuInfo, Wnd); - } - - Rect->bottom = Rect->top + MenuInfo.cyMenu; - - FillRect(DC, Rect, GetSysColorBrush(flat_menu ? COLOR_MENUBAR : COLOR_MENU)); - - SelectObject(DC, GetStockObject(DC_PEN)); - SetDCPenColor(DC, GetSysColor(COLOR_3DFACE)); - MoveToEx(DC, Rect->left, Rect->bottom - 1, NULL); - LineTo(DC, Rect->right, Rect->bottom - 1); - - if (0 == MenuInfo.cItems) - { - SelectObject(DC, FontOld); - return GetSystemMetrics(SM_CYMENU); - } - - MenuInitRosMenuItemInfo(&ItemInfo); - for (i = 0; i < MenuInfo.cItems; i++) - { - if (MenuGetRosMenuItemInfo(MenuInfo.Self, i, &ItemInfo)) - { - MenuDrawMenuItem(Wnd, &MenuInfo, Wnd, DC, &ItemInfo, - MenuInfo.cyMenu, TRUE, ODA_DRAWENTIRE); - } - } - MenuCleanupRosMenuItemInfo(&ItemInfo); - - SelectObject(DC, FontOld); - - return MenuInfo.cyMenu; -} - - -/*********************************************************************** - * MenuShowSubPopup - * - * Display the sub-menu of the selected item of this menu. - * Return the handle of the submenu, or menu if no submenu to display. - */ -static HMENU FASTCALL -MenuShowSubPopup(HWND WndOwner, PROSMENUINFO MenuInfo, BOOL SelectFirst, UINT Flags) -{ - RECT Rect; - ROSMENUITEMINFO ItemInfo; - ROSMENUINFO SubMenuInfo; - HDC Dc; - HMENU Ret; - - TRACE("owner=%x menu=%p 0x%04x\n", WndOwner, MenuInfo, SelectFirst); - - if (MenuInfo->iItem == NO_SELECTED_ITEM) return MenuInfo->Self; - - MenuInitRosMenuItemInfo(&ItemInfo); - if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo)) - { - MenuCleanupRosMenuItemInfo(&ItemInfo); - return MenuInfo->Self; - } - - //item = &menu->rgItems[menu->iItem]; - if (!(ItemInfo.hSubMenu) || (ItemInfo.fState & (MF_GRAYED | MF_DISABLED))) - { - MenuCleanupRosMenuItemInfo(&ItemInfo); - return MenuInfo->Self; - } - - /* message must be sent before using item, - because nearly everything may be changed by the application ! */ - - /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */ - if (!(Flags & TPM_NONOTIFY)) - { - SendMessageW(WndOwner, WM_INITMENUPOPUP, (WPARAM) ItemInfo.hSubMenu, - MAKELPARAM(MenuInfo->iItem, IS_SYSTEM_MENU(MenuInfo))); - } - - if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo)) - { - MenuCleanupRosMenuItemInfo(&ItemInfo); - return MenuInfo->Self; - } - - //item = &menu->rgItems[menu->iItem]; - Rect = ItemInfo.Rect; - - /* correct item if modified as a reaction to WM_INITMENUPOPUP message */ - if (!(ItemInfo.fState & MF_HILITE)) - { - if (MenuInfo->fFlags & MNF_POPUP) Dc = GetDC(MenuInfo->Wnd); - else Dc = GetDCEx(MenuInfo->Wnd, 0, DCX_CACHE | DCX_WINDOW); - - SelectObject(Dc, hMenuFont); - ItemInfo.fMask |= MIIM_STATE; - ItemInfo.fState |= MF_HILITE; - MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo); - MenuDrawMenuItem(MenuInfo->Wnd, MenuInfo, WndOwner, Dc, &ItemInfo, MenuInfo->cyMenu, - !(MenuInfo->fFlags & MNF_POPUP), ODA_DRAWENTIRE); - ReleaseDC(MenuInfo->Wnd, Dc); - } - - if (!ItemInfo.Rect.top && !ItemInfo.Rect.left && !ItemInfo.Rect.bottom && !ItemInfo.Rect.right) - ItemInfo.Rect = Rect; - - ItemInfo.fMask |= MIIM_STATE; - ItemInfo.fState |= MF_MOUSESELECT; - MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo); - - if (IS_SYSTEM_MENU(MenuInfo)) - { - MenuInitSysMenuPopup(ItemInfo.hSubMenu, - GetWindowLongPtrW(MenuInfo->Wnd, GWL_STYLE), - GetClassLongPtrW(MenuInfo->Wnd, GCL_STYLE), HTSYSMENU); - - NcGetSysPopupPos(MenuInfo->Wnd, &Rect); - if (Flags & TPM_LAYOUTRTL) Rect.left = Rect.right; - Rect.top = Rect.bottom; - Rect.right = GetSystemMetrics(SM_CXSIZE); - Rect.bottom = GetSystemMetrics(SM_CYSIZE); - } - else - { - GetWindowRect(MenuInfo->Wnd, &Rect); - if (MenuInfo->fFlags & MNF_POPUP) - { - RECT rc = ItemInfo.Rect; - - MENU_AdjustMenuItemRect(MenuInfo, &rc); - - /* The first item in the popup menu has to be at the - same y position as the focused menu item */ - if(Flags & TPM_LAYOUTRTL) - Rect.left += GetSystemMetrics(SM_CXBORDER); - else - Rect.left += rc.right /*ItemInfo.Rect.right*/ - GetSystemMetrics(SM_CXBORDER); - Rect.top += rc.top - MENU_TOP_MARGIN;//3; - Rect.right = rc.left - rc.right + GetSystemMetrics(SM_CXBORDER); - Rect.bottom = rc.top - rc.bottom - MENU_TOP_MARGIN - MENU_BOTTOM_MARGIN/*2*/ - - GetSystemMetrics(SM_CYBORDER); - } - else - { - if(Flags & TPM_LAYOUTRTL) - Rect.left += Rect.right - ItemInfo.Rect.left; - else - Rect.left += ItemInfo.Rect.left; - Rect.top += ItemInfo.Rect.bottom; - Rect.right = ItemInfo.Rect.right - ItemInfo.Rect.left; - Rect.bottom = ItemInfo.Rect.bottom - ItemInfo.Rect.top; - } - } - - /* use default alignment for submenus */ - Flags &= ~(TPM_CENTERALIGN | TPM_RIGHTALIGN | TPM_VCENTERALIGN | TPM_BOTTOMALIGN); - - MENU_InitPopup( WndOwner, ItemInfo.hSubMenu, Flags ); - - MenuShowPopup(WndOwner, ItemInfo.hSubMenu, MenuInfo->iItem, Flags, - Rect.left, Rect.top, Rect.right, Rect.bottom ); - if (SelectFirst && MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu)) - { - MenuMoveSelection(WndOwner, &SubMenuInfo, ITEM_NEXT); - } - - Ret = ItemInfo.hSubMenu; - MenuCleanupRosMenuItemInfo(&ItemInfo); - - return Ret; -} - -/********************************************************************** - * MENU_EndMenu - * - * Calls EndMenu() if the hwnd parameter belongs to the menu owner - * - * Does the (menu stuff) of the default window handling of WM_CANCELMODE - */ -void MENU_EndMenu( HWND hwnd ) -{ - MENU *menu; - menu = top_popup_hmenu ? MENU_GetMenu( top_popup_hmenu ) : NULL; - if (menu && ( hwnd == menu->hWnd || hwnd == (menu->spwndNotify ? menu->spwndNotify->head.h : NULL)) ) - EndMenu(); -} - -/*********************************************************************** - * MenuHideSubPopups - * - * Hide the sub-popup menus of this menu. - */ -static void FASTCALL -MenuHideSubPopups(HWND WndOwner, PROSMENUINFO MenuInfo, - BOOL SendMenuSelect, UINT wFlags) -{ - ROSMENUINFO SubMenuInfo; - ROSMENUITEMINFO ItemInfo; - - TRACE("owner=%x menu=%x 0x%04x\n", WndOwner, MenuInfo, SendMenuSelect); - - if (MenuInfo && top_popup && NO_SELECTED_ITEM != MenuInfo->iItem) - { - //item = &menu->rgItems[menu->iItem]; - MenuInitRosMenuItemInfo(&ItemInfo); - ItemInfo.fMask |= MIIM_FTYPE | MIIM_STATE; - if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo) - || !(ItemInfo.hSubMenu) - || !(ItemInfo.fState & MF_MOUSESELECT)) - { - MenuCleanupRosMenuItemInfo(&ItemInfo); - return; - } - ItemInfo.fState &= ~MF_MOUSESELECT; - ItemInfo.fMask |= MIIM_STATE; - MenuSetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo); - if (MenuGetRosMenuInfo(&SubMenuInfo, ItemInfo.hSubMenu)) - { - MenuHideSubPopups(WndOwner, &SubMenuInfo, FALSE, wFlags); - MenuSelectItem(WndOwner, &SubMenuInfo, NO_SELECTED_ITEM, SendMenuSelect, NULL); - - DestroyWindow(SubMenuInfo.Wnd); - /* Native returns handle to destroyed window */ - if (!(wFlags & TPM_NONOTIFY)) - SendMessageW( WndOwner, WM_UNINITMENUPOPUP, (WPARAM)ItemInfo.hSubMenu, - MAKELPARAM(0, IS_SYSTEM_MENU(&SubMenuInfo)) ); - //// - // Call WM_UNINITMENUPOPUP FIRST before destroy!! - // Fixes todo_wine User32 test menu.c line 2233 GetMenuBarInfo callback.... - // - SubMenuInfo.Wnd = NULL; - MenuSetRosMenuInfo(&SubMenuInfo); - //// - } - } -} - -/*********************************************************************** - * MenuSwitchTracking - * - * Helper function for menu navigation routines. - */ -static void FASTCALL -MenuSwitchTracking(MTRACKER* pmt, PROSMENUINFO PtMenuInfo, UINT Index, UINT wFlags) -{ - ROSMENUINFO TopMenuInfo; - - TRACE("%x menu=%x 0x%04x\n", pmt, PtMenuInfo->Self, Index); - - if ( MenuGetRosMenuInfo(&TopMenuInfo, pmt->TopMenu) && - pmt->TopMenu != PtMenuInfo->Self && - !((PtMenuInfo->fFlags | TopMenuInfo.fFlags) & MNF_POPUP) ) - { - /* both are top level menus (system and menu-bar) */ - MenuHideSubPopups(pmt->OwnerWnd, &TopMenuInfo, FALSE, wFlags); - MenuSelectItem(pmt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM, FALSE, NULL); - pmt->TopMenu = PtMenuInfo->Self; - } - else - { - MenuHideSubPopups(pmt->OwnerWnd, PtMenuInfo, FALSE, wFlags); - } - - MenuSelectItem(pmt->OwnerWnd, PtMenuInfo, Index, TRUE, NULL); -} - -/*********************************************************************** - * MenuExecFocusedItem - * - * Execute a menu item (for instance when user pressed Enter). - * Return the wID of the executed item. Otherwise, -1 indicating - * that no menu item was executed, -2 if a popup is shown; - * Have to receive the flags for the TrackPopupMenu options to avoid - * sending unwanted message. - * - */ -static INT FASTCALL -MenuExecFocusedItem(MTRACKER *pmt, PROSMENUINFO MenuInfo, UINT Flags) -{ - ROSMENUITEMINFO ItemInfo; - UINT wID; - - TRACE("%p menu=%p\n", pmt, MenuInfo); - - if (0 == MenuInfo->cItems || NO_SELECTED_ITEM == MenuInfo->iItem) - { - return -1; - } - - MenuInitRosMenuItemInfo(&ItemInfo); - if (! MenuGetRosMenuItemInfo(MenuInfo->Self, MenuInfo->iItem, &ItemInfo)) - { - MenuCleanupRosMenuItemInfo(&ItemInfo); - return -1; - } - - TRACE("%p %08x %p\n", MenuInfo, ItemInfo.wID, ItemInfo.hSubMenu); - - if (0 == (ItemInfo.hSubMenu)) - { - if (0 == (ItemInfo.fState & (MF_GRAYED | MF_DISABLED)) - && 0 == (ItemInfo.fType & MF_SEPARATOR)) - { - /* If TPM_RETURNCMD is set you return the id, but - do not send a message to the owner */ - if (0 == (Flags & TPM_RETURNCMD)) - { - if (0 != (MenuInfo->fFlags & MNF_SYSMENU)) - { - PostMessageW(pmt->OwnerWnd, WM_SYSCOMMAND, ItemInfo.wID, - MAKELPARAM((SHORT) pmt->Pt.x, (SHORT) pmt->Pt.y)); - } - else - { - ROSMENUINFO topmenuI; - BOOL ret = MenuGetRosMenuInfo(&topmenuI, pmt->TopMenu); - DWORD dwStyle = MenuInfo->dwStyle | (ret ? topmenuI.dwStyle : 0); - - if (dwStyle & MNS_NOTIFYBYPOS) - PostMessageW(pmt->OwnerWnd, WM_MENUCOMMAND, MenuInfo->iItem, (LPARAM)MenuInfo->Self); - else - PostMessageW(pmt->OwnerWnd, WM_COMMAND, ItemInfo.wID, 0); - } - } - wID = ItemInfo.wID; - MenuCleanupRosMenuItemInfo(&ItemInfo); - return wID; - } - } - else - { - pmt->CurrentMenu = MenuShowSubPopup(pmt->OwnerWnd, MenuInfo, TRUE, Flags); - return -2; - } - - return -1; -} - -/*********************************************************************** - * MenuButtonDown - * - * Return TRUE if we can go on with menu tracking. - */ -static BOOL FASTCALL -MENU_ButtonDown(MTRACKER* pmt, HMENU hPtMenu, UINT Flags) -{ - int Index; - ROSMENUINFO MenuInfo; - ROSMENUITEMINFO Item; - - TRACE("%x PtMenu=%p\n", pmt, hPtMenu); - - if (NULL != hPtMenu) - { - if (! MenuGetRosMenuInfo(&MenuInfo, hPtMenu)) - { - return FALSE; - } - if (IS_SYSTEM_MENU(&MenuInfo)) - { - Index = 0; - } - else - { - Index = NtUserMenuItemFromPoint(pmt->OwnerWnd, hPtMenu, pmt->Pt.x, pmt->Pt.y); - } - MenuInitRosMenuItemInfo(&Item); - if (NO_SELECTED_ITEM == Index || ! MenuGetRosMenuItemInfo(hPtMenu, Index, &Item)) - { - MenuCleanupRosMenuItemInfo(&Item); - return FALSE; - } - - if (!(Item.fType & MF_SEPARATOR) && - !(Item.fState & (MFS_DISABLED | MFS_GRAYED)) ) - { - if (MenuInfo.iItem != Index) - { - MenuSwitchTracking(pmt, &MenuInfo, Index, Flags); - } - - /* If the popup menu is not already "popped" */ - if (!(Item.fState & MF_MOUSESELECT)) - { - pmt->CurrentMenu = MenuShowSubPopup(pmt->OwnerWnd, &MenuInfo, FALSE, Flags); - } - - MenuCleanupRosMenuItemInfo(&Item); - - return TRUE; - } - /* Else the click was on the menu bar, finish the tracking */ - } - return FALSE; -} - -/*********************************************************************** - * MenuButtonUp - * - * Return the value of MenuExecFocusedItem if - * the selected item was not a popup. Else open the popup. - * A -1 return value indicates that we go on with menu tracking. - * - */ -static INT FASTCALL -MENU_ButtonUp(MTRACKER *pmt, HMENU hPtMenu, UINT Flags) -{ - INT Id; - ROSMENUINFO MenuInfo; - ROSMENUITEMINFO ItemInfo; - - TRACE("%p hmenu=%x\n", pmt, hPtMenu); - - if (hPtMenu) - { - Id = 0; - if (! MenuGetRosMenuInfo(&MenuInfo, hPtMenu)) - { - return -1; - } - - if (! IS_SYSTEM_MENU(&MenuInfo)) - { - Id = NtUserMenuItemFromPoint(pmt->OwnerWnd, MenuInfo.Self, pmt->Pt.x, pmt->Pt.y); - } - MenuInitRosMenuItemInfo(&ItemInfo); - if (0 <= Id && MenuGetRosMenuItemInfo(MenuInfo.Self, Id, &ItemInfo) && - MenuInfo.iItem == Id) - { - if (0 == (ItemInfo.hSubMenu)) - { - INT ExecutedMenuId = MenuExecFocusedItem( pmt, &MenuInfo, Flags); - MenuCleanupRosMenuItemInfo(&ItemInfo); - if (ExecutedMenuId == -1 || ExecutedMenuId == -2) return -1; - return ExecutedMenuId; - } - MenuCleanupRosMenuItemInfo(&ItemInfo); - - /* If we are dealing with the menu bar */ - /* and this is a click on an already "popped" item: */ - /* Stop the menu tracking and close the opened submenus */ - if (pmt->TopMenu == MenuInfo.Self && MenuInfo.TimeToHide) - { - MenuCleanupRosMenuItemInfo(&ItemInfo); - return 0; - } - } - MenuCleanupRosMenuItemInfo(&ItemInfo); - if( GetMenu(MenuInfo.Wnd) == hPtMenu ) - { - MenuInfo.TimeToHide = TRUE; - MenuSetRosMenuInfo(&MenuInfo); - } - } - return -1; -} - -/*********************************************************************** - * MenuPtMenu - * - * Walks menu chain trying to find a menu pt maps to. - */ -static HMENU FASTCALL -MENU_PtMenu(HMENU hMenu, POINT pt) -{ - MENU *menu; - PITEM pItem; - HMENU ret = NULL; - - menu = MENU_GetMenu( hMenu ); - if (!menu) return NULL; - - /* try subpopup first (if any) */ - if (menu->iItem != NO_SELECTED_ITEM) - { - pItem = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL; - if ( pItem ) pItem = &pItem[menu->iItem]; - if ( pItem && pItem->spSubMenu && pItem->fState & MF_MOUSESELECT) - { - PMENU pSubMenu = DesktopPtrToUser(pItem->spSubMenu); - ret = MENU_PtMenu( UserHMGetHandle(pSubMenu), pt); - } - } - - /* check the current window (avoiding WM_HITTEST) */ - if (!ret) - { - INT ht = DefWndNCHitTest(menu->hWnd, pt); - if ( menu->fFlags & MNF_POPUP ) - { - if (ht != HTNOWHERE && ht != HTERROR) ret = hMenu; - } - else if (ht == HTSYSMENU) - ret = get_win_sys_menu(menu->hWnd); - else if (ht == HTMENU) - ret = GetMenu( menu->hWnd ); - } - return ret; -} - -/*********************************************************************** - * MenuMouseMove - * - * Return TRUE if we can go on with menu tracking. - */ -static BOOL FASTCALL -MenuMouseMove(MTRACKER *pmt, HMENU PtMenu, UINT Flags) -{ - INT Index; - ROSMENUINFO MenuInfo; - ROSMENUITEMINFO ItemInfo; - - if (NULL != PtMenu) - { - if (! MenuGetRosMenuInfo(&MenuInfo, PtMenu)) - { - return TRUE; - } - if (IS_SYSTEM_MENU(&MenuInfo)) - { - Index = 0; - } - else - { - Index = NtUserMenuItemFromPoint(pmt->OwnerWnd, PtMenu, pmt->Pt.x, pmt->Pt.y); - } - } - else - { - Index = NO_SELECTED_ITEM; - } - - if (NO_SELECTED_ITEM == Index) - { - if (pmt->CurrentMenu == MenuInfo.Self || - MenuGetRosMenuInfo(&MenuInfo, pmt->CurrentMenu)) - { - MenuSelectItem(pmt->OwnerWnd, &MenuInfo, NO_SELECTED_ITEM, - TRUE, pmt->TopMenu); - } - } - else if (MenuInfo.iItem != Index) - { - MenuInitRosMenuItemInfo(&ItemInfo); - if (MenuGetRosMenuItemInfo(MenuInfo.Self, Index, &ItemInfo) && - !(ItemInfo.fType & MF_SEPARATOR)) - { - MenuSwitchTracking(pmt, &MenuInfo, Index, Flags); - if (!(ItemInfo.fState & (MFS_DISABLED | MFS_GRAYED))) - pmt->CurrentMenu = MenuShowSubPopup(pmt->OwnerWnd, &MenuInfo, FALSE, Flags); - } - MenuCleanupRosMenuItemInfo(&ItemInfo); - } - - return TRUE; -} - -/*********************************************************************** - * MenuGetSubPopup - * - * Return the handle of the selected sub-popup menu (if any). - */ -static -HMENU MENU_GetSubPopup( HMENU hmenu ) -{ - MENU *menu; - ITEM *item; - - menu = MENU_GetMenu( hmenu ); - - if ((!menu) || (menu->iItem == NO_SELECTED_ITEM)) return 0; - - //item = &menu->rgItems[menu->iItem]; - item = menu->rgItems ? DesktopPtrToUser(menu->rgItems) : NULL; - if (item) item = &item[menu->iItem]; - if (item && (item->spSubMenu) && (item->fState & MF_MOUSESELECT)) - { - PMENU pSubMenu = DesktopPtrToUser(item->spSubMenu); - return UserHMGetHandle(pSubMenu); - } - return 0; -} - -/*********************************************************************** - * MenuDoNextMenu - * - * NOTE: WM_NEXTMENU documented in Win32 is a bit different. - */ -static LRESULT FASTCALL -MenuDoNextMenu(MTRACKER* pmt, UINT Vk, UINT wFlags) -{ - ROSMENUINFO TopMenuInfo; - ROSMENUINFO MenuInfo; - - if (! MenuGetRosMenuInfo(&TopMenuInfo, pmt->TopMenu)) - { - return (LRESULT) FALSE; - } - - if ((VK_LEFT == Vk && 0 == TopMenuInfo.iItem) - || (VK_RIGHT == Vk && TopMenuInfo.iItem == TopMenuInfo.cItems - 1)) - { - MDINEXTMENU NextMenu; - HMENU NewMenu; - HWND NewWnd; - UINT Id = 0; - - NextMenu.hmenuIn = (IS_SYSTEM_MENU(&TopMenuInfo)) ? GetSubMenu(pmt->TopMenu, 0) : pmt->TopMenu; - NextMenu.hmenuNext = NULL; - NextMenu.hwndNext = NULL; - SendMessageW(pmt->OwnerWnd, WM_NEXTMENU, Vk, (LPARAM) &NextMenu); - - TRACE("%p [%p] -> %p [%p]\n", - pmt->CurrentMenu, pmt->OwnerWnd, NextMenu.hmenuNext, NextMenu.hwndNext ); - - if (NULL == NextMenu.hmenuNext || NULL == NextMenu.hwndNext) - { - DWORD Style = GetWindowLongPtrW(pmt->OwnerWnd, GWL_STYLE); - NewWnd = pmt->OwnerWnd; - if (IS_SYSTEM_MENU(&TopMenuInfo)) - { - /* switch to the menu bar */ - - if (0 != (Style & WS_CHILD) - || NULL == (NewMenu = GetMenu(NewWnd))) - { - return FALSE; - } - - if (VK_LEFT == Vk) - { - if (! MenuGetRosMenuInfo(&MenuInfo, NewMenu)) - { - return FALSE; - } - Id = MenuInfo.cItems - 1; - } - } - else if (0 != (Style & WS_SYSMENU)) - { - /* switch to the system menu */ - NewMenu = get_win_sys_menu(NewWnd); - } - else - { - return FALSE; - } - } - else /* application returned a new menu to switch to */ - { - NewMenu = NextMenu.hmenuNext; - NewWnd = NextMenu.hwndNext; - - if (IsMenu(NewMenu) && IsWindow(NewWnd)) - { - DWORD Style = GetWindowLongPtrW(NewWnd, GWL_STYLE); - - if (0 != (Style & WS_SYSMENU) - && get_win_sys_menu(NewWnd) == NewMenu) - { - /* get the real system menu */ - NewMenu = get_win_sys_menu(NewWnd); - } - else if (0 != (Style & WS_CHILD) || GetMenu(NewWnd) != NewMenu) - { - /* FIXME: Not sure what to do here; - * perhaps try to track NewMenu as a popup? */ - - WARN(" -- got confused.\n"); - return FALSE; - } - } - else - { - return FALSE; - } - } - - if (NewMenu != pmt->TopMenu) - { - MenuSelectItem(pmt->OwnerWnd, &TopMenuInfo, NO_SELECTED_ITEM, - FALSE, 0 ); - if (pmt->CurrentMenu != pmt->TopMenu) - { - MenuHideSubPopups(pmt->OwnerWnd, &TopMenuInfo, FALSE, wFlags); - } - } - - if (NewWnd != pmt->OwnerWnd) - { - pmt->OwnerWnd = NewWnd; - NtUserxSetGUIThreadHandle(MSQ_STATE_MENUOWNER, pmt->OwnerWnd); // 1 - SetCapture(pmt->OwnerWnd); // 2 - } - - pmt->TopMenu = pmt->CurrentMenu = NewMenu; /* all subpopups are hidden */ - if (MenuGetRosMenuInfo(&TopMenuInfo, pmt->TopMenu)) - { - MenuSelectItem(pmt->OwnerWnd, &TopMenuInfo, Id, TRUE, 0); - } - - return TRUE; - } - - return FALSE; -} - -/*********************************************************************** - * MenuSuspendPopup - * - * The idea is not to show the popup if the next input message is - * going to hide it anyway. - */ -static BOOL FASTCALL -MenuSuspendPopup(MTRACKER* pmt, UINT uMsg) -{ - MSG msg; - - msg.hwnd = pmt->OwnerWnd; - - PeekMessageW( &msg, 0, uMsg, uMsg, PM_NOYIELD | PM_REMOVE); // ported incorrectly since 8317 GvG - //pmt->TrackFlags |= TF_SKIPREMOVE; // This sends TrackMenu into a loop with arrow keys!!!! - - switch( uMsg ) - { - case WM_KEYDOWN: - PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE); - if( msg.message == WM_KEYUP || msg.message == WM_PAINT ) - { - PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_REMOVE); - PeekMessageW( &msg, 0, 0, 0, PM_NOYIELD | PM_NOREMOVE); - if( msg.message == WM_KEYDOWN && - (msg.wParam == VK_LEFT || msg.wParam == VK_RIGHT)) - { - pmt->TrackFlags |= TF_SUSPENDPOPUP; - return TRUE; - } - } - break; - } - /* failures go through this */ - pmt->TrackFlags &= ~TF_SUSPENDPOPUP; - return FALSE; -} - -/*********************************************************************** - * MenuKeyEscape - * - * Handle a VK_ESCAPE key event in a menu. - */ -static BOOL FASTCALL -MenuKeyEscape(MTRACKER *pmt, UINT Flags) -{ - BOOL EndMenu = TRUE; - ROSMENUINFO MenuInfo; - HMENU MenuTmp, MenuPrev; - - if (pmt->CurrentMenu != pmt->TopMenu) - { - if (MenuGetRosMenuInfo(&MenuInfo, pmt->CurrentMenu) - && 0 != (MenuInfo.fFlags & MNF_POPUP)) - { - MenuPrev = MenuTmp = pmt->TopMenu; - - /* close topmost popup */ - while (MenuTmp != pmt->CurrentMenu) - { - MenuPrev = MenuTmp; - MenuTmp = MENU_GetSubPopup(MenuPrev); - } - - if (MenuGetRosMenuInfo(&MenuInfo, MenuPrev)) - { - MenuHideSubPopups(pmt->OwnerWnd, &MenuInfo, TRUE, Flags); - } - pmt->CurrentMenu = MenuPrev; - EndMenu = FALSE; - } - } - - return EndMenu; -} - -/*********************************************************************** - * MenuKeyLeft - * - * Handle a VK_LEFT key event in a menu. - */ -static void FASTCALL -MenuKeyLeft(MTRACKER* pmt, UINT Flags) -{ - ROSMENUINFO MenuInfo; - ROSMENUINFO TopMenuInfo; - ROSMENUINFO PrevMenuInfo; - HMENU MenuTmp, MenuPrev; - UINT PrevCol; - - MenuPrev = MenuTmp = pmt->TopMenu; - - /* Try to move 1 column left (if possible) */ - if ( (PrevCol = MENU_GetStartOfPrevColumn(pmt->CurrentMenu)) != NO_SELECTED_ITEM) - { - if (MenuGetRosMenuInfo(&MenuInfo, pmt->CurrentMenu)) - { - MenuSelectItem(pmt->OwnerWnd, &MenuInfo, PrevCol, TRUE, 0); - } - return; - } - - /* close topmost popup */ - while (MenuTmp != pmt->CurrentMenu) - { - MenuPrev = MenuTmp; - MenuTmp = MENU_GetSubPopup(MenuPrev); - } - - if (! MenuGetRosMenuInfo(&PrevMenuInfo, MenuPrev)) - { - return; - } - MenuHideSubPopups(pmt->OwnerWnd, &PrevMenuInfo, TRUE, Flags); - pmt->CurrentMenu = MenuPrev; - - if (! MenuGetRosMenuInfo(&TopMenuInfo, pmt->TopMenu)) - { - return; - } - if ((MenuPrev == pmt->TopMenu) && !(TopMenuInfo.fFlags & MNF_POPUP)) - { - /* move menu bar selection if no more popups are left */ - - if (!MenuDoNextMenu(pmt, VK_LEFT, Flags)) - { - MenuMoveSelection(pmt->OwnerWnd, &TopMenuInfo, ITEM_PREV); - } - - if (MenuPrev != MenuTmp || pmt->TrackFlags & TF_SUSPENDPOPUP) - { - /* A sublevel menu was displayed - display the next one - * unless there is another displacement coming up */ - - if (! MenuSuspendPopup(pmt, WM_KEYDOWN) - && MenuGetRosMenuInfo(&TopMenuInfo, pmt->TopMenu)) - { - pmt->CurrentMenu = MenuShowSubPopup(pmt->OwnerWnd, &TopMenuInfo, - TRUE, Flags); - } - } - } -} - -/*********************************************************************** - * MenuKeyRight - * - * Handle a VK_RIGHT key event in a menu. - */ -static void FASTCALL MenuKeyRight(MTRACKER *pmt, UINT Flags) -{ - HMENU hmenutmp; - ROSMENUINFO MenuInfo; - ROSMENUINFO CurrentMenuInfo; - UINT NextCol; - - TRACE("MenuKeyRight called, cur %p, top %p.\n", - pmt->CurrentMenu, pmt->TopMenu); - - if (! MenuGetRosMenuInfo(&MenuInfo, pmt->TopMenu)) return; - if ((MenuInfo.fFlags & MNF_POPUP) || (pmt->CurrentMenu != pmt->TopMenu)) - { - /* If already displaying a popup, try to display sub-popup */ - - hmenutmp = pmt->CurrentMenu; - if (MenuGetRosMenuInfo(&CurrentMenuInfo, pmt->CurrentMenu)) - { - pmt->CurrentMenu = MenuShowSubPopup(pmt->OwnerWnd, &CurrentMenuInfo, TRUE, Flags); - } - - /* if subpopup was displayed then we are done */ - if (hmenutmp != pmt->CurrentMenu) return; - } - - /* Check to see if there's another column */ - if ( (NextCol = MENU_GetStartOfNextColumn(pmt->CurrentMenu)) != NO_SELECTED_ITEM) - { - TRACE("Going to %d.\n", NextCol); - if (MenuGetRosMenuInfo(&MenuInfo, pmt->CurrentMenu)) - { - MenuSelectItem(pmt->OwnerWnd, &MenuInfo, NextCol, TRUE, 0); - } - return; - } - - if (!(MenuInfo.fFlags & MNF_POPUP)) /* menu bar tracking */ - { - if (pmt->CurrentMenu != pmt->TopMenu) - { - MenuHideSubPopups(pmt->OwnerWnd, &MenuInfo, FALSE, Flags); - hmenutmp = pmt->CurrentMenu = pmt->TopMenu; - } - else - { - hmenutmp = NULL; - } - - /* try to move to the next item */ - if ( !MenuDoNextMenu(pmt, VK_RIGHT, Flags)) - MenuMoveSelection(pmt->OwnerWnd, &MenuInfo, ITEM_NEXT); - - if ( hmenutmp || pmt->TrackFlags & TF_SUSPENDPOPUP ) - { - if (! MenuSuspendPopup(pmt, WM_KEYDOWN) - && MenuGetRosMenuInfo(&MenuInfo, pmt->TopMenu)) - { - pmt->CurrentMenu = MenuShowSubPopup(pmt->OwnerWnd, &MenuInfo, - TRUE, Flags); - } - } - } -} - -/*********************************************************************** - * MenuTrackMenu - * - * Menu tracking code. - */ -static INT FASTCALL MenuTrackMenu(HMENU hmenu, UINT wFlags, INT x, INT y, - HWND hwnd, const RECT *lprect ) -{ - MSG msg; - ROSMENUINFO MenuInfo; - ROSMENUITEMINFO ItemInfo; - PMENU menu; - BOOL fRemove; - INT executedMenuId = -1; - MTRACKER mt; - HWND capture_win; - BOOL enterIdleSent = FALSE; - - mt.TrackFlags = 0; - mt.CurrentMenu = hmenu; - mt.TopMenu = hmenu; - mt.OwnerWnd = hwnd; - mt.Pt.x = x; - mt.Pt.y = y; - - TRACE("hmenu=%p flags=0x%08x (%d,%d) hwnd=%x (%ld,%ld)-(%ld,%ld)\n", - hmenu, wFlags, x, y, hwnd, lprect ? lprect->left : 0, lprect ? lprect->top : 0, - lprect ? lprect->right : 0, lprect ? lprect->bottom : 0); - - if (!IsMenu(hmenu)) - { - WARN("Invalid menu handle %p\n", hmenu); // Error already set in IsMenu. - return FALSE; - } - - if (! MenuGetRosMenuInfo(&MenuInfo, hmenu)) - { - return FALSE; - } - - if (wFlags & TPM_BUTTONDOWN) - { - /* Get the result in order to start the tracking or not */ - fRemove = MENU_ButtonDown( &mt, hmenu, wFlags ); - fEndMenu = !fRemove; - } - - if (wFlags & TF_ENDMENU) fEndMenu = TRUE; - - /* owner may not be visible when tracking a popup, so use the menu itself */ - capture_win = (wFlags & TPM_POPUPMENU) ? MenuInfo.Wnd : mt.OwnerWnd; - NtUserxSetGUIThreadHandle(MSQ_STATE_MENUOWNER, capture_win); // 1 - SetCapture(capture_win); // 2 - - while (!fEndMenu) - { - BOOL ErrorExit = FALSE; - menu = MENU_GetMenu( mt.CurrentMenu ); - if (!menu) /* sometimes happens if I do a window manager close */ - break; - - /* we have to keep the message in the queue until it's - * clear that menu loop is not over yet. */ - - for (;;) - { - if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE )) - { - if (!CallMsgFilterW( &msg, MSGF_MENU )) break; - /* remove the message from the queue */ - PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); - } - else - { - /* ReactOS Check */ - if (!ValidateHwndNoErr(mt.OwnerWnd) || !ValidateHwndNoErr(MenuInfo.Wnd)) - { - ErrorExit = TRUE; // Do not wait on dead windows, now test_capture_4 works. - break; - } - if (!enterIdleSent) - { - HWND win = MenuInfo.fFlags & MNF_POPUP ? MenuInfo.Wnd : NULL; - enterIdleSent = TRUE; - SendMessageW( mt.OwnerWnd, WM_ENTERIDLE, MSGF_MENU, (LPARAM) win); - } - WaitMessage(); - } - } - - if (ErrorExit) break; // Gracefully dropout. - - /* check if EndMenu() tried to cancel us, by posting this message */ - if (msg.message == WM_CANCELMODE) - { - /* we are now out of the loop */ - fEndMenu = TRUE; - - /* remove the message from the queue */ - PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); - - /* break out of internal loop, ala ESCAPE */ - break; - } - - TranslateMessage( &msg ); - mt.Pt = msg.pt; - - if ( (msg.hwnd == MenuInfo.Wnd) || (msg.message!=WM_TIMER) ) - enterIdleSent=FALSE; - - fRemove = FALSE; - if ((msg.message >= WM_MOUSEFIRST) && (msg.message <= WM_MOUSELAST)) - { - /* - * Use the mouse coordinates in lParam instead of those in the MSG - * struct to properly handle synthetic messages. They are already - * in screen coordinates. - */ - mt.Pt.x = (short)LOWORD(msg.lParam); - mt.Pt.y = (short)HIWORD(msg.lParam); - - /* Find a menu for this mouse event */ - hmenu = MENU_PtMenu(mt.TopMenu, mt.Pt); - - switch(msg.message) - { - /* no WM_NC... messages in captured state */ - - case WM_RBUTTONDBLCLK: - case WM_RBUTTONDOWN: - if (!(wFlags & TPM_RIGHTBUTTON)) break; - /* fall through */ - case WM_LBUTTONDBLCLK: - case WM_LBUTTONDOWN: - /* If the message belongs to the menu, removes it from the queue */ - /* Else, end menu tracking */ - fRemove = MENU_ButtonDown(&mt, hmenu, wFlags); - fEndMenu = !fRemove; - if (msg.message == WM_LBUTTONDBLCLK) fEndMenu = TRUE; // Must exit or loop forever! - break; - - case WM_RBUTTONUP: - if (!(wFlags & TPM_RIGHTBUTTON)) break; - /* fall through */ - case WM_LBUTTONUP: - /* Check if a menu was selected by the mouse */ - if (hmenu) - { - executedMenuId = MENU_ButtonUp( &mt, hmenu, wFlags); - TRACE("executedMenuId %d\n", executedMenuId); - - /* End the loop if executedMenuId is an item ID */ - /* or if the job was done (executedMenuId = 0). */ - fEndMenu = fRemove = (executedMenuId != -1); - } - /* No menu was selected by the mouse */ - /* if the function was called by TrackPopupMenu, continue - with the menu tracking. If not, stop it */ - else - fEndMenu = ((wFlags & TPM_POPUPMENU) ? FALSE : TRUE); - - break; - - case WM_MOUSEMOVE: - /* the selected menu item must be changed every time */ - /* the mouse moves. */ - - if (hmenu) - fEndMenu |= !MenuMouseMove( &mt, hmenu, wFlags ); - - } /* switch(msg.message) - mouse */ - } - else if ((msg.message >= WM_KEYFIRST) && (msg.message <= WM_KEYLAST)) - { - fRemove = TRUE; /* Keyboard messages are always removed */ - switch(msg.message) - { - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - switch(msg.wParam) - { - case VK_MENU: - case VK_F10: - fEndMenu = TRUE; - break; - - case VK_HOME: - case VK_END: - if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu)) - { - MenuSelectItem(mt.OwnerWnd, &MenuInfo, NO_SELECTED_ITEM, FALSE, 0 ); - MenuMoveSelection(mt.OwnerWnd, &MenuInfo, VK_HOME == msg.wParam ? ITEM_NEXT : ITEM_PREV); - } - break; - - case VK_UP: - case VK_DOWN: /* If on menu bar, pull-down the menu */ - if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu)) - { - if (!(MenuInfo.fFlags & MNF_POPUP)) - { - if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu)) - mt.CurrentMenu = MenuShowSubPopup(mt.OwnerWnd, &MenuInfo, TRUE, wFlags); - } - else /* otherwise try to move selection */ - MenuMoveSelection(mt.OwnerWnd, &MenuInfo, (msg.wParam == VK_UP)? ITEM_PREV : ITEM_NEXT ); - } - break; - - case VK_LEFT: - MenuKeyLeft( &mt, wFlags ); - break; - - case VK_RIGHT: - MenuKeyRight( &mt, wFlags ); - break; - - case VK_ESCAPE: - fEndMenu = MenuKeyEscape(&mt, wFlags); - break; - - case VK_F1: - { - HELPINFO hi; - hi.cbSize = sizeof(HELPINFO); - hi.iContextType = HELPINFO_MENUITEM; - if (MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu)) - { - if (MenuInfo.iItem == NO_SELECTED_ITEM) - hi.iCtrlId = 0; - else - { - MenuInitRosMenuItemInfo(&ItemInfo); - if (MenuGetRosMenuItemInfo(MenuInfo.Self, - MenuInfo.iItem, - &ItemInfo)) - { - hi.iCtrlId = ItemInfo.wID; - } - else - { - hi.iCtrlId = 0; - } - MenuCleanupRosMenuItemInfo(&ItemInfo); - } - } - hi.hItemHandle = hmenu; - hi.dwContextId = MenuInfo.dwContextHelpID; - hi.MousePos = msg.pt; - SendMessageW(hwnd, WM_HELP, 0, (LPARAM)&hi); - break; - } - - default: - break; - } - break; /* WM_KEYDOWN */ - - case WM_CHAR: - case WM_SYSCHAR: - { - UINT pos; - - if (! MenuGetRosMenuInfo(&MenuInfo, mt.CurrentMenu)) break; - if (msg.wParam == L'\r' || msg.wParam == L' ') - { - executedMenuId = MenuExecFocusedItem(&mt, &MenuInfo, wFlags); - fEndMenu = (executedMenuId != -2); - break; - } - - /* Hack to avoid control chars. */ - /* We will find a better way real soon... */ - if (msg.wParam < 32) break; - - pos = MENU_FindItemByKey(mt.OwnerWnd, mt.CurrentMenu, LOWORD(msg.wParam), FALSE); - if (pos == (UINT)-2) fEndMenu = TRUE; - else if (pos == (UINT)-1) MessageBeep(0); - else - { - MenuSelectItem(mt.OwnerWnd, &MenuInfo, pos, TRUE, 0); - executedMenuId = MenuExecFocusedItem(&mt, &MenuInfo, wFlags); - fEndMenu = (executedMenuId != -2); - } - } - break; - } /* switch(msg.message) - kbd */ - } - else - { - PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); - DispatchMessageW( &msg ); - continue; - } - - if (!fEndMenu) fRemove = TRUE; - - /* finally remove message from the queue */ - - if (fRemove && !(mt.TrackFlags & TF_SKIPREMOVE) ) - PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); - else mt.TrackFlags &= ~TF_SKIPREMOVE; - } - - NtUserxSetGUIThreadHandle(MSQ_STATE_MENUOWNER, NULL); - SetCapture(NULL); /* release the capture */ - - /* If dropdown is still painted and the close box is clicked on - then the menu will be destroyed as part of the DispatchMessage above. - This will then invalidate the menu handle in mt.hTopMenu. We should - check for this first. */ - if( IsMenu( mt.TopMenu ) ) - { - if (IsWindow(mt.OwnerWnd)) - { - if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu)) - { - MenuHideSubPopups(mt.OwnerWnd, &MenuInfo, FALSE, wFlags); - - if (MenuInfo.fFlags & MNF_POPUP) - { - IntNotifyWinEvent(EVENT_SYSTEM_MENUPOPUPEND, MenuInfo.Wnd, OBJID_CLIENT, CHILDID_SELF, 0); - DestroyWindow(MenuInfo.Wnd); - MenuInfo.Wnd = NULL; - MenuSetRosMenuInfo(&MenuInfo); - - if (!(wFlags & TPM_NONOTIFY)) - SendMessageW( mt.OwnerWnd, WM_UNINITMENUPOPUP, (WPARAM)mt.TopMenu, - MAKELPARAM(0, IS_SYSTEM_MENU(&MenuInfo)) ); - } - MenuSelectItem( mt.OwnerWnd, &MenuInfo, NO_SELECTED_ITEM, FALSE, 0 ); - } - - SendMessageW( mt.OwnerWnd, WM_MENUSELECT, MAKEWPARAM(0, 0xffff), 0 ); - } - - /* Reset the variable for hiding menu */ - if (MenuGetRosMenuInfo(&MenuInfo, mt.TopMenu)) - { - MenuInfo.TimeToHide = FALSE; - MenuSetRosMenuInfo(&MenuInfo); - } - } - - /* The return value is only used by TrackPopupMenu */ - if (!(wFlags & TPM_RETURNCMD)) return TRUE; - if (executedMenuId == -1) executedMenuId = 0; - return executedMenuId; -} - -/*********************************************************************** - * MenuInitTracking - */ -static BOOL FASTCALL MenuInitTracking(HWND hWnd, HMENU hMenu, BOOL bPopup, UINT wFlags) -{ - ROSMENUINFO MenuInfo = {0}; - - TRACE("hwnd=%p hmenu=%p\n", hWnd, hMenu); - - if (!MenuGetRosMenuInfo(&MenuInfo, hMenu)) return FALSE; - - HideCaret(0); - - /* This makes the menus of applications built with Delphi work. - * It also enables menus to be displayed in more than one window, - * but there are some bugs left that need to be fixed in this case. - */ - if (!bPopup) - { - MenuInfo.Wnd = hWnd; - MenuSetRosMenuInfo(&MenuInfo); - } - //if (!bPopup) menu->hWnd = hWnd; - if (!top_popup) - { - top_popup = MenuInfo.Wnd;//menu->hWnd; - top_popup_hmenu = hMenu; - } - - fEndMenu = FALSE; - - /* Send WM_ENTERMENULOOP and WM_INITMENU message only if TPM_NONOTIFY flag is not specified */ - if (!(wFlags & TPM_NONOTIFY)) - SendMessageW( hWnd, WM_ENTERMENULOOP, bPopup, 0 ); - - SendMessageW( hWnd, WM_SETCURSOR, (WPARAM)hWnd, HTCAPTION ); - - if (!(wFlags & TPM_NONOTIFY)) - { - SendMessageW( hWnd, WM_INITMENU, (WPARAM)hMenu, 0 ); - /* If an app changed/recreated menu bar entries in WM_INITMENU - * menu sizes will be recalculated once the menu created/shown. - */ - - if (!MenuInfo.cyMenu) - { - /* app changed/recreated menu bar entries in WM_INITMENU - Recalculate menu sizes else clicks will not work */ - SetWindowPos(hWnd, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | - SWP_NOACTIVATE | SWP_NOZORDER | SWP_FRAMECHANGED ); - - } - } - - IntNotifyWinEvent( EVENT_SYSTEM_MENUSTART, - hWnd, - MenuInfo.fFlags & MNF_SYSMENU ? OBJID_SYSMENU : OBJID_MENU, - CHILDID_SELF, 0); - return TRUE; -} - -/*********************************************************************** - * MenuExitTracking - */ -static BOOL FASTCALL MenuExitTracking(HWND hWnd, BOOL bPopup) -{ - TRACE("hwnd=%p\n", hWnd); - - IntNotifyWinEvent( EVENT_SYSTEM_MENUEND, hWnd, OBJID_WINDOW, CHILDID_SELF, 0); - SendMessageW( hWnd, WM_EXITMENULOOP, bPopup, 0 ); - ShowCaret(0); - top_popup = 0; - top_popup_hmenu = NULL; - return TRUE; -} - -/*********************************************************************** - * MenuTrackMouseMenuBar - * - * Menu-bar tracking upon a mouse event. Called from NC_HandleSysCommand(). - */ -VOID MenuTrackMouseMenuBar( HWND hWnd, ULONG ht, POINT pt) -{ - HMENU hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu(hWnd); - UINT wFlags = TPM_BUTTONDOWN | TPM_LEFTALIGN | TPM_LEFTBUTTON; - - TRACE("wnd=%p ht=0x%04x (%ld,%ld)\n", hWnd, ht, pt.x, pt.y); - - if (GetWindowLongW( hWnd, GWL_EXSTYLE ) & WS_EX_LAYOUTRTL) wFlags |= TPM_LAYOUTRTL; - if (IsMenu(hMenu)) - { - /* map point to parent client coordinates */ - HWND Parent = GetAncestor(hWnd, GA_PARENT ); - if (Parent != GetDesktopWindow()) - { - ScreenToClient(Parent, &pt); - } - - MenuInitTracking(hWnd, hMenu, FALSE, wFlags); - /* fetch the window menu again, it may have changed */ - hMenu = (ht == HTSYSMENU) ? get_win_sys_menu( hWnd ) : GetMenu( hWnd ); - MenuTrackMenu(hMenu, wFlags, pt.x, pt.y, hWnd, NULL); - MenuExitTracking(hWnd, FALSE); - } -} - -/*********************************************************************** - * MenuTrackKbdMenuBar - * - * Menu-bar tracking upon a keyboard event. Called from NC_HandleSysCommand(). - */ -VOID MenuTrackKbdMenuBar(HWND hwnd, UINT wParam, WCHAR wChar) -{ - UINT uItem = NO_SELECTED_ITEM; - HMENU hTrackMenu; - ROSMENUINFO MenuInfo; - UINT wFlags = TPM_LEFTALIGN | TPM_LEFTBUTTON; - - TRACE("hwnd %p wParam 0x%04x wChar 0x%04x\n", hwnd, wParam, wChar); - - /* find window that has a menu */ - - while (!((GetWindowLongPtrW( hwnd, GWL_STYLE ) & - (WS_CHILD | WS_POPUP)) != WS_CHILD)) - if (!(hwnd = GetAncestor( hwnd, GA_PARENT ))) return; - - /* check if we have to track a system menu */ - - hTrackMenu = GetMenu( hwnd ); - if (!hTrackMenu || IsIconic(hwnd) || wChar == ' ' ) - { - if (!(GetWindowLongPtrW( hwnd, GWL_STYLE ) & WS_SYSMENU)) return; - hTrackMenu = get_win_sys_menu(hwnd); - uItem = 0; - wParam |= HTSYSMENU; /* prevent item lookup */ - } - - if (!IsMenu( hTrackMenu )) return; - - MenuInitTracking( hwnd, hTrackMenu, FALSE, wFlags ); - - /* fetch the window menu again, it may have changed */ - hTrackMenu = (wParam & HTSYSMENU) ? get_win_sys_menu( hwnd ) : GetMenu( hwnd ); - - if (! MenuGetRosMenuInfo(&MenuInfo, hTrackMenu)) - { - goto track_menu; - } - - if( wChar && wChar != ' ' ) - { - uItem = MENU_FindItemByKey( hwnd, hTrackMenu, wChar, (wParam & HTSYSMENU) ); - if ( uItem >= (UINT)(-2) ) - { - if( uItem == (UINT)(-1) ) MessageBeep(0); - /* schedule end of menu tracking */ - wFlags |= TF_ENDMENU; - goto track_menu; - } - } - - MenuSelectItem( hwnd, &MenuInfo, uItem, TRUE, 0 ); - - if (!(wParam & HTSYSMENU) || wChar == ' ') - { - if( uItem == NO_SELECTED_ITEM ) - MenuMoveSelection( hwnd, &MenuInfo, ITEM_NEXT ); - else - PostMessageW( hwnd, WM_KEYDOWN, VK_RETURN, 0 ); - } - -track_menu: - MenuTrackMenu( hTrackMenu, wFlags, 0, 0, hwnd, NULL ); - MenuExitTracking( hwnd, FALSE ); -} - -/********************************************************************** - * TrackPopupMenuEx (USER32.@) - */ -BOOL WINAPI TrackPopupMenuEx( HMENU hMenu, UINT wFlags, int x, int y, - HWND hWnd, LPTPMPARAMS lpTpm) -{ - BOOL ret = FALSE; - MENU *menu; - - TRACE("hmenu %p flags %04x (%d,%d) hwnd %p lpTpm %p rect %s\n", - hMenu, wFlags, x, y, hWnd, lpTpm, - lpTpm ? wine_dbgstr_rect( &lpTpm->rcExclude) : "-" ); - - /* Parameter check */ - /* FIXME: this check is performed several times, here and in the called - functions. That could be optimized */ - if (!(menu = MENU_GetMenu( hMenu ))) - { - SetLastError( ERROR_INVALID_MENU_HANDLE ); - return FALSE; - } - - if (IsWindow(menu->hWnd)) - { - SetLastError( ERROR_POPUP_ALREADY_ACTIVE ); - return FALSE; - } - - if (MENU_InitPopup( hWnd, hMenu, wFlags )) - { - MenuInitTracking(hWnd, hMenu, TRUE, wFlags); - - /* Send WM_INITMENUPOPUP message only if TPM_NONOTIFY flag is not specified */ - if (!(wFlags & TPM_NONOTIFY)) - SendMessageW(hWnd, WM_INITMENUPOPUP, (WPARAM) hMenu, 0); - - if (MenuShowPopup(hWnd, hMenu, 0, wFlags, x, y, 0, 0 )) - ret = MenuTrackMenu(hMenu, wFlags | TPM_POPUPMENU, 0, 0, hWnd, - lpTpm ? &lpTpm->rcExclude : NULL); - MenuExitTracking(hWnd, TRUE); - - if (menu->hWnd) - { - ROSMENUINFO MenuInfo; - if (IsWindow( menu->hWnd )) // wine hack around this with their destroy function. - DestroyWindow( menu->hWnd ); // Fix wrong error return. - //menu->hWnd = 0; - MenuGetRosMenuInfo(&MenuInfo, menu->head.h); - MenuInfo.Wnd = 0; - MenuSetRosMenuInfo(&MenuInfo); - - if (!(wFlags & TPM_NONOTIFY)) - SendMessageW( hWnd, WM_UNINITMENUPOPUP, (WPARAM)hMenu, - MAKELPARAM(0, IS_SYSTEM_MENU(menu)) ); - } - } - return ret; -} - -/********************************************************************** - * TrackPopupMenu (USER32.@) - */ -BOOL WINAPI TrackPopupMenu( HMENU Menu, UINT Flags, int x, int y, - int Reserved, HWND Wnd, CONST RECT *Rect) -{ - return TrackPopupMenuEx( Menu, Flags, x, y, Wnd, NULL); -} - /********************************************************************** * MENU_mnu2mnuii * @@ -4411,99 +643,21 @@ static BOOL MENU_NormalizeMenuItemInfoStruct( const MENUITEMINFOW *pmii_in, BOOL MenuInit(VOID) { - NONCLIENTMETRICSW ncm; - - /* get the menu font */ - if(!hMenuFont || !hMenuFontBold) - { - ncm.cbSize = sizeof(ncm); - if(!SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0)) - { - ERR("MenuInit(): SystemParametersInfoW(SPI_GETNONCLIENTMETRICS) failed!\n"); - return FALSE; - } - - hMenuFont = CreateFontIndirectW(&ncm.lfMenuFont); - if(hMenuFont == NULL) - { - ERR("MenuInit(): CreateFontIndirectW(hMenuFont) failed!\n"); - return FALSE; - } - - ncm.lfMenuFont.lfWeight = max(ncm.lfMenuFont.lfWeight + 300, 1000); - hMenuFontBold = CreateFontIndirectW(&ncm.lfMenuFont); - if(hMenuFontBold == NULL) - { - ERR("MenuInit(): CreateFontIndirectW(hMenuFontBold) failed!\n"); - DeleteObject(hMenuFont); - hMenuFont = NULL; - return FALSE; - } - } - return TRUE; } VOID MenuCleanup(VOID) { - if (hMenuFont) - { - DeleteObject(hMenuFont); - hMenuFont = NULL; - } - - if (hMenuFontBold) - { - DeleteObject(hMenuFontBold); - hMenuFontBold = NULL; - } } -HMENU FASTCALL MENU_LoadSystemMenu(BOOL mdi) -{ - HMENU hmenu = LoadMenuW(User32Instance, L"SYSMENU"); - - if (hmenu) - { - MENUINFO menuinfo = {0}; - MENUITEMINFOW info = {0}; - //WCHAR buf[128]; - - // removing space for checkboxes from menu - menuinfo.cbSize = sizeof(menuinfo); - menuinfo.fMask = MIM_STYLE; - GetMenuInfo(hmenu, &menuinfo); - menuinfo.dwStyle |= MNS_CHECKORBMP; - SetMenuInfo(hmenu, &menuinfo); - - // adding bitmaps to menu items - info.cbSize = sizeof(info); - info.fMask |= MIIM_BITMAP; - info.hbmpItem = HBMMENU_POPUP_MINIMIZE; - SetMenuItemInfoW(hmenu, SC_MINIMIZE, FALSE, &info); - info.hbmpItem = HBMMENU_POPUP_RESTORE; - SetMenuItemInfoW(hmenu, SC_RESTORE, FALSE, &info); - info.hbmpItem = HBMMENU_POPUP_MAXIMIZE; - SetMenuItemInfoW(hmenu, SC_MAXIMIZE, FALSE, &info); - info.hbmpItem = HBMMENU_POPUP_CLOSE; - SetMenuItemInfoW(hmenu, SC_CLOSE, FALSE, &info); - if (mdi) - { - AppendMenuW(hmenu, MF_SEPARATOR, 0, NULL); - //LoadStringW(User32Instance, IDS_MDI_NEXT, buf, sizeof(buf)/sizeof(WCHAR)); - //AppendMenuW(hmenu, MF_STRING, SC_NEXTWINDOW, buf); - } - } - return hmenu; -} NTSTATUS WINAPI User32LoadSysMenuTemplateForKernel(PVOID Arguments, ULONG ArgumentLength) { LRESULT Result = 0; - // Will be converted to load bitmaps for OBMI! + // Use this for Menu Ole!! return(ZwCallbackReturn(&Result, sizeof(LRESULT), STATUS_SUCCESS)); } @@ -4683,7 +837,6 @@ CheckMenuRadioItem(HMENU hMenu, HMENU WINAPI CreateMenu(VOID) { - MenuLoadBitmaps(); return NtUserxCreateMenu(); } @@ -4693,7 +846,6 @@ CreateMenu(VOID) HMENU WINAPI CreatePopupMenu(VOID) { - MenuLoadBitmaps(); return NtUserxCreatePopupMenu(); } @@ -4717,58 +869,6 @@ EnableMenuItem(HMENU hMenu, return NtUserEnableMenuItem(hMenu, uIDEnableItem, uEnable); } -/* - * @implemented - */ -BOOL WINAPI -EndMenu(VOID) -{ - GUITHREADINFO guii; - guii.cbSize = sizeof(GUITHREADINFO); - if(GetGUIThreadInfo(GetCurrentThreadId(), &guii) && guii.hwndMenuOwner) - { - if (!fEndMenu && - top_popup && - guii.hwndMenuOwner != top_popup ) - { - ERR("Capture GUI pti hWnd does not match top_popup!\n"); - } - } - - /* if we are in the menu code, and it is active */ - if (!fEndMenu && top_popup) - { - /* terminate the menu handling code */ - fEndMenu = TRUE; - - /* needs to be posted to wakeup the internal menu handler */ - /* which will now terminate the menu, in the event that */ - /* the main window was minimized, or lost focus, so we */ - /* don't end up with an orphaned menu */ - PostMessageW( top_popup, WM_CANCELMODE, 0, 0); - } - return fEndMenu; -} - -BOOL WINAPI HiliteMenuItem( HWND hWnd, HMENU hMenu, UINT wItemID, - UINT wHilite ) -{ - ROSMENUINFO MenuInfo; - TRACE("(%p, %p, %04x, %04x);\n", hWnd, hMenu, wItemID, wHilite); - // Force bits to be set call server side.... - // This alone works and passes all the menu test_menu_hilitemenuitem tests. - if (!NtUserHiliteMenuItem(hWnd, hMenu, wItemID, wHilite)) return FALSE; - // Without the above call we fail 3 out of the wine failed todo tests, see CORE-7967 - // Now redraw menu. - if (MenuGetRosMenuInfo(&MenuInfo, hMenu)) - { - if (MenuInfo.iItem == wItemID) return TRUE; - MenuHideSubPopups( hWnd, &MenuInfo, FALSE, 0 ); - MenuSelectItem( hWnd, &MenuInfo, wItemID, TRUE, 0 ); - } - return TRUE; // Always returns TRUE! -} - /* * @implemented */ @@ -4783,25 +883,6 @@ GetMenu(HWND hWnd) return UlongToHandle(Wnd->IDMenu); } -/* - * @implemented - */ -BOOL WINAPI GetMenuBarInfo( HWND hwnd, LONG idObject, LONG idItem, PMENUBARINFO pmbi ) -{ - BOOL Ret; - Ret = NtUserGetMenuBarInfo( hwnd, idObject, idItem, pmbi); - // Reason to move to server side!!!!! - if (!Ret) return Ret; - // EL HAXZO!!! - pmbi->fBarFocused = top_popup_hmenu == pmbi->hMenu; - if (!idItem) - { - pmbi->fFocused = pmbi->fBarFocused; - } - - return TRUE; -} - /* * @implemented */ @@ -5085,11 +1166,9 @@ GetSubMenu( */ HMENU WINAPI -GetSystemMenu( - HWND hWnd, - BOOL bRevert) +GetSystemMenu(HWND hWnd, BOOL bRevert) { - return NtUserGetSystemMenu(hWnd, bRevert); + return NtUserGetSystemMenu(hWnd, bRevert); } /* @@ -5336,19 +1415,6 @@ LoadMenuW(HINSTANCE hInstance, return(LoadMenuIndirectW((PVOID)LoadResource(hInstance, Resource))); } -/* - * @implemented - */ -int -WINAPI -MenuItemFromPoint( - HWND hWnd, - HMENU hMenu, - POINT ptScreen) -{ - return NtUserMenuItemFromPoint(hWnd, hMenu, ptScreen.x, ptScreen.y); -} - /* * @implemented */ @@ -5443,7 +1509,7 @@ SetMenuInfo( HMENU hmenu, LPCMENUINFO lpcmi) { - ROSMENUINFO mi; + MENUINFO mi; BOOL res = FALSE; if (!lpcmi || (lpcmi->cbSize != sizeof(MENUINFO))) @@ -5581,14 +1647,9 @@ SetSystemMenu ( return NtUserSetSystemMenu(hwnd, hMenu); } -// -// Example for the Win32/User32 rewrite. -// Def = TrackPopupMenuEx@24=NtUserTrackPopupMenuEx@24 -// -// BOOL WINAPI -NEWTrackPopupMenu( +TrackPopupMenu( HMENU Menu, UINT Flags, int x, @@ -5608,7 +1669,7 @@ NEWTrackPopupMenu( /* * @unimplemented */ -BOOL +LRESULT WINAPI MenuWindowProcA( HWND hWnd, @@ -5620,14 +1681,7 @@ MenuWindowProcA( { if ( Msg < WM_USER) { - LRESULT lResult; - lResult = PopupMenuWndProcA(hWnd, Msg, wParam, lParam ); - if (Result) - { - Result = (ULONG_PTR)lResult; - return TRUE; - } - return FALSE; + return PopupMenuWndProcA(hWnd, Msg, wParam, lParam ); } return NtUserMessageCall(hWnd, Msg, wParam, lParam, Result, FNID_MENU, TRUE); } @@ -5635,7 +1689,7 @@ MenuWindowProcA( /* * @unimplemented */ -BOOL +LRESULT WINAPI MenuWindowProcW( HWND hWnd, @@ -5647,14 +1701,7 @@ MenuWindowProcW( { if ( Msg < WM_USER) { - LRESULT lResult; - lResult = PopupMenuWndProcW(hWnd, Msg, wParam, lParam ); - if (Result) - { - Result = (ULONG_PTR)lResult; - return TRUE; - } - return FALSE; + return PopupMenuWndProcW(hWnd, Msg, wParam, lParam ); } return NtUserMessageCall(hWnd, Msg, wParam, lParam, Result, FNID_MENU, FALSE); } diff --git a/reactos/win32ss/user/user32/windows/nonclient.c b/reactos/win32ss/user/user32/windows/nonclient.c index b46648fb9ac..358057b13ec 100644 --- a/reactos/win32ss/user/user32/windows/nonclient.c +++ b/reactos/win32ss/user/user32/windows/nonclient.c @@ -43,35 +43,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(user32); (WindowRect.right - WindowRect.left == ParentClientRect.right) && \ (WindowRect.bottom - WindowRect.top == ParentClientRect.bottom))) -#ifndef STATE_SYSTEM_OFFSCREEN -#define STATE_SYSTEM_OFFSCREEN 0x00010000 -#endif - -/* - * FIXME: This should be moved to a header - */ -VOID -IntDrawScrollBar(HWND hWnd, HDC hDC, INT nBar); -DWORD -IntScrollHitTest(HWND hWnd, INT nBar, POINT pt, BOOL bDragging); - -BOOL WINAPI GdiGradientFill(HDC,PTRIVERTEX,ULONG,PVOID,ULONG,ULONG); - -extern ATOM AtomInternalPos; /* PRIVATE FUNCTIONS **********************************************************/ -BOOL -IntIsScrollBarVisible(HWND hWnd, INT hBar) -{ - SCROLLBARINFO sbi; - sbi.cbSize = sizeof(SCROLLBARINFO); - if(!NtUserGetScrollBarInfo(hWnd, hBar, &sbi)) - return FALSE; - - return !(sbi.rgstate[0] & STATE_SYSTEM_OFFSCREEN); -} - BOOL UserHasWindowEdge(DWORD Style, DWORD ExStyle) { @@ -112,582 +86,6 @@ UserGetWindowBorders(DWORD Style, DWORD ExStyle, SIZE *Size, BOOL WithClient) Size->cy *= GetSystemMetrics(SM_CYBORDER); } -BOOL -UserHasMenu(HWND hWnd, ULONG Style) -{ - return (!(Style & WS_CHILD) && GetMenu(hWnd) != 0); -} - -HICON -UserGetWindowIcon(HWND hwnd) -{ - HICON hIcon = 0; - - SendMessageTimeout(hwnd, WM_GETICON, ICON_SMALL2, 0, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR)&hIcon); - - if (!hIcon) hIcon = UserGetProp(hwnd, gpsi->atomIconSmProp); - if (!hIcon) hIcon = UserGetProp(hwnd, gpsi->atomIconProp); - if (!hIcon) hIcon = (HICON)GetClassLongPtr(hwnd, GCL_HICONSM); - if (!hIcon) hIcon = (HICON)GetClassLongPtr(hwnd, GCL_HICON); - if (!hIcon && (GetWindowLongW( hwnd, GWL_STYLE ) & DS_MODALFRAME)) - { - if (!hIcon) hIcon = gpsi->hIconSmWindows; // Both are IDI_WINLOGO Small - if (!hIcon) hIcon = gpsi->hIconWindows; // Reg size. - } - return hIcon; -} - -BOOL -UserDrawSysMenuButton(HWND hWnd, HDC hDC, LPRECT Rect, BOOL Down) -{ - HICON WindowIcon; - - if ((WindowIcon = UserGetWindowIcon(hWnd))) - { - return DrawIconEx(hDC, Rect->left + 2, Rect->top + 2, WindowIcon, - GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), - 0, NULL, DI_NORMAL); - } - - return FALSE; -} - -/* - * FIXME: - * - Cache bitmaps, then just bitblt instead of calling DFC() (and - * wasting precious CPU cycles) every time - * - Center the buttons verticaly in the rect - */ -VOID -UserDrawCaptionButton(HWND hWnd, LPRECT Rect, DWORD Style, DWORD ExStyle, HDC hDC, BOOL bDown, ULONG Type) -{ - RECT TempRect; - - if (!(Style & WS_SYSMENU)) - { - return; - } - - TempRect = *Rect; - - switch (Type) - { - case DFCS_CAPTIONMIN: - { - if (ExStyle & WS_EX_TOOLWINDOW) - return; /* ToolWindows don't have min/max buttons */ - - if (Style & WS_SYSMENU) - TempRect.right -= GetSystemMetrics(SM_CXSIZE) + 1; - if (Style & (WS_MAXIMIZEBOX | WS_MINIMIZEBOX)) - TempRect.right -= GetSystemMetrics(SM_CXSIZE) - 2; - TempRect.left = TempRect.right - GetSystemMetrics(SM_CXSIZE) + 1; - TempRect.bottom = TempRect.top + GetSystemMetrics(SM_CYSIZE) - 2; - TempRect.top += 2; - TempRect.right -= 1; - - DrawFrameControl(hDC, &TempRect, DFC_CAPTION, - ((Style & WS_MINIMIZE) ? DFCS_CAPTIONRESTORE : DFCS_CAPTIONMIN) | - (bDown ? DFCS_PUSHED : 0) | - ((Style & WS_MINIMIZEBOX) ? 0 : DFCS_INACTIVE)); - break; - } - case DFCS_CAPTIONMAX: - { - if (ExStyle & WS_EX_TOOLWINDOW) - return; /* ToolWindows don't have min/max buttons */ - - if (Style & WS_SYSMENU) - TempRect.right -= GetSystemMetrics(SM_CXSIZE) + 1; - TempRect.left = TempRect.right - GetSystemMetrics(SM_CXSIZE) + 1; - TempRect.bottom = TempRect.top + GetSystemMetrics(SM_CYSIZE) - 2; - TempRect.top += 2; - TempRect.right -= 1; - - DrawFrameControl(hDC, &TempRect, DFC_CAPTION, - ((Style & WS_MAXIMIZE) ? DFCS_CAPTIONRESTORE : DFCS_CAPTIONMAX) | - (bDown ? DFCS_PUSHED : 0) | - ((Style & WS_MAXIMIZEBOX) ? 0 : DFCS_INACTIVE)); - break; - } - case DFCS_CAPTIONCLOSE: - { - HMENU hSysMenu = GetSystemMenu(hWnd, FALSE); - UINT MenuState = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND); /* in case of error MenuState==0xFFFFFFFF */ - - /* FIXME: A tool window has a smaller Close button */ - - if (ExStyle & WS_EX_TOOLWINDOW) - { - TempRect.left = TempRect.right - GetSystemMetrics(SM_CXSMSIZE); - TempRect.bottom = TempRect.top + GetSystemMetrics(SM_CYSMSIZE) - 2; - } - else - { - TempRect.left = TempRect.right - GetSystemMetrics(SM_CXSIZE); - TempRect.bottom = TempRect.top + GetSystemMetrics(SM_CYSIZE) - 2; - } - TempRect.top += 2; - TempRect.right -= 2; - - DrawFrameControl(hDC, &TempRect, DFC_CAPTION, - (DFCS_CAPTIONCLOSE | (bDown ? DFCS_PUSHED : 0) | - ((!(MenuState & (MF_GRAYED|MF_DISABLED)) && !(GetClassLong(hWnd, GCL_STYLE) & CS_NOCLOSE)) ? 0 : DFCS_INACTIVE))); - break; - } - } -} - -VOID -UserDrawCaptionButtonWnd(HWND hWnd, HDC hDC, BOOL bDown, ULONG Type) -{ - RECT WindowRect; - SIZE WindowBorder; - DWORD Style, ExStyle; - - GetWindowRect(hWnd, &WindowRect); - WindowRect.right -= WindowRect.left; - WindowRect.bottom -= WindowRect.top; - WindowRect.left = WindowRect.top = 0; - Style = GetWindowLongPtrW(hWnd, GWL_STYLE); - ExStyle = GetWindowLongPtrW(hWnd, GWL_EXSTYLE); - UserGetWindowBorders(Style, ExStyle, &WindowBorder, FALSE); - InflateRect(&WindowRect, -WindowBorder.cx, -WindowBorder.cy); - UserDrawCaptionButton(hWnd, &WindowRect, Style, ExStyle, hDC, bDown, Type); -} - -// Note from Wine: -/* MSDN docs are pretty idiotic here, they say app CAN use clipRgn in - the call to GetDCEx implying that it is allowed not to use it either. - However, the suggested GetDCEx( , DCX_WINDOW | DCX_INTERSECTRGN) - will cause clipRgn to be deleted after ReleaseDC(). - Now, how is the "system" supposed to tell what happened? - */ -/* - * FIXME: - * - Drawing of WS_BORDER after scrollbars - * - Correct drawing of size-box - */ -LRESULT -DefWndNCPaint(HWND hWnd, HRGN hRgn, BOOL Active) -{ - HDC hDC; - DWORD Style, ExStyle; - HWND Parent; - RECT ClientRect, WindowRect, CurrentRect, TempRect; - - if (!IsWindowVisible(hWnd)) - return 0; - - Style = GetWindowLongPtrW(hWnd, GWL_STYLE); - - TRACE("DefWndNCPaint: hWnd %p, hRgn %p, Active %s.\n", - hWnd, hRgn, Active ? "TRUE" : "FALSE"); - - hDC = GetDCEx(hWnd, hRgn, DCX_WINDOW | DCX_INTERSECTRGN | DCX_USESTYLE | DCX_KEEPCLIPRGN); - if (hDC == 0) - { - ERR("hDC is NULL!\n"); - if (hRgn != HRGN_WINDOW) - DeleteObject(hRgn); - return 0; - } - - Parent = GetParent(hWnd); - ExStyle = GetWindowLongPtrW(hWnd, GWL_EXSTYLE); - if (Active == -1) - { - if (ExStyle & WS_EX_MDICHILD) - { - Active = IsChild(GetForegroundWindow(), hWnd); - if (Active) - Active = (hWnd == (HWND)SendMessageW(Parent, WM_MDIGETACTIVE, 0, 0)); - } - else - { - Active = (GetForegroundWindow() == hWnd); - } - } - GetWindowRect(hWnd, &WindowRect); - GetClientRect(hWnd, &ClientRect); - - CurrentRect.top = CurrentRect.left = 0; - CurrentRect.right = WindowRect.right - WindowRect.left; - CurrentRect.bottom = WindowRect.bottom - WindowRect.top; - - /* Draw outer edge */ - if (UserHasWindowEdge(Style, ExStyle)) - { - DrawEdge(hDC, &CurrentRect, EDGE_RAISED, BF_RECT | BF_ADJUST); - } else - if (ExStyle & WS_EX_STATICEDGE) - { -#if 0 - DrawEdge(hDC, &CurrentRect, BDR_SUNKENINNER, BF_RECT | BF_ADJUST | BF_FLAT); -#else - SelectObject(hDC, GetSysColorBrush(COLOR_BTNSHADOW)); - PatBlt(hDC, CurrentRect.left, CurrentRect.top, CurrentRect.right - CurrentRect.left, 1, PATCOPY); - PatBlt(hDC, CurrentRect.left, CurrentRect.top, 1, CurrentRect.bottom - CurrentRect.top, PATCOPY); - - SelectObject(hDC, GetSysColorBrush(COLOR_BTNHIGHLIGHT)); - PatBlt(hDC, CurrentRect.left, CurrentRect.bottom - 1, CurrentRect.right - CurrentRect.left, 1, PATCOPY); - PatBlt(hDC, CurrentRect.right - 1, CurrentRect.top, 1, CurrentRect.bottom - CurrentRect.top, PATCOPY); - - InflateRect(&CurrentRect, -1, -1); -#endif - } - - /* Firstly the "thick" frame */ - if ((Style & WS_THICKFRAME) && !(Style & WS_MINIMIZE)) - { - LONG Width = - (GetSystemMetrics(SM_CXFRAME) - GetSystemMetrics(SM_CXDLGFRAME)) * - GetSystemMetrics(SM_CXBORDER); - LONG Height = - (GetSystemMetrics(SM_CYFRAME) - GetSystemMetrics(SM_CYDLGFRAME)) * - GetSystemMetrics(SM_CYBORDER); - - SelectObject(hDC, GetSysColorBrush(Active ? COLOR_ACTIVEBORDER : - COLOR_INACTIVEBORDER)); - - /* Draw frame */ - PatBlt(hDC, CurrentRect.left, CurrentRect.top, CurrentRect.right - CurrentRect.left, Height, PATCOPY); - PatBlt(hDC, CurrentRect.left, CurrentRect.top, Width, CurrentRect.bottom - CurrentRect.top, PATCOPY); -#ifdef __REACTOS__ - PatBlt(hDC, CurrentRect.left, CurrentRect.bottom - 1, CurrentRect.right - CurrentRect.left, -Height, PATCOPY); - PatBlt(hDC, CurrentRect.right - 1, CurrentRect.top, -Width, CurrentRect.bottom - CurrentRect.top, PATCOPY); -#else - PatBlt(hDC, CurrentRect.left, CurrentRect.bottom, CurrentRect.right - CurrentRect.left, -Height, PATCOPY); - PatBlt(hDC, CurrentRect.right, CurrentRect.top, -Width, CurrentRect.bottom - CurrentRect.top, PATCOPY); -#endif - - InflateRect(&CurrentRect, -Width, -Height); - } - - /* Now the other bit of the frame */ - if (Style & (WS_DLGFRAME | WS_BORDER) || ExStyle & WS_EX_DLGMODALFRAME) - { - DWORD Width = GetSystemMetrics(SM_CXBORDER); - DWORD Height = GetSystemMetrics(SM_CYBORDER); - - SelectObject(hDC, GetSysColorBrush( - (ExStyle & (WS_EX_DLGMODALFRAME | WS_EX_CLIENTEDGE)) ? COLOR_3DFACE : - (ExStyle & WS_EX_STATICEDGE) ? COLOR_WINDOWFRAME : - (Style & (WS_DLGFRAME | WS_THICKFRAME)) ? COLOR_3DFACE : - COLOR_WINDOWFRAME)); - - /* Draw frame */ - PatBlt(hDC, CurrentRect.left, CurrentRect.top, CurrentRect.right - CurrentRect.left, Height, PATCOPY); - PatBlt(hDC, CurrentRect.left, CurrentRect.top, Width, CurrentRect.bottom - CurrentRect.top, PATCOPY); -#ifdef __REACTOS__ - PatBlt(hDC, CurrentRect.left, CurrentRect.bottom - 1, CurrentRect.right - CurrentRect.left, -Height, PATCOPY); - PatBlt(hDC, CurrentRect.right - 1, CurrentRect.top, -Width, CurrentRect.bottom - CurrentRect.top, PATCOPY); -#else - PatBlt(hDC, CurrentRect.left, CurrentRect.bottom, CurrentRect.right - CurrentRect.left, -Height, PATCOPY); - PatBlt(hDC, CurrentRect.right, CurrentRect.top, -Width, CurrentRect.bottom - CurrentRect.top, PATCOPY); -#endif - - InflateRect(&CurrentRect, -Width, -Height); - } - - /* Draw caption */ - if ((Style & WS_CAPTION) == WS_CAPTION) - { - DWORD CaptionFlags = DC_ICON | DC_TEXT | DC_BUTTONS; - HPEN PreviousPen; - BOOL Gradient = FALSE; - - if(SystemParametersInfoW(SPI_GETGRADIENTCAPTIONS, 0, &Gradient, 0) && Gradient) - { - CaptionFlags |= DC_GRADIENT; - } - - TempRect = CurrentRect; - - if (Active) - { - CaptionFlags |= DC_ACTIVE; - } - - if (ExStyle & WS_EX_TOOLWINDOW) - { - CaptionFlags |= DC_SMALLCAP; - TempRect.bottom = TempRect.top + GetSystemMetrics(SM_CYSMCAPTION) - 1; - CurrentRect.top += GetSystemMetrics(SM_CYSMCAPTION); - } - else - { - TempRect.bottom = TempRect.top + GetSystemMetrics(SM_CYCAPTION) - 1; - CurrentRect.top += GetSystemMetrics(SM_CYCAPTION); - } - - NtUserDrawCaption(hWnd, hDC, &TempRect, CaptionFlags); - - /* Draw buttons */ - if (Style & WS_SYSMENU) - { - UserDrawCaptionButton(hWnd, &TempRect, Style, ExStyle, hDC, FALSE, DFCS_CAPTIONCLOSE); - if ((Style & (WS_MAXIMIZEBOX | WS_MINIMIZEBOX)) && !(ExStyle & WS_EX_TOOLWINDOW)) - { - UserDrawCaptionButton(hWnd, &TempRect, Style, ExStyle, hDC, FALSE, DFCS_CAPTIONMIN); - UserDrawCaptionButton(hWnd, &TempRect, Style, ExStyle, hDC, FALSE, DFCS_CAPTIONMAX); - } - } - if(!(Style & WS_MINIMIZE)) - { - /* Line under caption */ - PreviousPen = SelectObject(hDC, GetStockObject(DC_PEN)); - SetDCPenColor(hDC, GetSysColor( - ((ExStyle & (WS_EX_STATICEDGE | WS_EX_CLIENTEDGE | - WS_EX_DLGMODALFRAME)) == WS_EX_STATICEDGE) ? - COLOR_WINDOWFRAME : COLOR_3DFACE)); - MoveToEx(hDC, TempRect.left, TempRect.bottom, NULL); - LineTo(hDC, TempRect.right, TempRect.bottom); - SelectObject(hDC, PreviousPen); - } - } - - if(!(Style & WS_MINIMIZE)) - { - HMENU menu = GetMenu(hWnd); - /* Draw menu bar */ - if (menu && !(Style & WS_CHILD)) - { - TempRect = CurrentRect; - TempRect.bottom = TempRect.top + (UINT)NtUserxSetMenuBarHeight(menu, 0); - CurrentRect.top += MenuDrawMenuBar(hDC, &TempRect, hWnd, FALSE); - } - - if (ExStyle & WS_EX_CLIENTEDGE) - { - DrawEdge(hDC, &CurrentRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST); - } - - /* Draw the scrollbars */ - if ((Style & WS_VSCROLL) && (Style & WS_HSCROLL) && - IntIsScrollBarVisible(hWnd, OBJID_VSCROLL) && IntIsScrollBarVisible(hWnd, OBJID_HSCROLL)) - { - RECT ParentClientRect; - - TempRect = CurrentRect; - if (ExStyle & WS_EX_LEFTSCROLLBAR) - TempRect.right = TempRect.left + GetSystemMetrics(SM_CXVSCROLL); - else - TempRect.left = TempRect.right - GetSystemMetrics(SM_CXVSCROLL); - TempRect.top = TempRect.bottom - GetSystemMetrics(SM_CYHSCROLL); - FillRect(hDC, &TempRect, GetSysColorBrush(COLOR_BTNFACE)); - /* FIXME: Correct drawing of size-box with WS_EX_LEFTSCROLLBAR */ - if(Parent) - GetClientRect(Parent, &ParentClientRect); - if (HASSIZEGRIP(Style, ExStyle, GetWindowLongPtrW(Parent, GWL_STYLE), WindowRect, ParentClientRect)) - { - DrawFrameControl(hDC, &TempRect, DFC_SCROLL, DFCS_SCROLLSIZEGRIP); - } - IntDrawScrollBar(hWnd, hDC, SB_VERT); - IntDrawScrollBar(hWnd, hDC, SB_HORZ); - } - else - { - if (Style & WS_VSCROLL && IntIsScrollBarVisible(hWnd, OBJID_VSCROLL)) - IntDrawScrollBar(hWnd, hDC, SB_VERT); - else if (Style & WS_HSCROLL && IntIsScrollBarVisible(hWnd, OBJID_HSCROLL)) - IntDrawScrollBar(hWnd, hDC, SB_HORZ); - } - } - - ReleaseDC(hWnd, hDC); - if (hRgn != HRGN_WINDOW) - DeleteObject(hRgn); // We use DCX_KEEPCLIPRGN - - return 0; // For WM_NCPAINT message, return 0. -} - -LRESULT -DefWndNCCalcSize(HWND hWnd, BOOL CalcSizeStruct, RECT *Rect) -{ - LRESULT Result = 0; - DWORD Style = GetClassLongPtrW(hWnd, GCL_STYLE); - DWORD ExStyle; - SIZE WindowBorders; - RECT OrigRect; - - if (Rect == NULL) - { - return Result; - } - OrigRect = *Rect; - - if (CalcSizeStruct) - { - if (Style & CS_VREDRAW) - { - Result |= WVR_VREDRAW; - } - if (Style & CS_HREDRAW) - { - Result |= WVR_HREDRAW; - } - Result |= WVR_VALIDRECTS; - } - - Style = GetWindowLongPtrW(hWnd, GWL_STYLE); - ExStyle = GetWindowLongPtrW(hWnd, GWL_EXSTYLE); - - if (!(Style & WS_MINIMIZE)) - { - HMENU menu = GetMenu(hWnd); - - if (UserHasWindowEdge(Style, ExStyle)) - { - UserGetWindowBorders(Style, ExStyle, &WindowBorders, FALSE); - InflateRect(Rect, -WindowBorders.cx, -WindowBorders.cy); - } else - if ((ExStyle & WS_EX_STATICEDGE) || (Style & WS_BORDER)) - { - InflateRect(Rect, -1, -1); - } - - if ((Style & WS_CAPTION) == WS_CAPTION) - { - if (ExStyle & WS_EX_TOOLWINDOW) - Rect->top += GetSystemMetrics(SM_CYSMCAPTION); - else - Rect->top += GetSystemMetrics(SM_CYCAPTION); - } - - if (menu && !(Style & WS_CHILD)) - { - HDC hDC = GetWindowDC(hWnd); - if(hDC) - { - RECT CliRect = *Rect; - CliRect.bottom -= OrigRect.top; - CliRect.right -= OrigRect.left; - CliRect.left -= OrigRect.left; - CliRect.top -= OrigRect.top; - Rect->top += MenuDrawMenuBar(hDC, &CliRect, hWnd, TRUE); - ReleaseDC(hWnd, hDC); - } - } - - if (ExStyle & WS_EX_CLIENTEDGE) - { - InflateRect(Rect, -2 * GetSystemMetrics(SM_CXBORDER), - -2 * GetSystemMetrics(SM_CYBORDER)); - } - - if(Style & (WS_VSCROLL | WS_HSCROLL)) - { - SCROLLBARINFO sbi; - SETSCROLLBARINFO ssbi; - - sbi.cbSize = sizeof(SCROLLBARINFO); - if((Style & WS_VSCROLL) && NtUserGetScrollBarInfo(hWnd, OBJID_VSCROLL, &sbi)) - { - int i; - LONG sx = Rect->right; - - sx -= GetSystemMetrics(SM_CXVSCROLL); - for(i = 0; i <= CCHILDREN_SCROLLBAR; i++) - ssbi.rgstate[i] = sbi.rgstate[i]; - if(sx <= Rect->left) - ssbi.rgstate[0] |= STATE_SYSTEM_OFFSCREEN; - else - ssbi.rgstate[0] &= ~STATE_SYSTEM_OFFSCREEN; - NtUserSetScrollBarInfo(hWnd, OBJID_VSCROLL, &ssbi); - if(ssbi.rgstate[0] & STATE_SYSTEM_OFFSCREEN) - Style &= ~WS_VSCROLL; - } - else - Style &= ~WS_VSCROLL; - - if((Style & WS_HSCROLL) && NtUserGetScrollBarInfo(hWnd, OBJID_HSCROLL, &sbi)) - { - int i; - LONG sy = Rect->bottom; - - sy -= GetSystemMetrics(SM_CYHSCROLL); - for(i = 0; i <= CCHILDREN_SCROLLBAR; i++) - ssbi.rgstate[i] = sbi.rgstate[i]; - if(sy <= Rect->top) - ssbi.rgstate[0] |= STATE_SYSTEM_OFFSCREEN; - else - ssbi.rgstate[0] &= ~STATE_SYSTEM_OFFSCREEN; - NtUserSetScrollBarInfo(hWnd, OBJID_HSCROLL, &ssbi); - if(ssbi.rgstate[0] & STATE_SYSTEM_OFFSCREEN) - Style &= ~WS_HSCROLL; - } - else - Style &= ~WS_HSCROLL; - } - - if ((Style & WS_VSCROLL) && (Style & WS_HSCROLL)) - { - if ((ExStyle & WS_EX_LEFTSCROLLBAR) != 0) - Rect->left += GetSystemMetrics(SM_CXVSCROLL); - else - Rect->right -= GetSystemMetrics(SM_CXVSCROLL); - Rect->bottom -= GetSystemMetrics(SM_CYHSCROLL); - } - else - { - if (Style & WS_VSCROLL) - { - if ((ExStyle & WS_EX_LEFTSCROLLBAR) != 0) - Rect->left += GetSystemMetrics(SM_CXVSCROLL); - else - Rect->right -= GetSystemMetrics(SM_CXVSCROLL); - } - else if (Style & WS_HSCROLL) - Rect->bottom -= GetSystemMetrics(SM_CYHSCROLL); - } - if (Rect->top > Rect->bottom) - Rect->bottom = Rect->top; - if (Rect->left > Rect->right) - Rect->right = Rect->left; - } - else - { - Rect->right = Rect->left; - Rect->bottom = Rect->top; - } - - return Result; -} - -LRESULT -DefWndNCActivate(HWND hWnd, WPARAM wParam, LPARAM lParam) -{ - PWND Wnd = ValidateHwnd(hWnd); - - if (!Wnd) return 0; - - /* Lotus Notes draws menu descriptions in the caption of its main - * window. When it wants to restore original "system" view, it just - * sends WM_NCACTIVATE message to itself. Any optimizations here in - * attempt to minimize redrawings lead to a not restored caption. - */ - if (wParam) - NtUserxSetWindowState(Wnd, WNDSACTIVEFRAME); - else - NtUserxClearWindowState(Wnd, WNDSACTIVEFRAME); - - if (Wnd->state & WNDS_NONCPAINT) - return 0; - - /* This isn't documented but is reproducible in at least XP SP2 and - * Outlook 2007 depends on it - */ - // MSDN: - // If this parameter is set to -1, DefWindowProc does not repaint the - // nonclient area to reflect the state change. - if (lParam != -1) - { - DefWndNCPaint(hWnd, HRGN_WINDOW, wParam); - } - return TRUE; -} - /* * FIXME: * - Check the scrollbar handling @@ -902,334 +300,17 @@ DefWndNCHitTest(HWND hWnd, POINT Point) return HTNOWHERE; } -VOID -DefWndDoButton(HWND hWnd, WPARAM wParam) -{ - MSG Msg; - HDC WindowDC; - BOOL Pressed = TRUE, OldState; - WPARAM SCMsg; - HMENU hSysMenu; - ULONG ButtonType; - DWORD Style; - UINT MenuState; - - Style = GetWindowLongPtrW(hWnd, GWL_STYLE); - switch (wParam) - { - case HTCLOSE: - hSysMenu = GetSystemMenu(hWnd, FALSE); - MenuState = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND); /* in case of error MenuState==0xFFFFFFFF */ - if (!(Style & WS_SYSMENU) || (MenuState & (MF_GRAYED|MF_DISABLED)) || (GetClassLongPtrW(hWnd, GCL_STYLE) & CS_NOCLOSE)) - return; - ButtonType = DFCS_CAPTIONCLOSE; - SCMsg = SC_CLOSE; - break; - case HTMINBUTTON: - if (!(Style & WS_MINIMIZEBOX)) - return; - ButtonType = DFCS_CAPTIONMIN; - SCMsg = ((Style & WS_MINIMIZE) ? SC_RESTORE : SC_MINIMIZE); - break; - case HTMAXBUTTON: - if (!(Style & WS_MAXIMIZEBOX)) - return; - ButtonType = DFCS_CAPTIONMAX; - SCMsg = ((Style & WS_MAXIMIZE) ? SC_RESTORE : SC_MAXIMIZE); - break; - - default: - ASSERT(FALSE); - return; - } - - /* - * FIXME: Not sure where to do this, but we must flush the pending - * window updates when someone clicks on the close button and at - * the same time the window is overlapped with another one. This - * looks like a good place for now... - */ - UpdateWindow(hWnd); - - WindowDC = GetWindowDC(hWnd); - UserDrawCaptionButtonWnd(hWnd, WindowDC, TRUE, ButtonType); - - SetCapture(hWnd); - - for (;;) - { - if (GetMessageW(&Msg, 0, WM_MOUSEFIRST, WM_MOUSELAST) <= 0) - break; - if (CallMsgFilterW( &Msg, MSGF_MAX )) continue; - - if (Msg.message == WM_LBUTTONUP) - break; - - if (Msg.message != WM_MOUSEMOVE) - continue; - - OldState = Pressed; - Pressed = (DefWndNCHitTest(hWnd, Msg.pt) == wParam); - if (Pressed != OldState) - UserDrawCaptionButtonWnd(hWnd, WindowDC, Pressed, ButtonType); - } - - if (Pressed) - UserDrawCaptionButtonWnd(hWnd, WindowDC, FALSE, ButtonType); - ReleaseCapture(); - ReleaseDC(hWnd, WindowDC); - if (Pressed) - SendMessageW(hWnd, WM_SYSCOMMAND, SCMsg, MAKELONG(Msg.pt.x,Msg.pt.y)); -} - - -LRESULT -DefWndNCLButtonDown(HWND hWnd, WPARAM wParam, LPARAM lParam) -{ - PWND Wnd = ValidateHwnd(hWnd); - - switch (wParam) - { - case HTCAPTION: - { - HWND hTopWnd = hWnd, parent; - while(1) - { - if ((GetWindowLongW( hTopWnd, GWL_STYLE ) & (WS_POPUP|WS_CHILD)) != WS_CHILD) - break; - parent = GetAncestor( hTopWnd, GA_PARENT ); - if (!parent || parent == GetDesktopWindow()) break; - hTopWnd = parent; - } - - if ( NtUserCallHwndLock(hTopWnd, HWNDLOCK_ROUTINE_SETFOREGROUNDWINDOWMOUSE) || - GetActiveWindow() == hTopWnd) - { - SendMessageW(hWnd, WM_SYSCOMMAND, SC_MOVE + HTCAPTION, lParam); - } - break; - } - case HTSYSMENU: - { - LONG style = GetWindowLongPtrW( hWnd, GWL_STYLE ); - if (style & WS_SYSMENU) - { - if( Wnd && !(style & WS_MINIMIZE) ) - { - RECT rect; - HDC hDC = GetWindowDC(hWnd); - UserGetInsideRectNC(Wnd, &rect); - UserDrawSysMenuButton(hWnd, hDC, &rect, TRUE); - ReleaseDC( hWnd, hDC ); - } - SendMessageW(hWnd, WM_SYSCOMMAND, SC_MOUSEMENU + HTSYSMENU, lParam); - } - break; - } - case HTMENU: - { - SendMessageW(hWnd, WM_SYSCOMMAND, SC_MOUSEMENU + HTMENU, lParam); - break; - } - case HTHSCROLL: - { - SendMessageW(hWnd, WM_SYSCOMMAND, SC_HSCROLL + HTHSCROLL, lParam); - break; - } - case HTVSCROLL: - { - SendMessageW(hWnd, WM_SYSCOMMAND, SC_VSCROLL + HTVSCROLL, lParam); - break; - } - case HTMINBUTTON: - case HTMAXBUTTON: - case HTCLOSE: - { - DefWndDoButton(hWnd, wParam); - break; - } - case HTLEFT: - case HTRIGHT: - case HTTOP: - case HTBOTTOM: - case HTTOPLEFT: - case HTTOPRIGHT: - case HTBOTTOMLEFT: - case HTBOTTOMRIGHT: - { - /* Old comment: - * "make sure hittest fits into 0xf and doesn't overlap with HTSYSMENU" - * This was previously done by setting wParam=SC_SIZE + wParam - 2 - */ - /* But that is not what WinNT does. Instead it sends this. This - * is easy to differentiate from HTSYSMENU, because HTSYSMENU adds - * SC_MOUSEMENU into wParam. - */ - SendMessageW(hWnd, WM_SYSCOMMAND, SC_SIZE + wParam - (HTLEFT - WMSZ_LEFT), lParam); - break; - } - case HTBORDER: - break; - } - return(0); -} - - -LRESULT -DefWndNCLButtonDblClk(HWND hWnd, WPARAM wParam, LPARAM lParam) -{ - ULONG Style; - - Style = GetWindowLongPtrW(hWnd, GWL_STYLE); - switch(wParam) - { - case HTCAPTION: - { - /* Maximize/Restore the window */ - if((Style & WS_CAPTION) == WS_CAPTION && (Style & WS_MAXIMIZEBOX)) - { - SendMessageW(hWnd, WM_SYSCOMMAND, ((Style & (WS_MINIMIZE | WS_MAXIMIZE)) ? SC_RESTORE : SC_MAXIMIZE), 0); - } - break; - } - case HTSYSMENU: - { - HMENU hSysMenu = GetSystemMenu(hWnd, FALSE); - UINT state = GetMenuState(hSysMenu, SC_CLOSE, MF_BYCOMMAND); - - /* If the close item of the sysmenu is disabled or not present do nothing */ - if ((state & (MF_DISABLED | MF_GRAYED)) || (state == 0xFFFFFFFF)) - break; - - SendMessageW(hWnd, WM_SYSCOMMAND, SC_CLOSE, lParam); - break; - } - default: - return DefWndNCLButtonDown(hWnd, wParam, lParam); - } - return(0); -} - -/*********************************************************************** - * NC_HandleNCRButtonDown - * - * Handle a WM_NCRBUTTONDOWN message. Called from DefWindowProc(). +/* + RealUserDrawCaption: This function is passed through RegisterUserApiHook to uxtheme + to call it when the classic caption is needed to be drawn. */ -LRESULT NC_HandleNCRButtonDown( HWND hwnd, WPARAM wParam, LPARAM lParam ) +LRESULT WINAPI +RealUserDrawCaption(HWND hWnd, HDC hDC, LPCRECT lpRc, UINT uFlags) { - MSG msg; - INT hittest = wParam; - - switch (hittest) - { - case HTCAPTION: - case HTSYSMENU: - if (!GetSystemMenu( hwnd, FALSE )) break; - - SetCapture( hwnd ); - for (;;) - { - if (!GetMessageW( &msg, 0, WM_MOUSEFIRST, WM_MOUSELAST )) break; - if (CallMsgFilterW( &msg, MSGF_MAX )) continue; - if (msg.message == WM_RBUTTONUP) - { - hittest = DefWndNCHitTest( hwnd, msg.pt ); - break; - } - if (hwnd != GetCapture()) return 0; - } - ReleaseCapture(); - if (hittest == HTCAPTION || hittest == HTSYSMENU || hittest == HTHSCROLL || hittest == HTVSCROLL) - { - TRACE("Msg pt %x and Msg.lParam %x and lParam %x\n",MAKELONG(msg.pt.x,msg.pt.y),msg.lParam,lParam); - SendMessageW( hwnd, WM_CONTEXTMENU, (WPARAM)hwnd, MAKELONG(msg.pt.x,msg.pt.y)); - } - break; - } - return 0; + ERR("Real DC flags %08x\n",uFlags); + return NtUserDrawCaption(hWnd, hDC, lpRc, uFlags); } -/*********************************************************************** - * NcGetInsideRect - * - * Get the 'inside' rectangle of a window, i.e. the whole window rectangle - * but without the borders (if any). - * The rectangle is in window coordinates (for drawing with GetWindowDC()). - */ -static void FASTCALL -NcGetInsideRect(HWND Wnd, RECT *Rect) -{ - DWORD Style; - DWORD ExStyle; - - GetWindowRect(Wnd, Rect); - Rect->right = Rect->right - Rect->left; - Rect->left = 0; - Rect->bottom = Rect->bottom - Rect->top; - Rect->top = 0; - - Style = GetWindowLongPtrW(Wnd, GWL_STYLE); - if (0 != (Style & WS_ICONIC)) - { - return; - } - - /* Remove frame from rectangle */ - ExStyle = GetWindowLongPtrW(Wnd, GWL_EXSTYLE); - if (HAS_THICKFRAME(Style, ExStyle)) - { - InflateRect(Rect, - GetSystemMetrics(SM_CXFRAME), - GetSystemMetrics(SM_CYFRAME)); - } - else if (HAS_DLGFRAME(Style, ExStyle)) - { - InflateRect(Rect, - GetSystemMetrics(SM_CXDLGFRAME), - GetSystemMetrics(SM_CYDLGFRAME)); - } - else if (HAS_THINFRAME(Style, ExStyle)) - { - InflateRect(Rect, - GetSystemMetrics(SM_CXBORDER), - GetSystemMetrics(SM_CYBORDER)); - } - - /* We have additional border information if the window - * is a child (but not an MDI child) */ - if (0 != (Style & WS_CHILD) - && 0 == (ExStyle & WS_EX_MDICHILD)) - { - if (0 != (ExStyle & WS_EX_CLIENTEDGE)) - { - InflateRect(Rect, - GetSystemMetrics(SM_CXEDGE), - GetSystemMetrics(SM_CYEDGE)); - } - if (0 != (ExStyle & WS_EX_STATICEDGE)) - { - InflateRect(Rect, - GetSystemMetrics(SM_CXBORDER), - GetSystemMetrics(SM_CYBORDER)); - } - } -} - -/*********************************************************************** - * NcGetSysPopupPos - */ -void FASTCALL -NcGetSysPopupPos(HWND Wnd, RECT *Rect) -{ - RECT WindowRect; - - if (IsIconic(Wnd)) - { - GetWindowRect(Wnd, Rect); - } - else - { - NcGetInsideRect(Wnd, Rect); - GetWindowRect(Wnd, &WindowRect); - OffsetRect(Rect, WindowRect.left, WindowRect.top); - if (0 != (GetWindowLongPtrW(Wnd, GWL_STYLE) & WS_CHILD)) - { - ClientToScreen(GetParent(Wnd), (POINT *) Rect); - } - Rect->right = Rect->left + GetSystemMetrics(SM_CYCAPTION) - 1; - Rect->bottom = Rect->top + GetSystemMetrics(SM_CYCAPTION) - 1; - } -} /* PUBLIC FUNCTIONS ***********************************************************/