windows-nt/Source/XPSP1/NT/shell/shell32/menuband/menubar.cpp
2020-09-26 16:20:57 +08:00

1602 lines
43 KiB
C++

#include "shellprv.h"
#include "clsobj.h"
#include "basebar.h"
#include "bands.h"
#include "menubar.h"
#include "menuband.h"
#include "isfband.h"
#include "util.h"
#include "apithk.h"
#undef WINEVENT_VALID //It's tripping on this...
#include "winable.h"
#include "oleacc.h"
#ifdef UNIX
#include "unixstuff.h"
#endif
#define THISCLASS CMenuDeskBar
#define SUPERCLASS CBaseBar
// Don't fade the menu if it's larger than this magical number. Based on experiments
// on a Pentium II - 233
#define MAGICAL_NO_FADE_HEIGHT 600
// For TraceMsg
#define DM_POPUP DM_TRACE
#define UP 0
#define DOWN 1
#define LEFT 2
#define RIGHT 3
#ifdef ENABLE_CHANNELS
IDeskBand * ChannelBand_Create(LPCITEMIDLIST pidl);
#endif // ENABLE_CHANNELS
STDAPI CMenuDeskBar_CreateInstance(IUnknown* pUnkOuter, REFIID riid, void** ppv)
{
// aggregation checking is handled in class factory
HRESULT hr = E_OUTOFMEMORY;
CMenuDeskBar *pwbar = new CMenuDeskBar();
if (pwbar)
{
hr = pwbar->QueryInterface(riid, ppv);
pwbar->Release();
}
return hr;
}
CMenuDeskBar::CMenuDeskBar() : SUPERCLASS()
{
_dwMode = DBIF_VIEWMODE_VERTICAL;
_iIconSize = BMICON_SMALL;
}
CMenuDeskBar::~CMenuDeskBar()
{
SetSite(NULL);
}
STDMETHODIMP CMenuDeskBar::QueryInterface(REFIID riid, void **ppvObj)
{
HRESULT hres;
static const QITAB qit[] = {
QITABENT(THISCLASS, IMenuPopup),
QITABENT(THISCLASS, IObjectWithSite),
QITABENT(THISCLASS, IBanneredBar),
QITABENT(THISCLASS, IInitializeObject),
{ 0 },
};
hres = QISearch(this, (LPCQITAB)qit, riid, ppvObj);
if (FAILED(hres))
hres = SUPERCLASS::QueryInterface(riid, ppvObj);
return hres;
}
/*----------------------------------------------------------
Purpose: IMenuPopup::SetSubmenu method
*/
STDMETHODIMP CMenuDeskBar::SetSubMenu(IMenuPopup* pmp, BOOL fSet)
{
if (fSet)
{
if (_pmpChild)
_pmpChild->Release();
_pmpChild = pmp;
_pmpChild->AddRef();
}
else
{
if (_pmpChild && SHIsSameObject(pmp, _pmpChild))
{
_pmpChild->Release();
_pmpChild = NULL;
}
}
return S_OK;
}
void CMenuDeskBar::_PopDown()
{
DAD_ShowDragImage(FALSE);
if (_pmpChild)
_pmpChild->OnSelect(MPOS_CANCELLEVEL);
// ShowWindow(_hwnd, SW_HIDE);
SetWindowPos(_hwnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_HIDEWINDOW);
ShowDW(FALSE);
if (_pmpParent)
{
_pmpParent->SetSubMenu(this, FALSE);
}
UIActivateIO(FALSE, NULL);
_fActive = FALSE;
DAD_ShowDragImage(TRUE);
}
/*----------------------------------------------------------
Purpose: IMenuPopup::OnSelect method
*/
STDMETHODIMP CMenuDeskBar::OnSelect(DWORD dwSelectType)
{
switch (dwSelectType)
{
case MPOS_CHILDTRACKING:
if (_pmpParent)
_pmpParent->OnSelect(dwSelectType);
break;
case MPOS_SELECTRIGHT:
case MPOS_SELECTLEFT:
if (_pmpParent)
_pmpParent->OnSelect(dwSelectType);
break;
case MPOS_EXECUTE:
case MPOS_FULLCANCEL:
_PopDown();
if (_pmpParent)
_pmpParent->OnSelect(dwSelectType);
break;
case MPOS_CANCELLEVEL:
_PopDown();
break;
}
return S_OK;
}
void SetExpandedBorder(HWND hwnd, BOOL fExpanded)
{
#ifdef MAINWIN
// IEUNIX : WS_DLGFRAME implementaion looks ugly on UNIX.
fExpanded = TRUE;
#endif
DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE);
DWORD dwExStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
if (fExpanded)
{
dwStyle |= WS_BORDER;
dwStyle &= ~WS_DLGFRAME;
}
else
{
dwStyle &= ~WS_BORDER;
dwStyle |= WS_DLGFRAME;
}
SetWindowLong(hwnd, GWL_STYLE, dwStyle);
SetWindowLong(hwnd, GWL_EXSTYLE, dwExStyle);
SetWindowPos(hwnd, NULL, 0, 0, 0, 0,
SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
UpdateWindow(hwnd);
}
void CMenuDeskBar::_OnCreate()
{
if (!_fFlatMenuMode)
SetExpandedBorder(_hwnd, _fExpanded);
}
DWORD CMenuDeskBar::_GetClassStyle()
{
DWORD dwStyle = CS_SAVEBITS; // Faster repaint for menus when they go away
if (IsOS(OS_WHISTLERORGREATER))
{
dwStyle |= CS_DROPSHADOW; // Cool dropshadow effect on whistler....
}
return dwStyle;
}
DWORD CMenuDeskBar::_GetExStyle()
{
#ifndef MAINWIN
return WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
#else
return WS_EX_TOOLWINDOW | WS_EX_TOPMOST | WS_EX_MW_UNMANAGED_WINDOW;
#endif
}
// We use the following structure to pass a whole bunch of information from
// the GetPopupWindowPosition to WillFit function. We have WillFit function
// to cut the amount of duplicate code in getpopup window position. The order
// in which different the sides are checked is the only difference for popping
// up a window on a particular side.
//
// Having this function helps us to do that check by means of a parameter instead
// of repeating portions of code again and again.
typedef struct {
RECT rcAvail; // Available dimensions b/t monitor edge and exclude edge
SIZE sizeAdjust; // Size of menu edge
int cyMonitor; // Size of monitor
int cxMonitor;
int cx; // Size of menu
int cy;
int cyExtendDiff; // Difference b/t calc'd size and available size
RECT *prcResult;
RECT *prcExclude; // Exclude rect
RECT *prcMonitor;
} PopupInfo;
#define TOP 0
#define BOTTOM 1
#define LEFT 2
#define RIGHT 3
/*----------------------------------------------------------
Purpose: Attempt to fit and position a menu in the given direction
relative to an exclude rect.
Setting fForce to TRUE will cause the menu size to be adjusted
to fit, if necessary.
This function only sets the top and left coords, not the bottom
and right coords.
Returns TRUE if the desired direction can be accomplished.
*/
BOOL WillFit(PopupInfo * pinfo, int side, BOOL fForce)
{
BOOL bRet = FALSE;
LPRECT prcResult = pinfo->prcResult;
pinfo->cyExtendDiff = 0;
switch(side)
{
case TOP:
pinfo->cyExtendDiff = pinfo->cy - pinfo->rcAvail.top;
if (fForce)
{
// Doesn't make sense to subtract a negative value
ASSERT(pinfo->cyExtendDiff >= 0);
// +2 for some breathing room at the edge of the screen
pinfo->cy -= pinfo->cyExtendDiff + 2;
}
// Can the menu be positioned above?
if (pinfo->cy <= pinfo->rcAvail.top)
{
// Yes
prcResult->top = pinfo->prcExclude->top - pinfo->cy;
goto AdjustHorzPos;
}
break;
case BOTTOM:
pinfo->cyExtendDiff = pinfo->cy - pinfo->rcAvail.bottom;
if (fForce)
{
// Doesn't make sense to subtract a negative value
ASSERT(pinfo->cyExtendDiff >= 0);
// +2 for some breathing room at the edge of the screen
pinfo->cy -= pinfo->cyExtendDiff + 2;
}
// Can the menu be positioned below?
if (pinfo->cy <= pinfo->rcAvail.bottom)
{
// Yes
prcResult->top = pinfo->prcExclude->bottom;
AdjustHorzPos:
prcResult->left = max(pinfo->prcExclude->left, pinfo->prcMonitor->left);
// Can the menu be positioned relative to its left edge (hanging right)?
if (prcResult->left + pinfo->cx >= pinfo->prcMonitor->right)
{
// No; move it in so it is on the screen
// (cx has already been adjusted to fit inside the monitor dimensions)
prcResult->left = pinfo->prcMonitor->right - pinfo->cx - 1;
}
bRet = TRUE;
}
break;
case LEFT:
// Can the menu be positioned to the left?
if (pinfo->cx <= pinfo->rcAvail.left || fForce)
{
// Yes
// When cascading left, the menu does not overlap. Also align
// so the client rect is vertically aligned with the exclude top.
prcResult->left = pinfo->prcExclude->left - pinfo->cx - 1;
goto AdjustVerticalPos;
}
break;
case RIGHT:
// Can the menu be positioned to the right?
if (pinfo->cx <= pinfo->rcAvail.right || fForce)
{
// Yes
// Adjust the menu to slightly overlap the parent menu. Also align
// so the client rect is vertically aligned with the exclude top.
prcResult->left = pinfo->prcExclude->right - pinfo->sizeAdjust.cx;
AdjustVerticalPos:
prcResult->top = pinfo->prcExclude->top - pinfo->sizeAdjust.cy;
// Can the menu be positioned relative to its top edge (hanging down)?
if (prcResult->top + pinfo->cy >= pinfo->prcMonitor->bottom)
{
// No; can it be positioned relative to its bottom edge (hanging up)?
prcResult->top = pinfo->prcExclude->bottom + pinfo->sizeAdjust.cy - pinfo->cy;
if (prcResult->top < pinfo->prcMonitor->top)
{
// No; move the menu so it fits, but isn't vertically snapped.
// (cy has already been adjusted to fit inside the monitor
// dimensions)
prcResult->top = pinfo->prcMonitor->bottom - pinfo->cy - 1;
}
}
bRet = TRUE;
}
break;
}
return bRet;
}
void CMenuDeskBar::_GetPopupWindowPosition(RECT* prcDesired, RECT* prcExclude,
RECT *prcResult, SIZE * psizeAdjust, UINT uSide)
{
PopupInfo info;
MONITORINFO mi;
HMONITOR hMonitor;
RECT rcMonitor;
int cyExtendDiff = 0;
// Is this going to display the banner bitmap?
if (_iIconSize == BMICON_LARGE)
{
// Yes; add that to the dimensions
prcDesired->right += _sizeBmp.cx;
}
// First get the monitor information
hMonitor = MonitorFromRect(prcExclude, MONITOR_DEFAULTTONEAREST);
mi.cbSize = sizeof(mi);
if (GetMonitorInfo(hMonitor, &mi))
{
rcMonitor = mi.rcMonitor;
// Set the result rectangle same as the desired window
prcResult->left = prcDesired->left;
prcResult->top = prcDesired->top;
// Calculate some sizes needed for calculation
info.rcAvail.left = prcExclude->left - rcMonitor.left;
info.rcAvail.right = rcMonitor.right - prcExclude->right;
info.rcAvail.top = prcExclude->top - rcMonitor.top;
info.rcAvail.bottom = rcMonitor.bottom - prcExclude->bottom;
info.sizeAdjust = *psizeAdjust;
info.cyMonitor = RECTHEIGHT(rcMonitor);
info.cxMonitor = RECTWIDTH(rcMonitor);
info.cx = RECTWIDTH(*prcDesired);
info.cy = RECTHEIGHT(*prcDesired);
// If the desired rect is bigger than monitor then clip it to the monitor size
if (info.cy > info.cyMonitor)
info.cy = info.cyMonitor;
if (info.cx > info.cxMonitor)
info.cx = info.cxMonitor;
info.prcResult = prcResult;
info.prcExclude = prcExclude;
info.prcMonitor = &rcMonitor;
//Now Adjust the rectangle for the correct position
switch(uSide)
{
int iSide;
case MENUBAR_TOP:
if (WillFit(&info, TOP, FALSE))
{
_uSide = MENUBAR_TOP;
}
else
{
// We couldn't fit it above, how badly did we fall short?
cyExtendDiff = info.cyExtendDiff;
if (WillFit(&info, BOTTOM, FALSE))
_uSide = MENUBAR_BOTTOM;
// We can't fit it below either, which dir was closest?
// If they are equal, default to requested direction
else if (info.cyExtendDiff < cyExtendDiff)
{
_uSide = MENUBAR_BOTTOM;
WillFit(&info, BOTTOM, TRUE);
}
else
{
_uSide = MENUBAR_TOP;
WillFit(&info, TOP, TRUE);
}
}
break;
case MENUBAR_BOTTOM:
if (WillFit(&info, BOTTOM, FALSE))
{
_uSide = MENUBAR_BOTTOM;
}
else
{
// We couldn't fit it below, how badly did we fall short?
cyExtendDiff = info.cyExtendDiff;
if (WillFit(&info, TOP, FALSE))
_uSide = MENUBAR_TOP;
// We can't fit it above either, which dir was closest?
// If they are equal, default to requested direction
else if (info.cyExtendDiff < cyExtendDiff)
{
_uSide = MENUBAR_TOP;
WillFit(&info, TOP, TRUE);
}
else
{
_uSide = MENUBAR_BOTTOM;
WillFit(&info, BOTTOM, TRUE);
}
}
break;
case MENUBAR_LEFT:
if (WillFit(&info, LEFT, FALSE))
{
_uSide = MENUBAR_LEFT;
}
else if (WillFit(&info, RIGHT, FALSE))
{
_uSide = MENUBAR_RIGHT;
}
else
{
// fit where have most room and can show most of menu.
if ((info.cx - (info.prcExclude)->right) > (info.prcExclude)->left)
{
_uSide = MENUBAR_RIGHT;
iSide = RIGHT;
}
else
{
_uSide = MENUBAR_LEFT;
iSide = LEFT;
}
WillFit(&info, iSide, TRUE);
}
break;
case MENUBAR_RIGHT:
if (WillFit(&info, RIGHT, FALSE))
{
_uSide = MENUBAR_RIGHT;
}
else if (WillFit(&info, LEFT, FALSE))
{
_uSide = MENUBAR_LEFT;
}
else
{
// fit where have most room and can show most of menu.
if ((info.cx - (info.prcExclude)->right) >= (info.prcExclude)->left)
{
_uSide = MENUBAR_RIGHT;
iSide = RIGHT;
}
else
{
_uSide = MENUBAR_LEFT;
iSide = LEFT;
}
WillFit(&info, iSide, TRUE);
}
break;
}
// Finally set the bottom and right
if (prcResult->top < rcMonitor.top)
prcResult->top = rcMonitor.top;
if (prcResult->left < rcMonitor.left)
prcResult->left = rcMonitor.left;
prcResult->bottom = prcResult->top + info.cy;
prcResult->right = prcResult->left + info.cx;
if (prcResult->bottom > rcMonitor.bottom)
{
// -2 for some breathing room at the edge of the screen
prcResult->bottom = rcMonitor.bottom - 2;
prcResult->top = prcResult->bottom - info.cy;
}
}
}
void SlideAnimate(HWND hwnd, RECT* prc, UINT uFlags, UINT uSide)
{
DWORD dwAnimateFlags = AW_CENTER;
switch(uSide)
{
case MENUBAR_LEFT: dwAnimateFlags = AW_HOR_NEGATIVE;
break;
case MENUBAR_RIGHT: dwAnimateFlags = AW_HOR_POSITIVE;
break;
case MENUBAR_TOP: dwAnimateFlags = AW_VER_NEGATIVE;
break;
case MENUBAR_BOTTOM: dwAnimateFlags = AW_VER_POSITIVE;
break;
}
AnimateWindow(hwnd, 120, dwAnimateFlags | AW_SLIDE);
}
void AnimateSetMenuPos(HWND hwnd, RECT* prc, UINT uFlags, UINT uSide, BOOL fNoAnimate)
{
// simple check to see if we're too big for animatewindow, based on menu area.
// this is because it has to read from video mem to do the alpha and thats slow, maybe
// let it back if we get hardware acceleration in a future release.
BOOL fPerfBad = (RECTWIDTH(*prc) * RECTHEIGHT(*prc) > 600 * 200);
if (!fNoAnimate && !fPerfBad)
{
BOOL fAnimate = FALSE;
SystemParametersInfo(SPI_GETMENUANIMATION, 0, &fAnimate, 0);
if (fAnimate)
{
SetWindowPos(hwnd, HWND_TOPMOST, prc->left, prc->top,
RECTWIDTH(*prc), RECTHEIGHT(*prc), uFlags);
fAnimate = FALSE;
SystemParametersInfo(SPI_GETMENUFADE, 0, &fAnimate, 0);
if (fAnimate)
{
AnimateWindow(hwnd, 175, AW_BLEND);
}
else
{
SlideAnimate(hwnd, prc, uFlags, uSide);
}
}
else
goto UseSetWindowPos;
}
else
{
UseSetWindowPos:
// Enable the show window so that it gets displayed.
uFlags |= SWP_SHOWWINDOW;
SetWindowPos(hwnd, HWND_TOPMOST, prc->left, prc->top, RECTWIDTH(*prc), RECTHEIGHT(*prc),
uFlags);
}
}
HRESULT CMenuDeskBar::_PositionWindow(POINTL *ppt, RECTL* prcExclude, DWORD dwFlags)
{
ASSERT(IS_VALID_READ_PTR(ppt, POINTL));
ASSERT(NULL == prcExclude || IS_VALID_READ_PTR(prcExclude, RECTL));
BOOL bSetFocus = (dwFlags & MPPF_SETFOCUS);
RECT rcDesired;
RECT rcExclude;
RECT rc;
SIZE sizeAdjust;
UINT uAnimateSide;
BOOL bMirroredWindow=IS_WINDOW_RTL_MIRRORED(_hwnd);
static const iPosition[] = {MENUBAR_TOP, MENUBAR_LEFT, MENUBAR_RIGHT, MENUBAR_BOTTOM};
if (dwFlags & MPPF_POS_MASK)
{
UINT uPosIndex = ((dwFlags & MPPF_POS_MASK) >> 29) - 1;
ASSERT(uPosIndex < 4);
_uSide = iPosition[uPosIndex];
}
if (bSetFocus)
SetForegroundWindow(_hwnd);
_pt = *(POINT*)ppt;
// Get the size of the ideal client rect of the child
RECT rcChild = {0};
// (scotth): This only sets the bottom and the right values
_pDBC->GetSize(DBC_GS_IDEAL, &rcChild);
DWORD dwStyle = GetWindowLong(_hwnd, GWL_STYLE);
DWORD dwExStyle = GetWindowLong(_hwnd, GWL_EXSTYLE);
// Adjust for the window border style
rcDesired = rcChild; // use rcDesired as a temp variable
if (!_fNoBorder)
{
AdjustWindowRectEx(&rcChild, dwStyle, FALSE, dwExStyle);
}
// Calculate the edge of the menu border, and add a fudge factor so
// left/right-cascaded menus overlap the parent menu a bit and are
// correctly aligned vertically.
sizeAdjust.cx = (RECTWIDTH(rcChild) - RECTWIDTH(rcDesired)) / 2;
sizeAdjust.cy = (RECTHEIGHT(rcChild) - RECTHEIGHT(rcDesired)) / 2;
if (prcExclude)
{
CopyRect(&rcExclude, (RECT*)prcExclude);
//
// If mirroring is enabled, let's mirror this guy
// by simulating a different mirrored rect. This is
// only for dropdown menus. [samera]
//
if (bMirroredWindow)
{
if ((_uSide != MENUBAR_LEFT) &&
(_uSide != MENUBAR_RIGHT) )
{
int x;
int iW = rcExclude.right-rcExclude.left;
int icW = (rcChild.right-rcChild.left);
if( icW > iW )
{
x = icW - iW;
rcExclude.left -= x ;
rcExclude.right -= x ;
}
else
{
x = iW - icW;
rcExclude.left += x;
rcExclude.right += x;
}
ppt->x = rcExclude.left;
}
}
TraceMsg(DM_POPUP, "Parent Side is %d ", _uSide);
switch(_uSide)
{
case MENUBAR_LEFT :
rcDesired.left = rcExclude.left - rcChild.right; // right is width
rcDesired.top = rcExclude.top;
break;
case MENUBAR_RIGHT :
rcDesired.left = rcExclude.right;
rcDesired.top = rcExclude.top;
break;
case MENUBAR_TOP:
rcDesired.left = rcExclude.left;
rcDesired.top = rcExclude.top - rcChild.bottom; // bottom is height
break;
case MENUBAR_BOTTOM:
rcDesired.left = rcExclude.left;
rcDesired.top = rcExclude.bottom;
break;
default:
rcDesired.left = _pt.x;
rcDesired.top = _pt.y;
}
}
else
{
SetRectEmpty(&rcExclude);
rcDesired.left = _pt.x;
rcDesired.top = _pt.y;
}
rcDesired.right = rcDesired.left + RECTWIDTH(rcChild);
rcDesired.bottom = rcDesired.top + RECTHEIGHT(rcChild);
_GetPopupWindowPosition(&rcDesired, &rcExclude, &rc, &sizeAdjust, _uSide);
UINT uFlags = SWP_NOOWNERZORDER;
if (!bSetFocus)
uFlags |= SWP_NOACTIVATE;
//
// Open the menus properly. In case of a RTL mirrored window,
// flip the animation side. [samera]
//
if( bMirroredWindow )
{
switch( _uSide )
{
case MENUBAR_LEFT:
uAnimateSide = MENUBAR_RIGHT;
break;
case MENUBAR_RIGHT:
uAnimateSide = MENUBAR_LEFT;
break;
default:
uAnimateSide = _uSide;
}
}
else
{
uAnimateSide = _uSide;
}
TraceMsg(TF_MENUBAND, "CMenuBar::_PositionWindow (%d,%d,%d,%d)",
rc.left, rc.top, rc.right, rc.bottom);
// Last minuite tweak. Since we're in large icon, we need to add this
// so that the bitmap is painted correctly.
if(_iIconSize == BMICON_LARGE && _fExpanded)
rc.right += 1;
// We _DO_ want to do a Z-Order position when this flag is specified. This is
// for full repositioning where we need to preserve the overlap state of all bands.
// Otherwize we just want to size the bar without changing it's z-order.
if (!(dwFlags & MPPF_FORCEZORDER) &&
(S_OK == IUnknown_QueryServiceExec(_punkChild, SID_SMenuBandChild,
&CGID_MenuBand, MBANDCID_ISINSUBMENU, 0, NULL, NULL)))
{
uFlags |= SWP_NOZORDER;
}
// If it's bigger than this magical number, then we don't animate. change to taste
if (RECTHEIGHT(rc) > MAGICAL_NO_FADE_HEIGHT)
dwFlags |= MPPF_NOANIMATE;
AnimateSetMenuPos(_hwnd, &rc, uFlags, uAnimateSide, dwFlags & MPPF_NOANIMATE);
// Save information so we can later resize this window
// We already have: _pt, _uSide
if (prcExclude)
{
_fExcludeRect = TRUE;
CopyRect(&_rcExclude, (RECT*)prcExclude);
}
else
_fExcludeRect = FALSE;
return S_OK;
}
/*----------------------------------------------------------
Purpose: IMenuPopup::Popup method
*/
STDMETHODIMP CMenuDeskBar::Popup(POINTL* ppt, RECTL* prcExclude, DWORD dwFlags)
{
HRESULT hr;
// Is the caller telling us to reposition?
if (dwFlags & MPPF_REPOSITION)
{
if (ppt == NULL)
ppt = (POINTL*)&_pt;
if (prcExclude == NULL)
prcExclude = (RECTL*)&_rcExclude;
// Yes; Then we don't need to do any First show stuff.
_PositionWindow(ppt, prcExclude, dwFlags);
return S_OK;
}
ASSERT(IS_VALID_READ_PTR(ppt, POINTL));
ASSERT(NULL == prcExclude || IS_VALID_READ_PTR(prcExclude, RECTL));
if (_pmpParent)
{
_pmpParent->SetSubMenu(this, TRUE);
}
IOleCommandTarget* poct;
hr = IUnknown_QueryService(_punkChild, SID_SMenuBandChild, IID_PPV_ARG(IOleCommandTarget, &poct));
if (SUCCEEDED(hr))
{
// We need to do this before the ShowDW. This saves us from doing the setting twice
// Because in the ShowDW of MenuBand, we actually go an initialize the toolbar with
// the current default setting which should be "No Keyboard Cues." If we set the state
// here, then the state will be "Show keyboard cues." Then we will update the toolbar.
if (dwFlags & MPPF_KEYBOARD)
poct->Exec(&CGID_MenuBand, MBANDCID_KEYBOARD, 0, NULL, NULL);
}
else
{
ASSERT(poct == NULL);
}
_NotifyModeChange(_dwMode);
hr = ShowDW(TRUE);
if (SUCCEEDED(hr) && _pmpParent)
{
VARIANT varg;
// If this Exec fails, don't panic; it just means we use the default side
if (SUCCEEDED(IUnknown_Exec(_pmpParent, &CGID_MENUDESKBAR, MBCID_GETSIDE, 0, NULL, &varg)))
{
if (varg.vt == VT_I4)
{
_uSide = (UINT) varg.lVal;
}
}
}
if (SUCCEEDED(hr))
{
SHPlaySound(TEXT("MenuPopup"));
_PositionWindow(ppt, prcExclude, dwFlags);
// Set focus
UIActivateIO(TRUE, NULL);
_fActive = TRUE;
// Select the first/last item?
if ((dwFlags & (MPPF_INITIALSELECT | MPPF_FINALSELECT)) && poct)
{
DWORD nCmd = (dwFlags & MPPF_INITIALSELECT) ? MBSI_FIRSTITEM : MBSI_LASTITEM;
poct->Exec(&CGID_MenuBand, MBANDCID_SELECTITEM, nCmd, NULL, NULL);
}
}
ATOMICRELEASE(poct);
return hr;
}
/*----------------------------------------------------------
Purpose: IInputObjectSite::OnFocusChangeIS
Returns:
Cond: --
*/
HRESULT CMenuDeskBar::OnFocusChangeIS(IUnknown *punk, BOOL fSetFocus)
{
return NOERROR;
}
/*----------------------------------------------------------
Purpose: IObjectWithSite::SetSite method
*/
STDMETHODIMP CMenuDeskBar::SetSite(IUnknown* punkSite)
{
ASSERT(NULL == punkSite || IS_VALID_CODE_PTR(punkSite, IUnknown));
if (_fShow)
_PopDown();
ATOMICRELEASE(_punkSite);
ATOMICRELEASE(_pmpParent);
_punkSite = punkSite;
if (_punkSite)
{
_punkSite->AddRef();
IUnknown_QueryService(_punkSite, SID_SMenuPopup, IID_PPV_ARG(IMenuPopup, &_pmpParent));
}
else
{
CloseDW(0);
}
return S_OK;
}
/*----------------------------------------------------------
Purpose: IObjectWithSite::GetSite method
*/
STDMETHODIMP CMenuDeskBar::GetSite(REFIID riid, LPVOID* ppvSite)
{
if (_punkSite)
{
return _punkSite->QueryInterface(riid, ppvSite);
}
*ppvSite = NULL;
return E_FAIL;
}
void CMenuDeskBar::AdjustForTheme()
{
if (_fFlatMenuMode)
{
SHSetWindowBits(_hwnd, GWL_STYLE, WS_CLIPCHILDREN | WS_DLGFRAME | WS_BORDER, WS_BORDER);
}
else if (!_fExpanded)
{
SHSetWindowBits(_hwnd, GWL_STYLE, WS_CLIPCHILDREN | WS_DLGFRAME | WS_BORDER, WS_CLIPCHILDREN | WS_DLGFRAME);
}
}
/*----------------------------------------------------------
Purpose: IOleCommandTarget::Exec method
*/
STDMETHODIMP CMenuDeskBar::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt,
VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
{
if (pguidCmdGroup == NULL)
{
}
else if (IsEqualGUID(CGID_DeskBarClient, *pguidCmdGroup))
{
switch (nCmdID)
{
case DBCID_EMPTY:
// if we have no bands left, close
OnSelect(MPOS_FULLCANCEL);
return S_OK;
default:
return OLECMDERR_E_NOTSUPPORTED;
}
}
else if (IsEqualGUID(CGID_MENUDESKBAR, *pguidCmdGroup))
{
switch(nCmdID)
{
case MBCID_GETSIDE :
pvarargOut->vt = VT_I4;
pvarargOut->lVal = _GetSide();
return S_OK;
case MBCID_RESIZE:
if (_fActive)
{
if (_fExcludeRect)
_PositionWindow((POINTL *)&_pt, (RECTL *)&_rcExclude, 0);
else
_PositionWindow((POINTL *)&_pt, NULL, 0);
}
return S_OK;
case MBCID_SETEXPAND:
if ((BOOL)_fExpanded != (BOOL)nCmdexecopt && !_fFlatMenuMode)
{
_fExpanded = nCmdexecopt;
SetExpandedBorder(_hwnd, _fExpanded);
}
return S_OK;
case MBCID_SETFLAT:
{
_fFlatMenuMode = BOOLIFY(nCmdexecopt);
AdjustForTheme();
}
break;
case MBCID_NOBORDER:
{
_fNoBorder = BOOLIFY(nCmdexecopt);
SetWindowPos(_hwnd, NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED);
}
break;
default :
return OLECMDERR_E_NOTSUPPORTED;
}
}
return SUPERCLASS::Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
}
/*----------------------------------------------------------
Purpose: IServiceProvider::QueryService method
*/
STDMETHODIMP CMenuDeskBar::QueryService(REFGUID guidService, REFIID riid, void **ppvObj)
{
if (IsEqualGUID(guidService, SID_SMenuPopup))
{
return QueryInterface(riid, ppvObj);
}
else if (IsEqualIID(guidService, SID_SMenuBandBottom) ||
IsEqualIID(guidService, SID_SMenuBandBottomSelected)||
IsEqualIID(guidService, SID_SMenuBandChild))
{
// SID_SMenuBandBottom queries down
return IUnknown_QueryService(_punkChild, guidService, riid, ppvObj);
}
else
{
HRESULT hres = SUPERCLASS::QueryService(guidService, riid, ppvObj);
if (FAILED(hres))
{
hres = IUnknown_QueryService(_punkSite, guidService, riid, ppvObj);
}
return hres;
}
}
/*----------------------------------------------------------
Purpose: IServiceProvider::QueryService method
*/
STDMETHODIMP CMenuDeskBar::SetIconSize(DWORD iIcon)
{
HRESULT hres;
_iIconSize = iIcon;
hres = IUnknown_QueryServiceExec(_punkChild, SID_SMenuBandChild, &CGID_MenuBand,
MBANDCID_SETICONSIZE, iIcon == BMICON_SMALL? ISFBVIEWMODE_SMALLICONS: ISFBVIEWMODE_LARGEICONS, NULL, NULL);
return hres;
}
/*----------------------------------------------------------
Purpose: IServiceProvider::QueryService method
*/
STDMETHODIMP CMenuDeskBar::SetBitmap(HBITMAP hBitmap)
{
ASSERT(hBitmap);
BITMAP bm;
_hbmp = hBitmap;
if (_hbmp)
{
if(!GetObject(_hbmp, sizeof(bm), &bm))
return E_FAIL;
_sizeBmp.cx = bm.bmWidth;
_sizeBmp.cy = bm.bmHeight;
// Hack to get color
HDC hdc = GetDC(_hwnd);
if (hdc)
{
HDC hdcMem = CreateCompatibleDC(hdc);
if (hdcMem)
{
HBITMAP hbmpOld = (HBITMAP)SelectObject(hdcMem, _hbmp);
_rgb = GetPixel(hdcMem, 0, 0);
SelectObject(hdcMem, hbmpOld);
DeleteDC(hdcMem);
}
ReleaseDC(_hwnd, hdc);
}
}
return NOERROR;
}
void CMenuDeskBar::_OnSize()
{
RECT rc;
if (!_hwndChild)
return;
GetClientRect(_hwnd, &rc);
if(_iIconSize == BMICON_LARGE)
{
rc.left += _sizeBmp.cx;
if (_fExpanded)
rc.left++;
}
SetWindowPos(_hwndChild, 0,
rc.left, rc.top, RECTWIDTH(rc), RECTHEIGHT(rc),
SWP_NOACTIVATE|SWP_NOZORDER|SWP_FRAMECHANGED);
rc.right = rc.left;
rc.left -= _sizeBmp.cx;
if (_fShow)
InvalidateRect(_hwnd, &rc, TRUE);
}
LRESULT CMenuDeskBar::_DoPaint(HWND hwnd, HDC hdc, DWORD dwFlags)
{
HDC hdcmem;
HBITMAP hbmpOld;
RECT rc;
HBRUSH hbrush;
int iDC = SaveDC(hdc);
GetClientRect(hwnd, &rc);
//Create a compatable DC
hdcmem = CreateCompatibleDC(hdc);
if(hdcmem)
{
// Offset the stuff we're paining if we're expanded
BYTE bOffset = 0;
// Store this for the Bar fill cycle
int cyBitmap = 0;
if (!_fFlatMenuMode)
{
bOffset = _fExpanded? 1 : 0;
}
if (_sizeBmp.cy <= RECTHEIGHT(rc) + 1)
{
//Select the bitmap into the memory DC
hbmpOld = (HBITMAP)SelectObject(hdcmem, _hbmp);
//Blit to the window
BitBlt(hdc, bOffset, rc.bottom - _sizeBmp.cy - bOffset, _sizeBmp.cx, _sizeBmp.cy, hdcmem, 0, 0, SRCCOPY);
// Ok, We need to subtract this value to see how much we need to paint for the banner.
cyBitmap = _sizeBmp.cy;
//Restore the DC
SelectObject(hdcmem, hbmpOld);
}
rc.right = _sizeBmp.cx + bOffset;
if (_fExpanded && !_fFlatMenuMode && !_fNoBorder)
DrawEdge(hdc, &rc, BDR_RAISEDINNER, BF_LEFT | BF_TOP | BF_BOTTOM);
//Paint the rest
hbrush = CreateSolidBrush(_rgb);
if(hbrush)
{
rc.bottom -= cyBitmap + bOffset;
if (_fExpanded)
{
rc.left += bOffset;
rc.top += bOffset;
}
FillRect(hdc, &rc, hbrush);
DeleteObject(hbrush);
}
//Delete the DC.
DeleteDC(hdcmem);
}
RestoreDC(hdc, iDC);
return 0;
}
void CMenuDeskBar::_DoNCPaint(HWND hwnd, HDC hdc)
{
if (!_fNoBorder)
{
RECT rc;
// Since we need to paint the border, we get the window rect
GetWindowRect(hwnd, &rc);
// then change the rect so that it represents values relative to
// the origin.
OffsetRect(&rc, -rc.left, -rc.top);
if (hdc)
{
if (_fFlatMenuMode)
{
SHOutlineRect(hdc, &rc, GetSysColor(COLOR_3DSHADOW));
}
else
DrawEdge(hdc, &rc, BDR_RAISEDOUTER, BF_RECT);
}
}
}
LRESULT CMenuDeskBar::v_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LRESULT lres;
switch (uMsg)
{
#ifdef MAINWIN
case WM_NCPAINTSPECIALFRAME:
// In case of motif look the MwPaintBorder paints a Etched In
// border if WM_NCPAINTSPECIALFRAME returns FALSE. We are handling
// this message here and drawing the Etched Out frame explicitly.
// wParam - HDC
if (MwCurrentLook() == LOOK_MOTIF)
{
MwPaintSpecialEOBorder( hwnd, (HDC)wParam );
return TRUE;
}
break;
#endif
case WM_GETOBJECT:
if (lParam == OBJID_MENU)
{
IAccessible* pacc;
if (SUCCEEDED(QueryService(SID_SMenuBandChild, IID_PPV_ARG(IAccessible, &pacc))))
{
lres = LresultFromObject(IID_IAccessible, wParam, SAFECAST(pacc, IAccessible*));
pacc->Release();
return lres;
}
}
break;
case WM_NCCREATE:
//
// Since this is a mirrored menu, then open it
// on the left (mirrored) edge if possible. WillFit(...) will
// ensure this for us [samera]
//
// Mirror the menu initially if its window is mirrored
//
ASSERT(_uSide == 0);
if (IS_WINDOW_RTL_MIRRORED(_hwnd))
_uSide = MENUBAR_LEFT;
else
_uSide = MENUBAR_RIGHT;
break;
case WM_ACTIVATE:
if (LOWORD(wParam) == WA_INACTIVE)
{
if (_fActive && !_pmpChild)
{
// if we were active, and the thing going active now
// is not one of our parent menubars, then cancel everything.
// if it's a parent of ours going active, assume that
// they will tell us to die when they want us to...
if (!_IsMyParent((HWND)lParam))
OnSelect(MPOS_FULLCANCEL);
}
}
else
{
if (_pmpChild)
{
// if we're becoming active, and we have a child, that child should go away
_pmpChild->OnSelect(MPOS_CANCELLEVEL);
}
}
break;
case WM_PRINTCLIENT:
if (_iIconSize == BMICON_LARGE)
{
_DoPaint(hwnd, (HDC)wParam, (DWORD)lParam);
return 0;
}
break;
case WM_PAINT:
// Paint the banner if we're in showing large icons
if (_iIconSize == BMICON_LARGE)
{
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
_DoPaint(hwnd, ps.hdc, 0);
EndPaint(hwnd, &ps);
return 0;
}
break;
case WM_PRINT:
if ((_fFlatMenuMode || _fExpanded) && PRF_NONCLIENT & lParam)
{
HDC hdc = (HDC)wParam;
DefWindowProcWrap(hwnd, WM_PRINT, wParam, lParam);
// Do this after so that we look right...
_DoNCPaint(hwnd, hdc);
return 1;
}
break;
case WM_NCCALCSIZE:
if (_fNoBorder)
{
return 0;
}
else
{
return SUPERCLASS::v_WndProc(hwnd, uMsg, wParam, lParam);
}
break;
case WM_NCPAINT:
if (_fNoBorder)
{
return 0;
}
else if (_fExpanded || _fFlatMenuMode)
{
HDC hdc;
hdc = GetWindowDC(hwnd);
if (hdc)
{
_DoNCPaint(hwnd, hdc);
ReleaseDC(hwnd, hdc);
}
return 1;
}
break;
case WM_NCHITTEST:
lres = SUPERCLASS::v_WndProc(hwnd, uMsg, wParam, lParam);
switch (lres)
{
case HTBOTTOM:
case HTBOTTOMLEFT:
case HTBOTTOMRIGHT:
case HTLEFT:
case HTRIGHT:
case HTTOP:
case HTTOPLEFT:
case HTTOPRIGHT:
// Don't allow the window to be resized
lres = HTBORDER;
break;
case HTTRANSPARENT:
// Don't let a click go thru to the window underneath
lres = HTCLIENT;
break;
}
return lres;
// HACKHACKHACKHACKHACKHACKHACKHACKHACKHACKHACKHACKHACKHACKHACKHACKHACKHACK
// (lamadio) 1.25.99
// This hack is here to fix a problem on down level Windows with Integrated
// IE4.01, IE5 and Office 2000.
// The bug revolves around Start Menu not being destroyed when Explorer.exe shuts
// down. Start Menu unregisters itself at CloseDW, but since the menubar never gets
// destroyed, Start Menu never deregisters itself.
// When an System service, such as MSTASK.dll keeps shell32 alive in the background,
// it leaves an outstanding reference to a change notify. When a new user logs in,
// O2k and IE5 fire up group conv, generating more than 10 change notify events in the
// start menu. This causes the batching code to be fired up: Which does not really
// work without the shell started. GroupConv also adds these events using memory
// alloced from it's process heap. Since there is an outstanding change notify handler
// these pidls get forced to be handled. Shell32 then faults derefing a bad pidl.
// By detecting an Endsession, we can eliminate this problem. Doing a SetClient(NULL)
// cause Menubar to free it's references to MenuSite. Menusite, calls CloseDW on menuband
// menuband then causes MNFolder to unregister itself. Since no one is listening any more
// the crash is avoided.
case WM_ENDSESSION:
if (wParam != 0)
{
SetClient(NULL);
}
break;
}
return SUPERCLASS::v_WndProc(hwnd, uMsg, wParam, lParam);
}
IMenuPopup* CMenuDeskBar::_GetMenuBarParent(IUnknown* punk)
{
IMenuPopup *pmp = NULL;
IObjectWithSite* pows;
punk->QueryInterface(IID_PPV_ARG(IObjectWithSite, &pows));
if (pows)
{
IServiceProvider* psp;
pows->GetSite(IID_PPV_ARG(IServiceProvider, &psp));
if (psp)
{
psp->QueryService(SID_SMenuPopup, IID_PPV_ARG(IMenuPopup, &pmp));
psp->Release();
}
pows->Release();
}
return pmp;
}
// this assumes that hwnd is a toplevel window and that the menudeskbars are also
// the only hosts and are themselves toplevel
BOOL CMenuDeskBar::_IsMyParent(HWND hwnd)
{
BOOL fRet = FALSE;
if (hwnd)
{
HWND hwndMenu;
IMenuPopup *pmpParent = _pmpParent;
if (pmpParent)
pmpParent->AddRef();
while (pmpParent && !fRet &&
SUCCEEDED(IUnknown_GetWindow(pmpParent, &hwndMenu)))
{
if (hwndMenu == hwnd)
{
fRet = TRUE;
}
IMenuPopup* pmpNext = _GetMenuBarParent(pmpParent);
pmpParent->Release();
pmpParent = pmpNext;
}
}
return fRet;
}
IMenuPopup* CreateMenuPopup(IMenuPopup* pmpParent, IShellFolder* psf, LPCITEMIDLIST pidl,
BANDINFOSFB * pbi, BOOL bMenuBand)
{
return CreateMenuPopup2(pmpParent, NULL, psf, pidl, pbi, bMenuBand);
}
IMenuPopup* CreateMenuPopup2(IMenuPopup* pmpParent, IMenuBand* pmb, IShellFolder* psf, LPCITEMIDLIST pidl,
BANDINFOSFB * pbi, BOOL bMenuBand)
{
ASSERT(pmb == NULL || IS_VALID_CODE_PTR(pmb, IMenuBand));
ASSERT(psf == NULL || IS_VALID_CODE_PTR(psf, IShellFolder));
ASSERT(pmpParent == NULL || IS_VALID_CODE_PTR(pmpParent, IMenuPopup));
ASSERT(pidl && IS_VALID_PIDL(pidl));
ASSERT(pbi == NULL || IS_VALID_READ_PTR(pbi, BANDINFOSFB));
IMenuPopup* pmp = NULL;
IDeskBand *pdb = NULL;
IBandSite *pbs = NULL;
HRESULT hres = E_FAIL;
if (!pdb)
{
TraceMsg(TF_MENUBAND, "CreateMenuPopup2 : Did not find a this (0x%x) band.", pidl);
if (bMenuBand)
{
pdb = CMenuBand_Create(psf, pidl, FALSE);
}
else
{
CISFBand_CreateEx(psf, pidl, IID_PPV_ARG(IDeskBand, &pdb));
}
if (pdb)
{
if (pbi)
{
IShellFolderBand *pisfBand;
if (SUCCEEDED(pdb->QueryInterface(IID_IShellFolderBand, (LPVOID*)&pisfBand)))
{
pisfBand->SetBandInfoSFB(pbi);
pisfBand->Release();
}
}
if (!pmpParent)
{
const CLSID * pclsid;
if (bMenuBand)
pclsid = &CLSID_MenuBandSite;
else
pclsid = &CLSID_RebarBandSite;
CoCreateInstance(*pclsid, NULL, CLSCTX_INPROC_SERVER, IID_IBandSite, (LPVOID*)&pbs);
if (pbs)
{
if (bMenuBand)
{
BANDSITEINFO bsinfo;
// Don't show the gripper for vertical menubands
bsinfo.dwMask = BSIM_STYLE;
bsinfo.dwStyle = BSIS_NOGRIPPER | BSIS_NODROPTARGET;
pbs->SetBandSiteInfo(&bsinfo);
}
CMenuDeskBar *pcmdb = new CMenuDeskBar();
if (pcmdb)
{
if (SUCCEEDED(pcmdb->SetClient(pbs)))
pcmdb->QueryInterface(IID_IMenuPopup, (LPVOID *)&pmp);
pcmdb->Release();
}
}
}
if (pbs)
{
pbs->AddBand(pdb);
}
}
}
ATOMICRELEASE(pdb);
ATOMICRELEASE(pbs);
if (!pmp)
IUnknown_Set((IUnknown**) &pmp, pmpParent);
return pmp;
}