#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