You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
413 lines
13 KiB
413 lines
13 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: menudd.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* Menu drag and drop - kernel
|
|
*
|
|
* History:
|
|
* 10/29/96 GerardoB Created
|
|
\***************************************************************************/
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#include "callback.h"
|
|
/*
|
|
* xxxClient* are callbacks from the kernel to call/load OLE functions
|
|
* The other functions in this file are client calls into the kernel
|
|
*/
|
|
/**************************************************************************\
|
|
* xxxClientLoadOLE
|
|
*
|
|
* 11/06/96 GerardoB Created
|
|
\**************************************************************************/
|
|
NTSTATUS xxxClientLoadOLE (void)
|
|
{
|
|
NTSTATUS Status;
|
|
PPROCESSINFO ppiCurrent = PpiCurrent();
|
|
|
|
if (ppiCurrent->W32PF_Flags & W32PF_OLELOADED) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
Status = xxxUserModeCallback(FI_CLIENTLOADOLE, NULL, 0, NULL, 0);
|
|
if (NT_SUCCESS(Status)) {
|
|
ppiCurrent->W32PF_Flags |= W32PF_OLELOADED;
|
|
}
|
|
return Status;
|
|
}
|
|
/**************************************************************************\
|
|
* xxxClientRegisterDragDrop
|
|
*
|
|
* 10/28/96 GerardoB Created
|
|
\**************************************************************************/
|
|
NTSTATUS xxxClientRegisterDragDrop (HWND hwnd)
|
|
{
|
|
return xxxUserModeCallback(FI_CLIENTREGISTERDRAGDROP, &hwnd, sizeof(&hwnd), NULL, 0);
|
|
}
|
|
/**************************************************************************\
|
|
* xxxClientRevokeDragDrop
|
|
*
|
|
* 10/28/96 GerardoB Created
|
|
\**************************************************************************/
|
|
NTSTATUS xxxClientRevokeDragDrop (HWND hwnd)
|
|
{
|
|
return xxxUserModeCallback(FI_CLIENTREVOKEDRAGDROP, &hwnd, sizeof(&hwnd), NULL, 0);
|
|
|
|
}
|
|
/**************************************************************************\
|
|
* xxxMNSetGapState
|
|
*
|
|
* 11/15/96 GerardoB Created
|
|
\**************************************************************************/
|
|
void xxxMNSetGapState (ULONG_PTR uHitArea, UINT uIndex, UINT uFlags, BOOL fSet)
|
|
{
|
|
int yTop;
|
|
PITEM pItem, pItemGap;
|
|
PPOPUPMENU ppopup;
|
|
RECT rc;
|
|
TL tlHitArea;
|
|
|
|
/*
|
|
* Bail if there is nothing to do.
|
|
*/
|
|
if (!(uFlags & MNGOF_GAP) || !IsMFMWFPWindow(uHitArea)) {
|
|
return;
|
|
}
|
|
|
|
ppopup = ((PMENUWND)uHitArea)->ppopupmenu;
|
|
pItem = MNGetpItem(ppopup, uIndex);
|
|
|
|
/*
|
|
* The menu window might be destroyed by now so pItem could be NULL.
|
|
*/
|
|
if (pItem == NULL) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Mark the item and set the rectangle we need to redraw.
|
|
* Drawing/erasing the insertion bar unhilites/hiltes the
|
|
* item, so pItem needs to be redrawn completely. In additon,
|
|
* we need to draw the insertion bar in the next/previous item.
|
|
*/
|
|
rc.left = pItem->xItem;
|
|
rc.right = pItem->xItem + pItem->cxItem;
|
|
rc.top = pItem->yItem;
|
|
rc.bottom = pItem->yItem + pItem->cyItem;
|
|
|
|
if (uFlags & MNGOF_TOPGAP) {
|
|
pItemGap = MNGetpItem(ppopup, uIndex - 1);
|
|
if (fSet) {
|
|
SetMFS(pItem, MFS_TOPGAPDROP);
|
|
if (pItemGap != NULL) {
|
|
SetMFS(pItemGap, MFS_BOTTOMGAPDROP);
|
|
}
|
|
} else {
|
|
ClearMFS(pItem, MFS_TOPGAPDROP);
|
|
if (pItemGap != NULL) {
|
|
ClearMFS(pItemGap, MFS_BOTTOMGAPDROP);
|
|
}
|
|
}
|
|
if (pItemGap != NULL) {
|
|
rc.top -= SYSMET(CYDRAG);
|
|
}
|
|
} else {
|
|
pItemGap = MNGetpItem(ppopup, uIndex + 1);
|
|
if (fSet) {
|
|
SetMFS(pItem, MFS_BOTTOMGAPDROP);
|
|
if (pItemGap != NULL) {
|
|
SetMFS(pItemGap, MFS_TOPGAPDROP);
|
|
}
|
|
} else {
|
|
ClearMFS(pItem, MFS_BOTTOMGAPDROP);
|
|
if (pItemGap != NULL) {
|
|
ClearMFS(pItemGap, MFS_TOPGAPDROP);
|
|
}
|
|
}
|
|
if (pItemGap != NULL) {
|
|
rc.bottom += SYSMET(CYDRAG);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Adjust to "menu" coordinates (for scrollable menus)
|
|
*/
|
|
yTop = MNGetToppItem(ppopup->spmenu)->yItem;
|
|
rc.top -= yTop;
|
|
rc.bottom -= yTop;
|
|
|
|
/*
|
|
* Invalidate this rect to repaint it later
|
|
*/
|
|
ThreadLockAlways((PWND)uHitArea, &tlHitArea);
|
|
xxxInvalidateRect((PWND)uHitArea, &rc, TRUE);
|
|
ThreadUnlock(&tlHitArea);
|
|
}
|
|
/**************************************************************************\
|
|
* xxxMNDragOver
|
|
*
|
|
* Menu windows involved in drag drop are registered as targets. This function
|
|
* is called from the client side IDropTarget functions so the menu code can
|
|
* update the selection given the mouse position
|
|
*
|
|
* 10/28/96 GerardoB Created
|
|
\**************************************************************************/
|
|
BOOL xxxMNDragOver(POINT * ppt, PMNDRAGOVERINFO pmndoi)
|
|
{
|
|
BOOL fRet;
|
|
PMENUSTATE pMenuState;
|
|
PWND pwnd;
|
|
PPOPUPMENU ppopup;
|
|
TL tlpwnd;
|
|
|
|
/*
|
|
* OLE always calls us in context (proxy/marshall stuff). So the
|
|
* current thread must be in menu mode
|
|
*/
|
|
pMenuState = PtiCurrent()->pMenuState;
|
|
if (pMenuState == NULL) {
|
|
RIPMSG0(RIP_WARNING, "xxxMNDragOver: Not in menu mode");
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* This must be a drag and drop menu
|
|
*/
|
|
UserAssert(pMenuState->fDragAndDrop);
|
|
|
|
/*
|
|
* We might have not initiated this DoDragDrop so make sure
|
|
* the internal flag is set.
|
|
*/
|
|
pMenuState->fInDoDragDrop = TRUE;
|
|
|
|
/*
|
|
* Get a window to call xxxCallHandleMenuMessages
|
|
*/
|
|
pwnd = GetMenuStateWindow(pMenuState);
|
|
if (pwnd == NULL) {
|
|
RIPMSG0(RIP_WARNING, "xxxMNDragOver: Failed to get MenuStateWindow");
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* We need this after calling back, so lock it
|
|
*/
|
|
LockMenuState(pMenuState);
|
|
|
|
/*
|
|
* Update the selection and the dragging info
|
|
* Use WM_NCMOUSEMOVE because the point is in screen coordinates already.
|
|
*/
|
|
ThreadLockAlways(pwnd, &tlpwnd);
|
|
xxxCallHandleMenuMessages(pMenuState, pwnd, WM_NCMOUSEMOVE, 0, MAKELONG(ppt->x, ppt->y));
|
|
ThreadUnlock(&tlpwnd);
|
|
|
|
/*
|
|
* If we're on a popup, propagate the hit test info
|
|
*/
|
|
if (pMenuState->uDraggingHitArea != MFMWFP_OFFMENU) {
|
|
ppopup = ((PMENUWND)pMenuState->uDraggingHitArea)->ppopupmenu;
|
|
pmndoi->hmenu = PtoH(ppopup->spmenu);
|
|
pmndoi->uItemIndex = pMenuState->uDraggingIndex;
|
|
pmndoi->hwndNotify = PtoH(ppopup->spwndNotify);
|
|
pmndoi->dwFlags = pMenuState->uDraggingFlags;
|
|
/*
|
|
* Bottom gap of item N corresponds to N+1 gap
|
|
*/
|
|
if (pmndoi->dwFlags & MNGOF_BOTTOMGAP) {
|
|
UserAssert(pmndoi->uItemIndex != MFMWFP_NOITEM);
|
|
(pmndoi->uItemIndex)++;
|
|
}
|
|
fRet = TRUE;
|
|
} else {
|
|
fRet = FALSE;
|
|
}
|
|
|
|
xxxUnlockMenuState(pMenuState);
|
|
return fRet;;
|
|
|
|
}
|
|
/**************************************************************************\
|
|
* xxxMNDragLeave
|
|
*
|
|
* 11/15/96 GerardoB Created
|
|
\**************************************************************************/
|
|
BOOL xxxMNDragLeave (VOID)
|
|
{
|
|
PMENUSTATE pMenuState;
|
|
|
|
pMenuState = PtiCurrent()->pMenuState;
|
|
if (pMenuState == NULL) {
|
|
RIPMSG0(RIP_WARNING, "xxxMNDragLeave: Not in menu mode");
|
|
return FALSE;
|
|
}
|
|
|
|
LockMenuState(pMenuState);
|
|
|
|
/*
|
|
* Clean up any present insertion bar state
|
|
*/
|
|
xxxMNSetGapState(pMenuState->uDraggingHitArea,
|
|
pMenuState->uDraggingIndex,
|
|
pMenuState->uDraggingFlags,
|
|
FALSE);
|
|
|
|
/*
|
|
* Forget the last dragging area
|
|
*/
|
|
UnlockMFMWFPWindow(&pMenuState->uDraggingHitArea);
|
|
pMenuState->uDraggingIndex = MFMWFP_NOITEM;
|
|
pMenuState->uDraggingFlags = 0;
|
|
|
|
|
|
/*
|
|
* The DoDragDrop loop has left our window.
|
|
*/
|
|
pMenuState->fInDoDragDrop = FALSE;
|
|
|
|
xxxUnlockMenuState(pMenuState);
|
|
|
|
return TRUE;
|
|
}
|
|
/**************************************************************************\
|
|
* xxxMNUpdateDraggingInfo
|
|
*
|
|
* 10/28/96 GerardoB Created
|
|
\**************************************************************************/
|
|
void xxxMNUpdateDraggingInfo (PMENUSTATE pMenuState, ULONG_PTR uHitArea, UINT uIndex)
|
|
{
|
|
BOOL fCross;
|
|
int y, iIndexDelta;
|
|
PITEM pItem;
|
|
PPOPUPMENU ppopup;
|
|
TL tlLastHitArea;
|
|
ULONG_PTR uLastHitArea;
|
|
UINT uLastIndex, uLastFlags;
|
|
|
|
/*
|
|
* Remember current dragging area so we can detected when
|
|
* crossing item/gap boundries.
|
|
*/
|
|
UserAssert((pMenuState->uDraggingHitArea == 0) || IsMFMWFPWindow(pMenuState->uDraggingHitArea));
|
|
ThreadLock((PWND)pMenuState->uDraggingHitArea, &tlLastHitArea);
|
|
uLastHitArea = pMenuState->uDraggingHitArea;
|
|
uLastIndex = pMenuState->uDraggingIndex;
|
|
uLastFlags = pMenuState->uDraggingFlags & MNGOF_GAP;
|
|
|
|
/*
|
|
* Store new dragging area.
|
|
*/
|
|
LockMFMWFPWindow(&pMenuState->uDraggingHitArea, uHitArea);
|
|
pMenuState->uDraggingIndex = uIndex;
|
|
|
|
/*
|
|
* If we're not on a popup, done.
|
|
*/
|
|
if (!IsMFMWFPWindow(pMenuState->uDraggingHitArea)) {
|
|
pMenuState->uDraggingHitArea = MFMWFP_OFFMENU;
|
|
pMenuState->uDraggingIndex = MFMWFP_NOITEM;
|
|
ThreadUnlock(&tlLastHitArea);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Get the popup and item we're on
|
|
*/
|
|
ppopup = ((PMENUWND)pMenuState->uDraggingHitArea)->ppopupmenu;
|
|
pItem = MNGetpItem(ppopup, pMenuState->uDraggingIndex);
|
|
|
|
/*
|
|
* Find out if we're on the gap, that is, the "virtual" space
|
|
* between items. Some apps want to distinguish between a drop
|
|
* ON the item and a drop BEFORE/AFTER the item; there is no
|
|
* actual space between items so we define a virtual gap
|
|
*
|
|
*/
|
|
pMenuState->uDraggingFlags = 0;
|
|
if (pItem != NULL) {
|
|
/*
|
|
* Map the point to client coordinates and then to "menu"
|
|
* coordinates (to take care of scrollable menus)
|
|
*/
|
|
y = pMenuState->ptMouseLast.y;
|
|
y -= ((PWND)pMenuState->uDraggingHitArea)->rcClient.top;
|
|
y += MNGetToppItem(ppopup->spmenu)->yItem;
|
|
#if DBG
|
|
if ((y < (int)pItem->yItem)
|
|
|| (y > (int)(pItem->yItem + pItem->cyItem))) {
|
|
RIPMSG4(RIP_ERROR, "xxxMNUpdateDraggingInfo: y Point not in selected item. "
|
|
"pwnd:%#lx ppopup:%#lx Index:%#lx pItem:%#lx",
|
|
pMenuState->uDraggingHitArea, ppopup, pMenuState->uDraggingIndex, pItem);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Top/bottom gap check
|
|
*/
|
|
if (y <= (int)(pItem->yItem + SYSMET(CYDRAG))) {
|
|
pMenuState->uDraggingFlags = MNGOF_TOPGAP;
|
|
} else if (y >= (int)(pItem->yItem + pItem->cyItem - SYSMET(CYDRAG))) {
|
|
pMenuState->uDraggingFlags = MNGOF_BOTTOMGAP;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Have we crossed an item/gap boundary?
|
|
* We don't cross a boundary when we move from the bottom
|
|
* of an item to the top of the next, or, from the top
|
|
* of an item to the bottom of the previous.
|
|
* (Item N is on top of and previous to item N+1).
|
|
*/
|
|
fCross = (uLastHitArea != pMenuState->uDraggingHitArea);
|
|
if (!fCross) {
|
|
iIndexDelta = (int)pMenuState->uDraggingIndex - (int)uLastIndex;
|
|
switch (iIndexDelta) {
|
|
case 0:
|
|
/*
|
|
* We're on the same item.
|
|
*/
|
|
fCross = (uLastFlags != pMenuState->uDraggingFlags);
|
|
break;
|
|
|
|
case 1:
|
|
/*
|
|
* We've moved to the next item
|
|
*/
|
|
fCross = !((pMenuState->uDraggingFlags == MNGOF_TOPGAP)
|
|
&& (uLastFlags == MNGOF_BOTTOMGAP));
|
|
break;
|
|
|
|
case -1:
|
|
/*
|
|
* We've moved to the previous item
|
|
*/
|
|
fCross = !((pMenuState->uDraggingFlags == MNGOF_BOTTOMGAP)
|
|
&& (uLastFlags == MNGOF_TOPGAP));
|
|
break;
|
|
|
|
default:
|
|
/*
|
|
* We've skipped more than one item.
|
|
*/
|
|
fCross = TRUE;
|
|
}
|
|
}
|
|
|
|
if (fCross) {
|
|
pMenuState->uDraggingFlags |= MNGOF_CROSSBOUNDARY;
|
|
|
|
/*
|
|
* Update the insertion bar state.
|
|
*/
|
|
xxxMNSetGapState(uLastHitArea, uLastIndex, uLastFlags, FALSE);
|
|
xxxMNSetGapState(pMenuState->uDraggingHitArea,
|
|
pMenuState->uDraggingIndex,
|
|
pMenuState->uDraggingFlags,
|
|
TRUE);
|
|
}
|
|
|
|
ThreadUnlock(&tlLastHitArea);
|
|
}
|
|
|