You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2725 lines
86 KiB
2725 lines
86 KiB
// 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));
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
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<CDeskMovr>::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:
|
|
// <none>
|
|
//
|
|
// 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:
|
|
// <none>
|
|
//
|
|
// 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:
|
|
// <none>
|
|
//
|
|
// 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<CDeskMovr>::OnPaint( uMsg, wParam, lParam, bHandled );
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// CDeskMovrControl::DoMouseMove [instance method]
|
|
//=--------------------------------------------------------------------------=
|
|
// Respond to mouse move messages in our control and when moving/sizing.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// Output:
|
|
// <none>
|
|
//
|
|
// 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<CDeskMovr>::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, ARRAYSIZE(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, ARRAYSIZE(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_SERVER, 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:
|
|
// <none>
|
|
//
|
|
// Output:
|
|
// <none>
|
|
//
|
|
// 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, ARRAYSIZE(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, <left=%d, top=%d, wid=%d, h=%d>)", 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];
|
|
|
|
if (SUCCEEDED(GetHTMLElementStrMember(pielem, szID, ARRAYSIZE(szID), (BSTR)(s_sstrIDMember.wsz))))
|
|
{
|
|
GetSavedStateInfo(szID, pCompState, fRestoredState);
|
|
}
|
|
|
|
TraceMsg(TF_CUSTOM2, "ObtainSavedStateForElem(pielem=%s, <left=%d, top=%d, wid=%d, h=%d>)", 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, ARRAYSIZE(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, ARRAYSIZE(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 );
|
|
}
|