// DeskMovr.cpp : Implementation of CDeskMovr #include "stdafx.h" #pragma hdrstop #include "deskmovr.h" #define DEFAULT_INTERVAL 200 // check every 1/5th of a second. #define DEFAULT_ENABLED TRUE #define DETECT_TIMER_ID 2323 #ifndef SHDOC401_DLL #define ANIMATE_TIMER_ID 2324 #define ANIMATE_TIMER_INTERVAL (60*1000) #endif #ifdef DEBUG const static TCHAR sz_DM1[] = TEXT("no dragable part"); const static TCHAR sz_DM2[] = TEXT("caption menu button"); const static TCHAR sz_DM3[] = TEXT("caption close button"); const static TCHAR sz_DM4[] = TEXT("move the component"); const static TCHAR sz_DM5[] = TEXT("resize width and height from bottom right corner"); const static TCHAR sz_DM6[] = TEXT("resize width and height from top left corner"); const static TCHAR sz_DM7[] = TEXT("resize width and height from top right corner"); const static TCHAR sz_DM8[] = TEXT("resize width and height from bottom left corner"); const static TCHAR sz_DM9[] = TEXT("resize from the top edge"); const static TCHAR sz_DM10[] = TEXT("resize from the bottom edge"); const static TCHAR sz_DM11[] = TEXT("resize from the left edge"); const static TCHAR sz_DM12[] = TEXT("resize from the right edge"); const LPCTSTR g_szDragModeStr[] = { sz_DM1, sz_DM2, sz_DM3, sz_DM4, sz_DM5, sz_DM6, sz_DM7, sz_DM8, sz_DM9, sz_DM10, sz_DM11, sz_DM12 }; #endif // DEBUG // Globals used to track cdeskmovr instances. Useful for optimizing the // detection code so we can turn off the timer when the mouse is not over our // window. We track the cdeskmovr instances only on the first thread that instantiates // us to keep the code simple, this should be the active desktop case. #define CDESKMOVR_TRACK_COUNT 16 // 2 is what we use now for the active desktop, but we need // extra slots in the array due to the fact that a new instance // is created before the old one is destroyed during refresh. // Make the array large to handle nested refreshes! HHOOK g_hMouseHook; HHOOK g_hKeyboardHook; DWORD g_dwHookThreadId; #ifndef SHDOC401_DLL BOOL g_fAnimTimer = FALSE; #endif typedef CDeskMovr *PDM; PDM g_apDM[CDESKMOVR_TRACK_COUNT]; BOOL CombView_EnableAnimations(BOOL fEnable); DWORD g_fIgnoreTimers = 0; #define IGNORE_CONTEXTMENU_UP 0x0001 #define IGNORE_CAPTURE_SET 0x0002 #define GET_SKIP_COUNT (2 * ((GetDoubleClickTime() / m_lInterval) + 1)) MAKE_CONST_BSTR(s_sstrNameMember, L"name"); MAKE_CONST_BSTR(s_sstrHidden, L"hidden"); MAKE_CONST_BSTR(s_sstrVisible, L"visible"); MAKE_CONST_BSTR(s_sstrResizeableMember, L"resizeable"); // These were declared in shellprv.h, so use DEFINE instead of MAKE DEFINE_CONST_BSTR(s_sstrIDMember, L"id"); DEFINE_CONST_BSTR(s_sstrSubSRCMember, L"subscribed_url"); DEFINE_CONST_BSTR(s_sstrSRCMember, L"src"); #define CAPTION_ONLY (m_ItemState & (IS_FULLSCREEN | IS_SPLIT)) #define ISNORMAL (m_ItemState & IS_NORMAL) #define ISFULLSCREEN (m_ItemState & IS_FULLSCREEN) #define ISSPLIT (m_ItemState & IS_SPLIT) #define CAPTIONBAR_HOTAREA(cyDefaultCaption, cyCurrentCaption) (((cyCurrentCaption == 0) && CAPTION_ONLY) ? (cyDefaultCaption / 2) : 3 * cyDefaultCaption) #define MAX_ID_LENGTH 5 void ObtainSavedStateForElem( IHTMLElement *pielem, LPCOMPSTATEINFO pCompState, BOOL fRestoredState); DWORD g_aDMtoCSPushed[] = {0, CS_MENUPUSHED, CS_CLOSEPUSHED, CS_RESTOREPUSHED, CS_FULLSCREENPUSHED, CS_SPLITPUSHED}; DWORD g_aDMtoCSTracked[] = {0, CS_MENUTRACKED, CS_CLOSETRACKED, CS_RESTORETRACKED, CS_FULLSCREENTRACKED, CS_SPLITTRACKED}; DWORD g_aDMDCfromDragMode[] = {0, DMDC_MENU, DMDC_CLOSE, DMDC_RESTORE, DMDC_FULLSCREEN, DMDC_SPLIT}; #define PUSHED(dm) (g_aDMtoCSPushed[(dm)]) #define TRACKED(dm) (g_aDMtoCSTracked[(dm)]) #define DMDCFROMDM(dm) (g_aDMDCfromDragMode[(dm)]) // Trident will flash if you change the zindex, even if it's to the same index, // so we prevent the no-op call. HRESULT SafeZOrderSet(IHTMLStyle * pistyle, LONG lNewZIndex) { HRESULT hr = S_OK; VARIANT varZ; ASSERT(pistyle); pistyle->get_zIndex(&varZ); // Does the component need to be moved to the top? if ((VT_I4 != varZ.vt) || (varZ.lVal != lNewZIndex)) { // Yes. varZ.vt = VT_I4; varZ.lVal = lNewZIndex; hr = pistyle->put_zIndex(varZ); } return hr; } // Keyboard hook is installed when first instance of deskmovr is created. Used to implement the keyboard // interface for accessing the deskmovr control. Hook is removed when there are no more deskmovr's // being tracked. LRESULT CALLBACK DeskMovr_KeyboardHook(int nCode, WPARAM wParam, LPARAM lParam) { LRESULT lRes; BOOL fHaveMover = FALSE; for (int i = 0; i < CDESKMOVR_TRACK_COUNT; i++) { if (g_apDM[i]) { g_apDM[i]->OnKeyboardHook(wParam, lParam); fHaveMover = TRUE; } } lRes = CallNextHookEx(g_hKeyboardHook, nCode, wParam, lParam); if (!fHaveMover) { UnhookWindowsHookEx(g_hKeyboardHook); g_hKeyboardHook = NULL; } return lRes; } // Helper function used to track cdeskmovr intances so that we can turn off the // timer if the mouse leaves our window. void TrackMover(PDM pdm, BOOL fAdd) { if (!g_dwHookThreadId) g_dwHookThreadId = GetCurrentThreadId(); if (!g_hKeyboardHook && fAdd) g_hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD, DeskMovr_KeyboardHook, NULL, GetCurrentThreadId()); if (!fAdd || (g_dwHookThreadId == GetCurrentThreadId())) { int i = 0; PDM pdmFind = fAdd ? NULL : pdm; PDM pdmAssign = fAdd ? pdm : NULL; while (i < CDESKMOVR_TRACK_COUNT) { if (g_apDM[i] == pdmFind) { g_apDM[i] = pdmAssign; break; } i++; } // If we ever fail to track a mover then we'll never be able to optimize // again. Shouldn't happen in practice for the case we care about. if (fAdd && (i >= CDESKMOVR_TRACK_COUNT)) g_dwHookThreadId = 0xffffffff; ASSERT(!fAdd || (i < CDESKMOVR_TRACK_COUNT)); } } #if 0 void AnimateToTray(HWND hwnd, LONG lLeft, LONG lTop, LONG lWidth, LONG lHeight) { HWND hwndTray; if (hwndTray = FindWindow(c_szTrayClass, NULL)) { RECT rcComp, rcTray; SetRect(&rcComp, lLeft, lTop, lLeft + lWidth, lTop + lHeight); MapWindowPoints(hwnd, NULL, (LPPOINT)&rcComp, 2); GetWindowRect(hwndTray, &rcTray); if ((rcTray.right - rcTray.left) > (rcTray.bottom - rcTray.top)) rcTray.left = rcTray.right - GetSystemMetrics(SM_CXSMICON); else rcTray.top = rcTray.bottom - GetSystemMetrics(SM_CYSMICON); DrawAnimatedRects(hwnd, IDANI_CAPTION, (CONST RECT *)&rcComp, (CONST RECT *)&rcTray); } } #endif void AnimateComponent(HWND hwnd, LONG lLeftS, LONG lTopS, LONG lWidthS, LONG lHeightS, LONG lLeftD, LONG lTopD, LONG lWidthD, LONG lHeightD) { RECT rcSource, rcDest; SetRect(&rcSource, lLeftS, lTopS, lLeftS + lWidthS, lTopS + lHeightS); SetRect(&rcDest, lLeftD, lTopD, lLeftD + lWidthD, lTopD + lHeightD); // 98/10/02 vtan: Removed the mapping as not required. // MapWindowPoints(hwnd, NULL, (LPPOINT)&rcSource, 2); // MapWindowPoints(hwnd, NULL, (LPPOINT)&rcDest, 2); DrawAnimatedRects(hwnd, IDANI_CAPTION, (CONST RECT *)&rcSource, (CONST RECT *)&rcDest); } // Hook is installed when we detect we can turn our tracking timer off. The first // time we get a mouse event in the hook we reactivate all the movers and unhook // ourself. LRESULT CALLBACK DeskMovr_MouseHook(int nCode, WPARAM wParam, LPARAM lParam) { LRESULT lRes; #ifndef SHDOC401_DLL // If we are getting mouse messages then a portion of the window must be // visible so enable animations. CombView_EnableAnimations(TRUE); #endif for (int i = 0; i < CDESKMOVR_TRACK_COUNT; i++) { if (g_apDM[i]) g_apDM[i]->SmartActivateMovr(ERROR_SUCCESS); } lRes = CallNextHookEx(g_hMouseHook, nCode, wParam, lParam); UnhookWindowsHookEx(g_hMouseHook); g_hMouseHook = NULL; return lRes; } ///////////////////////////////////////////////////////////////////////////// // CDeskMovr CDeskMovr::CDeskMovr() : m_TimerWnd(_T("STATIC"), this, 1) { TraceMsg(TF_CUSTOM2, "CDeskMovr::CDeskMovr()"); m_fEnabled = DEFAULT_ENABLED; m_lInterval = DEFAULT_INTERVAL; m_cxSMBorder = GetSystemMetrics(SM_CXBORDER); m_cySMBorder = GetSystemMetrics(SM_CYBORDER); m_cxBorder = m_cxSMBorder; m_cyBorder = m_cySMBorder; m_cyCaption = 0; m_dmCur = dmNull; m_dmTrack = dmNull; m_hcursor = LoadCursor(NULL, IDC_ARROW); m_CaptionState = 0; m_hwndParent; m_fTimer = FALSE; m_fCaptured = FALSE; m_uiTimerID = DETECT_TIMER_ID; m_pistyle = NULL; m_pistyleTarget = NULL; m_pielemTarget = NULL; m_iSrcTarget = -1; m_bstrTargetName = NULL; m_dx = m_dy = 0; m_top = m_left = m_width = m_height = 0; // Tell ATL that we don't want to be Windowless m_bWindowOnly = TRUE; // Track this instance TrackMover(this, TRUE); } CDeskMovr::~CDeskMovr(void) { TraceMsg(TF_CUSTOM2, "CDeskMovr::~CDeskMovr() m_bstrTargetName=%ls.", GEN_DEBUGSTRW(m_bstrTargetName)); // clean up, detach from events, if necessary. DeactivateMovr(TRUE); if ( m_bstrTargetName != NULL ) SysFreeString( m_bstrTargetName ); TrackMover(this, FALSE); } HRESULT CDeskMovr::SmartActivateMovr(HRESULT hrPropagate) { if ((FALSE == m_nFreezeEvents) && m_fEnabled && !m_pielemTarget) { #ifndef SHDOC401_DLL // Release our animation timer if it exists and create our regular one if (g_fAnimTimer && (m_uiTimerID == ANIMATE_TIMER_ID)) { m_TimerWnd.KillTimer(m_uiTimerID); m_uiTimerID = DETECT_TIMER_ID; g_fAnimTimer = FALSE; m_fTimer = m_TimerWnd.SetTimer(m_uiTimerID, m_lInterval) != 0; } #endif hrPropagate = ActivateMovr(); if (!EVAL(SUCCEEDED(hrPropagate))) DeactivateMovr(FALSE); // Clean up mess. } return hrPropagate; } HRESULT CDeskMovr::FreezeEvents(BOOL fFreeze) { HRESULT hr = IOleControlImpl::FreezeEvents(fFreeze); TraceMsg(TF_CUSTOM1, "CDeskMovr::FreezeEvents(fFreeze=%lx) m_nFreezeEvents=%lx; m_fEnabled=%lx, m_bstrTargetName=%ls", (DWORD)fFreeze, m_nFreezeEvents, m_fEnabled, GEN_DEBUGSTRW(m_bstrTargetName)); m_nFreezeEvents = fFreeze; if (fFreeze) DeactivateMovr(FALSE); else hr = SmartActivateMovr(hr); return hr; } HRESULT CDeskMovr::Load(LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog) { HRESULT hr; VARIANT var; ATLTRACE(_T("IPersistPropertyBagImpl::Load\n")); var.vt = VT_BOOL; hr = pPropBag->Read(L"Enabled", &var, NULL); if (SUCCEEDED(hr) && var.vt==VT_BOOL) { m_fEnabled = var.boolVal; } var.vt = VT_I4; hr = pPropBag->Read(L"Interval", &var, NULL); if (SUCCEEDED(hr) && var.vt==VT_I4) { m_lInterval = var.lVal; } var.vt = VT_BSTR; var.bstrVal = NULL; hr = pPropBag->Read(L"TargetName", &var, NULL); if (SUCCEEDED(hr) && var.vt==VT_BSTR) { m_bstrTargetName = var.bstrVal; } // This PARAM determines whether the control will be in the // windowed or windowless "layer" of the Trident layout. var.vt = VT_BOOL; hr = pPropBag->Read(L"WindowOnly", &var, NULL); if (SUCCEEDED(hr) && var.vt==VT_BOOL) { m_bWindowOnly = var.boolVal; } hr = _GetZOrderSlot(&m_zIndexTop, TRUE); ASSERT(SUCCEEDED(hr)); hr = _GetZOrderSlot(&m_zIndexBottom, FALSE); ASSERT(SUCCEEDED(hr)); return hr; } HRESULT CDeskMovr::Save(LPPROPERTYBAG pPropBag, BOOL fClearDirty, BOOL fSaveAllProperties) { return E_NOTIMPL; } BOOL CDeskMovr::GetCaptionButtonRect(DragMode dm, LPRECT lprc) { BOOL fSuccess; *lprc = m_rectCaption; switch (dm) { case dmClose: lprc->left = m_rectCaption.right - (m_cyCaption + m_cxSMBorder); fSuccess = (lprc->left > (m_rectCaption.left + m_cyCaption)); break; case dmMenu: lprc->right = lprc->left + (m_cyCaption + m_cxSMBorder); fSuccess = (m_rectCaption.right > (m_rectCaption.left + m_cyCaption)); break; case dmRestore: if (ISNORMAL) return FALSE; else if (ISSPLIT) goto CalcSplit; else if (ISFULLSCREEN) goto CalcFullScreen; ASSERT(FALSE); case dmSplit: if (ISSPLIT || !m_fCanResizeX || !m_fCanResizeY) { return FALSE; } CalcSplit: lprc->left = m_rectCaption.right - (m_cyCaption + m_cxSMBorder); OffsetRect(lprc, -(lprc->right - lprc->left), 0); fSuccess = (lprc->left > (m_rectCaption.left + 2 * m_cyCaption)); break; case dmFullScreen: if (ISFULLSCREEN || !m_fCanResizeX || !m_fCanResizeY) { return FALSE; } CalcFullScreen: lprc->left = m_rectCaption.right - (m_cyCaption + m_cxSMBorder); OffsetRect(lprc, -((lprc->right - lprc->left) * 2 - 2 * m_cxSMBorder), 0); fSuccess = (lprc->left > (m_rectCaption.left + 2 * m_cyCaption)); break; default: ASSERT(FALSE); fSuccess = FALSE; break; } // Shrink the button within the caption and position it adjacent to the border if (fSuccess) { OffsetRect(lprc, ((dm == dmClose) ? m_cxSMBorder : -m_cxSMBorder), -m_cySMBorder); InflateRect(lprc, -m_cxSMBorder, -m_cySMBorder); lprc->bottom -= m_cySMBorder; // Take an extra border off the bottom } return fSuccess; } void CDeskMovr::DrawCaptionButton(HDC hdc, LPRECT lprc, UINT uType, UINT uState, BOOL fErase) { RECT rcT; HRGN hrgnWnd, hrgnRect; int iRet; if (fErase) FillRect(hdc, lprc, (HBRUSH)(COLOR_3DFACE + 1)); rcT = *lprc; InflateRect(&rcT, -2*m_cxSMBorder, -2*m_cySMBorder); switch (uType) { case DMDC_CLOSE: uType = DFC_CAPTION; goto Draw; case DMDC_MENU: uType = DFC_SCROLL; Draw: // We need to clip the border of the outer edge in order to get the drawing effect we // want here... if (hrgnWnd = CreateRectRgn(0, 0, 0, 0)) { if ((iRet = GetClipRgn(hdc, hrgnWnd)) != -1) { if (hrgnRect = CreateRectRgnIndirect(&rcT)) { SelectClipRgn(hdc, hrgnRect); DeleteObject(hrgnRect); } } } DrawFrameControl(hdc, lprc, uType, uState); if (hrgnWnd != NULL) { SelectClipRgn(hdc, (iRet == 1) ? hrgnWnd : NULL); } if (hrgnWnd) DeleteObject(hrgnWnd); break; case DMDC_FULLSCREEN: case DMDC_SPLIT: case DMDC_RESTORE: { if (uState & DFCS_PUSHED) OffsetRect(&rcT, 1, 1); DrawEdge(hdc, &rcT, BDR_OUTER, BF_FLAT | BF_MONO | BF_RECT); #ifndef OLD_CODE switch (uType) { case DMDC_RESTORE: rcT.right = rcT.left + (rcT.right - rcT.left) * 3 / 4; rcT.bottom = rcT.top + (rcT.bottom - rcT.top) * 3 / 4; rcT.left += (rcT.right - rcT.left) / 2 + 1; rcT.top += (rcT.bottom - rcT.top) / 2 + 1; FillRect(hdc, &rcT, (HBRUSH)(COLOR_WINDOWFRAME + 1)); break; case DMDC_SPLIT: rcT.top += m_cySMBorder; rcT.left += (rcT.right - rcT.left) * 3 / 10; DrawEdge(hdc, &rcT, BDR_OUTER, BF_FLAT | BF_MONO | BF_TOP | BF_LEFT); break; case DMDC_FULLSCREEN: rcT.top += m_cySMBorder; DrawEdge(hdc, &rcT, BDR_OUTER, BF_FLAT | BF_MONO | BF_TOP); break; } #else switch (uType) { case DMDC_RESTORE: rcT.right = rcT.left + (rcT.right - rcT.left) * 3 / 4; rcT.bottom = rcT.top + (rcT.bottom - rcT.top) * 3 / 4; rcT.left += (rcT.right - rcT.left) / 2 + 1; rcT.top += (rcT.bottom - rcT.top) / 2 + 1; break; case DMDC_SPLIT: rcT.left += (rcT.right - rcT.left) * 3 / 10; break; case DMDC_FULLSCREEN: break; } FillRect(hdc, &rcT, (HBRUSH)(COLOR_WINDOWFRAME + 1)); #endif } break; } // DFCS_FLAT means no border to us if (!(uState & DFCS_FLAT)) DrawEdge(hdc, lprc, ((uState & DFCS_PUSHED) ? BDR_SUNKENOUTER : BDR_RAISEDINNER), BF_RECT); } void CDeskMovr::DrawCaption(HDC hdc, UINT uDrawFlags, int x, int y) { RECT rect; UINT uState; DragMode dmT; // Draw the caption if (uDrawFlags & DMDC_CAPTION) { rect = m_rectCaption; OffsetRect(&rect, x, y); FillRect( hdc, &rect, (HBRUSH)(COLOR_3DFACE + 1) ); } // Draw the caption frame controls for (dmT = dmMenu; dmT < dmMove; dmT = (DragMode)((int)dmT + 1)) { if ((uDrawFlags & DMDCFROMDM(dmT)) && GetCaptionButtonRect(dmT, &rect)) { if (dmT == dmMenu) uState = DFCS_SCROLLDOWN; else if (dmT == dmClose) uState = DFCS_CAPTIONCLOSE; else uState = 0; if ((dmT == dmClose) && SHRestricted(REST_NOCLOSEDESKCOMP)) uState |= DFCS_INACTIVE | DFCS_FLAT; else { if (m_CaptionState & PUSHED(dmT)) uState |= DFCS_PUSHED; if (!(m_CaptionState & (TRACKED(dmT) | PUSHED(dmT)))) uState |= DFCS_FLAT; } OffsetRect(&rect, x, y); DrawCaptionButton(hdc, &rect, DMDCFROMDM(dmT), uState, !(uDrawFlags & DMDC_CAPTION)); } } } HRESULT CDeskMovr::OnDraw(ATL_DRAWINFO& di) { RECT& rc = *(RECT*)di.prcBounds; RECT r; HBRUSH hbrush = (HBRUSH)(COLOR_3DFACE + 1); // top edge r.left = rc.left; r.top = rc.top; r.right = rc.right; r.bottom = rc.top + m_cyBorder; FillRect( di.hdcDraw, &r, hbrush ); // left edge r.top = rc.top + m_cyBorder; r.right = rc.left + m_cxBorder; r.bottom = rc.bottom - m_cyBorder; FillRect( di.hdcDraw, &r, hbrush ); // right edge r.right = rc.right; r.left = rc.right - m_cxBorder; FillRect( di.hdcDraw, &r, hbrush ); // bottom edge r.left = rc.left; r.top = rc.bottom - m_cyBorder; r.right = rc.right; r.bottom = rc.bottom; FillRect( di.hdcDraw, &r, hbrush ); if ( m_cyCaption != 0 ) { DrawCaption(di.hdcDraw, DMDC_ALL, rc.left, rc.top); } return S_OK; } HRESULT CDeskMovr::GetParentWindow(void) { HRESULT hr = S_OK; if (!m_hwndParent) { if (m_spInPlaceSite) hr = m_spInPlaceSite->GetWindow(&m_hwndParent); else { IOleInPlaceSiteWindowless * poipsw; ASSERT(m_spClientSite); if (m_spClientSite && SUCCEEDED(hr = m_spClientSite->QueryInterface(IID_IOleInPlaceSiteWindowless, (void **)&poipsw))) { hr = poipsw->GetWindow(&m_hwndParent); poipsw->Release(); } } if (!m_hwndParent) hr = S_FALSE; // We failed to get it. } return hr; } void CDeskMovr::DeactivateMovr(BOOL fDestroy) { TraceMsg(TF_CUSTOM2, "CDeskMovr::DeactivateMovr() m_fTimer=%lx, m_bstrTargetName=%ls", m_fTimer, GEN_DEBUGSTRW(m_bstrTargetName)); if (fDestroy || (m_uiTimerID == DETECT_TIMER_ID)) { if (m_fTimer) { m_TimerWnd.KillTimer(m_uiTimerID); m_fTimer = FALSE; } if (m_TimerWnd.m_hWnd) m_TimerWnd.DestroyWindow(); #ifndef SHDOC401_DLL if (m_uiTimerID == ANIMATE_TIMER_ID) g_fAnimTimer = FALSE; #endif } // DismissSelfNow(); ATOMICRELEASE( m_pistyle ); ATOMICRELEASE( m_pistyleTarget ); ATOMICRELEASE( m_pielemTarget ); _ChangeCapture(FALSE); } HRESULT CDeskMovr::ActivateMovr() { HRESULT hr; // flush out old interface pointers DeactivateMovr(FALSE); TraceMsg(TF_CUSTOM2, "CDeskMovr::ActivateMovr() m_fTimer=%lx, m_bstrTargetName=%ls", m_fTimer, GEN_DEBUGSTRW(m_bstrTargetName)); if (m_fEnabled) { if (SUCCEEDED(hr = GetOurStyle())) { if ((m_bstrTargetName != NULL) && (m_lInterval > 0)) { if (!m_TimerWnd.m_hWnd) { // create a new timer. RECT rc = {0, 0, 0 , 0}; // We attempt to get our parent HWND (m_hwndParent) now. // If we fail (and we will sometimes), then we will get it later when // we need it and Trident is then ready. GetParentWindow(); m_TimerWnd.Create(NULL, rc, _T("Timer"), WS_POPUP); } if (!m_fTimer) m_fTimer = m_TimerWnd.SetTimer(m_uiTimerID, m_lInterval) != 0; } else { #ifdef HIDE_ALL_HANDLES hr = S_FALSE; #else hr = E_FAIL; #endif } } } else { hr = E_FAIL; } return hr; } HRESULT CDeskMovr::GetOurStyle(void) { HRESULT hr; IOleControlSite *pictlsite = 0; IDispatch *pidisp = 0; // Reach up to get our extender, who is the custodian of our element style if (m_spClientSite && EVAL(SUCCEEDED(hr = m_spClientSite->QueryInterface(IID_IOleControlSite, (LPVOID*)&pictlsite))) && EVAL(SUCCEEDED(hr = pictlsite->GetExtendedControl(&pidisp)))) { DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0}; VARIANT var; VariantInit( &var ); // Alas, all we have is IDispatch on our extender, so we'll have to use Invoke to get // the style object... hr = pidisp->Invoke( DISPID_IHTMLELEMENT_STYLE, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &dispparamsNoArgs, &var, NULL, NULL ); if ( SUCCEEDED(hr) ) { if ( var.vt == VT_DISPATCH ) hr = var.pdispVal->QueryInterface( IID_IHTMLStyle, (LPVOID*)&m_pistyle ); else hr = E_FAIL; // Try VariantChangeType????? VariantClear( &var ); } } ATOMICRELEASE( pictlsite ); ATOMICRELEASE( pidisp ); return hr; } void CDeskMovr::UpdateCaption(UINT uDrawFlags) { HDC hdc; int x = 0, y = 0; if (m_bWndLess) { if (!m_spInPlaceSite || !SUCCEEDED(m_spInPlaceSite->GetDC(NULL, 0, &hdc))) return; } else { hdc = ::GetDC(m_hWnd); } _MapPoints(&x, &y); DrawCaption(hdc, uDrawFlags, -x, -y); if (m_bWndLess) { m_spInPlaceSite->ReleaseDC(hdc); } else { ::ReleaseDC(m_hWnd, hdc); } } void CDeskMovr::CheckCaptionState(int x, int y) { DragMode dm, dmT; UINT uDrawFlags = 0; _MapPoints (&x, &y); POINT pt = { x, y }; if (m_fCaptured) dm = dmNull; else dm = DragModeFromPoint( pt ); if (dm >= dmMenu && dm < dmMove) { if (!(m_CaptionState & (PUSHED(dm) | TRACKED(dm)))) { m_CaptionState |= TRACKED(dm); uDrawFlags |= DMDCFROMDM(dm); } } for (dmT = dmMenu; dmT < dmMove; dmT = (DragMode)((int)dmT + 1)) { if (dm != dmT && (m_CaptionState & (PUSHED(dmT) | TRACKED(dmT)))) { m_CaptionState &= ~(PUSHED(dmT) | TRACKED(dmT)); uDrawFlags |= DMDCFROMDM(dmT); } } if (uDrawFlags) UpdateCaption(uDrawFlags); } //=--------------------------------------------------------------------------= // CDeskMovr::DoMouseDown [instance method] //=--------------------------------------------------------------------------= // Respond to mouse down messages in our control. Initiate move/resize. // // Parameters: // int - [in] mouse message key flags // int - [in] mouse x location in control coords // int - [in] mouse y location in control coords // // Output: // // // Notes: BOOL CDeskMovr::HandleNonMoveSize(DragMode dm) { m_dmCur = dm; switch (dm) { case dmMenu: case dmClose: case dmRestore: case dmFullScreen: case dmSplit: if (m_dmCur != dmClose || !SHRestricted(REST_NOCLOSEDESKCOMP)) // Special case for Close, check restriction { m_CaptionState &= ~(TRACKED(m_dmCur)); m_CaptionState |= PUSHED(m_dmCur); UpdateCaption(DMDCFROMDM(m_dmCur)); // Perform the operation on the up-click of the mouse... } if (m_dmCur == dmMenu && EVAL(S_OK == GetParentWindow())) // Special case for Menu, invoke on the down click { _DisplayContextMenu(); } return TRUE; break; default: return FALSE; break; } } LRESULT CDeskMovr::OnMouseDown( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { int x = (short)LOWORD(lParam); int y = (short)HIWORD(lParam); _MapPoints(&x, &y); TraceMsg(TF_CUSTOM2, "CDeskMovr::OnMouseDown() Mouse=<%d,%d>, Inner=<%d,%d,%d,%d>, Caption=<%d,%d,%d,%d>, m_bstrTargetName=%ls", x, y, m_rectInner.left, m_rectInner.top, m_rectInner.right, m_rectInner.bottom, m_rectCaption.left, m_rectCaption.top, m_rectCaption.right, m_rectCaption.bottom, GEN_DEBUGSTRW(m_bstrTargetName)); POINT pt = { x, y }; m_dmCur = DragModeFromPoint( pt ); if (HandleNonMoveSize(m_dmCur)) return 0; switch ( m_dmCur ) { case dmMove: m_dx = -x; m_dy = -y; break; case dmSizeWHBR: m_dx = m_rectInner.right - x; m_dy = m_rectInner.bottom - y; break; case dmSizeWHTL: m_dx = m_rectInner.left - x; m_dy = m_rectInner.top + m_cyCaption - y; break; case dmSizeWHTR: m_dx = m_rectInner.right - x; m_dy = m_rectInner.top + m_cyCaption - y; break; case dmSizeWHBL: m_dx = m_rectInner.left - x; m_dy = m_rectInner.bottom - y; break; case dmSizeTop: m_dx = 0; m_dy = m_rectInner.top + m_cyCaption - y; break; case dmSizeBottom: m_dx = 0; m_dy = m_rectInner.bottom - y; break; case dmSizeLeft: m_dx = m_rectInner.left - x; m_dy = 0; break; case dmSizeRight: m_dx = m_rectInner.right - x; m_dy = 0; break; default: bHandled = FALSE; return 1; } #ifdef DEBUG TraceMsg(TF_CUSTOM2, "CDeskMovr::OnMouseDown() New DragMode=""%s""", g_szDragModeStr[m_dmCur]); #endif // DEBUG // NOTE: (seanf, 1/31/97) temporary defense against 17902. We really // shouldn't ever be in visible and non-targeted at the same time, but // the resize trick we pull in CDeskMovr::ActivateMovr() to get us // in-place active exposes a 1X1 pixel area, just big enough for StanTak // to click on when we don't have a target, which then kills us when // we try to move the non-existent target. if ( m_pielemTarget != NULL ) { _ChangeCapture(TRUE); if (m_fCaptured) { // Move the target to the top and put ourselves just under it VARIANT varZ; m_pistyleTarget->get_zIndex(&varZ); // Does the component need to be moved to the top? if (!CAPTION_ONLY && ((VT_I4 != varZ.vt) || (varZ.lVal != m_zIndexTop))) { // Yes. varZ.vt = VT_I4; varZ.lVal = ++m_zIndexTop; // Move the DeskMover ActiveX Control on top of everything. m_pistyle->put_zIndex(varZ); // Move the Desktop Item on top of the DeskMover varZ.lVal = ++m_zIndexTop; m_pistyleTarget->put_zIndex(varZ); } } #ifdef DEBUG if (!m_fCaptured) TraceMsg(TF_CUSTOM2, "CDeskMovr::OnMouseDown() Unable to get capture, tracking will fail!"); #endif } return 0; } //=--------------------------------------------------------------------------= // CDeskMovr::DoMouseUp [instance method] //=--------------------------------------------------------------------------= // Respond to mouse down messages in our control. Terminate move/resize. // // Parameters: // int - [in] mouse message key flags // int - [in] mouse x location in control coords // int - [in] mouse y location in control coords // UINT - [in] from the DeskMovrParts enum // // Output: // // // Notes: LRESULT CDeskMovr::OnMouseUp( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { if ( m_fCaptured ) { PersistTargetPosition( m_pielemTarget, m_left, m_top, m_width, m_height, m_zIndexTop, FALSE, FALSE, m_ItemState ); _ChangeCapture(FALSE); } else { int x = (short)LOWORD(lParam); int y = (short)HIWORD(lParam); _MapPoints(&x, &y); POINT pt = { x, y }; DragMode dm = DragModeFromPoint( pt ); if ((dm >= dmMenu) && (dm < dmMove) && (m_CaptionState & PUSHED(dm))) { m_CaptionState &= ~(PUSHED(dm)); m_CaptionState |= TRACKED(dm); UpdateCaption(DMDCFROMDM(dm)); switch ( dm ) { case dmClose: // AnimateToTray(m_hwndParent, m_left, m_top, m_width, m_height); IElemCloseDesktopComp(m_pielemTarget); break; case dmRestore: _HandleZoom(IDM_DCCM_RESTORE); break; case dmFullScreen: _HandleZoom(IDM_DCCM_FULLSCREEN); break; case dmSplit: _HandleZoom(IDM_DCCM_SPLIT); break; } if (dm != dmMenu) DismissSelfNow(); } } return 0; } //=--------------------------------------------------------------------------= // CDeskMovrControl::DoMouseMove [instance method] //=--------------------------------------------------------------------------= // Respond to mouse move messages in our control and when moving/sizing. // // Parameters: // // Output: // // // Notes: LRESULT CDeskMovr::OnPaint( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { TraceMsg(TF_CUSTOM2, "CDeskMovr::OnPaint() uMsg=%lx, wParam=%lx, lParam=%lx, m_bstrTargetName=%ls", uMsg, wParam, lParam, GEN_DEBUGSTRW(m_bstrTargetName)); return CComControl::OnPaint( uMsg, wParam, lParam, bHandled ); } //=--------------------------------------------------------------------------= // CDeskMovrControl::DoMouseMove [instance method] //=--------------------------------------------------------------------------= // Respond to mouse move messages in our control and when moving/sizing. // // Parameters: // // Output: // // // Notes: LRESULT CDeskMovr::OnMouseMove( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { CheckCaptionState((short)LOWORD(lParam), (short)HIWORD(lParam)); if (m_fCaptured && EVAL(S_OK == GetParentWindow())) { // Okay, it's a hit on one of our gadgets. // We're only interested in mouse moves and mouse ups if we're in the // process of a drag or resize HRESULT hr; POINT ptDoc; // location in document window coords POINT ptScreen; HWND hwndParent = m_hwndParent; int x = (short)LOWORD(lParam); int y = (short)HIWORD(lParam); ptScreen.x = x; ptScreen.y = y; ptDoc = ptScreen; if ( !m_bWndLess ) ::MapWindowPoints( m_hWnd, hwndParent, &ptDoc, 1 ); if ( m_dmCur == dmMove ) hr = MoveSelfAndTarget( ptDoc.x + m_dx + m_cxBorder, ptDoc.y + m_dy + m_cyBorder + m_cyCaption ); else if ( m_dmCur > dmMove ) hr = SizeSelfAndTarget( ptDoc ); ASSERT(SUCCEEDED(hr)); } // Set m_cSkipTimer so that we delay dismissing the mover... m_cSkipTimer = GET_SKIP_COUNT; return 0; } LRESULT CDeskMovr::OnTimer( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { HRESULT hr; IHTMLElement *pielem; POINT ptCursor; BOOL fDidWork = FALSE; #ifndef SHDOC401_DLL /* * Check our animation timer first. If we are able to disable animations then * blow away the timer. Otherwise reset the timer for 60 seconds and keep on * looking. */ if (wParam == ANIMATE_TIMER_ID) { if (CombView_EnableAnimations(FALSE)) { m_TimerWnd.SetTimer(ANIMATE_TIMER_ID, ANIMATE_TIMER_INTERVAL); } else { m_TimerWnd.KillTimer(m_uiTimerID); m_uiTimerID = DETECT_TIMER_ID; g_fAnimTimer = FALSE; m_fTimer = FALSE; } return 0; } #endif if (!m_fTimer || g_fIgnoreTimers || !GetCursorPos( &ptCursor ) || !m_pistyle) return 0; if (ptCursor.x == m_ptMouseCursor.x && ptCursor.y == m_ptMouseCursor.y) // Mouse stayed still from last time we did a timer so, do nothing return 0; pielem = NULL; if (S_OK == GetParentWindow()) { HWND hwndParent = m_hwndParent; HWND hwndCursor = WindowFromPoint(ptCursor); if ((hwndCursor != hwndParent) && !::IsChild(hwndParent, hwndCursor)) { // The mouse has drifted out of our window, so lose our target, if any if (m_iSrcTarget >= 0) { hr = MoveSelfToTarget( NULL, NULL ); ASSERT(SUCCEEDED(hr)); if (hr != S_FALSE) { fDidWork = TRUE; } } if (GetCurrentThreadId() == g_dwHookThreadId) { #ifndef SHDOC401_DLL // Set ourselves up so we can look to see if our animations can be turned off if (!g_fAnimTimer) { if (m_fTimer) m_TimerWnd.KillTimer(m_uiTimerID); if (g_fAnimTimer = (m_TimerWnd.SetTimer(ANIMATE_TIMER_ID, ANIMATE_TIMER_INTERVAL / 10) != 0)) m_uiTimerID = ANIMATE_TIMER_ID; m_fTimer = g_fAnimTimer; } #endif DismissSelfNow(); DeactivateMovr(FALSE); if (!g_hMouseHook) g_hMouseHook = SetWindowsHookEx(WH_MOUSE, DeskMovr_MouseHook, NULL, GetCurrentThreadId()); } } else if (!(GetDesktopFlags() & COMPONENTS_LOCKED) && SUCCEEDED(hr = _IsInElement(hwndParent, &ptCursor, &pielem))) { // See if we need to do anything based on the element under the mouse pointer hr = _TrackElement(&ptCursor, pielem, &fDidWork); // we're done with this particular interface pointer pielem->Release(); } else if (m_iSrcTarget != -1) { // Check to see if we should expand border to size border width if (TrackCaption ( &ptCursor )) { TrackTarget(NULL); } } } if (!fDidWork) m_ptMouseCursor = ptCursor; return 0; } LRESULT CDeskMovr::OnEraseBkgnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { if (!m_bWndLess) { RECT rc; ::GetClientRect(m_hWnd, &rc); FillRect((HDC)wParam, &rc, (HBRUSH)(COLOR_3DFACE + 1)); } bHandled = TRUE; return 0; } // // DismissSelfNow - Little helper function to dismiss the mover immediately // // Normally dismissal of the mover is desired to be done on a delayed basis. However, // there are situations such as when the user clicks on UI or capture is lost etc. where // it is desirable to dismiss the mover immediately. // void CDeskMovr::DismissSelfNow(void) { HRESULT hr; m_cSkipTimer = 0; hr = MoveSelfToTarget(NULL, NULL); ASSERT(SUCCEEDED(hr) && (hr != S_FALSE)); } LRESULT CDeskMovr::OnCaptureChanged( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled ) { if ( m_fCaptured ) { _ChangeCapture(FALSE); PersistTargetPosition( m_pielemTarget, m_left, m_top, m_width, m_height, m_zIndexTop, FALSE, FALSE, m_ItemState ); DismissSelfNow(); } return 0; } HRESULT CDeskMovr::InPlaceDeactivate(void) { DeactivateMovr(FALSE); TraceMsg(TF_CUSTOM1, "CDeskMovr::InPlaceDeactivate()"); return CComControl::IOleInPlaceObject_InPlaceDeactivate(); } LRESULT CDeskMovr::OnSetCursor( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) { if (EVAL(S_OK == GetParentWindow())) { POINT ptCursor; DragMode dm; GetCursorPos( &ptCursor ); ::ScreenToClient( m_hwndParent, &ptCursor ); // Get ptCursor into deskmovr local coords ptCursor.x -= m_left - (CAPTION_ONLY ? 0 : m_cxBorder); ptCursor.y -= m_top - (CAPTION_ONLY ? 0 : (m_cyBorder + m_cyCaption)); dm = DragModeFromPoint(ptCursor); m_hcursor = CursorFromDragMode(dm); TraceMsg(TF_CUSTOM2, "CDeskMovr::OnSetCursor() Mouse=<%d,%d>, Inner=<%d,%d,%d,%d>, Caption=<%d,%d,%d,%d>, m_bstrTargetName=%ls", ptCursor.x, ptCursor.y, m_rectInner.left, m_rectInner.top, m_rectInner.right, m_rectInner.bottom, m_rectCaption.left, m_rectCaption.top, m_rectCaption.right, m_rectCaption.bottom, GEN_DEBUGSTRW(m_bstrTargetName)); #ifdef DEBUG TraceMsg(TF_CUSTOM2, "CDeskMovr::OnSetCursor() New DragMode=""%s""", g_szDragModeStr[dm]); #endif // DEBUG if (EVAL(m_hcursor != NULL)) SetCursor( m_hcursor ); else bHandled = FALSE; } return !bHandled; } void CDeskMovr::TrackTarget(POINT * pptDoc) { HRESULT hr = S_OK; if ( m_fEnabled && m_pielemTarget != NULL ) { LONG left, top; POINT pt; VARIANT varZ; COMPSTATEINFO CompState; varZ.vt = VT_I4; CLEANUP_ON_FAILURE(hr = CSSOM_TopLeft(m_pielemTarget, &pt)); m_top = pt.y; m_left = pt.x; CLEANUP_ON_FAILURE(hr = m_pielemTarget->get_offsetHeight( &m_height )); CLEANUP_ON_FAILURE(hr = m_pielemTarget->get_offsetWidth( &m_width )); // Hack so we don't get weird painting effect of the window hopping to the new // target with the old target's size. if (!m_bWndLess && m_cyCaption == 0) ::SetWindowPos(m_hWnd, NULL, 0, 0, 0, 0, SWP_NOZORDER | SWP_NOACTIVATE); // Get our rectangle synced with the target (so TrackCaption works properly) SyncRectsToTarget(); // If we discover we want to display the size-border or caption // right now then we need to recalculate our rects. if (pptDoc && TrackCaption(pptDoc)) SyncRectsToTarget(); CLEANUP_ON_FAILURE(hr = m_pistyleTarget->get_zIndex( &varZ )); if (!CAPTION_ONLY || (m_cxBorder == m_cxSMBorder)) --varZ.lVal; else ++varZ.lVal; CLEANUP_ON_FAILURE(hr = SafeZOrderSet(m_pistyle, varZ.lVal)); // NTRAID94268-2000/03/14 (stephstm): If this is hosted in a window that // has scrollbars, we don't correctly add the screen to document // offset when changing the location of the component. // This causes us to drag incorrectly. // 98/10/02 #176729 vtan: Now uses the component left and top to // position the caption. Offset the caption if the component is // not zoomed. If zoomed then just draw over the component. left = m_left; top = m_top; if (!CAPTION_ONLY) { left -= m_cxBorder; top -= m_cyBorder; top -= m_cyCaption; } hr = m_pistyle->put_pixelLeft(left); hr = m_pistyle->put_pixelWidth( m_rectOuter.right ); hr = m_pistyle->put_pixelTop(top); hr = m_pistyle->put_pixelHeight( m_rectOuter.bottom ); hr = m_pistyle->put_visibility((BSTR)s_sstrVisible.wsz); // We need to persist the original state of the item out now if the item's current width/height is -1 // This occurs when we are fitting an image to it's default size, we need to make sure the // original size real values so it works properly. ObtainSavedStateForElem(m_pielemTarget, &CompState, FALSE); if (m_bWndLess && CompState.dwWidth == COMPONENT_DEFAULT_WIDTH && CompState.dwHeight == COMPONENT_DEFAULT_HEIGHT) PersistTargetPosition(m_pielemTarget, m_left, m_top, m_width, m_height, varZ.lVal, FALSE, TRUE, CompState.dwItemState); } CleanUp: ASSERT(SUCCEEDED(hr)); } BOOL CDeskMovr::TrackCaption( POINT *pptDoc ) { int cyCaption, cyCaptionNew; POINT ptMovr; DragMode dmNew; BOOL fRetVal = FALSE; //TraceMsg(TF_CUSTOM2, "CDeskMovr::TrackCaption() Mouse=<%d,%d>", ptMovr.x, ptMovr.y); if (pptDoc) { ptMovr = *pptDoc; // need a hit test of some sort within the deskmovr to control border swelling ptMovr.x -= m_left - m_cxBorder; ptMovr.y -= m_top - (m_cyBorder + m_cyCaption); dmNew = DragModeFromPoint( ptMovr ); cyCaption = GET_CYCAPTION; if (dmNew == dmNull) { BOOL fInner; int iInflate; RECT rc; // Treat something near the size border as a size border hit // so we expand to the size border as the user nears the edge. fInner = PtInRect(&m_rectInner, ptMovr); if (fInner) { rc = m_rectInner; iInflate = -cyCaption; } else { rc = m_rectOuter; iInflate = cyCaption; } InflateRect(&rc, iInflate, iInflate); if (fInner != PtInRect(&rc, ptMovr)) dmNew = dmSizeRight; } if ( (pptDoc->y >= m_top - (m_cyBorder + 2 * m_cyCaption) && pptDoc->y <= (m_top + CAPTIONBAR_HOTAREA(cyCaption, m_cyCaption)) ) ) cyCaptionNew = cyCaption; else cyCaptionNew = 0; } else { cyCaptionNew = GET_CYCAPTION; dmNew = dmSizeRight; } if ( cyCaptionNew != m_cyCaption || (m_dmTrack != dmNew && !((m_dmTrack > dmMove) && (dmNew > dmMove))) ) { m_cyCaption = cyCaptionNew; if (m_cyCaption == 0) m_CaptionState = 0; m_dmTrack = dmNew; fRetVal = TRUE; } else m_cyCaption = cyCaptionNew; return fRetVal; } int CDeskMovr::CountActiveCaptions() { int iCount = 0; if (g_dwHookThreadId == GetCurrentThreadId()) { for (int i = 0; i < CDESKMOVR_TRACK_COUNT; i++) { if (g_apDM[i] && g_apDM[i]->m_pistyleTarget) iCount++; } } return iCount; } HRESULT CDeskMovr::_TrackElement(POINT * ppt, IHTMLElement * pielem, BOOL * fDidWork) { HRESULT hr; IHTMLElement *pTargElem = NULL; LONG iSrcTarget = -1; ASSERT(pielem); if ( FFindTargetElement( pielem, &pTargElem ) ) { hr = pTargElem->get_sourceIndex( &iSrcTarget ); ASSERT(SUCCEEDED(hr)); } // If the m_iSrcTarget isn't the same as the SrcTarget under our cursor, // then we should move on top of it. if ( m_iSrcTarget != iSrcTarget ) { *fDidWork = TRUE; if ((CountActiveCaptions() > 1) && (-1 == iSrcTarget)) m_cSkipTimer = 0; // Yes, we need to move on top of it. hr = MoveSelfToTarget( pTargElem, ppt ); ASSERT(SUCCEEDED(hr)); if (hr != S_FALSE) m_iSrcTarget = iSrcTarget; } else { // No, so that means we already have focus... if (ppt && TrackCaption(ppt)) { TrackTarget(NULL); } } if ( pTargElem != NULL ) { pTargElem->Release(); // MoveSelfToTarget will have secured our reference } hr = (m_iSrcTarget == -1) ? S_FALSE : S_OK; return hr; } //=--------------------------------------------------------------------------= // CDeskMovr::InitAttributes [instance method] //=--------------------------------------------------------------------------= // Finds out if the element is resizeable in X and Y direction and sets the // BITBOOLs accordingly. // // Also determines what state the element is in and sets m_ItemState. // // Parameters: // IHTMLElement* [in] - interface on event source element // // Output: // HRESULT - various. S_OK if operation succeeded. // HRESULT CDeskMovr::InitAttributes(IHTMLElement *pielem) { HRESULT hr; TCHAR szMember[MAX_ID_LENGTH]; ASSERT(pielem); m_fCanResizeX = m_fCanResizeY = FALSE; //Assume "Can't resize! // The resizeable member is not required to be specified, only override defaults if present. if (SUCCEEDED(GetHTMLElementStrMember(pielem, szMember, SIZECHARS(szMember), (BSTR)(s_sstrResizeableMember.wsz)))) { if(StrChr(szMember, TEXT('X'))) m_fCanResizeX = TRUE; if(StrChr(szMember, TEXT('Y'))) m_fCanResizeY = TRUE; } // The ItemState is required, return failure if we fail to find the ID if (SUCCEEDED(hr = GetHTMLElementStrMember(pielem, szMember, SIZECHARS(szMember), (BSTR)(s_sstrIDMember.wsz)))) m_ItemState = GetCurrentState(szMember); return hr; } //=--------------------------------------------------------------------------= // CDeskMovr::MoveSelfToTarget [instance method] //=--------------------------------------------------------------------------= // Handles Trident document events as mouse moves over the desktop. // // Parameters: // IHTMLElement* [in] - interface on event source element // POINT* [in] - location of mouse (to determine if caption should be displayed) // // Output: // HRESULT - various. S_OK if operation succeeded. // HRESULT CDeskMovr::MoveSelfToTarget(IHTMLElement *pielem, POINT * pptDoc) { HRESULT hr = S_OK; TraceMsg(TF_CUSTOM2, "CDeskMovr::MoveSelfToTarget(pielem=%lx) %s, m_bstrTargetName=%ls", pielem, (pielem ? "We are GETTING focus." : "We are LOOSING focus."), GEN_DEBUGSTRW(m_bstrTargetName)); if (!pielem) { // The m_cSkipTimer variable is used to allow the skipping of timer ticks when determining // if the mover should be dismissed. By doing this it gives the user more time and thus // a better chance to manipulate the target if they are prone to drifting the mouse // outside the target by accident. // Check the m_cSkipTimer before dismissing the mover. if (!m_cSkipTimer) { _ChangeCapture(FALSE); if (m_pistyle) hr = m_pistyle->put_visibility((BSTR)s_sstrHidden.wsz); ATOMICRELEASE( m_pistyleTarget ); ATOMICRELEASE( m_pielemTarget ); m_iSrcTarget = -1; } else { m_cSkipTimer--; hr = S_FALSE; } // These are actions we want to happen right away. m_hcursor = CursorFromDragMode(dmNull); if (m_hcursor != NULL) SetCursor(m_hcursor); } // These are actions we want to happen after the Desktop Item // looses focus. if (hr != S_FALSE) { m_cyCaption = 0; m_cxBorder = m_cxSMBorder; m_cyBorder = m_cySMBorder; m_CaptionState = 0; m_dmTrack = dmNull; } if (pielem) { ASSERT(m_pielemTarget != pielem); // exchange our new target ( if any ) for the old target, if any... ATOMICRELEASE( m_pistyleTarget ); ATOMICRELEASE( m_pielemTarget ); hr = pielem->get_style(&m_pistyleTarget); if (SUCCEEDED(hr)) { // We are gaining focus. m_pielemTarget = pielem; m_pielemTarget->AddRef(); EVAL(SUCCEEDED(InitAttributes(m_pielemTarget))); if (!pptDoc) TrackCaption(NULL); TrackTarget(pptDoc); // Set m_cSkipTimer so that we delay dismissing the mover... m_cSkipTimer = GET_SKIP_COUNT; if (!m_bWndLess && !m_hWnd) { // This is all a hack until trident fixes the UIDeactivate stuff, bug 243801 IOleInPlaceObject_InPlaceDeactivate(); InPlaceActivate(OLEIVERB_UIACTIVATE); SetControlFocus(TRUE); } } } return hr; } //=--------------------------------------------------------------------------= // CDeskMovrControl::MoveSelfAndTarget [instance method] //=--------------------------------------------------------------------------= // Moves the control and it's target to a new location. // // Parameters: // LONG [in] - x location, in document coord's to move to // LONG [in] - y location, in document coord's to move to // // Output: // HRESULT - various. S_OK if operation succeeded. // // Notes: // We read back the target's location so that we stay consistent with // any constraint's Trident might impose on our movement. HRESULT CDeskMovr::MoveSelfAndTarget( LONG x, LONG y ) { HRESULT hr; m_top = y; CLEANUP_ON_FAILURE((hr = m_pistyle->put_pixelTop( y - m_cyBorder - m_cyCaption ))); CLEANUP_ON_FAILURE((hr = m_pistyleTarget->put_pixelTop( y ))); // read it back to catch Trident constraint. //CLEANUP_ON_FAILURE((hr = m_pielemTarget->get_docTop( &m_top ))); //CLEANUP_ON_FAILURE((hr = m_pistyle->put_pixelTop( m_top ))); m_left = x; CLEANUP_ON_FAILURE((hr = m_pistyle->put_pixelLeft( x - m_cxBorder ))); CLEANUP_ON_FAILURE((hr = m_pistyleTarget->put_pixelLeft( x ))); // read it back to catch Trident constraint. //CLEANUP_ON_FAILURE((hr = m_pielemTarget->get_docLeft( &m_left ))); //CLEANUP_ON_FAILURE((hr = m_pistyle->put_pixelLeft( m_left ))); // if ( !m_bWndLess ) if (EVAL(S_OK == GetParentWindow())) ::UpdateWindow(m_hwndParent); CleanUp: return hr; } BOOL CDeskMovr::FFindTargetElement( IHTMLElement *pielem, IHTMLElement **ppielem ) { *ppielem = NULL; if ( pielem != NULL ) { IDeskMovr *pidm = NULL; // If it is over the mover return the current target, otherwise // find out which component if any we are over. if ( m_pielemTarget != NULL && SUCCEEDED(pielem->QueryInterface(IID_IDeskMovr, (LPVOID*)&pidm))) { m_pielemTarget->AddRef(); *ppielem = m_pielemTarget; ATOMICRELEASE(pidm); } else { HRESULT hr; IHTMLElement *pielem2 = pielem; pielem2->AddRef(); do { VARIANT var; VariantInit( &var ); if ( SUCCEEDED(hr = pielem2->getAttribute( (BSTR)s_sstrNameMember.wsz, TRUE, &var)) ) { if ( var.vt == VT_BSTR && var.bstrVal != NULL ) { if ( StrCmpW( var.bstrVal, m_bstrTargetName ) == 0 ) hr = S_OK; else hr = S_FALSE; } else hr = S_FALSE; // Try VariantChangeType????? } else hr = S_FALSE; // not here, maybe in parent. VariantClear( &var ); if ( hr == S_OK ) { // we found it hr = pielem2->QueryInterface( IID_IHTMLElement, (LPVOID*)ppielem ); } else if ( hr == S_FALSE ) { // not this one, climb up IHTMLElement *pielemParent = NULL; pielem2->get_parentElement( &pielemParent ); pielem2->Release(); // we're through at this level pielem2 = pielemParent; // may be null, which just means we've reached the top. } } while ( SUCCEEDED(hr) && *ppielem == NULL && pielem2 != NULL ); ATOMICRELEASE(pielem2); } } return *ppielem != NULL; } //=--------------------------------------------------------------------------= // CDeskMovr::DragModeFromPoint [instance method] //=--------------------------------------------------------------------------= // Moves the control and it's target to a new location. // // Parameters: // POINT - point to test, in local coords // // Output: // DragMode - drag mode associated with the point // // Notes: // This is only a hit testing method. It does not alter state. CDeskMovr::DragMode CDeskMovr::DragModeFromPoint( POINT pt ) { enum DragMode dm = dmNull; RECT rc; if ( PtInRect( &m_rectInner, pt ) ) { // either no-hit, or on caption if ( PtInRect( &m_rectCaption, pt ) ) { DragMode dmT; for (dmT = dmMenu; dmT < dmMove; dmT = (DragMode)((int)dmT + 1)) { if (GetCaptionButtonRect(dmT, &rc) && PtInRect(&rc, pt)) { dm = dmT; break; } } if ((dmT == dmMove) && !CAPTION_ONLY) dm = dmMove; } } else { if ( PtInRect( &m_rectOuter, pt ) ) { if (!CAPTION_ONLY) { // a resize border hit if ( pt.y <= m_sizeCorner.cy ) { // upper edge or corners if ( pt.x <= m_sizeCorner.cx ) dm = dmSizeWHTL; else if ( pt.x >= m_rectOuter.right - m_sizeCorner.cx ) dm = dmSizeWHTR; else dm = dmSizeTop; } else if ( pt.y >= m_rectOuter.bottom - m_sizeCorner.cy ) { // bottom edge or corners if ( pt.x <= m_sizeCorner.cx ) dm = dmSizeWHBL; else if ( pt.x >= m_rectOuter.right - m_sizeCorner.cx ) dm = dmSizeWHBR; else dm = dmSizeBottom; } else { // side edge hit if ( pt.x > m_rectInner.left ) dm = dmSizeRight; else dm = dmSizeLeft; } } else { if (m_cyCaption == 0) { if(IS_BIDI_LOCALIZED_SYSTEM()) { dm = dmSizeRight; } else { dm = dmSizeLeft; } } else dm = dmNull; } } //Check if this element can be sized in both the directions. if(!m_fCanResizeX) { if((dm != dmSizeTop) && (dm != dmSizeBottom)) dm = dmNull; } if(!m_fCanResizeY) { if((dm != dmSizeLeft) && (dm != dmSizeRight)) dm = dmNull; } } return dm; } // Align our member RECTs with the dimensions of the target element. void CDeskMovr::SyncRectsToTarget(void) { // do the swelling thang if ( (m_dmTrack > dmMove) || m_cyCaption ) { m_cxBorder = GET_CXSIZE; m_cyBorder = GET_CYSIZE; } else { m_cxBorder = m_cxSMBorder; m_cyBorder = m_cySMBorder; } m_rectOuter.top = m_rectOuter.left = 0; if (CAPTION_ONLY) { if (m_cyCaption != 0) { // Displaying just caption m_rectOuter.bottom = m_cyCaption + m_cyBorder; m_rectOuter.right = m_width; } else { // Displaying just left size border m_rectOuter.bottom = m_height; if(IS_BIDI_LOCALIZED_SYSTEM()) { m_rectOuter.right = m_width; m_rectOuter.left = m_rectOuter.right - m_cxBorder; } else { m_rectOuter.right = m_cxBorder; } } } else { // Displaying caption and border m_rectOuter.bottom = m_height + 2 * m_cyBorder + m_cyCaption; m_rectOuter.right = m_width + 2 * m_cxBorder; } if (CAPTION_ONLY && m_cyCaption == 0) { // Displaying just left size border SetRectEmpty(&m_rectInner); SetRectEmpty(&m_rectCaption); } else { // Displaying caption and possibly border m_rectInner = m_rectOuter; InflateRect( &m_rectInner, -m_cxBorder, -m_cyBorder ); m_rectCaption = m_rectInner; m_rectCaption.bottom = m_cyBorder + m_cyCaption; } if ( m_rectOuter.bottom > 2 * m_cyCaption ) m_sizeCorner.cy = GET_CYCAPTION; else m_sizeCorner.cy = m_rectOuter.bottom / 2; if ( m_rectOuter.right > 2 * m_cyCaption ) m_sizeCorner.cx = GET_CYCAPTION; else m_sizeCorner.cx = m_rectOuter.right / 2; } HCURSOR CDeskMovr::CursorFromDragMode( DragMode dm ) { ASSERT( dm >= 0 && dm < cDragModes ); switch (dm) { case dmNull: case dmMenu: case dmClose: case dmMove: case dmRestore: case dmFullScreen: case dmSplit: default: return LoadCursor(NULL, IDC_ARROW); case dmSizeWHBR: case dmSizeWHTL: return LoadCursor(NULL, IDC_SIZENWSE); case dmSizeWHTR: case dmSizeWHBL: return LoadCursor(NULL, IDC_SIZENESW); case dmSizeTop: case dmSizeBottom: return LoadCursor( NULL, IDC_SIZENS ); case dmSizeLeft: case dmSizeRight: return LoadCursor( NULL, IDC_SIZEWE ); } } //=--------------------------------------------------------------------------= // CDeskMovr::SizeSelfAndTarget [instance method] //=--------------------------------------------------------------------------= // Resizes our control and its target element. // // Parameters: // LONG [in] - new width // LONG [in] - new height // // Output: // HRESULT - various. S_OK if operation succeeded. // // Notes: // We read back the target's dimensions so that we stay consistent with // any constraint's Trident might impose on our sizing. HRESULT CDeskMovr::SizeSelfAndTarget( POINT ptDoc ) { HRESULT hr; int topOld = m_top; int leftOld = m_left; int heightOld = m_height; int widthOld = m_width; int cyCaption = GET_CYCAPTION; switch ( m_dmCur ) { case dmSizeWHBR: m_width = (ptDoc.x + m_dx) - m_left; m_height = (ptDoc.y + m_dy) - m_top; break; case dmSizeWHTL: m_top = ptDoc.y + m_dy; m_height += topOld - m_top; m_left = ptDoc.x + m_dx; m_width += leftOld - m_left; break; case dmSizeWHTR: m_top = ptDoc.y + m_dy; m_height += topOld - m_top; m_width = (ptDoc.x + m_dx) - m_left; break; case dmSizeWHBL: m_height = (ptDoc.y + m_dy) - m_top; m_left = ptDoc.x + m_dx; m_width += leftOld - m_left; break; case dmSizeTop: m_top = ptDoc.y + m_dy; m_height += topOld - m_top; break; case dmSizeBottom: m_height = (ptDoc.y + m_dy) - m_top; break; case dmSizeLeft: m_left = ptDoc.x + m_dx; m_width += leftOld - m_left; break; case dmSizeRight: m_width = (ptDoc.x + m_dx) - m_left; break; default: ASSERT(FALSE); return E_FAIL; } // limit shrinkage to keep the handle accessible if ( m_height < cyCaption ) { m_height = cyCaption; if ( m_top != topOld ) m_top = topOld + heightOld - m_height; } // limit shrinkage to keep the handle accessible if ( m_width < (4 * cyCaption) ) { m_width = 4 * cyCaption; if ( m_left != leftOld ) m_left = leftOld + widthOld - m_width; } SyncRectsToTarget(); if ( m_top != topOld ) { CLEANUP_ON_FAILURE((hr = m_pistyleTarget->put_pixelTop( m_top ))); CLEANUP_ON_FAILURE((hr = m_pistyle->put_pixelTop( m_top - (m_cyBorder + m_cyCaption) ))); } if ( m_left != leftOld ) { CLEANUP_ON_FAILURE((hr = m_pistyleTarget->put_pixelLeft( m_left ))); CLEANUP_ON_FAILURE((hr = m_pistyle->put_pixelLeft( m_left - (CAPTION_ONLY ? 0 : m_cxBorder) ))); } CLEANUP_ON_FAILURE((hr = m_pistyleTarget->put_pixelHeight( m_height ))); // read it back to catch Trident constraint. //CLEANUP_ON_FAILURE((hr = m_pielemTarget->get_docHeight( &m_height ))); CLEANUP_ON_FAILURE((hr = m_pistyle->put_pixelHeight( m_rectOuter.bottom ))); CLEANUP_ON_FAILURE((hr = m_pistyleTarget->put_pixelWidth( m_width ))); // read it back to catch Trident constraint. //CLEANUP_ON_FAILURE((hr = m_pielemTarget->get_docWidth( &m_width ))); CLEANUP_ON_FAILURE((hr = m_pistyle->put_pixelWidth( m_rectOuter.right ))); if(IS_BIDI_LOCALIZED_SYSTEM() && CAPTION_ONLY) { CLEANUP_ON_FAILURE((hr = m_pistyle->put_pixelLeft(m_rectOuter.left ))); CLEANUP_ON_FAILURE((hr = m_pistyle->put_pixelWidth(m_rectOuter.right - m_rectOuter.left ))); } if (EVAL(S_OK == GetParentWindow())) ::UpdateWindow(m_hwndParent); CleanUp: return hr; } // IQuickActivate HRESULT CDeskMovr::QuickActivate(QACONTAINER *pQACont, QACONTROL *pQACtrl) { HRESULT hr = IQuickActivate_QuickActivate(pQACont, pQACtrl); if (pQACont) { ClearFlag(pQACtrl->dwViewStatus, VIEWSTATUS_OPAQUE); } return hr; } HRESULT CDeskMovr::_GetHTMLDoc(IOleClientSite * pocs, IHTMLDocument2 ** pphd2) { HRESULT hr; IOleContainer * poc = NULL; if (!EVAL(pocs) || !EVAL(pphd2)) return E_INVALIDARG; *pphd2 = NULL; hr = pocs->GetContainer(&poc); if (SUCCEEDED(hr)) { hr = poc->QueryInterface(IID_IHTMLDocument2, (LPVOID*) pphd2); poc->Release(); } return hr; } HRESULT CDeskMovr::_IsInElement(HWND hwndParent, POINT * ppt, IHTMLElement ** pphe) { HRESULT hr = E_FAIL; ASSERT(pphe); *pphe = NULL; if (!ppt || ::ScreenToClient(hwndParent, ppt)) { IHTMLDocument2 * phd2; ASSERT(m_spClientSite); hr = _GetHTMLDoc(m_spClientSite, &phd2); if (SUCCEEDED(hr)) { if (ppt) hr = phd2->elementFromPoint(ppt->x, ppt->y, pphe); else hr = phd2->get_activeElement(pphe); if (!*pphe && SUCCEEDED(hr)) hr = E_FAIL; // Sometimes Trident returns S_FALSE on error. phd2->Release(); } } return hr; } HRESULT CDeskMovr::_EnumComponents(LPFNCOMPENUM lpfn, LPVOID lpvData, DWORD dwData) { HRESULT hr = E_FAIL; IActiveDesktop * padt = NULL; hr = CoCreateInstance(CLSID_ActiveDesktop, NULL, CLSCTX_INPROC, IID_IActiveDesktop, (LPVOID *)&padt); if (SUCCEEDED(hr)) { int nCount; int nIndex; hr = padt->GetDesktopItemCount(&nCount, 0); if (EVAL(SUCCEEDED(hr))) { COMPONENT comp; for (nIndex = 0; nIndex < nCount; nIndex++) { comp.dwSize = sizeof(COMPONENT); hr = padt->GetDesktopItem(nIndex, &comp, 0); if (EVAL(SUCCEEDED(hr))) { if ((hr = lpfn(&comp, lpvData, dwData)) != S_OK) break; } } } padt->Release(); } return hr; } HRESULT CDeskMovr::_EnumElements(LPFNELEMENUM lpfn, LPVOID lpvData, DWORD dwData) { HRESULT hr; IHTMLDocument2 * phd2; ASSERT(m_spClientSite); if (SUCCEEDED(hr = _GetHTMLDoc(m_spClientSite, &phd2))) { IHTMLElementCollection * pelems; if (SUCCEEDED(hr = phd2->get_all(&pelems))) { VARIANT varIndex; VARIANT varDummy; IDispatch * pidisp; VariantInit(&varDummy); varIndex.vt = VT_I4; varIndex.lVal = 0; // Note: This loop terminates when trident returns SUCCESS - but with a NULL pidisp. while (SUCCEEDED(hr = pelems->item(varIndex, varDummy, &pidisp)) && pidisp) { IHTMLElement * pielem; if (SUCCEEDED(hr = pidisp->QueryInterface(IID_IHTMLElement, (LPVOID *)&pielem))) { hr = lpfn(pielem, lpvData, dwData); pielem->Release(); } pidisp->Release(); if (hr != S_OK) break; varIndex.lVal++; } pelems->Release(); } phd2->Release(); } return hr; } HRESULT lpfnZOrderCB(COMPONENT * pcomp, LPVOID lpvData, DWORD dwData) { #define LPZORDERSLOT ((LONG *)lpvData) if (dwData ? (pcomp->cpPos.izIndex > *LPZORDERSLOT) : (pcomp->cpPos.izIndex < *LPZORDERSLOT)) *LPZORDERSLOT = pcomp->cpPos.izIndex; return S_OK; } HRESULT CDeskMovr::_GetZOrderSlot(LONG * plZOrderSlot, BOOL fTop) { HRESULT hr; ASSERT(plZOrderSlot); *plZOrderSlot = m_bWindowOnly ? 10000 : 5000; hr = _EnumComponents(lpfnZOrderCB, (LPVOID)plZOrderSlot, (DWORD)fTop); *plZOrderSlot += fTop ? 2 : -2; // Make sure we are above / below. return hr; } //=--------------------------------------------------------------------------= // PersistTargetPosition [helper function] //=--------------------------------------------------------------------------= // Update the registry entries that are the persistence of the desktop HTML. // // Parameters: // // // Output: // // // Notes: // If we fail, we do it quietly. //=--------------------------------------------------------------------------= void PersistTargetPosition( IHTMLElement *pielem, int left, int top, int width, int height, int zIndex, BOOL fSaveState, BOOL fSaveOriginal, DWORD dwNewState) { // only do this persistence thing if we're in ( or completing ) an operation TCHAR szID[MAX_ID_LENGTH]; BOOL fOK; if (SUCCEEDED(GetHTMLElementStrMember(pielem, szID, SIZECHARS(szID), (BSTR)(s_sstrIDMember.wsz)))) { bool bChangedPosition, bChangedSize; COMPPOS compPos; // 99/03/23 #266412 vtan: The user has moved the deskmovr to a new position // make sure that it is within the work area of the display monitors. // ValidateComponentPosition() will do this for us and tell us whether the // the component got moved or resized. compPos.dwSize = sizeof(compPos); compPos.iLeft = left; compPos.iTop = top; compPos.dwWidth = width; compPos.dwHeight = height; ValidateComponentPosition(&compPos, dwNewState, COMP_TYPE_HTMLDOC, &bChangedPosition, &bChangedSize); if (bChangedPosition || bChangedSize) { IHTMLStyle *pIStyle; // If the component got moved or resized then tell the object model // where the deskmovr is now. left = compPos.iLeft; top = compPos.iTop; width = compPos.dwWidth; height = compPos.dwHeight; if (SUCCEEDED(pielem->get_style(&pIStyle))) { pIStyle->put_pixelLeft(left); pIStyle->put_pixelTop(top); pIStyle->put_pixelWidth(width); pIStyle->put_pixelHeight(height); pIStyle->Release(); } } fOK = UpdateDesktopPosition(szID, left, top, width, height, zIndex, fSaveState, fSaveOriginal, dwNewState); } TraceMsg(TF_CUSTOM2, "PersistTargetPosition(pielem=%s, )", szID, left, top, width, height); } void ObtainSavedStateForElem( IHTMLElement *pielem, LPCOMPSTATEINFO pCompState, BOOL fRestoredState) { // only do this persistence thing if we're in ( or completing ) an operation TCHAR szID[MAX_ID_LENGTH]; BOOL fOK; if (SUCCEEDED(GetHTMLElementStrMember(pielem, szID, SIZECHARS(szID), (BSTR)(s_sstrIDMember.wsz)))) fOK = GetSavedStateInfo(szID, pCompState, fRestoredState); TraceMsg(TF_CUSTOM2, "ObtainSavedStateForElem(pielem=%s, )", szID, pCompState->iLeft, pCompState->iTop, pCompState->dwWidth, pCompState->dwHeight); } // IOleObject HRESULT CDeskMovr::GetMiscStatus(DWORD dwAspect, DWORD *pdwStatus) { if (dwAspect == DVASPECT_CONTENT) { *pdwStatus = OLEMISMOVR; return S_OK; } else { return DV_E_DVASPECT; } // dead code } HRESULT CDeskMovr::SetClientSite(IOleClientSite * pClientSite) { if (!pClientSite) DeactivateMovr(FALSE); return CComControlBase::IOleObject_SetClientSite(pClientSite); } void HandleRestore(IHTMLElement * pielem, LONG lData) { VARIANT varZ; COMPSTATEINFO csiRestore; IHTMLStyle * pistyle; if (SUCCEEDED(pielem->get_style(&pistyle))) { csiRestore.dwSize = sizeof(csiRestore); ObtainSavedStateForElem(pielem, &csiRestore, TRUE); // TRUE => Get restored state! pistyle->put_pixelLeft(csiRestore.iLeft); pistyle->put_pixelTop(csiRestore.iTop); pistyle->put_pixelWidth(csiRestore.dwWidth); pistyle->put_pixelHeight(csiRestore.dwHeight); varZ.vt = VT_I4; varZ.lVal = lData; pistyle->put_zIndex(varZ); PersistTargetPosition(pielem, csiRestore.iLeft, csiRestore.iTop, csiRestore.dwWidth, csiRestore.dwHeight, varZ.lVal, FALSE, FALSE, IS_NORMAL); pistyle->Release(); } } HRESULT lpfnRestoreCB(IHTMLElement * pielem, LPVOID lpvData, LONG lData) { HRESULT hres = S_OK; TCHAR szID[MAX_ID_LENGTH]; if (SUCCEEDED(GetHTMLElementStrMember(pielem, szID, SIZECHARS(szID), (BSTR)(s_sstrIDMember.wsz)))) { DWORD dwState = GetCurrentState(szID); // Since there is only one in this state we can stop the enumeration if we // find a fullscreen/split item on this work area. if (dwState & (IS_FULLSCREEN | IS_SPLIT)) { POINT pt; if (SUCCEEDED(CSSOM_TopLeft(pielem, &pt)) && PtInRect((CONST RECT *)lpvData, pt)) { HandleRestore(pielem, lData); hres = S_FALSE; } } } return hres; } HRESULT CDeskMovr::_HandleZoom(LONG lCommand) { LONG x, y, cx, cy, zIndex; VARIANT varZ; DWORD dwOldItemState = m_ItemState, dwNewItemState; IHTMLStyle * pistyleTarget = m_pistyleTarget; IHTMLElement * pielemTarget = m_pielemTarget; // Paranoia if (!pistyleTarget || !pielemTarget) { ASSERT(FALSE); return E_FAIL; } // Hold on to these guys during this call, they could go away when we yield // like during the animation call below. pistyleTarget->AddRef(); pielemTarget->AddRef(); if (lCommand == IDM_DCCM_RESTORE) { COMPSTATEINFO csi; csi.dwSize = sizeof(csi); // The "Restore" command toggles with the "Reset Original Size" command. // Make sure we get the correct Restore or Reset position for the element. ObtainSavedStateForElem(pielemTarget, &csi, !ISNORMAL); if (ISNORMAL) { // This is the split case, dont move the item just resize it. x = m_left; y = m_top; } else { // 98/07/27 vtan #176721: The following checks restoration of a component // position from zoomed to user-specified position. If the component // is placed at the default position then it is positioned now using // the standard positioning code. if ((csi.iLeft == COMPONENT_DEFAULT_LEFT) && (csi.iTop == COMPONENT_DEFAULT_TOP) && (csi.dwWidth == COMPONENT_DEFAULT_WIDTH) && (csi.dwHeight == COMPONENT_DEFAULT_HEIGHT)) { COMPPOS compPos; GetNextComponentPosition(&compPos); IncrementComponentsPositioned(); csi.iLeft = compPos.iLeft; csi.iTop = compPos.iTop; csi.dwWidth = compPos.dwWidth; csi.dwHeight = compPos.dwHeight; } // Restore case, go ahead and move it. x = csi.iLeft; y = csi.iTop; } cx = csi.dwWidth; cy = csi.dwHeight; m_ItemState = (m_ItemState & ~IS_VALIDSIZESTATEBITS) | IS_NORMAL; dwNewItemState = m_ItemState; m_zIndexTop += 2; zIndex = m_zIndexTop; } else { RECT rcZoom, rcWork; GetZoomRect(lCommand == IDM_DCCM_FULLSCREEN, TRUE, m_left, m_top, m_width, m_height, &rcZoom, &rcWork); x = rcZoom.left; y = rcZoom.top; cx = rcZoom.right - rcZoom.left; cy = rcZoom.bottom - rcZoom.top; if (lCommand == IDM_DCCM_FULLSCREEN) { m_ItemState = (m_ItemState & ~IS_VALIDSIZESTATEBITS) | IS_FULLSCREEN; dwNewItemState = m_ItemState; } else { m_ItemState = (m_ItemState & ~IS_VALIDSIZESTATEBITS) | IS_SPLIT; dwNewItemState = m_ItemState; } varZ.vt = VT_I4; pistyleTarget->get_zIndex(&varZ); // We currently only allow 1 component to be either split or full screen per monitor (WorkArea), so // restore any other component that is currently in this state. _EnumElements(lpfnRestoreCB, (LPVOID)&rcWork, varZ.lVal); m_zIndexBottom -= 2; zIndex = m_zIndexBottom; } // We want to do the animation call before we start moving the target, it looks better // that way. AnimateComponent(m_hwndParent, m_left, m_top, m_width, m_height, x, y, cx, cy); pistyleTarget->put_pixelLeft(x); pistyleTarget->put_pixelTop(y); pistyleTarget->put_pixelWidth(cx); pistyleTarget->put_pixelHeight(cy); varZ.vt = VT_I4; varZ.lVal = zIndex; pistyleTarget->put_zIndex(varZ); PersistTargetPosition(pielemTarget, x, y, cx, cy, zIndex, (BOOL)((dwOldItemState & IS_NORMAL) && !(dwNewItemState & IS_NORMAL)), FALSE, dwNewItemState); pistyleTarget->Release(); pielemTarget->Release(); return S_OK; } /************************************************************************\ FUNCTION: CDeskMovr::_DisplayContextMenu PARAMETERS: x,y - Coordinates relative to the desktop window. \************************************************************************/ HRESULT CDeskMovr::_DisplayContextMenu() { HRESULT hr = S_OK; HMENU hmenuContext = LoadMenuPopup(MENU_DESKCOMP_CONTEXTMENU); TraceMsg(TF_CUSTOM2, "CDeskMovr::DisplayContextMenu(), m_bstrTargetName=%ls", GEN_DEBUGSTRW(m_bstrTargetName)); if (hmenuContext) { int nSelection; BOOL fSubscribe = FALSE; BOOL fRemoveSubscribe = FALSE; TCHAR szName[MAX_URL_STRING]; POINT point; if (CAPTION_ONLY) { point.x = m_left + m_cxBorder; point.y = m_top + (m_cyCaption + m_cyBorder) - 4 * m_cySMBorder; } else { point.x = m_left - m_cxSMBorder; point.y = m_top - 4 * m_cySMBorder; } ::ClientToScreen(m_hwndParent, &point); // This calculation needs to be revisited. The reason it's so // ugle and HACKy is because to look good, we want the context menu // to appear on top of the 3-D edge below the triangle. if (SUCCEEDED(GetHTMLElementStrMember(m_pielemTarget, szName, SIZECHARS(szName), (BSTR)(s_sstrSubSRCMember.wsz)))) { int nScheme = GetUrlScheme(szName); if ((URL_SCHEME_FILE == nScheme) || (URL_SCHEME_INVALID == nScheme)) fRemoveSubscribe = TRUE; } // check to see if we need to turn some things off or on // Mainly because we are disabling features Admins don't want users to have. hr = IElemCheckForExistingSubscription(m_pielemTarget); if (fRemoveSubscribe || FAILED(hr)) // This object/thing cannot be subscribed to. (Channel Changer, Orenge Blob). { MENUITEMINFO menuItemInfo; DeleteMenu(hmenuContext, IDM_DCCM_OFFLINE, MF_BYCOMMAND); DeleteMenu(hmenuContext, IDM_DCCM_SYNCHRONIZE, MF_BYCOMMAND); DeleteMenu(hmenuContext, IDM_DCCM_PROPERTIES, MF_BYCOMMAND); // Is the top item in the list a separator? menuItemInfo.cbSize = sizeof(menuItemInfo); menuItemInfo.fMask = MIIM_TYPE; if ((GetMenuItemInfo(hmenuContext, 0, TRUE, &menuItemInfo) != FALSE) && (menuItemInfo.fType == MFT_SEPARATOR)) { // Yes, it is, so remove it. DeleteMenu(hmenuContext, 0, MF_BYPOSITION); } } else if (S_FALSE == hr) // Not subscribed { DeleteMenu(hmenuContext, IDM_DCCM_SYNCHRONIZE, MF_BYCOMMAND); DeleteMenu(hmenuContext, IDM_DCCM_PROPERTIES, MF_BYCOMMAND); fSubscribe = TRUE; } else if (S_OK == hr) { if (SHRestricted2(REST_NoManualUpdates, NULL, 0)) DeleteMenu(hmenuContext, IDM_DCCM_SYNCHRONIZE, MF_BYCOMMAND); if (SHRestricted(REST_NOEDITDESKCOMP)) DeleteMenu(hmenuContext, IDM_DCCM_PROPERTIES, MF_BYCOMMAND); CheckMenuItem(hmenuContext, IDM_DCCM_OFFLINE, MF_BYCOMMAND |MF_CHECKED); } if (SHRestricted(REST_NOCLOSEDESKCOMP)) EnableMenuItem(hmenuContext, IDM_DCCM_CLOSE, MF_BYCOMMAND | MF_GRAYED); // If policy is set to lock down active desktop, don't put up the // menu that invokes the web-tab if (SHRestricted(REST_NOACTIVEDESKTOPCHANGES) || SHRestricted(REST_NODISPBACKGROUND)) { EnableMenuItem(hmenuContext, IDM_DCCM_CUSTOMIZE, MF_BYCOMMAND | MF_GRAYED); } if (ISNORMAL) { COMPSTATEINFO CompState; LoadString(HINST_THISDLL, IDS_MENU_RESET, szName, ARRAYSIZE(szName)); ModifyMenu(hmenuContext, IDM_DCCM_RESTORE, MF_BYCOMMAND | MF_STRING, IDM_DCCM_RESTORE, szName); ObtainSavedStateForElem(m_pielemTarget, &CompState, FALSE); if ((CompState.dwWidth == COMPONENT_DEFAULT_WIDTH && CompState.dwHeight == COMPONENT_DEFAULT_HEIGHT) || (CompState.dwWidth == (DWORD)m_width && CompState.dwHeight == (DWORD)m_height)) EnableMenuItem(hmenuContext, IDM_DCCM_RESTORE, MF_BYCOMMAND | MF_GRAYED); } if (ISSPLIT || !m_fCanResizeX || !m_fCanResizeY) EnableMenuItem(hmenuContext, IDM_DCCM_SPLIT, MF_BYCOMMAND | MF_GRAYED); if (ISFULLSCREEN || !m_fCanResizeX || !m_fCanResizeY) EnableMenuItem(hmenuContext, IDM_DCCM_FULLSCREEN, MF_BYCOMMAND | MF_GRAYED); g_fIgnoreTimers |= IGNORE_CONTEXTMENU_UP; nSelection = TrackPopupMenu(hmenuContext, TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_NONOTIFY | TPM_RETURNCMD, point.x, point.y, 0, m_hwndParent, NULL); DestroyMenu(hmenuContext); m_CaptionState &= ~CS_MENUPUSHED; UpdateCaption(DMDC_MENU); switch (nSelection) { case IDM_DCCM_OFFLINE: if (fSubscribe) hr = IElemSubscribeDialog(m_pielemTarget, m_hWnd); else hr = IElemUnsubscribe(m_pielemTarget); break; case IDM_DCCM_SYNCHRONIZE: hr = IElemUpdate(m_pielemTarget); break; case IDM_DCCM_PROPERTIES: // Subscriptions Dialog (Don't let the name fool you) TraceMsg(TF_CUSTOM2, "CDeskMovr::_DisplayContextMenu() IDM_DCCM_PROPERTIES m_bstrTargetName=%ls.", GEN_DEBUGSTRW(m_bstrTargetName)); hr = IElemGetSubscriptionsDialog(m_pielemTarget, NULL); break; case IDM_DCCM_CUSTOMIZE: // Show Display Control Panel set to Components Sheet LoadString(HINST_THISDLL, IDS_COMPSETTINGS, szName, ARRAYSIZE(szName)); SHRunControlPanel(szName, NULL); hr = S_OK; break; case IDM_DCCM_CLOSE: ASSERT(!SHRestricted(REST_NOCLOSEDESKCOMP)); // We should never be able to get here. TraceMsg(TF_CUSTOM2, "CDeskMovr::_DisplayContextMenu() IDM_DCCM_CLOSE m_bstrTargetName=%ls", GEN_DEBUGSTRW(m_bstrTargetName)); // AnimateToTray(m_hwndParent, m_left, m_top, m_width, m_height); hr = IElemCloseDesktopComp(m_pielemTarget); break; case IDM_DCCM_RESTORE: case IDM_DCCM_FULLSCREEN: case IDM_DCCM_SPLIT: hr = _HandleZoom(nSelection); break; case IDM_DCCM_OPEN: { BOOL fShowFrame = (GetKeyState(VK_SHIFT) < 0) ? !(m_fCanResizeX && m_fCanResizeY) : (m_fCanResizeX && m_fCanResizeY); hr = IElemOpenInNewWindow(m_pielemTarget, m_spClientSite, fShowFrame, m_width, m_height); } break; } g_fIgnoreTimers &= ~IGNORE_CONTEXTMENU_UP; if (nSelection) DismissSelfNow(); } return hr; } void CDeskMovr::_MapPoints(int * px, int * py) { if (m_bWndLess) { *px -= m_left - (CAPTION_ONLY ? 0 : m_cxBorder); *py -= m_top - (CAPTION_ONLY ? 0 : (m_cyBorder + m_cyCaption)); } } void CDeskMovr::_ChangeCapture(BOOL fSet) { if (m_fCaptured != fSet) { m_fCaptured = fSet; if (fSet) { ASSERT(m_spInPlaceSite); if (m_bWndLess && m_spInPlaceSite) { m_fCaptured = SUCCEEDED(m_spInPlaceSite->SetCapture(TRUE)); } else { ::SetCapture( m_hWnd ); m_fCaptured = (GetCapture() == m_hWnd); } if (m_fCaptured) g_fIgnoreTimers |= IGNORE_CAPTURE_SET; } else { ASSERT(m_spInPlaceSite); if (m_bWndLess && m_spInPlaceSite) { m_spInPlaceSite->SetCapture(FALSE); } else { ReleaseCapture(); } g_fIgnoreTimers &= ~IGNORE_CAPTURE_SET; } } } // Called from our keyboard hook so that we can implement keyboard invocation and dismissal // of the deskmovr. void CDeskMovr::OnKeyboardHook(WPARAM wParam, LPARAM lParam) { IHTMLElement * pielem; HWND hwndFocus = GetFocus(); if (!(g_fIgnoreTimers & IGNORE_CONTEXTMENU_UP) && SUCCEEDED(GetParentWindow()) && ((hwndFocus == m_hwndParent) || ::IsChild(m_hwndParent, hwndFocus))) { switch (wParam) { case VK_MENU: if (!m_pielemTarget && !(GetDesktopFlags() & COMPONENTS_LOCKED) && SUCCEEDED(SmartActivateMovr(ERROR_SUCCESS)) && SUCCEEDED(_IsInElement(NULL, NULL, &pielem))) { BOOL fDummy; _TrackElement(NULL, pielem, &fDummy); pielem->Release(); } break; case VK_ESCAPE: case VK_TAB: if ((lParam >= 0) && m_pielemTarget) // If key down, dismiss DismissSelfNow(); break; case VK_SPACE: if (m_pielemTarget && (GET_CYCAPTION == m_cyCaption) && (HIWORD(lParam) & KF_ALTDOWN)) { HandleNonMoveSize(dmMenu); } break; } } } STDAPI CDeskMovr_CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppunk) { return CComCreator< CComPolyObject< CDeskMovr > >::CreateInstance( (LPVOID)pUnkOuter, IID_IUnknown, (LPVOID*)ppunk ); }