3372 lines
101 KiB
C++
3372 lines
101 KiB
C++
#include "priv.h"
|
|
#include "sccls.h"
|
|
#include "resource.h"
|
|
#include "mshtmhst.h"
|
|
#include "desktopp.h" // DTRF_RAISE etc.
|
|
|
|
#define WANT_CBANDSITE_CLASS
|
|
#include "bandsite.h"
|
|
|
|
#include "deskbar.h"
|
|
#include "theater.h"
|
|
|
|
#include "mluisupp.h"
|
|
|
|
#define SUPERCLASS CBaseBar
|
|
|
|
#define DM_PERSIST 0 // trace IPS::Load, ::Save, etc.
|
|
#define DM_POPUI 0 // expando-UI (proto)
|
|
#define DM_MENU 0 // trace menu code
|
|
#define DM_DRAG 0 // drag move/size (terse)
|
|
#define DM_DRAG2 0 // ... (verbose)
|
|
#define DM_API 0 // trace API calls
|
|
#define DM_HIDE DM_TRACE // autohide
|
|
#define DM_HIDE2 DM_TRACE // autohide (verbose)
|
|
#define DM_APPBAR 0 // SHAppBarMessage calls
|
|
#define DM_OLECT 0 // IOleCommandTarget calls
|
|
#define DM_FOCUS 0 // focus change
|
|
#define DM_RES DM_WARNING // resolution
|
|
|
|
#define ABS(i) (((i) < 0) ? -(i) : (i))
|
|
|
|
#define RECTGETWH(uSide, prc) (ABE_HORIZ(uSide) ? RECTHEIGHT(*prc) : RECTWIDTH(*prc))
|
|
|
|
//*** CDB_INITED -- has CDockingBar::_Initialize been called
|
|
//
|
|
#define CDB_INITED() (_eInitLoaded && _fInitSited && _fInitShowed)
|
|
|
|
enum ips_e {
|
|
IPS_FALSE, // reserved, must be 0 (FALSE)
|
|
IPS_LOAD,
|
|
IPS_LOADBAG,
|
|
IPS_INITNEW,
|
|
IPS_LAST
|
|
};
|
|
|
|
CASSERT(IPS_FALSE == 0);
|
|
CASSERT(((IPS_LAST - 1) & 0x03) == (IPS_LAST - 1)); // 2-bit _eInitLoaded
|
|
|
|
|
|
//*** CXFLOAT -- distance from edge to 'float' zone
|
|
// NOTES
|
|
// pls forgive the lousy hungarian...
|
|
#define CXFLOAT() GetSystemMetrics(SM_CXICON)
|
|
#define CYFLOAT() GetSystemMetrics(SM_CYICON)
|
|
#define CXYHIDE(uSide) 2 // FEATUE: GetSystemMetrics(xxx), we need an appropriate system metric
|
|
|
|
#ifdef DEBUG
|
|
#if 0 // turn on to debug autohide boundary cases
|
|
int g_cxyHide = 8;
|
|
#undef CXYHIDE
|
|
#define CXYHIDE(uSide) g_cxyHide
|
|
#endif
|
|
#endif
|
|
|
|
#define CXSMSIZE() GetSystemMetrics(SM_CXSMSIZE)
|
|
#define CYSMSIZE() GetSystemMetrics(SM_CYSMSIZE)
|
|
|
|
|
|
#ifdef DEBUG
|
|
extern unsigned long DbStreamTell(IStream *pstm);
|
|
extern BOOL DbCheckWindow(HWND hwnd, RECT *prcExp, HWND hwndClient);
|
|
TCHAR *DbMaskToMneStr(UINT uMask, TCHAR *szMnemonics);
|
|
#else
|
|
#define DbStreamTell(pstm) 0
|
|
#define DbCheckWindow(hwnd, prcExp, hwndClient) 0
|
|
#define DbMaskToMneStr(uMask, szMnemonics) szMnemonics
|
|
#endif
|
|
|
|
//*** autohide -- design note
|
|
//
|
|
// here's an overview of how we do autohide. see the code for details.
|
|
//
|
|
// only a few routines really know about it. their behavior is driven
|
|
// by '_fHiding'. when FALSE, they behave normally. when TRUE, they
|
|
// do alternate 'fake' behavior.
|
|
//
|
|
// a 'real' or 'normal' rect is the full-size rect we display when not hidden.
|
|
// a 'fake' or 'tiny' rect is the very thin rect we display when hidden.
|
|
// (plus there's a '0-width' rect we register w/ the system when we're hidden).
|
|
//
|
|
// more specifically,
|
|
//
|
|
// when fHiding is TRUE, a few routines have alternate 'fake' behavior:
|
|
// _ProtoRect returns a 'tiny' rect rather than the 'real' rect
|
|
// _NegotiateRect is a NOOP (so we don't change the 'tiny' rect)
|
|
// _SetVRect is a NOOP (so we don't save the 'tiny' rect)
|
|
// AppBarSetPos is a NOOP (so we don't set the 'tiny' rect)
|
|
// plus, a few routines handle transitions (and setup):
|
|
// _DoHide hide/unhide helper
|
|
// _MoveSizeHelper detects and handles transitions
|
|
// _HideReg register autohide appbar w/ 0-width rect
|
|
// and finally, a few messages trigger the transitions:
|
|
// unhide WM_NCHITTEST on the 'tiny' rect starts the unhide.
|
|
// actually it starts a timer (IDT_AUTOUNHIDE) so there's a
|
|
// bit of hysteresis.
|
|
// hide WM_ACTIVATE(deact) starts a timer (IDT_AUTOHIDE)
|
|
// which we use to poll for mouse leave events. again, there
|
|
// is some hysteresis, plus some additional heuristics for hiding.
|
|
// WM_ACTIVATE(act) stops the timer.
|
|
//
|
|
// #if 0
|
|
// we also have 'manual hide'. manual hide differs from autohide as follows:
|
|
// autohide never negotiates space(*) , manual hide always does
|
|
// autohide is focus- and cursor- driven, manual hide is UI-driven
|
|
// (*) actually it negotiates space of '0'.
|
|
// e.g. 'manual hide' is used for the BrowserBar (e.g. search results).
|
|
// however for now at least 'manual hide' is simply a ShowDW(FALSE).
|
|
// #endif
|
|
|
|
void CDockingBar::_AdjustToChildSize()
|
|
{
|
|
if (_szChild.cx)
|
|
{
|
|
RECT rc, rcChild;
|
|
|
|
GetWindowRect(_hwnd, &rc);
|
|
GetClientRect(_hwndChild, &rcChild);
|
|
|
|
// we need to change rc by the delta of prc-rcChild
|
|
rc.right += _szChild.cx - RECTWIDTH(rcChild);
|
|
rc.bottom += _szChild.cy - RECTHEIGHT(rcChild);
|
|
|
|
_SetVRect(&rc);
|
|
|
|
_Recalc(); // _MoveSizeHelper(_eMode, _uSide, NULL, NULL, TRUE, TRUE);
|
|
|
|
_szChild.cx = 0;
|
|
}
|
|
}
|
|
|
|
void CDockingBar::_OnPostedPosRectChange()
|
|
{
|
|
if (_ptbSite) {
|
|
if (!_fDragging) {
|
|
_AdjustToChildSize();
|
|
}
|
|
}
|
|
}
|
|
|
|
HMENU CDockingBar::_GetContextMenu()
|
|
{
|
|
HMENU hmenu = LoadMenuPopup(MENU_WEBBAR);
|
|
if (hmenu) {
|
|
|
|
// _eMode
|
|
if (!ISWBM_DESKTOP())
|
|
{
|
|
EnableMenuItem(hmenu, IDM_AB_TOPMOST, MF_BYCOMMAND | MF_GRAYED);
|
|
EnableMenuItem(hmenu, IDM_AB_AUTOHIDE, MF_BYCOMMAND | MF_GRAYED);
|
|
}
|
|
CheckMenuItem(hmenu, IDM_AB_TOPMOST, WBM_IS_TOPMOST() ? (MF_BYCOMMAND | MF_CHECKED) : (MF_BYCOMMAND | MF_UNCHECKED));
|
|
|
|
// hide
|
|
// we use _fWantHide (not _fCanHide) to reflect what user asked
|
|
// for, not what he got. o.w. you can't tell what the state is
|
|
// unless you actually get it.
|
|
CheckMenuItem(hmenu, IDM_AB_AUTOHIDE,
|
|
MF_BYCOMMAND | (_fWantHide ? MF_CHECKED : MF_UNCHECKED));
|
|
|
|
CASSERT(PARENT_XTOPMOST == HWND_DESKTOP); // for WM_ACTIVATE
|
|
CASSERT(PARENT_BTMMOST() == HWND_DESKTOP); // for WM_ACTIVATE
|
|
if (_eMode & WBM_FLOATING)
|
|
{
|
|
// (for now) only desktop btm/topmost does autohide
|
|
EnableMenuItem(hmenu, IDM_AB_AUTOHIDE, MF_BYCOMMAND | MF_GRAYED);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
// FEATURE temporary until we make browser tell us about activation
|
|
CheckMenuItem(hmenu, IDM_AB_ACTIVATE,
|
|
MF_BYCOMMAND | (_fActive ? MF_CHECKED : MF_UNCHECKED));
|
|
#endif
|
|
|
|
}
|
|
return hmenu;
|
|
}
|
|
|
|
HRESULT CDockingBar::_TrackPopupMenu(const POINT* ppt)
|
|
{
|
|
HRESULT hres = S_OK;
|
|
|
|
HMENU hmenu = _GetContextMenu();
|
|
if (hmenu)
|
|
{
|
|
TrackPopupMenu(hmenu, /*TPM_LEFTALIGN|*/TPM_RIGHTBUTTON,
|
|
ppt->x, ppt->y, 0, _hwnd, NULL);
|
|
DestroyMenu(hmenu);
|
|
}
|
|
else
|
|
{
|
|
hres = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
void CDockingBar::_HandleWindowPosChanging(LPWINDOWPOS pwp)
|
|
{
|
|
}
|
|
|
|
/***
|
|
*/
|
|
LRESULT CDockingBar::v_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LRESULT lres = 0;
|
|
POINT pt;
|
|
DWORD pos;
|
|
RECT rc;
|
|
|
|
switch (uMsg) {
|
|
case WM_CLOSE:
|
|
_AppBarOnCommand(IDM_AB_CLOSE); // _RemoveToolbar(0)
|
|
break;
|
|
|
|
case WM_DESTROY:
|
|
if (_fAppRegistered)
|
|
_AppBarRegister(FALSE);
|
|
break;
|
|
|
|
case APPBAR_CALLBACK:
|
|
_AppBarCallback(hwnd, uMsg, wParam, lParam);
|
|
return 0;
|
|
|
|
case WM_CONTEXTMENU:
|
|
if (_CheckForwardWinEvent(uMsg, wParam, lParam, &lres))
|
|
break;
|
|
|
|
if ((LPARAM)-1 == lParam)
|
|
{
|
|
GetClientRect(_hwnd, &rc);
|
|
MapWindowRect(_hwnd, HWND_DESKTOP, &rc);
|
|
pt.x = rc.left + (rc.right - rc.left) / 2;
|
|
pt.y = rc.top + (rc.bottom - rc.top) / 2;
|
|
}
|
|
else
|
|
{
|
|
pt.x = GET_X_LPARAM(lParam);
|
|
pt.y = GET_Y_LPARAM(lParam);
|
|
}
|
|
_TrackPopupMenu(&pt);
|
|
break;
|
|
|
|
case WM_ENTERSIZEMOVE:
|
|
ASSERT(_fDragging == 0);
|
|
_fDragging = 0; // reset if busted
|
|
_xyPending = XY_NIL;
|
|
#if XXX_CANCEL
|
|
GetWindowRect(hwnd, &_rcCapture); // to detect cancel
|
|
#endif
|
|
break;
|
|
|
|
case WM_SYSCHAR:
|
|
if (wParam == TEXT(' '))
|
|
{
|
|
HMENU hmenu;
|
|
|
|
hmenu = GetSystemMenu(hwnd, FALSE);
|
|
if (hmenu) {
|
|
EnableMenuItem(hmenu, SC_RESTORE, MFS_GRAYED | MF_BYCOMMAND);
|
|
EnableMenuItem(hmenu, SC_MAXIMIZE, MFS_GRAYED | MF_BYCOMMAND);
|
|
EnableMenuItem(hmenu, SC_MINIMIZE, MFS_GRAYED | MF_BYCOMMAND);
|
|
}
|
|
}
|
|
goto DoDefault;
|
|
|
|
case WM_SIZING: // goto DoDefault? I doubt it.
|
|
case WM_MOVING:
|
|
{
|
|
LPRECT prc = (RECT*)lParam;
|
|
|
|
pos = GetMessagePos();
|
|
if (_fDragging == 0)
|
|
{
|
|
// 1st time
|
|
_DragEnter(uMsg, GET_X_LPARAM(pos), GET_Y_LPARAM(pos), prc);
|
|
ASSERT(_fDragging != 0);
|
|
}
|
|
else
|
|
{
|
|
// 2nd..Nth time
|
|
_DragTrack(uMsg, GET_X_LPARAM(pos), GET_Y_LPARAM(pos), prc, 0);
|
|
}
|
|
}
|
|
return 1;
|
|
|
|
case WM_MOVE: // xLeft , yTop
|
|
case WM_SIZE: // xWidth, yHeight
|
|
if (_fDragging)
|
|
{
|
|
RECT rcTmp;
|
|
|
|
CopyRect(&rcTmp, &_rcPending);
|
|
_DragTrack(uMsg, GET_X_LPARAM(_xyPending), GET_Y_LPARAM(_xyPending),
|
|
&rcTmp, 1);
|
|
}
|
|
|
|
_OnSize(); // PERF: only needed for WM_SIZE? At worst this might cause a sligth flicker.
|
|
break;
|
|
|
|
case WM_EXITSIZEMOVE:
|
|
_DragLeave(-1, -1, TRUE);
|
|
ASSERT(_fDragging == 0);
|
|
|
|
break;
|
|
|
|
case WM_CHILDACTIVATE:
|
|
if (_eMode == WBM_BFLOATING)
|
|
SendMessage(_hwnd, WM_MDIACTIVATE, (WPARAM)_hwnd, 0);
|
|
goto DoDefault;
|
|
|
|
case WM_WINDOWPOSCHANGING:
|
|
_HandleWindowPosChanging((LPWINDOWPOS)lParam);
|
|
break;
|
|
|
|
case WM_WINDOWPOSCHANGED:
|
|
Lfwdappbar:
|
|
if (_fAppRegistered)
|
|
_AppBarOnWM(uMsg, wParam, lParam);
|
|
goto DoDefault; // fwd on so we'll get WM_SIZE etc.
|
|
|
|
case WM_TIMER:
|
|
switch (wParam) {
|
|
|
|
case IDT_AUTOHIDE:
|
|
{
|
|
ASSERT(_fWantHide && _fCanHide);
|
|
|
|
GetCursorPos(&pt);
|
|
GetWindowRect(hwnd, &rc);
|
|
// add a bit of fudge so we don't hide when trying to grab the edge
|
|
InflateRect(&rc, GetSystemMetrics(SM_CXEDGE) * 4,
|
|
GetSystemMetrics(SM_CYEDGE)*4);
|
|
|
|
HWND hwndAct = GetActiveWindow();
|
|
|
|
if (!PtInRect(&rc, pt) && hwndAct != hwnd &&
|
|
(hwndAct == NULL || ::GetWindowOwner(hwndAct) != hwnd))
|
|
{
|
|
// to hide, we need to be outside the inflated window,
|
|
// and we can't be active (for keyboard users).
|
|
// (heuristics stolen from tray.c)
|
|
// FEATURE: tray.c also checks TM_SYSMENUCOUNT == 0
|
|
|
|
_DoHide(AHO_KILLDO|AHO_MOVEDO);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDT_AUTOUNHIDE: // FEATURE: share code w/ IDT_AUTOHIDE
|
|
ASSERT(_fWantHide && _fCanHide);
|
|
|
|
if (_fHiding)
|
|
{
|
|
ASSERT(_fHiding == HIDE_AUTO);
|
|
GetCursorPos(&pt);
|
|
GetWindowRect(hwnd, &rc);
|
|
if (PtInRect(&rc, pt))
|
|
_DoHide(AHO_KILLUN|AHO_MOVEUN|AHO_SETDO);
|
|
else
|
|
Lkillun:
|
|
_DoHide(AHO_KILLUN);
|
|
}
|
|
else
|
|
{
|
|
// if we mouse-over and then TAB very quickly, we can end
|
|
// up getting a WM_ACT followed by a WM_TIMER (despite the
|
|
// KillTimer inside OnAct). if so we need to be careful
|
|
// not to do an AHO_SETDO. just to be safe we do an
|
|
// AHO_KILLUN as well.
|
|
TraceMsg(DM_HIDE, "cwb.WM_T: !_fHiding (race!) => AHO_KILLUN");
|
|
goto Lkillun;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
goto DoDefault;
|
|
}
|
|
|
|
break;
|
|
|
|
case WM_NCLBUTTONDOWN:
|
|
case WM_LBUTTONDOWN:
|
|
goto DoDefault;
|
|
|
|
|
|
case WM_ACTIVATE:
|
|
_OnActivate(wParam, lParam);
|
|
goto Lfwdappbar;
|
|
|
|
case WM_GETMINMAXINFO: // prevent it from getting too small
|
|
// n.b. below stuff works for scheme 'win standard large'
|
|
// but not for v. large edges. not sure why, but we'll
|
|
// have to fix it or the original bug will still manifest
|
|
// on accessibility-enabled machines.
|
|
|
|
// nt5:149535: resize/drag of v. small deskbar.
|
|
// APPCOMPAT workaround USER hittest bug for v. small windows.
|
|
// DefWndProc(WM_NCHITTEST) gives wrong result (HTLEFT) when
|
|
// window gets too small. so stop it from getting v. small.
|
|
//
|
|
// the below calc actually gives us slightly *more* than the
|
|
// min size, but what the heck. e.g. it gives 8+15+1=24,
|
|
// whereas empirical tests give 20. not sure why there's a
|
|
// diff, but we'll use the bigger # to be safe.
|
|
{
|
|
RECT rcTmp = {100,100,100,100}; // arbitrary 0-sized rect
|
|
LONG ws, wsx;
|
|
HWND hwndTmp;
|
|
|
|
_GetStyleForMode(_eMode, &ws, &wsx, &hwndTmp);
|
|
AdjustWindowRectEx(&rcTmp, ws, FALSE, wsx);
|
|
|
|
((MINMAXINFO *)lParam)->ptMinTrackSize.x = RECTWIDTH(rcTmp) + CXSMSIZE() + 1;
|
|
((MINMAXINFO *)lParam)->ptMinTrackSize.y = RECTHEIGHT(rcTmp) + CYSMSIZE() + 1;
|
|
if (ISWBM_FLOAT(_eMode))
|
|
{
|
|
// nt5:169734 'close' button on v. small floating deskbar.
|
|
// APPCOMPAT workaround USER 'close' button bug for v. small windows.
|
|
// the button on a v. small TOOLWINDOW doesn't work.
|
|
// empirically the below adjustment seems to work.
|
|
((MINMAXINFO *)lParam)->ptMinTrackSize.x += (CXSMSIZE() + 1) * 3 / 2;
|
|
((MINMAXINFO *)lParam)->ptMinTrackSize.y += (CYSMSIZE() + 1) * 3 / 2;
|
|
}
|
|
TraceMsg(DM_TRACE, "cwb.GMMI: x=%d", ((MINMAXINFO *)lParam)->ptMinTrackSize.x);
|
|
}
|
|
break;
|
|
|
|
case WM_NCHITTEST:
|
|
return _OnNCHitTest(wParam, lParam);
|
|
|
|
case WM_WININICHANGE:
|
|
// Active Desktop *broadcasts* a WM_WININICHANGE SPI_SETDESKWALLPAPER
|
|
// message when starting up. If this message gets processed during
|
|
// startup at just the right time, then the bands will notify their
|
|
// preferred state, and we lose the persisted state. Since the desktop
|
|
// wallpaper changing is really of no interest to us, we filter it out here.
|
|
//
|
|
// REVIEW CDTURNER: Would we get a perf win by punting a larger class
|
|
// of these wininichange messages? It seems like most won't affect
|
|
// the contents of a deskbar...
|
|
//
|
|
if (SPI_SETDESKWALLPAPER == wParam)
|
|
break;
|
|
|
|
goto DoDefault;
|
|
|
|
default:
|
|
DoDefault:
|
|
return SUPERCLASS::v_WndProc(hwnd, uMsg, wParam, lParam);
|
|
}
|
|
|
|
return lres;
|
|
}
|
|
|
|
LRESULT CDockingBar::_OnNCHitTest(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
if (_fHiding)
|
|
_DoHide(AHO_SETUN);
|
|
|
|
// get 'pure' hittest...
|
|
LRESULT lres = _CalcHitTest(wParam, lParam);
|
|
|
|
// ... and perturb it based on where we're docked
|
|
BOOL fSizing = FALSE;
|
|
if (ISWBM_FLOAT(_eMode)) {
|
|
// standard sizing/moving behavior
|
|
return lres;
|
|
}
|
|
else {
|
|
// opposing edge sizes; any other edge moves
|
|
ASSERT(ISABE_DOCK(_uSide));
|
|
switch (_uSide) {
|
|
case ABE_LEFT:
|
|
//
|
|
// Mirror the edges (since we are dealing with screen coord)
|
|
// if the docked-window parent is mirrored. [samera]
|
|
//
|
|
if (IS_WINDOW_RTL_MIRRORED(GetParent(_hwnd)))
|
|
fSizing = (lres==HTLEFT);
|
|
else
|
|
fSizing = (lres==HTRIGHT);
|
|
break;
|
|
case ABE_RIGHT:
|
|
//
|
|
// Mirror the edges (since we are dealing with screen coord)
|
|
// if the docked-window parent is mirrored.
|
|
//
|
|
if (IS_WINDOW_RTL_MIRRORED(GetParent(_hwnd)))
|
|
fSizing = (lres==HTRIGHT);
|
|
else
|
|
fSizing = (lres==HTLEFT);
|
|
break;
|
|
case ABE_TOP:
|
|
fSizing = (lres==HTBOTTOM);
|
|
break;
|
|
case ABE_BOTTOM:
|
|
fSizing = (lres==HTTOP);
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!fSizing) {
|
|
lres = HTCAPTION;
|
|
}
|
|
return lres;
|
|
}
|
|
|
|
//*** _OnActivate --
|
|
//
|
|
void CDockingBar::_OnActivate(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
TraceMsg(DM_HIDE, "cwb.WM_ACTIVATE wParam=%x", wParam);
|
|
if (_fCanHide) {
|
|
ASSERT(_fHiding != HIDE_MANUAL);
|
|
if (LOWORD(wParam) != WA_INACTIVE) {
|
|
// activate
|
|
TraceMsg(DM_HIDE, "cdb._oa: WM_ACT(act) _fHiding=%d", _fHiding);
|
|
// turn off timers for perf
|
|
// nash:40992: unhide if hidden (e.g. TABed to hidden)
|
|
_DoHide(AHO_KILLDO|AHO_MOVEUN);
|
|
}
|
|
else {
|
|
// deactivate
|
|
_DoHide(AHO_SETDO); // restore
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/***
|
|
*/
|
|
CDockingBar::CDockingBar() : _eMode(WBM_NIL), _uSide(ABE_RIGHT)
|
|
{
|
|
ASSERT(_fIdtUnHide == FALSE);
|
|
ASSERT(_fIdtDoHide == FALSE);
|
|
_ptIdtUnHide.x = _ptIdtUnHide.y = -1;
|
|
|
|
// set up worst-case defaults. we'll end up using them for:
|
|
// - some of them for Load(bag)
|
|
// - all of them for InitNew()
|
|
// note that we might call _InitPos4 again in SetSite.
|
|
_InitPos4(TRUE);
|
|
|
|
return;
|
|
}
|
|
|
|
//*** _Initialize -- 2nd-phase ctor
|
|
// NOTES
|
|
// we need any IPS::Load settings and also a site before we can init
|
|
// ourself, so most initialization waits until here.
|
|
void CDockingBar::_Initialize()
|
|
{
|
|
ASSERT(!_fInitShowed);
|
|
ASSERT(_fInitSited && _eInitLoaded);
|
|
ASSERT(!CDB_INITED());
|
|
|
|
_fInitShowed = TRUE;
|
|
|
|
// warning: delicate phase-ordering here...
|
|
UINT eModeNew = _eMode;
|
|
_eMode = WBM_NIL;
|
|
UINT uSideNew = _uSide;
|
|
_uSide = ABE_NIL;
|
|
HMONITOR hMonNew = _hMon;
|
|
_hMon = NULL;
|
|
// 48463: beta reports fault on boot when we have deskbar+taskbar on
|
|
// same edge (non-merged). i'm guessing (no proof) that shdocvw isn't
|
|
// init'ed enough early on during boot to handle doing a MergeBS, or
|
|
// alternately that there's a race btwn the tray and desktop threads.
|
|
//
|
|
// plus in any case we shouldn't do the merge just because the guy did
|
|
// a logoff/logon!
|
|
_SetModeSide(eModeNew, uSideNew, hMonNew, /*fNoMerge*/_eInitLoaded == IPS_LOAD);
|
|
|
|
_NotifyModeChange(0);
|
|
|
|
// if we have a bar on the right and we drag a band from it to
|
|
// the top, we end up getting a sequence:
|
|
// create deskbar; AddBand; SetSite; _Initialize
|
|
// the AddBand of the (1st) band tries to do an autosize but
|
|
// there's no site yet, so nothing happens.
|
|
//
|
|
// so we need to force it here.
|
|
_AdjustToChildSize();
|
|
|
|
if (_fWantHide) {
|
|
_fWantHide = FALSE;
|
|
_AppBarOnCommand(IDM_AB_AUTOHIDE);
|
|
}
|
|
|
|
ASSERT(CDB_INITED());
|
|
|
|
return;
|
|
}
|
|
|
|
/***
|
|
*/
|
|
CDockingBar::~CDockingBar()
|
|
{
|
|
ASSERT(!_fAppRegistered); // make sure _ChangeTopMost(WBM_NIL) was called
|
|
|
|
// make sure SetSite(NULL); was called
|
|
ASSERT(!_ptbSite);
|
|
return;
|
|
}
|
|
|
|
|
|
void CDockingBar::_GetChildPos(LPRECT prc)
|
|
{
|
|
GetClientRect(_hwnd, prc);
|
|
}
|
|
|
|
//*** _OnSize -- compute size for OC, leaving room for toolbar (caption?)
|
|
//
|
|
void CDockingBar::_OnSize(void)
|
|
{
|
|
RECT rc;
|
|
|
|
if (!_hwndChild || !_eInitLoaded)
|
|
return;
|
|
|
|
ASSERT(IsWindow(_hwndChild));
|
|
|
|
// don't resize on a hide (it's temporary and we don't want things
|
|
// to jerk around or worse still do a destructive reformat)
|
|
// APPCOMPAT: should suppress resizing here in theater mode autohide
|
|
// too (see theater.cpp)
|
|
if (_fHiding)
|
|
return;
|
|
|
|
_GetChildPos(&rc);
|
|
// (used to do ISWBM_EDGELESS 'fake edge' adjustments here, someone
|
|
// nuked them, but should be o.k. now that visuals are frozen *provided*
|
|
// we don't go back to edgeless)
|
|
|
|
SetWindowPos(_hwndChild, 0,
|
|
rc.left, rc.top, RECTWIDTH(rc), RECTHEIGHT(rc),
|
|
SWP_NOACTIVATE|SWP_NOZORDER);
|
|
//ASSERT(DbCheckWindow(_hwndChild, &rc, xxx));
|
|
}
|
|
|
|
//*** _CalcHitTest --
|
|
// NOTES
|
|
// really only has to return an int (win16?)
|
|
LRESULT CDockingBar::_CalcHitTest(WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LRESULT lRet;
|
|
|
|
if (!(ISWBM_BOTTOM(_eMode) && ISWBM_EDGELESS(_eMode))) {
|
|
// For non-btmmost, we can ask USER to perform the default
|
|
// hit testing.
|
|
lRet = DefWindowProcWrap(_hwnd, WM_NCHITTEST, wParam, lParam);
|
|
} else {
|
|
// For btmmost, we need to do it.
|
|
// (possibly dead code if bottom is never edgeless)
|
|
// (if so, compiler should optimize it out)
|
|
|
|
//TraceMsg(DM_WARNING, "cdb.ro: edgeless!");
|
|
|
|
RECT rc;
|
|
GetWindowRect(_hwnd, &rc);
|
|
UINT x = GET_X_LPARAM(lParam);
|
|
UINT y = GET_Y_LPARAM(lParam);
|
|
// actually SM_C?SIZEFRAME is too big, but we get away w/ it
|
|
// since we've been initiated by a WM_NCHITTEST so we know
|
|
// we're on *some* edge
|
|
UINT cx = GetSystemMetrics(SM_CXSIZEFRAME);
|
|
UINT cy = GetSystemMetrics(SM_CYSIZEFRAME);
|
|
if (_eMode == WBM_BBOTTOMMOST)
|
|
cx *= 2;
|
|
|
|
lRet = HTCAPTION;
|
|
if (x > rc.right-cx) {
|
|
lRet = HTRIGHT;
|
|
} else if (x < rc.left+cx) {
|
|
lRet = HTLEFT;
|
|
} else if (y < rc.top+cy) {
|
|
lRet = HTTOP;
|
|
} else if (y > rc.bottom-cy) {
|
|
lRet = HTBOTTOM;
|
|
}
|
|
}
|
|
|
|
return lRet;
|
|
}
|
|
|
|
//***
|
|
//
|
|
void CDockingBar::_DragEnter(UINT uMsg, int x, int y, RECT* rcFeed)
|
|
{
|
|
ASSERT(_fDragging == 0);
|
|
|
|
if ((!(_eMode & WBM_FLOATING)) && uMsg == WM_MOVING)
|
|
{
|
|
// APPCOMPAT workaround USER non-full-drag drag rect bug
|
|
// by forcing rcFeed back to exact current location (rather than
|
|
// leaving at initial offset that USER gave us).
|
|
//
|
|
// w/o this code a drag from right to top in non-full-drag mode
|
|
// will leave drag-rect droppings at the top of the original
|
|
//
|
|
// APPCOMPAT but, this seems to make things *worse* if !ISWBM_DESKTOP(),
|
|
// so we don't do it in that case... (sigh).
|
|
_MoveSizeHelper(_eMode, _uSide, _hMon, NULL, rcFeed, FALSE, FALSE);
|
|
}
|
|
|
|
_eModePending = _eMode;
|
|
_uSidePending = _uSide;
|
|
_xyPending = MAKELPARAM(x, y);
|
|
_hMonPending = _hMon;
|
|
ASSERT(rcFeed != 0);
|
|
if (rcFeed != 0)
|
|
CopyRect(&_rcPending, rcFeed);
|
|
|
|
#if XXX_CANCEL
|
|
RECT rcTmp;
|
|
|
|
GetWindowRect(_hwnd, &rcTmp);
|
|
TraceMsg(DM_DRAG2,
|
|
"cwb.de: rcTmp=(%d,%d,%d,%d) (%dx%d) _rcCapture=(%d,%d,%d,%d) (%dx%d)",
|
|
rcTmp.left, rcTmp.top, rcTmp.right, rcTmp.bottom,
|
|
RECTWIDTH(rcTmp), RECTHEIGHT(rcTmp),
|
|
_rcCapture.left, _rcCapture.top, _rcCapture.right, _rcCapture.bottom,
|
|
RECTWIDTH(_rcCapture), RECTHEIGHT(_rcCapture));
|
|
#endif
|
|
|
|
switch (uMsg) {
|
|
case WM_MOVING: _fDragging = DRAG_MOVE; break;
|
|
case WM_SIZING: _fDragging = DRAG_SIZE; break;
|
|
|
|
default: ASSERT(0); break;
|
|
}
|
|
|
|
if (_fDragging == DRAG_MOVE) {
|
|
// turn off size negotiation to prevent horz/vert pblms.
|
|
//
|
|
// e.g. when we drag a floating guy to horz/vert, there's
|
|
// a period of time during which we have a horz/vert size,
|
|
// but still think we're floating, which screws up size
|
|
// negotiation royally.
|
|
_ExecDrag(DRAG_MOVE);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//***
|
|
//
|
|
void CDockingBar::_DragTrack(UINT uMsg, int x, int y, RECT* rcFeed, int eState)
|
|
{
|
|
#if DM_API
|
|
TraceMsg(DM_DRAG2,
|
|
"cwb.dt: API s=%d xy=(%d,%d) rc=(%d,%d,%d,%d) (%dx%d)",
|
|
eState, x, y,
|
|
_PM(rcFeed,left), _PM(rcFeed,right), _PM(rcFeed,bottom), _PM(rcFeed,right),
|
|
_PX(rcFeed,RECTWIDTH(*rcFeed)), _PX(rcFeed,RECTHEIGHT(*rcFeed)));
|
|
#endif
|
|
|
|
ASSERT(_fDragging != 0);
|
|
|
|
switch (eState) {
|
|
case 0: // WM_MOVING
|
|
{
|
|
BOOL fImmediate = ((!_fDesktop) && uMsg == WM_SIZING) ? TRUE:FALSE;
|
|
|
|
// remember for eventual commit
|
|
_xyPending = MAKELPARAM(x, y);
|
|
ASSERT(rcFeed != NULL);
|
|
CopyRect(&_rcPending, rcFeed);
|
|
|
|
// snap and give feedback
|
|
_TrackSliding(x, y, rcFeed, fImmediate, fImmediate);
|
|
|
|
break;
|
|
}
|
|
case 1: // WM_MOVE
|
|
TraceMsg(DM_DRAG2,
|
|
"cwb.dt: %s _xyPend=(%d,%d) xy=(%d,%d)",
|
|
(_xyPending != MAKELPARAM(x, y)) ? "noop/cancel" : "commit",
|
|
GET_X_LPARAM(_xyPending), GET_Y_LPARAM(_xyPending), x, y);
|
|
|
|
break;
|
|
|
|
default: ASSERT(0); break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//***
|
|
//
|
|
void CDockingBar::_DragLeave(int x, int y, BOOL fCommit)
|
|
{
|
|
#if DM_API
|
|
TraceMsg(DM_DRAG,
|
|
"cwb.dl: API xy=(%d,%d) fCommit=%d",
|
|
x, y, fCommit);
|
|
#endif
|
|
|
|
if (_fDragging == 0) {
|
|
// when we're inside a browser and you move the browser window
|
|
// we get WM_ENTERSIZEMOVE/ WM_EXITSIZEMOVE but never any
|
|
// WM_MOVING/WM_MOVE/WM_SIZING/WM_SIZE
|
|
return;
|
|
}
|
|
|
|
switch (_fDragging) {
|
|
case DRAG_MOVE:
|
|
case DRAG_SIZE:
|
|
break;
|
|
default: ASSERT(0); break;
|
|
}
|
|
|
|
#if XXX_CANCEL
|
|
RECT rcTmp;
|
|
|
|
GetWindowRect(_hwnd, &rcTmp);
|
|
TraceMsg(DM_DRAG2,
|
|
"cwb.dl: rcTmp=(%d,%d,%d,%d) (%dx%d) _rcCapture=(%d,%d,%d,%d) (%dx%d)",
|
|
rcTmp.left, rcTmp.top, rcTmp.right, rcTmp.bottom,
|
|
RECTWIDTH(rcTmp), RECTHEIGHT(rcTmp),
|
|
_rcCapture.left, _rcCapture.top, _rcCapture.right, _rcCapture.bottom,
|
|
RECTWIDTH(_rcCapture), RECTHEIGHT(_rcCapture));
|
|
TraceMsg(DM_DRAG2, "cwb.dl: %s",
|
|
EqualRect(&rcTmp, &_rcCapture) ? "noop/cancel" : "commit");
|
|
#endif
|
|
|
|
BOOL fCancel = FALSE; // FEATURE: todo: cancel NYI
|
|
|
|
if (!fCancel) {
|
|
if (_fDragging == DRAG_MOVE) {
|
|
// nt5:187720 do this *before* the final move.
|
|
// o.w. addr band ends up w/ 80-high default rather than
|
|
// snapped to correct/negotiated size.
|
|
//
|
|
// why are we able to turn this on here when in general it
|
|
// had to be off during the drag? well, the preview of the
|
|
// drag went thru MoveSizeHelper which did a NotifyModeChange
|
|
// which told our client what its orientation really is. so
|
|
// by now things should be in sync.
|
|
|
|
// size negotiation had been turned off (to prevent horz/vert
|
|
// pblms). turn it on before the final move so that we'll
|
|
// recalc correctly.
|
|
_ExecDrag(0);
|
|
}
|
|
|
|
// (we're done w/ _rcPending so o.k. to pass it in and trash it)
|
|
// fMove==TRUE even though USER has already done the move for us,
|
|
// since it's only done the move not the resize (?). if we use
|
|
// fMove==FALSE we end up in the new location but w/ the old size,
|
|
// despite the fact that rcFeed has been updated along the way.
|
|
// this is because USER sets SWP_NOSIZE when it does the move.
|
|
_TrackSliding(GET_X_LPARAM(_xyPending), GET_Y_LPARAM(_xyPending),
|
|
&_rcPending, TRUE, TRUE);
|
|
|
|
// if we got a preferred child sizewhild dragging, set ourselves to that now.
|
|
// sizing up (cx > min), _szChild.cx == 0 and call a noop.
|
|
// sizing down (cx < min), _szChild.cx != 0 and call does something.
|
|
//ASSERT(_szChild.cx == 0); // 0 => _AdjustToChildSize is nop
|
|
_AdjustToChildSize();
|
|
}
|
|
else {
|
|
_MoveSizeHelper(_eMode, _uSide, _hMon, NULL, NULL, TRUE, FALSE); // FEATURE: fMove?
|
|
}
|
|
|
|
_fDragging = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
int g_dbNoExecDrag = 0; // to play w/ ExecDrag w/o recompiling
|
|
#endif
|
|
|
|
void DBC_ExecDrag(IUnknown *pDBC, int eDragging)
|
|
{
|
|
VARIANTARG vaIn = {0}; // VariantInit
|
|
|
|
ASSERT(eDragging == DRAG_MOVE || eDragging == 0);
|
|
|
|
#ifdef DEBUG
|
|
if (g_dbNoExecDrag)
|
|
return;
|
|
#endif
|
|
|
|
vaIn.vt = VT_I4;
|
|
vaIn.lVal = eDragging; // n.b. currently only 0/1 is supported
|
|
IUnknown_Exec(pDBC, &CGID_DeskBarClient, DBCID_ONDRAG, OLECMDEXECOPT_DONTPROMPTUSER, &vaIn, NULL);
|
|
// VariantClear
|
|
|
|
return;
|
|
}
|
|
|
|
void CDockingBar::_ExecDrag(int eDragging)
|
|
{
|
|
DBC_ExecDrag(_pDBC, eDragging);
|
|
return;
|
|
}
|
|
|
|
//*** _Recalc -- force recalc using current settings
|
|
//
|
|
void CDockingBar::_Recalc(void)
|
|
{
|
|
_MoveSizeHelper(_eMode, _uSide, _hMon, NULL, NULL, TRUE, TRUE);
|
|
return;
|
|
}
|
|
|
|
//*** _MoveSizeHelper -- shared code for menu and dragging forms of move/size
|
|
//
|
|
void CDockingBar::_MoveSizeHelper(UINT eModeNew, UINT eSideNew, HMONITOR hMonNew,
|
|
POINT* ptTrans, RECT* rcFeed, BOOL fCommit, BOOL fMove)
|
|
{
|
|
UINT eModeOld, eSideOld;
|
|
|
|
RECT rcNew;
|
|
|
|
// only desktop guys can go to TOPMOST
|
|
ASSERT(eModeNew != WBM_TOPMOST || ISWBM_DESKTOP());
|
|
|
|
eModeOld = _eMode;
|
|
eSideOld = _uSide;
|
|
|
|
ASSERT(CHKWBM_CHANGE(eModeNew, _eMode));
|
|
_eModePending = eModeNew; // for drag feedback, before commit
|
|
_uSidePending = eSideNew;
|
|
_hMonPending = hMonNew;
|
|
|
|
if (fCommit)
|
|
{
|
|
// we need to be careful when we call _ChangeHide or we'll recurse
|
|
BOOL fChangeHide = (_fWantHide &&
|
|
(eSideNew != _uSide || eModeNew != _eMode || hMonNew != _hMon));
|
|
|
|
if (fChangeHide)
|
|
_DoHide(AHO_KILLDO|AHO_UNREG);
|
|
|
|
_SetModeSide(eModeNew, eSideNew, hMonNew, FALSE);
|
|
|
|
if (fChangeHide)
|
|
{
|
|
// don't do AHO_SETDO now, wait for WM_ACTIVATE(deactivate)
|
|
_DoHide(AHO_REG);
|
|
}
|
|
}
|
|
|
|
// negotiate (and possibly commit to negotiation)
|
|
_ProtoRect(&rcNew, eModeNew, eSideNew, hMonNew, ptTrans);
|
|
_NegotiateRect(eModeNew, eSideNew, hMonNew, &rcNew, fCommit);
|
|
|
|
// commit
|
|
if (fCommit)
|
|
_SetVRect(&rcNew);
|
|
|
|
// feedback
|
|
if (rcFeed != 0)
|
|
{
|
|
CopyRect(rcFeed, &rcNew);
|
|
}
|
|
|
|
if (fMove)
|
|
{
|
|
// If we're in theater mode, out parent manages our width and
|
|
// horizontal position, unless we're being forced to a new
|
|
// size by szChild.
|
|
if (_fTheater && !_fDragging)
|
|
{
|
|
RECT rcCur;
|
|
GetWindowRect(_hwnd, &rcCur);
|
|
rcNew.left = rcCur.left;
|
|
rcNew.right = rcCur.right;
|
|
}
|
|
|
|
// aka ScreenToClient
|
|
MapWindowPoints(HWND_DESKTOP, GetParent(_hwnd), (POINT*) &rcNew, 2);
|
|
|
|
if (_fCanHide && eModeNew == eModeOld && eSideNew == eSideOld)
|
|
{
|
|
// if we're [un]hiding to the same state, we can do SlideWindow
|
|
ASSERT(ISWBM_HIDEABLE(eModeNew));
|
|
DAD_ShowDragImage(FALSE); // unlock the drag sink if we are dragging.
|
|
SlideWindow(_hwnd, &rcNew, _hMon, !_fHiding);
|
|
DAD_ShowDragImage(TRUE); // restore the lock state.
|
|
}
|
|
else
|
|
{
|
|
MoveWindow(_hwnd, rcNew.left, rcNew.top,
|
|
RECTWIDTH(rcNew), RECTHEIGHT(rcNew), TRUE);
|
|
}
|
|
}
|
|
|
|
// WARNING: rcNew is no longer in consistent coords! (ScreenToClient)
|
|
|
|
// notify the child of changes
|
|
_NotifyModeChange(0);
|
|
}
|
|
|
|
void CDockingBar::_NotifyModeChange(DWORD dwMode)
|
|
{
|
|
UINT eMode, uSide;
|
|
|
|
eMode = ((_fDragging == DRAG_MOVE) ? _eModePending : _eMode);
|
|
uSide = ((_fDragging == DRAG_MOVE) ? _uSidePending : _uSide);
|
|
//hMon = ((_fDragging == DRAG_MOVE) ? _hMonPending : _hMon);
|
|
|
|
if (ISWBM_FLOAT(eMode))
|
|
dwMode |= DBIF_VIEWMODE_FLOATING;
|
|
else if (!ABE_HORIZ(uSide))
|
|
dwMode |= DBIF_VIEWMODE_VERTICAL;
|
|
|
|
SUPERCLASS::_NotifyModeChange(dwMode);
|
|
|
|
}
|
|
|
|
void CDockingBar::_TrackSliding(int x, int y, RECT* rcFeed,
|
|
BOOL fCommit, BOOL fMove)
|
|
{
|
|
TraceMsg(DM_DRAG2,
|
|
"cwb.ts: _TrackSliding(x=%d, y=%d, rcFeed=(%d,%d,%d,%d)(%dx%d), fCommit=%d, fMove=%d)",
|
|
x, y,
|
|
_PM(rcFeed,left), _PM(rcFeed,top), _PM(rcFeed,right), _PM(rcFeed,bottom),
|
|
_PX(rcFeed,RECTWIDTH(*rcFeed)), _PX(rcFeed,RECTHEIGHT(*rcFeed)),
|
|
fCommit, fMove);
|
|
|
|
POINT pt = { x, y };
|
|
UINT eModeNew, uSideNew;
|
|
HMONITOR hMonNew;
|
|
if (_fDragging == DRAG_MOVE) {
|
|
// moving...
|
|
|
|
if (fCommit) {
|
|
// use last feedback position.
|
|
// o.w. (if we recompute) we end up in the wrong place since
|
|
// WM_MOVE gives us the (left,top), which often is in another
|
|
// docking zone.
|
|
ASSERT(x == GET_X_LPARAM(_xyPending) && y == GET_Y_LPARAM(_xyPending));
|
|
//eModeNew = _eModePending;
|
|
//uSideNew = _uSidePending;
|
|
}
|
|
|
|
//
|
|
// figure out snap position,
|
|
// and do a few special-case hacks to fix it up if necessary
|
|
//
|
|
uSideNew = _CalcDragPlace(pt, &hMonNew);
|
|
if (uSideNew == ABE_XFLOATING) {
|
|
// dock->float or float->float
|
|
eModeNew = _eMode | WBM_FLOATING;
|
|
uSideNew = _uSide; // FEATURE: _uSidePending? This seems to work correctly as is.
|
|
}
|
|
else {
|
|
// float->dock or dock->dock
|
|
eModeNew = _eMode & ~WBM_FLOATING;
|
|
}
|
|
|
|
TraceMsg(DM_DRAG2,
|
|
"cwb.ts: (m,s) _x=(%d,%d) _xPend=(%d,%d) xNew=(%d,%d)",
|
|
_eMode, _uSide, _eModePending, _uSidePending, eModeNew, uSideNew);
|
|
|
|
// 970725: we now allow bottom->float (for the desktop, not browser)
|
|
if (ISWBM_FLOAT(eModeNew) && ISWBM_BOTTOM(_eMode) && !ISWBM_DESKTOP()) {
|
|
// special case: don't allow switch from BTMMOST to FLOATING
|
|
ASSERT(CHKWBM_CHANGE(eModeNew, _eMode));
|
|
eModeNew = _eModePending; // the dead zone...
|
|
uSideNew = _uSidePending; // QUESTION: init case?
|
|
hMonNew = _hMonPending;
|
|
TraceMsg(DM_DRAG2,
|
|
"cwb.ts: (m,s) btm->flt override xNew=(%d,%d)",
|
|
eModeNew, uSideNew);
|
|
ASSERT(!ISWBM_FLOAT(eModeNew));
|
|
}
|
|
|
|
//
|
|
// smooth things out so we don't jump around
|
|
//
|
|
_SmoothDragPlace(eModeNew, uSideNew, hMonNew, &pt, rcFeed);
|
|
|
|
//
|
|
// now do the move
|
|
//
|
|
// | with _eMode & WBMF_BROWSER because dragging around doesn't change the
|
|
// browser owned bit
|
|
_MoveSizeHelper(eModeNew | (_eMode & WBMF_BROWSER), uSideNew, hMonNew,
|
|
ISWBM_FLOAT(eModeNew) ? &pt : NULL, rcFeed, fCommit, fMove);
|
|
}
|
|
else {
|
|
ASSERT(_fDragging == DRAG_SIZE);
|
|
|
|
// truncate to max size if necessary
|
|
_SmoothDragPlace(_eMode, _uSide, _hMon, NULL, rcFeed);
|
|
|
|
if (!fCommit) {
|
|
// USER does everything for us
|
|
return;
|
|
}
|
|
ASSERT(MAKELPARAM(x, y) != XY_NIL);
|
|
|
|
// APPCOMPAT: we're gonna commit so just blast it in here...
|
|
RECT rcNew;
|
|
|
|
GetWindowRect(_hwnd, &rcNew); // PERF: already set?
|
|
_SetVRect(&rcNew);
|
|
_MoveSizeHelper(_eMode, _uSide, _hMon,
|
|
NULL, NULL, // FEATURE: &rcNew?
|
|
fCommit, fMove);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*** _CalcDragPlace -- compute where drag will end up
|
|
* NOTES
|
|
* FEATURE: prelim version
|
|
*/
|
|
UINT CDockingBar::_CalcDragPlace(POINT& pt, HMONITOR * phMon)
|
|
{
|
|
TraceMsg(DM_DRAG2,
|
|
"cwb.cdp: _CalcDragPlace(pt=(%d,%d))",
|
|
pt.x, pt.y);
|
|
|
|
SIZE screen, error;
|
|
UINT uHorzEdge, uVertEdge, uPlace;
|
|
RECT rcDisplay = {0}; // _GetBorderRect doesn't always set rect.
|
|
|
|
// Get the correct hMonitor.
|
|
ASSERT(phMon);
|
|
*phMon = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
|
|
// FEATURE: todo: make hwndSite and rcDisplay args, then this can be
|
|
// a generic helper func
|
|
_GetBorderRect(*phMon, &rcDisplay);
|
|
|
|
// if we're outside our 'parent' (browser or desktop), we float.
|
|
// this really only applies to the browser case, since we'll never
|
|
// be outside the desktop (what about multi-monitor?).
|
|
if (!PtInRect(&rcDisplay, pt) || !_ptbSite) {
|
|
TraceMsg(DM_DRAG2,
|
|
"cwb.cdp: pt=(%d,%d) uSideNew=%u",
|
|
pt.x, pt.y, ABE_XFLOATING);
|
|
return ABE_XFLOATING;
|
|
}
|
|
|
|
// if we're not w/in min threshhold from edge, we float
|
|
{
|
|
RECT rcFloat; // FEATURE can just use rcDisplay
|
|
int cx = CXFLOAT();
|
|
int cy = CYFLOAT();
|
|
|
|
CopyRect(&rcFloat, &rcDisplay);
|
|
InflateRect(&rcFloat, -cx, -cy);
|
|
|
|
if (PtInRect(&rcFloat, pt)) {
|
|
TraceMsg(DM_DRAG2,
|
|
"cwb.cdp: pt=(%d,%d) uSideNew=%u",
|
|
pt.x, pt.y, ABE_XFLOATING);
|
|
return ABE_XFLOATING;
|
|
}
|
|
}
|
|
|
|
//
|
|
// re-origin at zero to make calculations simpler
|
|
//
|
|
screen.cx = RECTWIDTH(rcDisplay);
|
|
screen.cy = RECTHEIGHT(rcDisplay);
|
|
pt.x -= rcDisplay.left;
|
|
pt.y -= rcDisplay.top;
|
|
|
|
//
|
|
// are we closer to the left or right side of this display?
|
|
//
|
|
if (pt.x < (screen.cx / 2)) {
|
|
uVertEdge = ABE_LEFT;
|
|
error.cx = pt.x;
|
|
}
|
|
else {
|
|
uVertEdge = ABE_RIGHT;
|
|
error.cx = screen.cx - pt.x;
|
|
}
|
|
|
|
//
|
|
// are we closer to the top or bottom side of this display?
|
|
//
|
|
if (pt.y < (screen.cy / 2)) {
|
|
uHorzEdge = ABE_TOP;
|
|
error.cy = pt.y;
|
|
}
|
|
else {
|
|
uHorzEdge = ABE_BOTTOM;
|
|
error.cy = screen.cy - pt.y;
|
|
}
|
|
|
|
//
|
|
// closer to a horizontal or vertical edge?
|
|
//
|
|
uPlace = ((error.cy * screen.cx) > (error.cx * screen.cy))?
|
|
uVertEdge : uHorzEdge;
|
|
|
|
TraceMsg(DM_DRAG2,
|
|
"cwb.cdp: pt=(%d,%d) uSideNew=%u",
|
|
pt.x, pt.y, uPlace);
|
|
|
|
return uPlace;
|
|
}
|
|
|
|
//*** _SmoothDragPlace -- do some magic to smooth out dragging
|
|
// ENTRY/EXIT
|
|
// eModeNew where we're snapping to
|
|
// eSideNew ...
|
|
// [_eModePending] where we're snapping from
|
|
// [_eSidePending] ...
|
|
// pt INOUT cursor position
|
|
// rcFeed USER's original drag feedback rect
|
|
// NOTES
|
|
// this is the place to put excel-like heuristics. e.g. when coming
|
|
// back off the right side we could put the cursor at the top right of
|
|
// the floating rect (rather than the top left) to allow us to float
|
|
// as close as possible to the other side w/o docking. hmm, but how
|
|
// would we tell USER where to put the cursor...
|
|
//
|
|
void CDockingBar::_SmoothDragPlace(UINT eModeNew, UINT eSideNew, HMONITOR hMonNew,
|
|
INOUT POINT* pt, RECT* rcFeed)
|
|
{
|
|
if (_fDragging == DRAG_MOVE) {
|
|
if (ISWBM_FLOAT(eModeNew) && ISWBM_FLOAT(_eModePending) && rcFeed != 0 && pt) {
|
|
// use the feedback rect from USER to keep things smooth.
|
|
// o.w. if we use the cursor position we'll jump at the
|
|
// beginning (to move the left-top corner to the starting
|
|
// cursor position).
|
|
pt->x = rcFeed->left;
|
|
pt->y = rcFeed->top;
|
|
}
|
|
}
|
|
else {
|
|
ASSERT(_fDragging == DRAG_SIZE);
|
|
ASSERT(eModeNew == _eMode && eSideNew == _uSide && hMonNew == _hMon);
|
|
if (!ISWBM_FLOAT(_eMode)) {
|
|
// truncate to max size (1/2 of screen) if necessary
|
|
|
|
int iWH;
|
|
RECT rcScreen;
|
|
|
|
// we'd like to use 1/2 of browser, not 1/2 of screen. however
|
|
// this causes pblms if you maximize, grow to 1/2, and restore.
|
|
// then the 1st time you resize the bar it 'jumps' down to 1/2
|
|
// of the *current* size from 1/2 of the old size. kind of a
|
|
// hack, sigh...
|
|
//
|
|
// also note that there's still a bug here: if you size down
|
|
// the browser gradually, we don't go thru this logic, so you
|
|
// end up w/ a bar width > browser width so you the right edge
|
|
// is clipped and there's no way to size it down. probably
|
|
// when the browser resize is done we should re-smooth the bar.
|
|
//_GetBorderRect(_hMon, &rcScreen);
|
|
GetMonitorRect(_hMon, &rcScreen); // aka GetSystemMetrics(SM_CXSCREEN)
|
|
iWH = RECTGETWH(_uSide, &rcScreen);
|
|
iWH /= 2;
|
|
if (RECTGETWH(_uSide, rcFeed) > iWH) {
|
|
TraceMsg(DM_TRACE, "cwb.sdp: truncate iWH'=%d", iWH);
|
|
RectXform(rcFeed, RX_OPPOSE, rcFeed, NULL, iWH, _uSide, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/***
|
|
*/
|
|
LRESULT CDockingBar::_OnCommand(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
LRESULT lres = 0;
|
|
|
|
if (_CheckForwardWinEvent(uMsg, wParam, lParam, &lres))
|
|
return lres;
|
|
|
|
if ((Command_GetID(wParam) <= IDM_AB_LAST) &&
|
|
(Command_GetID(wParam) >= IDM_AB_FIRST)) {
|
|
_AppBarOnCommand(Command_GetID(wParam));
|
|
}
|
|
|
|
return lres;
|
|
}
|
|
|
|
/*** CDockingBar::_AppBarRegister -- register/unregister AppBar
|
|
* DESCRIPTION
|
|
* updates _fAppRegistered
|
|
* does nothings if it's already regisrered or unregistered
|
|
*/
|
|
void CDockingBar::_AppBarRegister(BOOL fRegister)
|
|
{
|
|
APPBARDATA abd;
|
|
|
|
abd.cbSize = sizeof(APPBARDATA);
|
|
abd.hWnd = _hwnd;
|
|
|
|
if (fRegister && !_fAppRegistered) {
|
|
abd.uCallbackMessage = APPBAR_CALLBACK;
|
|
TraceMsg(DM_APPBAR, "cwb.abr: call ABM_NEW");
|
|
UINT_PTR bT = SHAppBarMessage(ABM_NEW, &abd);
|
|
ASSERT(bT);
|
|
if (bT) {
|
|
_fAppRegistered = TRUE;
|
|
|
|
// fake a callback to set initial state
|
|
// #if XXX_TASKMAN
|
|
TraceMsg(DM_APPBAR, "cwb.abr: fake ABN_STATECHANGE");
|
|
_AppBarCallback(_hwnd, APPBAR_CALLBACK, ABN_STATECHANGE, 0);
|
|
// #endif
|
|
}
|
|
}
|
|
else if (!fRegister && _fAppRegistered) {
|
|
TraceMsg(DM_APPBAR, "cwb.abr: call ABM_REMOVE");
|
|
// n.b. sensitive phase ordering, must set flag before send message
|
|
// since the message causes a bunch of callbacks
|
|
_fAppRegistered = FALSE;
|
|
SHAppBarMessage(ABM_REMOVE, &abd);
|
|
}
|
|
}
|
|
|
|
//*** _SetVRect -- set our 'virtual rect' to reflect window state
|
|
//
|
|
void CDockingBar::_SetVRect(RECT* rcNew)
|
|
{
|
|
UINT eModeNew, uSideNew;
|
|
|
|
//ASSERT(_fDragging == 0); // o.w. we should look at _xxxPending
|
|
|
|
eModeNew = _eMode;
|
|
uSideNew = _uSide;
|
|
|
|
|
|
if (_fHiding && ISWBM_HIDEABLE(eModeNew)) {
|
|
TraceMsg(DM_HIDE, "cwb.svr: _fHiding => suppress rcNew=(%d,%d,%d,%d)",
|
|
rcNew->left, rcNew->top, rcNew->right, rcNew->bottom);
|
|
return;
|
|
}
|
|
|
|
|
|
if (ISWBM_FLOAT(eModeNew)) {
|
|
CopyRect(&_rcFloat, rcNew);
|
|
}
|
|
else {
|
|
_adEdge[uSideNew] = ABE_HORIZ(uSideNew) ? RECTHEIGHT(*rcNew) : RECTWIDTH(*rcNew);
|
|
}
|
|
return;
|
|
}
|
|
|
|
//*** _ChangeTopMost -- switch back and forth btwn TopMost and BottomMost
|
|
// ENTRY/EXIT
|
|
// eModeNew new mode we're switching to
|
|
//
|
|
void CDockingBar::_ChangeTopMost(UINT eModeNew)
|
|
{
|
|
BOOL fShouldRegister = (eModeNew & WBM_TOPMOST) && !(eModeNew & WBM_FLOATING);
|
|
|
|
// here's what's legal...
|
|
// to...................
|
|
// from btm top float
|
|
// ---- --- --- -----
|
|
// btm(desk) - top+ y(1) (1) force to top
|
|
// top top- - 'undock'
|
|
// float y(2) 'dock' - (2) force to right
|
|
// btm(app) - x(3) y(4) (3) foster child (4) 'owned' window
|
|
|
|
|
|
#if 0
|
|
// (1,4) going from BTMMOST to FLOATING is illegal (and NYI) unless desktop
|
|
ASSERT(eModeNew != WBM_FLOATING || _eMode != WBM_BOTTOMMOST || ISWBM_DESKTOP());
|
|
#endif
|
|
|
|
// (3) only desktop guys can go to TOPMOST
|
|
ASSERT(eModeNew != WBM_TOPMOST || ISWBM_DESKTOP());
|
|
|
|
// _uSide should always be laying around (even if floating)
|
|
ASSERT(_eMode == WBM_NIL || ISABE_DOCK(_uSide));
|
|
|
|
// note the ordering here, make sure window bits are right
|
|
// before doing resume new or else new will have unexpected state
|
|
_ChangeWindowStateAndParent(eModeNew);
|
|
_eMode = eModeNew;
|
|
_ChangeZorder();
|
|
|
|
// resume new
|
|
switch (_eMode) {
|
|
case WBM_NIL:
|
|
// dummy state for termination
|
|
return;
|
|
|
|
case WBM_BOTTOMMOST:
|
|
_ResetZorder();
|
|
#if ! XXX_BROWSEROWNED
|
|
// fall through
|
|
case WBM_BBOTTOMMOST:
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
_AppBarRegister(fShouldRegister);
|
|
}
|
|
|
|
//*** _ChangeZorder -- set z-order appropriately
|
|
// NOTES
|
|
// currently doesn't account for 'raised' mode (i.e. caller must call
|
|
// _ChangeZorder before _ResetZorder)
|
|
void CDockingBar::_ChangeZorder()
|
|
{
|
|
BOOL fWantTopmost = BOOLIFY(WBM_IS_TOPMOST());
|
|
BOOL fIsTopmost = BOOLIFY(GetWindowLong(_hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST);
|
|
if (fWantTopmost != fIsTopmost)
|
|
SetWindowPos(_hwnd, fWantTopmost ? HWND_TOPMOST : HWND_NOTOPMOST,
|
|
0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
|
return;
|
|
}
|
|
|
|
//*** _ResetZorder -- toggle DockingBars between 'normal' and 'raised' mode
|
|
// DESCRIPTION
|
|
// queries desktop state and does appropriate '_OnRaise'
|
|
//
|
|
void CDockingBar::_ResetZorder()
|
|
{
|
|
HRESULT hr;
|
|
VARIANTARG vaIn = {0}; // VariantInit
|
|
VARIANTARG vaOut = {0}; // VariantInit
|
|
|
|
vaIn.vt = VT_I4;
|
|
vaIn.lVal = DTRF_QUERY;
|
|
hr = IUnknown_Exec(_ptbSite, &CGID_ShellDocView, SHDVID_RAISE, OLECMDEXECOPT_DONTPROMPTUSER,
|
|
&vaIn, &vaOut);
|
|
if (SUCCEEDED(hr) && vaOut.vt == VT_I4)
|
|
_OnRaise(vaOut.lVal);
|
|
// VariantClear
|
|
return;
|
|
}
|
|
|
|
//*** _OnRaise -- handle desktop 'raise' command
|
|
// DESCRIPTION
|
|
// changes DockingBar z-order depending on desktop raise state:
|
|
// desktop DockingBar
|
|
// raised force on top (so visible)
|
|
// restored return to normal
|
|
// NOTES
|
|
// FEATURE: should we handle WBM_FLOATING too?
|
|
// FEATURE: should add ZORD_xxx to deskbar.h and handle non-WBM_BOTTOMMOST
|
|
void CDockingBar::_OnRaise(UINT flags)
|
|
{
|
|
HWND hwndZorder;
|
|
|
|
if (_eMode != WBM_BOTTOMMOST)
|
|
return;
|
|
|
|
switch (flags) {
|
|
case DTRF_RAISE:
|
|
hwndZorder = HWND_TOPMOST;
|
|
break;
|
|
|
|
case DTRF_LOWER:
|
|
hwndZorder = HWND_NOTOPMOST;
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
return;
|
|
}
|
|
|
|
SetWindowPos(_hwnd, hwndZorder, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOACTIVATE);
|
|
|
|
return;
|
|
}
|
|
|
|
#if XXX_BTMFLOAT && 0 // see 'NOTES'
|
|
//*** _MayReWindow -- change/restore window state for dragging
|
|
// DESCRIPTION
|
|
// USER won't let us drag outside of the browser unless we reparent
|
|
// ourselves.
|
|
// NOTES
|
|
// need to call this *before* we enter USER's move/size loop.
|
|
// i.e. on LBUTTONDOWN and on EXITSIZEMOVE. this gets a bit
|
|
// tricky due to CANCEL etc., since the LBUTTONUP may come thru
|
|
// either before or after we're done.
|
|
//
|
|
void CDockingBar::_MayReWindow(BOOL fToFloat)
|
|
{
|
|
|
|
if (ISWBM_DESKTOP() || _eMode != WBM_BOTTOMMOST)
|
|
return;
|
|
|
|
// do style bits 1st or re-parenting breaks
|
|
SHSetWindowBits(_hwnd, GWL_STYLE, WS_CHILD | WS_POPUP, fToFloat ? WS_POPUP | WS_CHILD);
|
|
|
|
if (!fToFloat) {
|
|
// float->btm
|
|
|
|
// nuke owner
|
|
SHSetParentHwnd(_hwnd, NULL);
|
|
|
|
// parent
|
|
SetParent(_hwnd, _hwndSite);
|
|
}
|
|
|
|
|
|
if (fToFloat) {
|
|
// btm->float, set owner
|
|
|
|
// parent
|
|
SetParent(_hwnd, PARENT_FLOATING);
|
|
|
|
// set owner
|
|
ASSERT(_hwndSite != NULL);
|
|
SHSetParentHwnd(_hwnd, _hwndSite);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
void CDockingBar::_GetStyleForMode(UINT eMode, LONG* plStyle, LONG* plExStyle, HWND* phwndParent)
|
|
{
|
|
switch (eMode) {
|
|
case WBM_NIL:
|
|
*plStyle = WS_NIL;
|
|
*plExStyle= WS_EX_NIL;
|
|
*phwndParent = PARENT_NIL;
|
|
break;
|
|
|
|
case WBM_BBOTTOMMOST:
|
|
*plStyle = WS_BBTMMOST;
|
|
*plExStyle= WS_EX_BBTMMOST;
|
|
*phwndParent = PARENT_BBTMMOST();
|
|
break;
|
|
|
|
case WBM_BOTTOMMOST:
|
|
*plStyle = WS_BTMMOST;
|
|
*plExStyle= WS_EX_BTMMOST;
|
|
*phwndParent = PARENT_BTMMOST();
|
|
break;
|
|
|
|
case WBM_BFLOATING:
|
|
// FEATURE: todo: FLOATING NYI
|
|
*plStyle = WS_BFLOATING;
|
|
*plExStyle = WS_EX_BFLOATING;
|
|
*phwndParent = _hwndSite;
|
|
break;
|
|
|
|
case (WBM_FLOATING | WBM_TOPMOST):
|
|
case WBM_FLOATING:
|
|
// FEATURE: todo: FLOATING NYI
|
|
*plStyle = WS_FLOATING;
|
|
*plExStyle = WS_EX_FLOATING;
|
|
*phwndParent = PARENT_FLOATING;
|
|
break;
|
|
|
|
case WBM_TOPMOST:
|
|
*plStyle = WS_XTOPMOST;
|
|
*plExStyle= WS_EX_XTOPMOST;
|
|
*phwndParent = PARENT_XTOPMOST;
|
|
break;
|
|
}
|
|
#ifdef DEBUG // {
|
|
if (_eMode == eMode) {
|
|
// style, exstyle
|
|
ASSERT(BITS_SET(GetWindowLong(_hwnd, GWL_STYLE), *plStyle));
|
|
ASSERT(BITS_SET(GetWindowLong(_hwnd, GWL_EXSTYLE), *plExStyle & ~WS_EX_TOPMOST));
|
|
|
|
// id
|
|
ASSERT(GetWindowLong(_hwnd, GWL_ID) == 0);
|
|
|
|
// parent
|
|
ASSERT(GetParent(_hwnd) == *phwndParent ||
|
|
(ISWBM_OWNED(_eMode) && GetParent(_hwnd)==_hwndSite));
|
|
}
|
|
#endif // }
|
|
}
|
|
|
|
//*** _ChangeWindowStateAndParent --
|
|
// NOTES
|
|
// todo: make table-driven (ws1, ws2, etc.)
|
|
//
|
|
void CDockingBar::_ChangeWindowStateAndParent(UINT eModeNew)
|
|
{
|
|
LONG ws1, wsx1, ws2, wsx2;
|
|
HWND hwnd;
|
|
|
|
if (eModeNew == _eMode) {
|
|
// same mode, nothing to do
|
|
return;
|
|
}
|
|
|
|
//
|
|
// nuke old bits
|
|
//
|
|
_GetStyleForMode(_eMode, &ws1, &wsx1, &hwnd);
|
|
|
|
|
|
//
|
|
// set new bits
|
|
//
|
|
_GetStyleForMode(eModeNew, &ws2, &wsx2, &hwnd);
|
|
|
|
// if it's going to be owned by the browser,
|
|
// override hwnd to our site's hwnd
|
|
if (eModeNew & WBMF_BROWSER)
|
|
hwnd = _hwndSite;
|
|
|
|
// style, exstyle
|
|
// (SWB can't do WS_EX_TOPMOST, we do it in caller w/ SWP)
|
|
SHSetWindowBits(_hwnd, GWL_STYLE, ws1|ws2 , ws2);
|
|
SHSetWindowBits(_hwnd, GWL_EXSTYLE, (wsx1|wsx2) & ~WS_EX_TOPMOST, wsx2);
|
|
|
|
// id
|
|
// (unchanged)
|
|
HWND hwndParent = GetParent(_hwnd);
|
|
if (hwndParent != hwnd) {
|
|
if (hwndParent != HWND_DESKTOP) {
|
|
// float->btm, nuke owner
|
|
SHSetParentHwnd(_hwnd, NULL);
|
|
}
|
|
|
|
// parent
|
|
SetParent(_hwnd, hwnd);
|
|
|
|
if (hwnd == _hwndSite) {
|
|
// btm->float, set owner
|
|
ASSERT(_hwndSite != NULL);
|
|
SHSetParentHwnd(_hwnd, _hwndSite);
|
|
}
|
|
}
|
|
//
|
|
// force redraw
|
|
//
|
|
SetWindowPos(_hwnd, NULL, 0, 0, 0, 0,
|
|
SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER);
|
|
|
|
return;
|
|
}
|
|
|
|
//*** _SetNewMonitor --
|
|
// When the Desktop is our Docking Site, set the new monitor I am on
|
|
// and return the old monitor
|
|
HMONITOR CDockingBar::_SetNewMonitor(HMONITOR hMonNew)
|
|
{
|
|
HMONITOR hMonOld = NULL;
|
|
if (ISWBM_DESKTOP() && _ptbSite)
|
|
{
|
|
IMultiMonitorDockingSite * pdds;
|
|
HRESULT hresT = _ptbSite->QueryInterface(IID_IMultiMonitorDockingSite, (LPVOID *)&pdds);
|
|
if (SUCCEEDED(hresT))
|
|
{
|
|
HMONITOR hMon;
|
|
ASSERT(pdds);
|
|
if (SUCCEEDED(pdds->GetMonitor(SAFECAST(this, IDockingWindow*), &hMon)))
|
|
{
|
|
if (hMon != hMonNew)
|
|
{
|
|
pdds->RequestMonitor(SAFECAST(this, IDockingWindow*), &hMonNew);
|
|
pdds->SetMonitor(SAFECAST(this, IDockingWindow*), hMonNew, &hMonOld);
|
|
// These two should be the same, otherwise something wierd is happening -- dli
|
|
ASSERT(hMonOld == hMon);
|
|
}
|
|
}
|
|
pdds->Release();
|
|
}
|
|
}
|
|
|
|
return hMonOld;
|
|
}
|
|
|
|
//*** _GetBorderRect --
|
|
// NOTES
|
|
// result in screen coordinates
|
|
//
|
|
void CDockingBar::_GetBorderRect(HMONITOR hMon, RECT* prc)
|
|
{
|
|
if (!ISWBM_BOTTOM(_eMode)) {
|
|
// FEATURE: todo: should use:
|
|
// floating: _hwndSite (not strictly correct, but good enough)
|
|
// topmost: UnionRect of:
|
|
// GetWindowRect(_hwndSite); // non-appbar rect
|
|
// GetWindowRect(self) // plus my personal appbar
|
|
ASSERT(IsWindow(_hwndSite));
|
|
if (ISWBM_DESKTOP())
|
|
GetMonitorRect(hMon, prc);
|
|
else
|
|
GetWindowRect(_hwndSite, prc);
|
|
#ifdef DEBUG
|
|
#if 0
|
|
RECT rcTmp;
|
|
|
|
// these asserts often fail. e.g. when dragging topmost right->top.
|
|
// weird: _hwndSite ends up being PROGMAN's hwnd.
|
|
// weird: also, the GetWindowRect fails.
|
|
ASSERT(_hwndSite == PARENT_XTOPMOST);
|
|
// _hwndSite is PROGMAN
|
|
GetWindowRect(PARENT_XTOPMOST, &rcTmp);
|
|
ASSERT(EqualRect(prc, &rcTmp));
|
|
#endif
|
|
#endif
|
|
}
|
|
else if (_ptbSite) {
|
|
HMONITOR hMonOld = _SetNewMonitor(hMon);
|
|
_ptbSite->GetBorderDW(SAFECAST(this, IDockingWindow*), prc);
|
|
if (hMonOld)
|
|
_SetNewMonitor(hMonOld);
|
|
ASSERT(_hwndSite != NULL);
|
|
//ASSERT(GetParent(_hwnd) == _hwndSite); // FEATURE ISWBM_OWNED?
|
|
// convert if necessary
|
|
// aka ClientToScreen
|
|
MapWindowPoints(_hwndSite, HWND_DESKTOP, (POINT*) prc, 2);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//*** _HideRegister -- (un)register auto-hide w/ edge
|
|
// ENTRY/EXIT
|
|
// fToHide TRUE if turning AutoHide on, FALSE if turning off
|
|
// _fCanHide [OUT] TRUE if successfully set autohide on; o.w. FALSE
|
|
// other pops up dialog if operation fails
|
|
//
|
|
void CDockingBar::_HideRegister(BOOL fToHide)
|
|
{
|
|
BOOL fSuccess;
|
|
APPBARDATA abd;
|
|
|
|
if (! ISWBM_HIDEABLE(_eMode))
|
|
return;
|
|
|
|
// (try to) register or unregister it
|
|
// n.b. we're allowed to do this even if we're not an AppBar
|
|
// that's good, because we want at most one autohide deskbar
|
|
// on an edge regardless of mode
|
|
abd.cbSize = SIZEOF(abd);
|
|
abd.hWnd = _hwnd;
|
|
abd.uEdge = _uSide;
|
|
abd.lParam = fToHide;
|
|
|
|
// FEATURE should we do a ABM_GETAUTOHIDEBAR at some point?
|
|
// (tray.c does, and so does the AB sample code...)
|
|
//ASSERT(_fAppRegistered);
|
|
fSuccess = (BOOL) SHAppBarMessage(ABM_SETAUTOHIDEBAR, &abd);
|
|
|
|
// set our state
|
|
_fCanHide = BOOLIFY(fSuccess);
|
|
// FEATURE: how handle failure?
|
|
|
|
// init some stuff
|
|
if (fToHide)
|
|
{
|
|
if (_fCanHide)
|
|
{
|
|
RECT rc;
|
|
|
|
ASSERT(_fCanHide); // so we won't SetVRect
|
|
|
|
ASSERT(!_fHiding); // (paranoia)
|
|
|
|
// force a '0-width' rectangle so we don't take up any space
|
|
RectXform(&rc, RX_EDGE|RX_OPPOSE|RX_ADJACENT, &rc, NULL,
|
|
0, _uSide, _hMon);
|
|
|
|
switch (_eMode) {
|
|
case WBM_TOPMOST:
|
|
// negotiate/commit it
|
|
APPBARDATA abd;
|
|
abd.cbSize = sizeof(APPBARDATA);
|
|
abd.hWnd = _hwnd;
|
|
ASSERT(_fCanHide);
|
|
// we used to do:
|
|
// _fCanHide = FALSE; // hack: so we surrender AppBar's space
|
|
// AppBarQuerySetPos(&rc, _uSide, &rc, &abd, TRUE);
|
|
// _fCanHide = TRUE; // hack: restore
|
|
// but the instant we do the ABSetPos the shell does a recalc
|
|
// by doing a ShowDW of all toolbars, which does a _Recalc,
|
|
// which does a MSH, which ends up doing ProtoRect w/ our
|
|
// 'temporary' _fCanHide=0, which ends up taking space (oops!).
|
|
//
|
|
// so instead we call the low-level ABQueryPos/ABSetPos guys
|
|
// directly.
|
|
AppBarQueryPos(&rc, _uSide, _hMon, &rc, &abd, TRUE);
|
|
AppBarSetPos0(_uSide, &rc, &abd);
|
|
break;
|
|
}
|
|
|
|
// do *not* start the hide here
|
|
// it's up to the caller, since a) might want delay or
|
|
// immediate and b) recursion pblms w/ _MoveSizeHelper
|
|
}
|
|
else
|
|
{
|
|
// FEATURE: do PostMessage a la tray.c?
|
|
MLShellMessageBox(_hwnd,
|
|
MAKEINTRESOURCE(IDS_ALREADYAUTOHIDEBAR),
|
|
MAKEINTRESOURCE(IDS_WEBBARTITLE),
|
|
MB_OK | MB_ICONINFORMATION);
|
|
ASSERT(!_fCanHide);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// do *not* start the unhide here
|
|
// it's up to the caller, since a) might want delay or
|
|
// immediate and b) recursion pblms w/ _MoveSizeHelper
|
|
|
|
_fCanHide = FALSE;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//*** IsNearPoint -- am i currently near specified point?
|
|
// ENTRY/EXIT
|
|
// pptBase (INOUT) IN previous cursor pos, OUT updated to current if !fNear
|
|
// fNear (ret) TRUE if near, o.w. FALSE
|
|
// NOTES
|
|
// heuristic stolen from explorer/tray.c!TraySetUnhideTimer
|
|
//
|
|
BOOL IsNearPoint(/*INOUT*/ POINT *pptBase)
|
|
{
|
|
POINT ptCur;
|
|
int dx, dy, dOff, dNear;
|
|
|
|
GetCursorPos(&ptCur);
|
|
dx = pptBase->x - ptCur.x;
|
|
dy = pptBase->y - ptCur.y;
|
|
dOff = dx * dx + dy * dy;
|
|
dNear = GetSystemMetrics(SM_CXDOUBLECLK) * GetSystemMetrics(SM_CYDOUBLECLK);
|
|
if (dOff <= dNear)
|
|
return TRUE;
|
|
TraceMsg(DM_HIDE2, "cwb.inp: ret=0 dOff=%d dNear=%d", dOff, dNear);
|
|
*pptBase = ptCur;
|
|
return FALSE;
|
|
}
|
|
|
|
//*** _DoHide --
|
|
// DESCRIPTION
|
|
// AHO_KILLDO kill timer for 'do' operation
|
|
// AHO_SETDO set timer for 'do' operation
|
|
// AHO_KILLUN kill timer for 'undo' operation
|
|
// AHO_SETUN set timer for 'undo' operation
|
|
// AHO_REG register
|
|
// AHO_UNREG unregister
|
|
// AHO_MOVEDO do the actual hide
|
|
// AHO_MOVEUN do the actual unhide
|
|
// NOTES
|
|
// the _fIdtXxHide stuff stops us from doing a 2nd SetTimer before the
|
|
// 1st one comes in, which makes us never get the 'earlier' ticks.
|
|
// this fixes nt5:142686: drag-over doesn't unhide. it was caused by us
|
|
// getting a bunch of WM_NCHITTESTs in rapid succession (OLE asking us on
|
|
// a fast timer?).
|
|
// REARCHITECT: i think there's a tiny race window on _fIdtXxHide (between the
|
|
// call to Set/Kill and the shadowing in _fIdtXxHide). not sure we can
|
|
// even hit it, but if we do, i think the worst that happens is somebody
|
|
// doesn't hide or unhide for a while.
|
|
//
|
|
void CDockingBar::_DoHide(UINT uOpMask)
|
|
{
|
|
TraceMsg(DM_HIDE, "cwb.dh enter(uOpMask=0x%x(%s))",
|
|
uOpMask, DbMaskToMneStr(uOpMask, AHO_MNE));
|
|
|
|
if (!ISWBM_HIDEABLE(_eMode)) {
|
|
TraceMsg(DM_HIDE, "cwb.dh !ISWBM_HIDEABLE(_eMode) => suppress");
|
|
return;
|
|
}
|
|
|
|
// nuke old timer
|
|
if (uOpMask & AHO_KILLDO) {
|
|
TraceMsg(DM_HIDE, "cwb.dh: KillTimer(idt_autohide)");
|
|
KillTimer(_hwnd, IDT_AUTOHIDE);
|
|
_fIdtDoHide = FALSE;
|
|
}
|
|
if (uOpMask & AHO_KILLUN) {
|
|
TraceMsg(DM_HIDE, "cwb.dh: KillTimer(idt_autoUNhide)");
|
|
KillTimer(_hwnd, IDT_AUTOUNHIDE);
|
|
_fIdtUnHide = FALSE;
|
|
_ptIdtUnHide.x = _ptIdtUnHide.y = -1;
|
|
}
|
|
|
|
if (uOpMask & (AHO_REG|AHO_UNREG)) {
|
|
_HideRegister(uOpMask & AHO_REG);
|
|
}
|
|
|
|
if (uOpMask & (AHO_MOVEDO|AHO_MOVEUN)) {
|
|
// tricky, tricky...
|
|
// all the smarts are in _MoveSizeHelper, driven by _fHiding (and _fCanHide)
|
|
// use correct one of (tiny,real)
|
|
_fHiding = (uOpMask & AHO_MOVEDO) ? HIDE_AUTO : FALSE;
|
|
|
|
TraceMsg(DM_HIDE, "cwb.dh: move _fHiding=%d", _fHiding);
|
|
ASSERT(_fCanHide); // suppress SetVRect
|
|
_Recalc(); // _MoveSizeHelper(_eMode, _uSide, NULL, NULL, TRUE, TRUE);
|
|
}
|
|
|
|
// start new timer
|
|
if (_fCanHide) {
|
|
if (uOpMask & AHO_SETDO) {
|
|
TraceMsg(DM_HIDE, "cwb.dh: SetTimer(idt_autohide) fAlready=%d", _fIdtDoHide);
|
|
if (!_fIdtDoHide) {
|
|
_fIdtDoHide = TRUE;
|
|
SetTimer(_hwnd, IDT_AUTOHIDE, DLY_AUTOHIDE, NULL);
|
|
}
|
|
}
|
|
if (uOpMask & AHO_SETUN) {
|
|
TraceMsg(DM_HIDE, "cwb.dh: SetTimer(idt_autoUNhide) fAlready=%d", _fIdtUnHide);
|
|
// IsNearPoint hysteresis prevents us from unhiding when we happen
|
|
// to be passed over on the way to something unrelated
|
|
if (!IsNearPoint(&_ptIdtUnHide) || !_fIdtUnHide) {
|
|
_fIdtUnHide = TRUE;
|
|
SetTimer(_hwnd, IDT_AUTOUNHIDE, DLY_AUTOUNHIDE, NULL);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
#ifdef DEBUG
|
|
if ((uOpMask & (AHO_SETDO|AHO_SETUN))) {
|
|
TraceMsg(DM_HIDE, "cwb.dh: !_fCanHide => suppress AHO_SET*");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//*** SlideWindow -- sexy slide effect
|
|
// NOTES
|
|
// stolen from tray.c
|
|
void SlideWindow(HWND hwnd, RECT *prc, HMONITOR hMonClip, BOOL fShow)
|
|
{
|
|
RECT rcMonitor, rcClip;
|
|
BOOL fRegionSet = FALSE;
|
|
|
|
SetRectEmpty(&rcMonitor);
|
|
if (GetNumberOfMonitors() > 1)
|
|
{
|
|
GetMonitorRect(hMonClip, &rcMonitor);
|
|
// aka ScreenToClient
|
|
MapWindowPoints(HWND_DESKTOP, GetParent(hwnd), (LPPOINT)&rcMonitor, 2); }
|
|
|
|
// Future: We could loop on the following code for the slide effect
|
|
IntersectRect(&rcClip, &rcMonitor, prc);
|
|
if (!IsRectEmpty(&rcClip))
|
|
{
|
|
HRGN hrgnClip;
|
|
|
|
// Change the clip region to be relative to the upper left corner of prc
|
|
// NOTE: this is not converting rcClip to prc client coordinate
|
|
OffsetRect(&rcClip, -prc->left, -prc->top);
|
|
|
|
hrgnClip = CreateRectRgnIndirect(&rcClip);
|
|
// LINTASSERT(hrgnClip || !hgnClip); // 0 semi-ok for SetWindowRgn
|
|
// nt5:149630: always repaint, o.w. auto-unhide BitBlt's junk
|
|
// from hide position
|
|
fRegionSet = SetWindowRgn(hwnd, hrgnClip, /*fRepaint*/TRUE);
|
|
}
|
|
MoveWindow(hwnd, prc->left, prc->top, RECTWIDTH(*prc), RECTHEIGHT(*prc), TRUE);
|
|
|
|
// Turn off the region stuff if we don't hide any more
|
|
if (fRegionSet && fShow)
|
|
SetWindowRgn(hwnd, NULL, TRUE);
|
|
|
|
return;
|
|
}
|
|
|
|
/*** AppBarQueryPos -- negotiate position
|
|
* ENTRY/EXIT
|
|
* return width (height) from docked edge to opposing edge
|
|
*/
|
|
int CDockingBar::AppBarQueryPos(RECT* prcOut, UINT uEdge, HMONITOR hMon, const RECT* prcReq,
|
|
PAPPBARDATA pabd, BOOL fCommit)
|
|
{
|
|
int iWH;
|
|
|
|
ASSERT(ISWBM_DESKTOP());
|
|
|
|
// snap to edge (in case another AppBar disappeared w/o us knowing),
|
|
// readjust opposing side to reflect that snap,
|
|
// and max out adjacent sides to fill up full strip.
|
|
iWH = RectGetWH(prcReq, uEdge);
|
|
|
|
RectXform(&(pabd->rc), RX_EDGE|RX_OPPOSE|RX_ADJACENT|(_fHiding ? RX_HIDE : 0), prcReq, NULL, iWH, uEdge, hMon);
|
|
|
|
ASSERT(EqualRect(&(pabd->rc), prcReq)); // caller guarantees?
|
|
|
|
// negotiate
|
|
// if we're dragging we might not be registered yet (floating->docked)
|
|
// in that case we'll just use the requested size (w/o negotiating).
|
|
// ditto for if we're in the middle of a top/non-top mode switch.
|
|
if (_fAppRegistered) {
|
|
pabd->uEdge = uEdge;
|
|
TraceMsg(DM_APPBAR, "cwb.abqp: call ABM_QUERYPOS");
|
|
SHAppBarMessage(ABM_QUERYPOS, pabd);
|
|
}
|
|
|
|
// readjust opposing side to reflect the negotiation (which only
|
|
// adjusts the moved edge-most side, not the opposing edge).
|
|
// FEATURE: (dli) need to find the right hmonitor to pass in
|
|
RectXform(prcOut, RX_OPPOSE, &(pabd->rc), NULL, iWH, uEdge, hMon);
|
|
|
|
return RectGetWH(prcOut, uEdge);
|
|
}
|
|
|
|
//*** AppBarSetPos --
|
|
// NOTES
|
|
// does *not* do _SetVRect and MoveWindow, that's up to caller
|
|
//
|
|
void CDockingBar::AppBarSetPos(UINT uEdge, const RECT* prcReq, PAPPBARDATA pabd)
|
|
{
|
|
ASSERT(_eMode == WBM_TOPMOST);
|
|
|
|
if (!_fCanHide && _fAppRegistered)
|
|
AppBarSetPos0(uEdge, prcReq, pabd);
|
|
|
|
return;
|
|
}
|
|
|
|
void CDockingBar::AppBarSetPos0(UINT uEdge, const RECT* prcReq, PAPPBARDATA pabd)
|
|
{
|
|
CopyRect(&(pabd->rc), prcReq);
|
|
pabd->uEdge = uEdge;
|
|
|
|
TraceMsg(DM_APPBAR, "cwb.absp: call ABM_SETPOS");
|
|
ASSERT(_fAppRegistered);
|
|
SHAppBarMessage(ABM_SETPOS, pabd);
|
|
|
|
// APPCOMPAT workaround explorer bug: during dragging we get:
|
|
// querypos*; wm_winposchanged; querypos; setpos
|
|
// the lack of a wm_winposchanged at the end screws up the
|
|
// autohide bring-to-top code.
|
|
ASSERT(pabd->cbSize == sizeof(APPBARDATA));
|
|
ASSERT(pabd->hWnd == _hwnd);
|
|
TraceMsg(DM_APPBAR, "cwb.absp: call ABM_WINPOSCHGED");
|
|
SHAppBarMessage(ABM_WINDOWPOSCHANGED, pabd);
|
|
|
|
// n.b. _SetVRect and MoveWindow done by caller
|
|
|
|
return;
|
|
}
|
|
|
|
//*** AppBarQuerySetPos --
|
|
//
|
|
void CDockingBar::AppBarQuerySetPos(RECT* prcOut, UINT uEdge, HMONITOR hMon, const RECT* prcReq,
|
|
PAPPBARDATA pabd, BOOL fCommit)
|
|
{
|
|
RECT rcTmp;
|
|
|
|
if (prcOut == NULL)
|
|
prcOut = &rcTmp;
|
|
|
|
AppBarQueryPos(prcOut, uEdge, hMon, prcReq, pabd, fCommit);
|
|
if (fCommit) {
|
|
AppBarSetPos(uEdge, prcOut, pabd);
|
|
ASSERT(EqualRect(prcOut, &(pabd->rc))); // callers assume prcOut correct
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void CDockingBar::_AppBarOnSize()
|
|
{
|
|
RECT rc;
|
|
APPBARDATA abd;
|
|
|
|
ASSERT(_eMode == WBM_TOPMOST);
|
|
ASSERT(ISABE_DOCK(_uSide));
|
|
|
|
if (!_fAppRegistered)
|
|
return;
|
|
|
|
// don't commit until done
|
|
if (_fDragging)
|
|
return;
|
|
|
|
abd.cbSize = sizeof(APPBARDATA);
|
|
abd.hWnd = _hwnd;
|
|
|
|
GetWindowRect(_hwnd, &rc);
|
|
AppBarQuerySetPos(NULL, _uSide, _hMon, &rc, &abd, TRUE);
|
|
|
|
return;
|
|
}
|
|
|
|
void CDockingBar::_RemoveToolbar(DWORD dwFlags)
|
|
{
|
|
if (_ptbSite) {
|
|
// WM_DESTROY will do _ChangeTopMost(WBM_NIL) for us
|
|
|
|
IDockingWindowFrame* ptbframe;
|
|
HRESULT hresT=_ptbSite->QueryInterface(IID_IDockingWindowFrame, (LPVOID*)&ptbframe);
|
|
if (SUCCEEDED(hresT)) {
|
|
AddRef(); // guard against self destruction
|
|
ptbframe->RemoveToolbar(SAFECAST(this, IDockingWindow*), dwFlags);
|
|
ptbframe->Release();
|
|
Release();
|
|
}
|
|
} else {
|
|
CloseDW(0);
|
|
}
|
|
}
|
|
|
|
void CDockingBar::_AppBarOnCommand(UINT idCmd)
|
|
{
|
|
UINT eModeNew;
|
|
|
|
switch (idCmd) {
|
|
case IDM_AB_TOPMOST:
|
|
eModeNew = _eMode ^ WBM_TOPMOST;
|
|
_MoveSizeHelper(eModeNew, _uSide, _hMon, NULL, NULL, TRUE, TRUE);
|
|
break;
|
|
|
|
case IDM_AB_AUTOHIDE:
|
|
if (_fWantHide)
|
|
{
|
|
// on->off
|
|
_DoHide(AHO_KILLDO|AHO_UNREG); // _ChangeHide
|
|
_fWantHide = FALSE;
|
|
}
|
|
else
|
|
{
|
|
// off->on
|
|
_fWantHide = TRUE;
|
|
// don't do AHO_SETDO now, wait for WM_ACTIVATE(deactivate)
|
|
_DoHide(AHO_REG); // _ChangeHide
|
|
}
|
|
|
|
// force it to happen *now*
|
|
// REARCHITECT potential race condition w/ the AHO_SETDO above,
|
|
// but worst case that should cause a 2nd redraw (?).
|
|
_Recalc(); // _MoveSizeHelper(_eMode, _uSide, NULL, NULL, TRUE, TRUE);
|
|
|
|
if (SHIsChildOrSelf(GetActiveWindow(), _hwnd) != S_OK)
|
|
{
|
|
// nt5:148444: if we're already deactive, we need to kick off
|
|
// the hide now. this is needed e.g. for login when we load
|
|
// persisted auto-hide deskbars. they come up inactive so we
|
|
// never get the initial deact to hide them.
|
|
_OnActivate(MAKEWPARAM(WA_INACTIVE, FALSE), (LPARAM)(HWND)0);
|
|
}
|
|
|
|
break;
|
|
#ifdef DEBUG
|
|
case IDM_AB_ACTIVATE:
|
|
// REARCHITECT temporary until we make browser tell us about activation
|
|
|
|
// note that since we're faking this w/ a menu our (normal) assumption
|
|
// in WM_ENTERMENU is bogus so make sure you keep the mouse over
|
|
// the BrowserBar during activation or it will hide away out from under
|
|
// you and the Activate won't work...
|
|
_OnActivate(MAKEWPARAM(_fActive ? WA_INACTIVE : WA_ACTIVE, FALSE),
|
|
(LPARAM) (HWND) 0);
|
|
_fActive = !_fActive;
|
|
break;
|
|
#endif
|
|
|
|
case IDM_AB_CLOSE:
|
|
_OnCloseBar(TRUE);
|
|
break;
|
|
|
|
default:
|
|
MessageBeep(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
BOOL CDockingBar::_OnCloseBar(BOOL fConfirm)
|
|
{
|
|
_RemoveToolbar(0);
|
|
return TRUE;
|
|
}
|
|
|
|
void CDockingBar::_AppBarOnWM(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
switch (uMsg) {
|
|
case WM_WINDOWPOSCHANGED:
|
|
case WM_ACTIVATE:
|
|
{
|
|
APPBARDATA abd;
|
|
|
|
abd.cbSize = sizeof(APPBARDATA);
|
|
abd.hWnd = _hwnd;
|
|
abd.lParam = (long) NULL;
|
|
if (uMsg == WM_WINDOWPOSCHANGED) {
|
|
TraceMsg(DM_APPBAR, "cwb.WM_WPC: call ABM_WINPOSCHGED");
|
|
SHAppBarMessage(ABM_WINDOWPOSCHANGED, &abd);
|
|
}
|
|
else {
|
|
//if (LOWORD(wParam) != WA_INACTIVE)
|
|
// just do it always, doesn't hurt...
|
|
TraceMsg(DM_APPBAR, "cwb.WM_ACT: call ABM_ACTIVATE");
|
|
SHAppBarMessage(ABM_ACTIVATE, &abd);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
ASSERT(0);
|
|
break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// try to preserve our thinkness
|
|
void CDockingBar::_AppBarOnPosChanged(PAPPBARDATA pabd)
|
|
{
|
|
RECT rcWindow;
|
|
|
|
ASSERT(_eMode == WBM_TOPMOST);
|
|
|
|
GetWindowRect(pabd->hWnd, &rcWindow);
|
|
RectXform(&rcWindow, RX_EDGE|RX_OPPOSE, &rcWindow, NULL, RectGetWH(&rcWindow, _uSide), _uSide, _hMon);
|
|
|
|
_Recalc(); // _MoveSizeHelper(_eMode, _uSide, NULL, NULL, TRUE, TRUE);
|
|
return;
|
|
}
|
|
|
|
/*** _InitPos4 -- initialize edge positions
|
|
* ENTRY/EXIT
|
|
* fCtor TRUE if called from constructor; o.w. FALSE
|
|
*/
|
|
void CDockingBar::_InitPos4(BOOL fCtor)
|
|
{
|
|
RECT rcSite;
|
|
|
|
TraceMsg(DM_PERSIST, "cdb.ip4(fCtor=%d) enter", fCtor);
|
|
|
|
if (fCtor)
|
|
{
|
|
// set some worst-case defaults for the Load(bag) case
|
|
_adEdge[ABE_TOP] = 80;
|
|
_adEdge[ABE_BOTTOM] = 80;
|
|
_adEdge[ABE_LEFT] = 80;
|
|
_adEdge[ABE_RIGHT] = 80;
|
|
|
|
SetRect(&_rcFloat, 10, 10, 310, 310); // FEATURE: todo: NYI
|
|
_hMon = GetPrimaryMonitor();
|
|
}
|
|
else
|
|
{
|
|
// set up semi-reasonable defaults for the InitNew case
|
|
ASSERT(_eInitLoaded == IPS_INITNEW); // not req'd, but expected
|
|
ASSERT(IsWindow(_hwndSite));
|
|
GetWindowRect(_hwndSite, &rcSite);
|
|
|
|
_adEdge[ABE_TOP] = AB_THEIGHT(rcSite);
|
|
_adEdge[ABE_BOTTOM] = AB_BHEIGHT(rcSite);
|
|
_adEdge[ABE_LEFT] = AB_LWIDTH(rcSite);
|
|
_adEdge[ABE_RIGHT] = AB_RWIDTH(rcSite);
|
|
|
|
// FEATURE: (dli) should we ask _hwndSite for it's hmonitor?
|
|
// This current implementation already seems acceptable -justmann
|
|
_hMon = MonitorFromRect(&rcSite, MONITOR_DEFAULTTONULL);
|
|
if (!_hMon)
|
|
{
|
|
POINT ptCenter;
|
|
ptCenter.x = (rcSite.left + rcSite.right) / 2;
|
|
ptCenter.y = (rcSite.top + rcSite.bottom) / 2;
|
|
_hMon = MonitorFromPoint(ptCenter, MONITOR_DEFAULTTONEAREST);
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*** RectXform -- transform RECT
|
|
* ENTRY/EXIT
|
|
* prcOut
|
|
* uRxMask
|
|
* prcIn initial rect
|
|
* prcBound bounding rect specifying min/max dimensions
|
|
* iWH
|
|
* uSide
|
|
* DESCRIPTION
|
|
* RX_EDGE set edgemost side to extreme (0 or max)
|
|
* RX_OPPOSE set opposing side to edge + width
|
|
* RX_ADJACENT set adjacent sides to extremes (0 and max)
|
|
* RX_GETWH get distance to opposing side
|
|
*
|
|
* Two common calls are:
|
|
* ...
|
|
* NOTES
|
|
* Note that rcOut, rcIn, and rcSize can all be the same.
|
|
*/
|
|
int CDockingBar::RectXform(RECT* prcOut, UINT uRxMask,
|
|
const RECT* prcIn, RECT* prcBound, int iWH, UINT uSide, HMONITOR hMon)
|
|
{
|
|
RECT rcDef;
|
|
int iRet = 0;
|
|
BOOL bMirroredWnd=FALSE;
|
|
|
|
if (prcOut != prcIn && prcOut != NULL) {
|
|
ASSERT(prcIn != NULL); // used to do SetRect(prcOut,0,0,0,0)
|
|
CopyRect(prcOut, prcIn);
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if (! (uRxMask & (RX_OPPOSE|RX_GETWH))) {
|
|
ASSERT(iWH == -1);
|
|
iWH = -1; // try to force something to go wrong...
|
|
}
|
|
#endif
|
|
|
|
if (uRxMask & (RX_EDGE|RX_ADJACENT)) {
|
|
if (prcBound == NULL) {
|
|
prcBound = &rcDef;
|
|
ASSERT(hMon);
|
|
GetMonitorRect(hMon, prcBound); // aka GetSystemMetrics(SM_CXSCREEN)
|
|
}
|
|
|
|
#define iXMin (prcBound->left)
|
|
#define iYMin (prcBound->top)
|
|
#define iXMax (prcBound->right);
|
|
#define iYMax (prcBound->bottom);
|
|
}
|
|
|
|
if (uRxMask & (RX_EDGE|RX_OPPOSE|RX_HIDE|RX_GETWH)) {
|
|
|
|
//
|
|
// If docking is happening on a horizontal size, then...
|
|
//
|
|
if ((ABE_LEFT == uSide) || (ABE_RIGHT == uSide)) {
|
|
bMirroredWnd = (IS_WINDOW_RTL_MIRRORED(GetParent(_hwnd)));
|
|
}
|
|
|
|
switch (uSide) {
|
|
case ABE_TOP:
|
|
if (prcOut)
|
|
{
|
|
if (uRxMask & RX_EDGE)
|
|
prcOut->top = iYMin;
|
|
if (uRxMask & RX_OPPOSE)
|
|
prcOut->bottom = prcOut->top + iWH;
|
|
if (uRxMask & RX_HIDE)
|
|
MoveRect(prcOut, prcOut->left, prcOut->top - iWH + CXYHIDE(uSide));
|
|
}
|
|
if (uRxMask & RX_GETWH)
|
|
iRet = RECTHEIGHT(*prcIn);
|
|
|
|
break;
|
|
case ABE_BOTTOM:
|
|
if (prcOut)
|
|
{
|
|
if (uRxMask & RX_EDGE)
|
|
prcOut->bottom = iYMax;
|
|
if (uRxMask & RX_OPPOSE)
|
|
prcOut->top = prcOut->bottom - iWH;
|
|
if (uRxMask & RX_HIDE)
|
|
MoveRect(prcOut, prcOut->left, prcOut->bottom - CXYHIDE(uSide));
|
|
}
|
|
if (uRxMask & RX_GETWH)
|
|
iRet = RECTHEIGHT(*prcIn);
|
|
|
|
break;
|
|
case ABE_LEFT:
|
|
if (prcOut)
|
|
{
|
|
if (uRxMask & RX_EDGE)
|
|
prcOut->left = iXMin;
|
|
if (uRxMask & RX_OPPOSE) {
|
|
//
|
|
// If the parent of this docked window is mirrored, then it is placed and
|
|
// aligned to the right. [samera]
|
|
//
|
|
if (bMirroredWnd)
|
|
prcOut->left = prcOut->right - iWH;
|
|
else
|
|
prcOut->right = prcOut->left + iWH;
|
|
}
|
|
if (uRxMask & RX_HIDE)
|
|
MoveRect(prcOut, prcOut->left - iWH + CXYHIDE(uSide), prcOut->top);
|
|
}
|
|
if (uRxMask & RX_GETWH)
|
|
iRet = RECTWIDTH(*prcIn);
|
|
|
|
break;
|
|
case ABE_RIGHT:
|
|
if (prcOut)
|
|
{
|
|
if (uRxMask & RX_EDGE)
|
|
prcOut->right = iXMax;
|
|
if (uRxMask & RX_OPPOSE) {
|
|
//
|
|
// If the parent of this docked window is mirrored, then it is placed and
|
|
// aligned to the left
|
|
//
|
|
if (bMirroredWnd)
|
|
prcOut->right = prcOut->left + iWH;
|
|
else
|
|
prcOut->left = prcOut->right - iWH;
|
|
}
|
|
if (uRxMask & RX_HIDE)
|
|
MoveRect(prcOut, prcOut->right - CXYHIDE(uSide), prcOut->top);
|
|
}
|
|
if (uRxMask & RX_GETWH)
|
|
iRet = RECTWIDTH(*prcIn);
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((uRxMask & RX_ADJACENT) && prcOut)
|
|
{
|
|
if (uSide == ABE_LEFT || uSide == ABE_RIGHT) {
|
|
prcOut->top = iYMin;
|
|
prcOut->bottom = iYMax;
|
|
}
|
|
else {
|
|
prcOut->left = iXMin;
|
|
prcOut->right = iXMax;
|
|
}
|
|
}
|
|
|
|
return iRet;
|
|
}
|
|
|
|
//*** _ProtoRect -- create best-guess proto rect for specified location
|
|
//
|
|
void CDockingBar::_ProtoRect(RECT* prcOut, UINT eModeNew, UINT uSideNew, HMONITOR hMonNew, POINT* ptXY)
|
|
{
|
|
if (ISWBM_FLOAT(eModeNew))
|
|
{
|
|
// start at last position/size, and move to new left-top if requested
|
|
CopyRect(prcOut, &_rcFloat);
|
|
if (ptXY != NULL)
|
|
MoveRect(prcOut, ptXY->x, ptXY->y);
|
|
|
|
// if we're (e.g.) floating on the far right and the display shrinks,
|
|
// we need to reposition ourselves
|
|
// PERF: wish we could do this at resolution-change time but
|
|
// WM_DISPLAYCHANGE comes in too early (before our [pseudo] parent
|
|
// has changed).
|
|
if (eModeNew == WBM_FLOATING)
|
|
{
|
|
// make sure we're still visible
|
|
// FEATURE todo: multi-mon
|
|
RECT rcTmp;
|
|
|
|
_GetBorderRect(hMonNew, &rcTmp);
|
|
|
|
if (prcOut->left > rcTmp.right || prcOut->top > rcTmp.bottom)
|
|
{
|
|
// WARNING note we don't explicitly account for other toolbars
|
|
// this may be a bug (though other apps seem to behave the
|
|
// same way)
|
|
MoveRect(prcOut,
|
|
prcOut->left <= rcTmp.right ? prcOut->left :
|
|
rcTmp.right - CXFLOAT(),
|
|
prcOut->top <= rcTmp.bottom ? prcOut->top :
|
|
rcTmp.bottom - CYFLOAT()
|
|
);
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ASSERT(ISABE_DOCK(uSideNew));
|
|
if (_fCanHide && ISWBM_HIDEABLE(eModeNew))
|
|
{
|
|
// force a 'tiny' rectangle
|
|
// (WARNING prcBound==NULL bogus for XXX_HIDEALL && XXX_BROWSEROWNED)
|
|
RectXform(prcOut, RX_EDGE|RX_OPPOSE|RX_ADJACENT|(_fHiding ? RX_HIDE : 0),
|
|
prcOut, NULL, _adEdge[uSideNew], uSideNew, hMonNew);
|
|
}
|
|
else
|
|
{
|
|
// get current rect, adjust opposing side per request
|
|
_GetBorderRect(hMonNew, prcOut);
|
|
RectXform(prcOut, RX_OPPOSE, prcOut, NULL, _adEdge[uSideNew], uSideNew, hMonNew);
|
|
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
//*** _NegotiateRect --
|
|
// NOTES
|
|
// will only return an approximate result in the non-commit case.
|
|
//
|
|
void CDockingBar::_NegotiateRect(UINT eModeNew, UINT uSideNew, HMONITOR hMonNew,
|
|
RECT* rcReq, BOOL fCommit)
|
|
{
|
|
switch (eModeNew) {
|
|
case WBM_TOPMOST:
|
|
APPBARDATA abd;
|
|
abd.cbSize = sizeof(APPBARDATA);
|
|
abd.hWnd = _hwnd;
|
|
|
|
AppBarQuerySetPos(rcReq, uSideNew, hMonNew, rcReq, &abd, fCommit);
|
|
if (_fCanHide)
|
|
{
|
|
// we did a query to adjust the adjacent sides (e.g. so we don't
|
|
// cover up the 'start' menu when we unhide). however that may
|
|
// have also moved us in from the edge, which we don't want.
|
|
// so snap back to edge.
|
|
int iWH;
|
|
|
|
iWH = RectGetWH(rcReq, uSideNew);
|
|
RectXform(rcReq, RX_EDGE|RX_OPPOSE|(_fHiding ? RX_HIDE : 0), rcReq, NULL, iWH, uSideNew, hMonNew);
|
|
}
|
|
goto Ldefault;
|
|
|
|
default:
|
|
Ldefault:
|
|
// everyone else just gives us what we want
|
|
|
|
// but, we need to free up border
|
|
_NegotiateBorderRect(NULL, NULL, fCommit); // free up space
|
|
|
|
break;
|
|
|
|
case WBM_BOTTOMMOST:
|
|
case WBM_BBOTTOMMOST:
|
|
_NegotiateBorderRect(rcReq, rcReq, fCommit);
|
|
break;
|
|
}
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
void CDockingBar::_AppBarCallback(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
APPBARDATA abd;
|
|
|
|
ASSERT(_eMode == WBM_TOPMOST);
|
|
|
|
abd.cbSize = sizeof(abd);
|
|
abd.hWnd = hwnd;
|
|
|
|
switch (wParam) {
|
|
case ABN_FULLSCREENAPP:
|
|
// when 1st app goes full-screen, move ourselves to BOTTOM;
|
|
// when last app leaves full-screen, move ourselves back
|
|
// todo: FullScreen(flg)
|
|
{
|
|
BOOL fIsTopmost = BOOLIFY(GetWindowLong(_hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST);
|
|
if (!lParam != fIsTopmost)
|
|
{
|
|
SetWindowPos(hwnd,
|
|
lParam ? HWND_BOTTOM : HWND_TOPMOST,
|
|
0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ABN_POSCHANGED:
|
|
TraceMsg(DM_APPBAR, "cwb.abcb: ABN_POSCHANGED");
|
|
|
|
// note that we do this even if _fHiding. while we want
|
|
// to stay snapped to the edge as a 'tiny' rect, a change
|
|
// in someone else *should* effect our adjacent edges.
|
|
//
|
|
// FEATURE: unfortunately this currently causes 'jiggle' of a hidden
|
|
// guy when another appbar moves (due to a SlideWindow of a 0-width
|
|
// hidden guy and a rounded-up 8-pixel wide guy). when we switch
|
|
// to explorer's new offscreen hide that should go away.
|
|
_AppBarOnPosChanged(&abd);
|
|
break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
HRESULT CDockingBar::QueryInterface(REFIID riid, LPVOID * ppvObj)
|
|
{
|
|
static const QITAB qit[] = {
|
|
QITABENT(CDockingBar, IDockingWindow), // IID_IDockingWindow
|
|
QITABENT(CDockingBar, IObjectWithSite), // IID_IObjectWithSite
|
|
QITABENT(CDockingBar, IPersistStreamInit), // IID_IPersistStreamInit
|
|
QITABENTMULTI(CDockingBar, IPersistStream, IPersistStreamInit), // IID_IPersistStream
|
|
QITABENTMULTI(CDockingBar, IPersist, IPersistStreamInit), // IID_IPersist
|
|
QITABENT(CDockingBar, IPersistPropertyBag), // IID_IPersistPropertyBag
|
|
{ 0 },
|
|
};
|
|
|
|
HRESULT hres = QISearch(this, qit, riid, ppvObj);
|
|
|
|
if (FAILED(hres))
|
|
hres = SUPERCLASS::QueryInterface(riid, ppvObj);
|
|
|
|
return hres;
|
|
}
|
|
|
|
HRESULT CDockingBar::QueryService(REFGUID guidService,
|
|
REFIID riid, void **ppvObj)
|
|
{
|
|
HRESULT hres = E_FAIL;
|
|
*ppvObj = NULL; // assume error
|
|
|
|
// Block IID_ITargetFrame, so we don't look like a frame of the
|
|
// window we are attached to
|
|
if (IsEqualGUID(guidService, IID_ITargetFrame)
|
|
||IsEqualGUID(guidService, IID_ITargetFrame2)) {
|
|
return hres;
|
|
}
|
|
|
|
hres = SUPERCLASS::QueryService(guidService, riid, ppvObj);
|
|
if (FAILED(hres))
|
|
{
|
|
const GUID* pguidService = &guidService;
|
|
|
|
if (IsEqualGUID(guidService, SID_SProxyBrowser)) {
|
|
pguidService = &SID_STopLevelBrowser;
|
|
}
|
|
|
|
if (_ptbSite) {
|
|
hres = IUnknown_QueryService(_ptbSite, *pguidService, riid, ppvObj);
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
void CDockingBar::_GrowShrinkBar(DWORD dwDirection)
|
|
{
|
|
RECT rcNew, rcOld;
|
|
int iMin;
|
|
|
|
iMin = GetSystemMetrics(SM_CXVSCROLL) * 4;
|
|
|
|
GetWindowRect(_hwnd, &rcNew);
|
|
rcOld = rcNew;
|
|
|
|
switch(_uSide)
|
|
{
|
|
case ABE_TOP:
|
|
if (VK_DOWN == dwDirection)
|
|
rcNew.bottom += GetSystemMetrics(SM_CYFRAME);
|
|
|
|
if (VK_UP == dwDirection)
|
|
rcNew.bottom -= GetSystemMetrics(SM_CYFRAME);
|
|
|
|
if ((rcNew.bottom - rcNew.top) < iMin)
|
|
rcNew.bottom = rcNew.top + iMin;
|
|
break;
|
|
|
|
case ABE_BOTTOM:
|
|
if (VK_UP == dwDirection)
|
|
rcNew.top -= GetSystemMetrics(SM_CYFRAME);
|
|
|
|
if (VK_DOWN == dwDirection)
|
|
rcNew.top += GetSystemMetrics(SM_CYFRAME);
|
|
|
|
if ((rcNew.bottom - rcNew.top) < iMin)
|
|
rcNew.top = rcNew.bottom - iMin;
|
|
break;
|
|
|
|
case ABE_LEFT:
|
|
if (VK_RIGHT == dwDirection)
|
|
rcNew.right += GetSystemMetrics(SM_CXFRAME);
|
|
|
|
if (VK_LEFT == dwDirection)
|
|
rcNew.right -= GetSystemMetrics(SM_CXFRAME);
|
|
|
|
if ((rcNew.right - rcNew.left) < iMin)
|
|
rcNew.right = rcNew.left + iMin;
|
|
break;
|
|
|
|
case ABE_RIGHT:
|
|
if (VK_LEFT == dwDirection)
|
|
rcNew.left -= GetSystemMetrics(SM_CXFRAME);
|
|
|
|
if (VK_RIGHT == dwDirection)
|
|
rcNew.left += GetSystemMetrics(SM_CXFRAME);
|
|
|
|
if ((rcNew.right - rcNew.left) < iMin)
|
|
rcNew.left = rcNew.right - iMin;
|
|
break;
|
|
|
|
}
|
|
|
|
if (!EqualRect(&rcOld, &rcNew))
|
|
{
|
|
int iWH;
|
|
RECT rcScreen;
|
|
|
|
// don't let the new size get > MonitorRect/2
|
|
GetMonitorRect(_hMon, &rcScreen); // aka GetSystemMetrics(SM_CXSCREEN)
|
|
iWH = RECTGETWH(_uSide, &rcScreen);
|
|
iWH /= 2;
|
|
if (RECTGETWH(_uSide, &rcNew) > iWH)
|
|
{
|
|
RectXform(&rcNew, RX_OPPOSE, &rcNew, NULL, iWH, _uSide, NULL);
|
|
}
|
|
|
|
_SetVRect(&rcNew);
|
|
_Recalc();
|
|
}
|
|
}
|
|
|
|
|
|
//*** CDockingBar::IOleCommandTarget::* {
|
|
|
|
HRESULT CDockingBar::Exec(const GUID *pguidCmdGroup,
|
|
DWORD nCmdID, DWORD nCmdexecopt,
|
|
VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
|
|
{
|
|
if (pguidCmdGroup == NULL) {
|
|
/*NOTHING*/
|
|
}
|
|
else if (IsEqualGUID(CGID_ShellDocView, *pguidCmdGroup)) {
|
|
switch (nCmdID) {
|
|
case SHDVID_RAISE:
|
|
ASSERT(pvarargIn && pvarargIn->vt == VT_I4);
|
|
if (pvarargIn->vt == VT_I4 && pvarargIn->lVal != DTRF_QUERY) {
|
|
_OnRaise(pvarargIn->lVal);
|
|
return S_OK;
|
|
}
|
|
break; // e.g. DTRF_QUERY
|
|
default:
|
|
// note that this means we may get OLECMDERR_E_UNKNOWNGROUP
|
|
// rather than OLECMDERR_E_NOTSUPPORTED for unhandled guys...
|
|
break;
|
|
}
|
|
}
|
|
else if (IsEqualGUID(CGID_DeskBarClient, *pguidCmdGroup))
|
|
{
|
|
if (DBCID_RESIZE == nCmdID)
|
|
{
|
|
_GrowShrinkBar(nCmdexecopt);
|
|
return S_OK;
|
|
}
|
|
}
|
|
|
|
return SUPERCLASS::Exec(pguidCmdGroup, nCmdID, nCmdexecopt,
|
|
pvarargIn, pvarargOut);
|
|
}
|
|
// }
|
|
|
|
//*** CDockingBar::IDockingWindow::* {
|
|
//
|
|
|
|
HRESULT CDockingBar::SetSite(IUnknown* punkSite)
|
|
{
|
|
ATOMICRELEASE(_ptbSite);
|
|
|
|
if (punkSite)
|
|
{
|
|
HRESULT hresT;
|
|
hresT = punkSite->QueryInterface(IID_IDockingWindowSite, (LPVOID*)&_ptbSite);
|
|
|
|
IUnknown_GetWindow(punkSite, &_hwndSite);
|
|
|
|
//
|
|
// Check if we are under the desktop browser or not and set
|
|
// the initial state correctly. (Always on top for Desktop)
|
|
//
|
|
IUnknown* punkT;
|
|
hresT = punkSite->QueryInterface(SID_SShellDesktop, (LPVOID*)&punkT);
|
|
if (SUCCEEDED(hresT))
|
|
{
|
|
_fDesktop = TRUE;
|
|
punkT->Release();
|
|
}
|
|
|
|
if (!_fInitSited)
|
|
{
|
|
if (!_eInitLoaded)
|
|
{
|
|
// if we haven't initialized, do it now.
|
|
InitNew();
|
|
_eMode = WBM_BOTTOMMOST;
|
|
}
|
|
|
|
ASSERT(_eInitLoaded);
|
|
if (_eInitLoaded == IPS_INITNEW)
|
|
{
|
|
_InitPos4(FALSE);
|
|
_eMode = _fDesktop ? WBM_TOPMOST : WBM_BBOTTOMMOST;
|
|
}
|
|
}
|
|
ASSERT(_eMode != WBM_NIL);
|
|
// WARNING actually we could also be owned floating...
|
|
ASSERT(ISWBM_DESKTOP() == _fDesktop);
|
|
ASSERT(_fDesktop || _eMode == WBM_BBOTTOMMOST);
|
|
ASSERT(ISWBM_DESKTOP() == _fDesktop);
|
|
ASSERT(ISWBM_DESKTOP() || _eMode == WBM_BBOTTOMMOST);
|
|
}
|
|
|
|
_fInitSited = TRUE; // done w/ 1st-time init
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDockingBar::ShowDW(BOOL fShow)
|
|
{
|
|
fShow = BOOLIFY(fShow); // so comparisons and assigns to bitfields work
|
|
|
|
// we used to early out if BOOLIFY(_fShow) == fShow.
|
|
// however we now count on ShowDW(TRUE) to force a refresh
|
|
// (e.g. when screen resolution changes CBB::v_ShowHideChildWindows
|
|
// calls us)
|
|
if (BOOLIFY(_fShow) == fShow)
|
|
return S_OK;
|
|
|
|
_fShow = fShow;
|
|
|
|
if (!_fInitShowed)
|
|
{
|
|
ASSERT(_fInitSited && _eInitLoaded);
|
|
_Initialize();
|
|
ASSERT(_fInitShowed);
|
|
}
|
|
|
|
if (_fShow)
|
|
{
|
|
// FEATURE: switch to using _ChangeTopMost, it already does this...
|
|
// Tell itself to resize.
|
|
|
|
// use _MoveSizeHelper (not just _NegotiateBorderRect) since we might
|
|
// actually be moving to a new position...
|
|
_Recalc(); // _MoveSizeHelper(_eMode, _uSide, NULL, NULL, TRUE, TRUE);
|
|
// _NegotiateBorderRect(NULL, NULL, FALSE)
|
|
|
|
if (_pDBC)
|
|
_pDBC->UIActivateDBC(DBC_SHOW);
|
|
|
|
// nt5:148444: SW_SHOWNA (vs. SW_SHOW) so we don't unhide on create
|
|
// this fix will cause a new bug -- newly created bars don't have
|
|
// focus (e.g. drag a band to floating, the new floating bar won't
|
|
// have focus) -- but that should be the lesser of evils.
|
|
//ShowWindow(_hwnd, ISWBM_FLOAT(_eMode) ? SW_SHOWNORMAL : SW_SHOWNA);
|
|
ShowWindow(_hwnd, SW_SHOWNA);
|
|
_OnSize();
|
|
}
|
|
else
|
|
{
|
|
ShowWindow(_hwnd, SW_HIDE);
|
|
if (EVAL(_pDBC))
|
|
_pDBC->UIActivateDBC(DBC_SHOWOBSCURE);
|
|
UIActivateIO(FALSE, NULL);
|
|
|
|
// Tell itself to resize.
|
|
|
|
// don't call MoveSizeHelper here since it will do (e.g.)
|
|
// negotiation, which will cause flicker and do destructive stuff.
|
|
//_Recalc(); //_MoveSizeHelper(_eMode, _uSide, NULL, NULL, TRUE, TRUE);
|
|
_NegotiateBorderRect(NULL, NULL, TRUE); // hide=>0 border space
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDockingBar::ResizeBorderDW(LPCRECT prcBorder,
|
|
IUnknown* punkToolbarSite, BOOL fReserved)
|
|
{
|
|
_Recalc(); // _MoveSizeHelper(_eMode, _uSide, NULL, NULL, TRUE, TRUE);
|
|
return S_OK; // FEATURE _NegotiateBorderRect()?
|
|
}
|
|
|
|
HRESULT CDockingBar::_NegotiateBorderRect(RECT* prcOut, RECT* prcReq, BOOL fCommit)
|
|
{
|
|
UINT eMode, uSide;
|
|
HMONITOR hMon;
|
|
int iWH;
|
|
|
|
// FEATURE: should be params like MSH etc.
|
|
eMode = ((_fDragging == DRAG_MOVE) ? _eModePending : _eMode);
|
|
uSide = ((_fDragging == DRAG_MOVE) ? _uSidePending : _uSide);
|
|
hMon = ((_fDragging == DRAG_MOVE) ? _hMonPending : _hMon);
|
|
|
|
if (prcOut != prcReq && prcOut != NULL && prcReq != NULL)
|
|
CopyRect(prcOut, prcReq);
|
|
|
|
if (_ptbSite) {
|
|
RECT rcRequest = { 0, 0, 0, 0 };
|
|
|
|
if (_fShow && ISWBM_BOTTOM(eMode)) {
|
|
|
|
if (prcReq)
|
|
{
|
|
iWH = RectGetWH(prcReq, uSide);
|
|
ASSERT(iWH == _adEdge[uSide]);
|
|
if ((!_fCanHide) && uSide != ABE_NIL)
|
|
((int*)&rcRequest)[uSide] = iWH;
|
|
}
|
|
|
|
if (_fTheater) {
|
|
// MOVE TO CBROWSERBAR
|
|
|
|
|
|
// we override the left that we request from the browser, but
|
|
// we need to notify theater what the user has requested for the expaneded width
|
|
VARIANTARG v = { 0 };
|
|
v.vt = VT_I4;
|
|
v.lVal = rcRequest.left;
|
|
IUnknown_Exec(_ptbSite, &CGID_Theater, THID_SETBROWSERBARWIDTH, 0, &v, NULL);
|
|
_iTheaterWidth = v.lVal;
|
|
|
|
// if we're in theater mode, we can only be on the left and we only grab left border
|
|
ASSERT(uSide == ABE_LEFT);
|
|
|
|
// if we're in autohide mode, we request no space
|
|
if (!_fNoAutoHide)
|
|
rcRequest.left = 0;
|
|
|
|
// END MOVE TO CBROWSERBAR
|
|
}
|
|
}
|
|
|
|
// FEATURE: leave alone (at 0 from HideRegister?) if _fHiding==HIDE_AUTO
|
|
HMONITOR hMonOld = _SetNewMonitor(hMon);
|
|
|
|
_ptbSite->RequestBorderSpaceDW(SAFECAST(this, IDockingWindow*), &rcRequest);
|
|
if (fCommit) {
|
|
RECT rcMirRequest;
|
|
LPRECT lprcRequest = &rcRequest;
|
|
|
|
if (IS_WINDOW_RTL_MIRRORED(_hwnd) &&
|
|
!IS_WINDOW_RTL_MIRRORED(GetParent(_hwnd))) {
|
|
// Swap left and right.
|
|
rcMirRequest.left = rcRequest.right;
|
|
rcMirRequest.right = rcRequest.left;
|
|
rcMirRequest.top = rcRequest.top;
|
|
rcMirRequest.bottom = rcRequest.bottom;
|
|
|
|
lprcRequest = &rcMirRequest;
|
|
}
|
|
_ptbSite->SetBorderSpaceDW(SAFECAST(this, IDockingWindow*), lprcRequest);
|
|
}
|
|
|
|
if (_fShow && ISWBM_BOTTOM(eMode) && !_fTheater) {
|
|
// were'd we end up (as a real rect not just a size)?
|
|
// start w/ our full border area, then apply negotiated width.
|
|
// however that may have also moved us in from the edge, which
|
|
// we don't want if we're autohide, so snap back to edge if so.
|
|
_ptbSite->GetBorderDW(SAFECAST(this, IDockingWindow*), prcOut);
|
|
|
|
// aka ClientToScreen
|
|
if (prcOut)
|
|
MapWindowPoints(_hwndSite, HWND_DESKTOP, (POINT*) prcOut, 2);
|
|
|
|
if ((!_fCanHide) && uSide != ABE_NIL)
|
|
iWH = ((int*)&rcRequest)[uSide];
|
|
|
|
RectXform(prcOut, (_fCanHide ? RX_EDGE : 0)|RX_OPPOSE|(_fHiding ? RX_HIDE : 0), prcOut, NULL, iWH, uSide, hMon);
|
|
}
|
|
|
|
if (hMonOld)
|
|
_SetNewMonitor(hMonOld);
|
|
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
// }
|
|
|
|
//*** CDockingBar::IPersistStream*::* {
|
|
//
|
|
|
|
HRESULT CDockingBar::IsDirty(void)
|
|
{
|
|
return S_FALSE; // Never be dirty
|
|
}
|
|
|
|
//
|
|
// Persisted CDockingBar
|
|
//
|
|
struct SWebBar
|
|
{
|
|
DWORD cbSize;
|
|
DWORD cbVersion;
|
|
UINT uSide : 3;
|
|
UINT fWantHide :1;
|
|
INT adEdge[4]; // FEATURE: wordsize dependent
|
|
RECT rcFloat;
|
|
POINT ptSiteCenter; // Center of the docking site -- in case of multiple docking sites
|
|
|
|
UINT eMode;
|
|
UINT fAlwaysOnTop;
|
|
|
|
RECT rcChild;
|
|
};
|
|
|
|
#define SWB_VERSION 8
|
|
|
|
HRESULT CDockingBar::Load(IStream *pstm)
|
|
{
|
|
SWebBar swb = {0};
|
|
ULONG cbRead;
|
|
|
|
TraceMsg(DM_PERSIST, "cwb.l enter(this=%x pstm=%x) tell()=%x", this, pstm, DbStreamTell(pstm));
|
|
|
|
ASSERT(!_eInitLoaded);
|
|
HRESULT hres = pstm->Read(&swb, SIZEOF(swb), &cbRead);
|
|
#ifdef DEBUG
|
|
// just in case we toast ourselves (offscreen or something)...
|
|
static BOOL fNoPersist = FALSE;
|
|
if (fNoPersist)
|
|
hres = E_FAIL;
|
|
#endif
|
|
|
|
if (hres==S_OK && cbRead==SIZEOF(swb)) {
|
|
// REARCHITECT: this is not forward compatible!
|
|
if (swb.cbSize==SIZEOF(SWebBar) && swb.cbVersion==SWB_VERSION) {
|
|
|
|
_eMode = swb.eMode;
|
|
_uSide = swb.uSide;
|
|
_hMon = MonitorFromPoint(swb.ptSiteCenter, MONITOR_DEFAULTTONEAREST);
|
|
// don't call _SetModeSide, _MoveSizeHelper, etc. until *after* _Initialize
|
|
_fWantHide = swb.fWantHide;
|
|
memcpy(_adEdge, swb.adEdge, SIZEOF(_adEdge));
|
|
_rcFloat = swb.rcFloat;
|
|
_NotifyModeChange(0);
|
|
|
|
// child (e.g. bandsite)
|
|
ASSERT(_pDBC != NULL);
|
|
if (_pDBC != NULL) {
|
|
// require IPersistStreamInit?
|
|
IPersistStream *ppstm;
|
|
hres = _pDBC->QueryInterface(IID_IPersistStream, (LPVOID*)&ppstm);
|
|
if (SUCCEEDED(hres)) {
|
|
|
|
// set the child size first because initialization layout might depend on it
|
|
SetWindowPos(_hwndChild, 0,
|
|
swb.rcChild.left, swb.rcChild.top, RECTWIDTH(swb.rcChild), RECTHEIGHT(swb.rcChild),
|
|
SWP_NOACTIVATE|SWP_NOZORDER);
|
|
|
|
ppstm->Load(pstm);
|
|
ppstm->Release();
|
|
}
|
|
}
|
|
|
|
_eInitLoaded = IPS_LOAD; // what if OLFS of bands fails?
|
|
TraceMsg(DM_PERSIST, "CDockingBar::Load succeeded");
|
|
} else {
|
|
TraceMsg(DM_ERROR, "CWB::Load failed swb.cbSize==SIZEOF(SWebBar) && swb.cbVersion==SWB_VERSION");
|
|
hres = E_FAIL;
|
|
}
|
|
} else {
|
|
TraceMsg(DM_ERROR, "CWB::Load failed (hres==S_OK && cbRead==SIZEOF(_adEdge)");
|
|
hres = E_FAIL;
|
|
}
|
|
TraceMsg(DM_PERSIST, "cwb.l leave tell()=%x", DbStreamTell(pstm));
|
|
return hres;
|
|
}
|
|
|
|
HRESULT CDockingBar::Save(IStream *pstm, BOOL fClearDirty)
|
|
{
|
|
HRESULT hres;
|
|
SWebBar swb = {0};
|
|
RECT rcMonitor;
|
|
|
|
swb.cbSize = SIZEOF(SWebBar);
|
|
swb.cbVersion = SWB_VERSION;
|
|
swb.uSide = _uSide;
|
|
swb.eMode = _eMode;
|
|
swb.fWantHide = _fWantHide;
|
|
memcpy(swb.adEdge, _adEdge, SIZEOF(_adEdge));
|
|
swb.rcFloat = _rcFloat;
|
|
GetWindowRect(_hwndChild, &swb.rcChild);
|
|
MapWindowRect(HWND_DESKTOP, _hwnd, &swb.rcChild);
|
|
|
|
ASSERT(_hMon);
|
|
GetMonitorRect(_hMon, &rcMonitor);
|
|
swb.ptSiteCenter.x = (rcMonitor.left + rcMonitor.right) / 2;
|
|
swb.ptSiteCenter.y = (rcMonitor.top + rcMonitor.bottom) / 2;
|
|
|
|
hres = pstm->Write(&swb, SIZEOF(swb), NULL);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
IPersistStream* ppstm;
|
|
hres = _pDBC->QueryInterface(IID_IPersistStream, (LPVOID*)&ppstm);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
hres = ppstm->Save(pstm, TRUE);
|
|
ppstm->Release();
|
|
}
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
HRESULT CDockingBar::GetSizeMax(ULARGE_INTEGER *pcbSize)
|
|
{
|
|
ULARGE_INTEGER cbMax = { SIZEOF(SWebBar), 0 };
|
|
*pcbSize = cbMax;
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDockingBar::InitNew(void)
|
|
{
|
|
ASSERT(!_eInitLoaded);
|
|
_eInitLoaded = IPS_INITNEW;
|
|
TraceMsg(DM_PERSIST, "CDockingBar::InitNew called");
|
|
|
|
// can't call _InitPos4 until set site in SetSite
|
|
// don't call _SetModeSide, _MoveSizeHelper, etc. until *after* _Initialize
|
|
|
|
// derived class (e.g. CBrowserBarApp) does the _Populate...
|
|
|
|
// on first creation, before bands are added, but the bandsite IS created, we need to notify the bandsite of the new position
|
|
_NotifyModeChange(0);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDockingBar::Load(IPropertyBag *pPropBag, IErrorLog *pErrorLog)
|
|
{
|
|
ASSERT(!_eInitLoaded);
|
|
|
|
_eInitLoaded = IPS_LOADBAG;
|
|
|
|
// TODO: We'll read following properties.
|
|
//
|
|
// URL = "..."
|
|
// Mode = 0 - TopMost, 1 - Bottom, 2 - Undocked
|
|
// Side = 0 - Right, 1 - Top, 2 - Left, 3 - Bottom
|
|
// Left/Right/Top/Bottom = Initial docked size
|
|
//
|
|
|
|
|
|
UINT uSide;
|
|
UINT eMode = _eMode;
|
|
|
|
if (WBM_NIL == eMode)
|
|
eMode = WBM_BOTTOMMOST;
|
|
|
|
eMode = PropBag_ReadInt4(pPropBag, L"Mode", eMode);
|
|
uSide = PropBag_ReadInt4(pPropBag, L"Side", _uSide);
|
|
_adEdge[ABE_LEFT] = PropBag_ReadInt4(pPropBag, L"Left", _adEdge[ABE_LEFT]);
|
|
_adEdge[ABE_RIGHT] = PropBag_ReadInt4(pPropBag, L"Right", _adEdge[ABE_RIGHT]);
|
|
_adEdge[ABE_TOP] = PropBag_ReadInt4(pPropBag, L"Top", _adEdge[ABE_TOP]);
|
|
_adEdge[ABE_BOTTOM] = PropBag_ReadInt4(pPropBag, L"Bottom", _adEdge[ABE_BOTTOM]);
|
|
|
|
int x = PropBag_ReadInt4(pPropBag, L"X", _rcFloat.left);
|
|
int y = PropBag_ReadInt4(pPropBag, L"Y", _rcFloat.top);
|
|
OffsetRect(&_rcFloat, x - _rcFloat.left, y - _rcFloat.top);
|
|
|
|
int cx = PropBag_ReadInt4(pPropBag, L"CX", RECTWIDTH(_rcFloat));
|
|
int cy = PropBag_ReadInt4(pPropBag, L"CY", RECTHEIGHT(_rcFloat));
|
|
_rcFloat.right = _rcFloat.left + cx;
|
|
_rcFloat.bottom = _rcFloat.top + cy;
|
|
|
|
// set up vars for eventual CDockingBar::_Initialize call
|
|
ASSERT(!CDB_INITED());
|
|
_eMode = eMode;
|
|
_uSide = uSide;
|
|
|
|
POINT pt = {x, y};
|
|
// (dli) compute the new hMonitor
|
|
_hMon = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
|
|
|
|
// don't call _SetModeSide, _MoveSizeHelper, etc. until *after* _Initialize
|
|
|
|
// derived class (e.g. CBrowserBarApp) does the _Populate...
|
|
|
|
// on first creation, before bands are added, but the bandsite IS created, we need to notify the bandsite of the new position
|
|
_NotifyModeChange(0);
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CDockingBar::Save(IPropertyBag *pPropBag, BOOL fClearDirty, BOOL fSaveAllProperties)
|
|
{
|
|
// We don't need to support this for now.
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
// }
|
|
|
|
//*** CDockingBar::IDocHostUIHandler::* {
|
|
//
|
|
|
|
HRESULT CDockingBar::ShowContextMenu(DWORD dwID,
|
|
POINT* ppt,
|
|
IUnknown* pcmdtReserved,
|
|
IDispatch* pdispReserved)
|
|
{
|
|
if (dwID==0) {
|
|
TraceMsg(DM_MENU, "cdb.scm: intercept");
|
|
return _TrackPopupMenu(ppt);
|
|
}
|
|
return S_FALSE;
|
|
}
|
|
|
|
|
|
// }
|
|
|
|
|
|
void CDockingBar::_SetModeSide(UINT eMode, UINT uSide, HMONITOR hMonNew, BOOL fNoMerge)
|
|
{
|
|
_ChangeTopMost(eMode);
|
|
_uSide = uSide;
|
|
_hMon = hMonNew;
|
|
_SetNewMonitor(hMonNew);
|
|
}
|
|
|
|
|
|
// *** IInputObjectSite methods ***
|
|
|
|
HRESULT CDockingBar::OnFocusChangeIS(IUnknown *punk, BOOL fSetFocus)
|
|
{
|
|
return IUnknown_OnFocusChangeIS(_ptbSite, SAFECAST(this, IInputObject*), fSetFocus);
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////
|
|
//
|
|
// A deskbar property bag
|
|
//////
|
|
HRESULT CDockingBarPropertyBag_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
|
|
{
|
|
CDockingBarPropertyBag* p = new CDockingBarPropertyBag();
|
|
if (p != NULL)
|
|
{
|
|
*ppunk = SAFECAST(p, IPropertyBag*);
|
|
return S_OK;
|
|
}
|
|
|
|
*ppunk = NULL;
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
ULONG CDockingBarPropertyBag::AddRef()
|
|
{
|
|
_cRef++;
|
|
return _cRef;
|
|
}
|
|
|
|
ULONG CDockingBarPropertyBag::Release()
|
|
{
|
|
ASSERT(_cRef > 0);
|
|
_cRef--;
|
|
|
|
if (_cRef > 0)
|
|
return _cRef;
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
HRESULT CDockingBarPropertyBag::QueryInterface(REFIID riid, LPVOID * ppvObj)
|
|
{
|
|
static const QITAB qit[] = {
|
|
QITABENT(CDockingBarPropertyBag, IPropertyBag), // IID_IPropertyBag
|
|
QITABENT(CDockingBarPropertyBag, IDockingBarPropertyBagInit), // IID_IDockingBarPropertyBagInit
|
|
{ 0 },
|
|
};
|
|
|
|
return QISearch(this, qit, riid, ppvObj);
|
|
}
|
|
|
|
|
|
const WCHAR * const c_szPropNames[] = {
|
|
L"Side",
|
|
L"Mode",
|
|
L"Left",
|
|
L"Top",
|
|
L"Right",
|
|
L"Bottom",
|
|
L"Deleteable",
|
|
L"X",
|
|
L"Y",
|
|
L"CX",
|
|
L"CY"
|
|
};
|
|
|
|
|
|
HRESULT CDockingBarPropertyBag::Read(
|
|
/* [in] */ LPCOLESTR pszPropName,
|
|
/* [out][in] */ VARIANT *pVar,
|
|
/* [in] */ IErrorLog *pErrorLog)
|
|
{
|
|
int epropdata;
|
|
|
|
for (epropdata = 0; epropdata < (int)PROPDATA_COUNT; epropdata++) {
|
|
if (!StrCmpW(pszPropName, c_szPropNames[epropdata])) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (epropdata < PROPDATA_COUNT &&
|
|
_props[epropdata]._fSet) {
|
|
pVar->lVal = _props[epropdata]._dwData;
|
|
pVar->vt = VT_I4;
|
|
return S_OK;
|
|
}
|
|
|
|
return E_FAIL;
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
//*** DbCheckWindow --
|
|
// NOTES
|
|
// FEATURE: Its a bad idea, why break working code, but here is the suggestion:
|
|
// nuke the 'hwndClient' param and just use GetParent (but what
|
|
// about 'owned' windows, does GetParent give the correct answer?)
|
|
BOOL DbCheckWindow(HWND hwnd, RECT *prcExp, HWND hwndClient)
|
|
{
|
|
RECT rcAct;
|
|
|
|
GetWindowRect(hwnd, &rcAct);
|
|
hwndClient = GetParent(hwnd); // nuke this param
|
|
if (hwndClient != NULL) {
|
|
// aka ClientToScreen
|
|
MapWindowPoints(HWND_DESKTOP, hwndClient, (POINT*) &rcAct, 2);
|
|
}
|
|
if (!EqualRect(&rcAct, prcExp)) {
|
|
TraceMsg(DM_TRACE,
|
|
"cwb.dbcw: !EqualRect rcAct=(%d,%d,%d,%d) (%dx%d) rcExp=(%d,%d,%d,%d) (%dx%d) hwndClient=0x%x",
|
|
rcAct.left, rcAct.top, rcAct.right, rcAct.bottom,
|
|
RECTWIDTH(rcAct), RECTHEIGHT(rcAct),
|
|
prcExp->left, prcExp->top, prcExp->right, prcExp->bottom,
|
|
RECTWIDTH(*prcExp), RECTHEIGHT(*prcExp),
|
|
hwndClient);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
//*** DbStreamTell -- get position in stream (low part only)
|
|
//
|
|
unsigned long DbStreamTell(IStream *pstm)
|
|
{
|
|
if (pstm == 0)
|
|
return (unsigned long) -1;
|
|
|
|
ULARGE_INTEGER liEnd;
|
|
|
|
pstm->Seek(c_li0, STREAM_SEEK_CUR, &liEnd);
|
|
if (liEnd.HighPart != 0)
|
|
TraceMsg(DM_TRACE, "DbStreamTell: hi!=0");
|
|
return liEnd.LowPart;
|
|
}
|
|
|
|
//*** DbMaskToMneStr -- pretty-print a bit mask in mnemonic form
|
|
// ENTRY/EXIT
|
|
// uMask bit mask
|
|
// szMne mnemonics, sz[0] for bit 0 .. sz[N] for highest bit
|
|
// return ptr to *static* buffer
|
|
// NOTES
|
|
// n.b.: non-reentrant!!!
|
|
TCHAR *DbMaskToMneStr(UINT uMask, TCHAR *szMnemonics)
|
|
{
|
|
static TCHAR buf[33]; // FEATURE: non-reentrant!!!
|
|
TCHAR *p;
|
|
|
|
p = &buf[ARRAYSIZE(buf) - 1]; // point at EOS
|
|
ASSERT(*p == '\0');
|
|
for (;;) {
|
|
if (*szMnemonics == 0) {
|
|
ASSERT(uMask == 0);
|
|
break;
|
|
}
|
|
|
|
--p;
|
|
*p = (uMask & 1) ? *szMnemonics : TEXT('-');
|
|
|
|
++szMnemonics;
|
|
uMask >>= 1;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
#endif
|