Leaked source code of windows server 2003
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

/****************************** 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);
}