- 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
{
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);
{
if(!IsAppThemed())
{
- if (Msg == WM_NCUAHDRAWCAPTION)
- {
- user32ApiHook.DrawCaption(hWnd, NULL, NULL, 0);
- return 0;
- }
-
return user32ApiHook.DefWindowProcW(hWnd,
Msg,
wParam,
{
if(!IsAppThemed())
{
- if (Msg == WM_NCUAHDRAWCAPTION)
- {
- user32ApiHook.DrawCaption(hWnd, NULL, NULL, 0);
- return 0;
- }
-
return user32ApiHook.DefWindowProcA(hWnd,
Msg,
wParam,
#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
#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
//
// 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
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
/* 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;
/* 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(
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.
Offset,
dwNewLong,
Ansi);
+ switch(Offset)
+ {
+ case GCLP_HICONSM:
+ case GCLP_HICON:
+ {
+ if (Ret && Ret != dwNewLong)
+ UserPaintCaption(Window, DC_ICON);
+ }
+ }
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
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)
}
}
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:
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
LPARAM lParam,
BOOL Ansi)
{
+ PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
LRESULT lResult = 0;
USER_REFERENCE_ENTRY Ref;
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;
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) )
{
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;
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 */
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:
{
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);
}
case WM_SETREDRAW:
- ERR("WM_SETREDRAW\n");
if (wParam)
{
if (!(Wnd->style & WS_VISIBLE))
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:
{
case WM_SYSCOLORCHANGE:
co_UserRedrawWindow(Wnd, NULL, NULL, RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN);
return TRUE;
+
case WM_SETCURSOR:
{
PCURICON_OBJECT pcurOld, pcurNew;
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 */
}
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 */
WCHAR Symbol;
switch(uFlags & 0xff)
{
+ case DFCS_MENUARROWUP:
+ Symbol = '5';
+ break;
+
+ case DFCS_MENUARROWDOWN:
+ Symbol = '6';
+ break;
+
case DFCS_MENUARROW:
Symbol = '8';
break;
hFont = GreCreateFontIndirectW(&lf);
/* save font */
hOldFont = NtGdiSelectFont(dc, hFont);
- // FIXME selecting color doesn't work
-#if 0
if(uFlags & DFCS_INACTIVE)
{
/* draw shadow */
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 */
//
+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)
{
(LPARAM)hWnd);
if (WndPrev)
- WndPrev->state &= ~WNDS_ACTIVEFRAME;
+ WndPrev->state &= ~(WNDS_ACTIVEFRAME|WNDS_HASCAPTION);
}
else
{
}
// 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;
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;
/* 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;
}
if (!Wnd) {ERR("Window is NULL\n");}
MsqPostMessage(pti, &Msg, TRUE, QS_KEY, 0, dwExtraInfo);
}
-
return TRUE;
}
/* 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
#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) { \
{
HMENU hMenu;
PITEM pItem;
+ ULONG Error;
UINT i;
if (!pMenu) return NULL;
+ Error = EngGetLastError();
+
_SEH2_TRY
{
hMenu = UserHMGetHandle(pMenu);
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
ERR("Run away LOOP!\n");
+ EngSetLastError(Error);
_SEH2_YIELD(return NULL);
}
_SEH2_END
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;
if(Menu)
{
PWND Window;
+ ULONG Error;
/* Remove all menu items */
IntDestroyMenu( Menu, 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)
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;
}
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
*
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
*
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 )
{
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;
res = (DWORD)(MenuItem->fState & MF_CHECKED);
- MenuItem->fState ^= (res ^ uCheck) & MF_CHECKED;
+ MenuItem->fState ^= (res ^ uCheck) & MF_CHECKED;
+
+ return res;
+}
+
+BOOL FASTCALL
+UserSetMenuDefaultItem(PMENU MenuObject, UINT uItem, UINT fByPos)
+{
+ UINT i;
+ PITEM MenuItem = MenuObject->rgItems;
+
+ if (!MenuItem) return FALSE;
+
+ /* reset all default-item flags */
+ for (i = 0; i < MenuObject->cItems; i++, MenuItem++)
+ {
+ MenuItem->fState &= ~MFS_DEFAULT;
+ }
+
+ /* no default item */
+ if(uItem == (UINT)-1)
+ {
+ return TRUE;
+ }
+ MenuItem = MenuObject->rgItems;
+ if ( fByPos )
+ {
+ if ( uItem >= MenuObject->cItems ) return FALSE;
+ MenuItem[uItem].fState |= MFS_DEFAULT;
+ return TRUE;
+ }
+ else
+ {
+ for (i = 0; i < MenuObject->cItems; i++, MenuItem++)
+ {
+ if (MenuItem->wID == uItem)
+ {
+ MenuItem->fState |= MFS_DEFAULT;
+ return TRUE;
+ }
+ }
+
+ }
+ return FALSE;
+}
+
+UINT FASTCALL
+IntGetMenuDefaultItem(PMENU MenuObject, UINT fByPos, UINT gmdiFlags, DWORD *gismc)
+{
+ UINT i = 0;
+ PITEM MenuItem = MenuObject->rgItems;
+
+ /* empty menu */
+ if (!MenuItem) return -1;
+
+ while ( !( MenuItem->fState & MFS_DEFAULT ) )
+ {
+ i++; MenuItem++;
+ if (i >= MenuObject->cItems ) return -1;
+ }
+
+ /* default: don't return disabled items */
+ if ( (!(GMDI_USEDISABLED & gmdiFlags)) && (MenuItem->fState & MFS_DISABLED )) return -1;
+
+ /* search rekursiv when needed */
+ if ( (MenuItem->fType & MF_POPUP) && (gmdiFlags & GMDI_GOINTOPOPUPS) && MenuItem->spSubMenu)
+ {
+ UINT ret;
+ (*gismc)++;
+ ret = IntGetMenuDefaultItem( MenuItem->spSubMenu, fByPos, gmdiFlags, gismc );
+ (*gismc)--;
+ if ( -1 != ret ) return ret;
+
+ /* when item not found in submenu, return the popup item */
+ }
+ return ( fByPos ) ? i : MenuItem->wID;
+}
+
+PMENU
+FASTCALL
+co_IntGetSubMenu(
+ PMENU pMenu,
+ int nPos)
+{
+ PITEM pItem;
+ if (!(pItem = MENU_FindItem( &pMenu, (UINT*)&nPos, MF_BYPOSITION ))) return NULL;
+ return pItem->spSubMenu;
+}
+
+/***********************************************************************
+ * MenuInitSysMenuPopup
+ *
+ * Grey the appropriate items in System menu.
+ */
+void FASTCALL MENU_InitSysMenuPopup(PMENU menu, DWORD style, DWORD clsStyle, LONG HitTest )
+{
+ BOOL gray;
+ UINT DefItem;
+
+ 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);
+}
+
+
+/***********************************************************************
+ * 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)
+{
+ 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 res;
+ return TRUE;
}
BOOL FASTCALL
UINT uHilite)
{
PITEM MenuItem;
+ UINT uItem = uItemHilite;
- if (!(MenuItem = MENU_FindItem( &MenuObject, &uItemHilite, uHilite ))) return FALSE;
+ if (!(MenuItem = MENU_FindItem( &MenuObject, &uItem, uHilite ))) return TRUE;
if (uHilite & MF_HILITE)
{
{
MenuItem->fState &= ~MF_HILITE;
}
- /* FIXME: Update the window's menu */
+ if (MenuObject->iItem == uItemHilite) return TRUE;
+ MENU_HideSubPopups( WindowObject, MenuObject, FALSE, 0 );
+ MENU_SelectItem( WindowObject, MenuObject, uItemHilite, TRUE, 0 );
return TRUE; // Always returns true!!!!
}
-BOOL FASTCALL
-UserSetMenuDefaultItem(PMENU MenuObject, UINT uItem, UINT fByPos)
-{
- UINT i;
- PITEM MenuItem = MenuObject->rgItems;
-
- if (!MenuItem) return FALSE;
-
- /* reset all default-item flags */
- for (i = 0; i < MenuObject->cItems; i++, MenuItem++)
- {
- MenuItem->fState &= ~MFS_DEFAULT;
- }
-
- /* no default item */
- if(uItem == (UINT)-1)
- {
- return TRUE;
- }
- MenuItem = MenuObject->rgItems;
- if ( fByPos )
- {
- if ( uItem >= MenuObject->cItems ) return FALSE;
- MenuItem[uItem].fState |= MFS_DEFAULT;
- return TRUE;
- }
- else
- {
- for (i = 0; i < MenuObject->cItems; i++, MenuItem++)
- {
- if (MenuItem->wID == uItem)
- {
- MenuItem->fState |= MFS_DEFAULT;
- return TRUE;
- }
- }
-
- }
- return FALSE;
-}
-
-
-UINT FASTCALL
-IntGetMenuDefaultItem(PMENU MenuObject, UINT fByPos, UINT gmdiFlags, DWORD *gismc)
-{
- UINT i = 0;
- PITEM MenuItem = MenuObject->rgItems;
-
- /* empty menu */
- if (!MenuItem) return -1;
-
- while ( !( MenuItem->fState & MFS_DEFAULT ) )
- {
- i++; MenuItem++;
- if (i >= MenuObject->cItems ) return -1;
- }
-
- /* default: don't return disabled items */
- if ( (!(GMDI_USEDISABLED & gmdiFlags)) && (MenuItem->fState & MFS_DISABLED )) return -1;
-
- /* search rekursiv when needed */
- if ( (MenuItem->fType & MF_POPUP) && (gmdiFlags & GMDI_GOINTOPOPUPS) && MenuItem->spSubMenu)
- {
- UINT ret;
- (*gismc)++;
- ret = IntGetMenuDefaultItem( MenuItem->spSubMenu, fByPos, gmdiFlags, gismc );
- (*gismc)--;
- if ( -1 != ret ) return ret;
-
- /* when item not found in submenu, return the popup item */
- }
- return ( fByPos ) ? i : MenuItem->wID;
-}
-
-VOID FASTCALL
-co_IntInitTracking(PWND Window, PMENU Menu, BOOL Popup,
- UINT Flags)
-{
- /* 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);
-}
-
-VOID FASTCALL
-co_IntExitTracking(PWND Window, PMENU Menu, BOOL Popup,
- UINT Flags)
-{
- if(!(Flags & TPM_NONOTIFY))
- co_IntSendMessage(Window->head.h, WM_EXITMENULOOP, 0 /* FIXME */, 0);
-
- /* FIXME: Show caret again */
-}
-
-INT FASTCALL
-IntTrackMenu(PMENU Menu, PWND Window, INT x, INT y,
- RECTL lprect)
-{
- return 0;
-}
-
-BOOL FASTCALL
-co_IntTrackPopupMenu(PMENU Menu, PWND Window,
- UINT Flags, POINT *Pos, UINT MenuPos, RECTL *ExcludeRect)
-{
- co_IntInitTracking(Window, Menu, TRUE, Flags);
-
- co_IntExitTracking(Window, Menu, TRUE, Flags);
- return FALSE;
-}
-
BOOLEAN APIENTRY
intGetTitleBarInfo(PWND pWindowObject, PTITLEBARINFO bti)
{
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;
}
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);
SetLastNtError(Status);
return( FALSE);
}
- if(Size < sizeof(MENUINFO) || sizeof(ROSMENUINFO) < Size)
+ if ( Size < sizeof(MENUINFO) || Size > sizeof(ROSMENUINFO) )
{
EngSetLastError(ERROR_INVALID_PARAMETER);
return( FALSE);
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,
{
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;
return FALSE;
}
+ if (!pWnd)
+ {
+ HWND hWnd = Menu->hWnd;
+ if (!(pWnd = UserGetWindowObject(hWnd))) return FALSE;
+ }
+
if (Menu->fFlags & MNF_POPUP)
{
XMove = pWnd->rcClient.left;
}
SysMenu->fFlags |= MNF_SYSMENU;
- SysMenu->hWnd = Window->head.h;
+ SysMenu->hWnd = UserHMGetHandle(Window);
if (!Popup)
{
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;
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
{
Wnd->IDMenu = (UINT) Menu;
if (NULL != NewMenu)
{
- NewMenu->hWnd = Wnd->head.h;
+ NewMenu->hWnd = UserHMGetHandle(Wnd);
IntReleaseMenuObject(NewMenu);
}
if (NULL != OldMenu)
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
*/
MENUBARINFO kmbi;
BOOL Ret;
PPOPUPMENU pPopupMenu;
+ USER_REFERENCE_ENTRY Ref;
NTSTATUS Status = STATUS_SUCCESS;
PMENU Menu = NULL;
DECLARE_RETURN(BOOL);
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:
}
// 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);
_SEH2_TRY
{
+ ProbeForRead(pmbi, sizeof(MENUBARINFO), 1);
kmbi.cbSize = pmbi->cbSize;
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
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;
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)
RETURN(TRUE);
CLEANUP:
+ if (pWnd) UserDerefObjectCo(pWnd);
TRACE("Leave NtUserGetMenuBarInfo, ret=%i\n",_ret_);
UserLeave();
END_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
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;
RETURN( FALSE);
}
- if (! IntSetMenu(Window, Menu, &Changed))
+ if (!IntSetMenu(Window, Menu, &Changed))
{
RETURN( FALSE);
}
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 */
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);
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);
{ 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
pMsg->message,
pMsg->wParam,
pMsg->lParam,
- &retval);
+ &retval);
+ case FNID_MENU:
+ DoCallBack = !PopupMenuWndProc( Window,
+ pMsg->message,
+ pMsg->wParam,
+ pMsg->lParam,
+ &retval);
break;
}
}
return TRUE;
}
-static BOOL FASTCALL
+BOOL FASTCALL
co_IntWaitMessage( PWND Window,
UINT MsgFilterMin,
UINT MsgFilterMax )
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)
{
case FNID_MESSAGEWND:
DoCallBack = !UserMessageWindowProc(Window, Msg, wParam, lParam,(LRESULT*)&Result);
break;
+ case FNID_MENU:
+ DoCallBack = !PopupMenuWndProc( Window, Msg, wParam, lParam,(LRESULT*)&Result);
+ break;
}
}
}
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;
}
case FNID_CALLWNDPROCRET:
case FNID_SCROLLBAR:
case FNID_DESKTOP:
+ case FNID_MENU:
if (ResultInfo)
{
_SEH2_TRY
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;
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;
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,
UINT Msg,
WPARAM wParam,
LPARAM lParam );
+BOOL FASTCALL
+co_IntWaitMessage( PWND Window,
+ UINT MsgFilterMin,
+ UINT MsgFilterMax );
/* EOF */
#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,
}
}
+/***********************************************************************
+ * 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)
{
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)
{
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.
*/
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)
{
END_CLEANUP;
}
-static const WCHAR ELLIPSISW[] = {'.','.','.', 0};
-
BOOL
UserDrawCaptionText(
PWND pWnd,
SIZE Size;
BOOL Ret = TRUE;
ULONG fit = 0, Length;
- WCHAR szText[128];
RECTL r = *lpRc;
TRACE("UserDrawCaptionText: %wZ\n", Text);
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)
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;
}
LPCRECT lpRc,
UINT uFlags)
{
- return NtUserDrawCaptionTemp(hWnd, hDC, lpRc, 0, 0, NULL, uFlags);
+ return NtUserDrawCaptionTemp(hWnd, hDC, lpRc, 0, 0, NULL, uFlags);
}
BOOL
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);
+
#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);
#include <win32k.h>
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() */
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)
{
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)
{
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
*
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_) \
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 ***************/
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 ***************/
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 */
extern struct _CLS *SystemClassList;
extern BOOL RegisteredSysClasses;
-typedef struct tagMENUSTATE MENUSTATE, *PMENUSTATE;
-
#include <pshpack1.h>
// FIXME: Move to ntuser.h
typedef struct _TL
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;
{
HWND hWnd;
UINT State, State2;
+ ULONG Error;
if (!pWnd) return NULL;
+ Error = EngGetLastError();
+
_SEH2_TRY
{
hWnd = UserHMGetHandle(pWnd);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
+ EngSetLastError(Error);
_SEH2_YIELD(return NULL);
}
_SEH2_END
if ( UserObjectInDestroy(hWnd) ||
State & WNDS_DESTROYED ||
State2 & WNDS2_INDESTROY )
- return NULL;
+ pWnd = NULL;
+ EngSetLastError(Error);
return pWnd;
}
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
*/
Window->IDMenu &&
(Menu = UserGetMenuObject((HMENU)Window->IDMenu)))
{
+ TRACE("UFW: IDMenu %p\n",Window->IDMenu);
IntDestroyMenuObject(Menu, TRUE);
Window->IDMenu = 0;
}
Ansi);
if (!OldValue) return 0;
}
-*/
+ */
*((LONG *)((PCHAR)(Window + 1) + Index)) = NewValue;
}
else
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
*
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);
Cmd = SW_SHOWDEFAULT;
}
FirstTime = TRUE;
- ERR("co_WPSW FT 1\n");
+ TRACE("co_WPSW FT 1\n");
}
}
}
{
Cmd = pti->ppi->usi.wShowWindow;
FirstTime = TRUE;
- ERR("co_WPSW FT 2\n");
+ TRACE("co_WPSW FT 2\n");
}
}
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);
/* Setup the icons */
co_IntSetWndIcons();
+ /* Setup Menu */
+ MenuInit();
+
/* Show the desktop */
pdesk = IntGetActiveDesktop();
ASSERT(pdesk);
--- /dev/null
+/*
+ * 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 <win32k.h>
+DBG_DEFAULT_CHANNEL(UserMenu);
+#else
+#include <user32.h>
+#include <wine/debug.h>
+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 <assert.h>
+
+#endif
+
+void _font_assert(const char *msg, const char *file, int line)
+{
+ /* Assertion failed at foo.c line 45: x<y */
+ DbgPrint("Assertion failed at %s line %d: %s\n", file, line, msg);
+#ifdef _WIN32K_
+ ASSERT(FALSE);
+#else
+ ExitProcess(3);
+ for(;;); /* eliminate warning by mingw */
+#endif
+}
+
+/***********************************************************************
+ * TEXT_TabbedTextOut
+ *
+ * Helper function for TabbedTextOut() and GetTabbedTextExtent().
+ * Note: this doesn't work too well for text-alignment modes other
+ * than TA_LEFT|TA_TOP. But we want bug-for-bug compatibility :-)
+ */
+/* WINE synced 22-May-2006 */
+LONG TEXT_TabbedTextOut( HDC hdc,
+ INT x,
+ INT y,
+ LPCWSTR lpstr,
+ INT count,
+ INT cTabStops,
+ const INT *lpTabPos,
+ INT nTabOrg,
+ BOOL fDisplayText )
+{
+ INT defWidth;
+ SIZE extent;
+ int i, j;
+ int start = x;
+
+ if (!lpTabPos)
+ cTabStops=0;
+
+ if (cTabStops == 1)
+ {
+ defWidth = *lpTabPos;
+ cTabStops = 0;
+ }
+ else
+ {
+#ifdef _WIN32K_
+ TEXTMETRICW tm;
+ GreGetTextMetricsW( hdc, &tm );
+#else
+ TEXTMETRICA tm;
+ GetTextMetricsA( hdc, &tm );
+#endif
+ defWidth = 8 * tm.tmAveCharWidth;
+ }
+
+ while (count > 0)
+ {
+ RECT r;
+ INT x0;
+ x0 = x;
+ r.left = x0;
+ /* chop the string into substrings of 0 or more <tabs>
+ * 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 <tab>, 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)