// ===================================================================================
// Class Implementation : COXDragDockContext
// ===================================================================================
// Header file : OXDragDockContext.cpp
// Version: 9.3
// This software along with its related components, documentation and files ("The Libraries")
// is � 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is
// governed by a software license agreement ("Agreement"). Copies of the Agreement are
// available at The Code Project (www.codeproject.com), as part of the package you downloaded
// to obtain this file, or directly from our office. For a copy of the license governing
// this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900.
// Some portions Copyright (C)1994-5 Micro Focus Inc, 2465 East Bayshore Rd, Palo Alto, CA 94303.
// //////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include <afxpriv.h>
#include "UTB64Bit.h"
#ifndef __OXMFCIMPL_H__
#if _MFC_VER >= 0x0700
#if _MFC_VER >= 1400
#include <afxtempl.h>
#endif
#include <..\src\mfc\afximpl.h>
#else
#include <..\src\afximpl.h>
#endif
#define __OXMFCIMPL_H__
#endif
#include "OXDragDockContext.h"
#include "OXMainRes.h" // for some resource strings
#include "OXSizeCtrlBar.h"
#include "OXSizeDockBar.h"
#include "OXMDIFloatWnd.h"
#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif
#define new DEBUG_NEW
#define HORZF(dw) (dw & CBRS_ORIENT_HORZ)
#define VERTF(dw) (dw & CBRS_ORIENT_VERT)
// adjust supplied rectangle, so that point, pt is inside it.
// Adjusted slightly from original orientation to ensure point is now
// within specificed rectangle.
static void AdjustRectangle(CRect& rect, CPoint pt)
{
int nXOffset = (pt.x < rect.left) ? (pt.x - rect.left) :
(pt.x > rect.right) ? (pt.x - rect.right) : 0;
// int nYOffset = (pt.y < rect.top) ? (pt.y - rect.top) :
// (pt.y > rect.bottom) ? (pt.y - rect.bottom) : 0;
// rect.OffsetRect(nXOffset, nYOffset);
rect.OffsetRect(nXOffset, 0);
}
/////////////////////////////////////////////////////////////////////////////
// Definition of static members
// Data members -------------------------------------------------------------
// protected:
// private:
// Member functions ---------------------------------------------------------
// public:
COXDragDockContext::COXDragDockContext(CControlBar* pBar)
: CDockContext(pBar)
{
}
COXDragDockContext::~COXDragDockContext()
{
}
/////////////////////////////////////////////////////////////////////////////
// COXDragDockContext operations
// start dragging. This is the only routine exposed externally.
// pt = mouse position at start of drag (screen co-ords)
void COXDragDockContext::StartDrag(CPoint pt)
{
ASSERT_VALID(m_pBar);
ASSERT(m_pBar->IsKindOf(RUNTIME_CLASS(COXSizeControlBar)));
COXSizeControlBar* pSzBar = (COXSizeControlBar*)m_pBar;
// get styles from bar
m_dwDockStyle = m_pBar->m_dwDockStyle;
m_dwStyle = m_pBar->m_dwStyle & CBRS_ALIGN_ANY;
ASSERT(m_dwStyle != 0);
// check to see we're not hanging from a COXMDIFloatWnd.
// Disallow dragging if we are...
if (m_pBar->IsFloating())
{
CFrameWnd* pFrameWnd = m_pBar->GetParentFrame();
ASSERT(pFrameWnd != NULL);
ASSERT(pFrameWnd->IsKindOf(RUNTIME_CLASS(CFrameWnd)));
if (pFrameWnd->IsKindOf(RUNTIME_CLASS(COXMDIFloatWnd)))
return; // do nothing if floating inside a COXMDIFloatWnd
}
// dragging has started message (only if window will actually dock !)
if ((m_dwDockStyle & CBRS_ALIGN_ANY) == CBRS_ALIGN_ANY)
AfxGetMainWnd()->SendMessage(WM_SETMESSAGESTRING, IDS_OX_MRC_STARTDOCKING);
// handle pending WM_PAINT messages
MSG msg;
while (::PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_NOREMOVE))
{
if (!GetMessage(&msg, NULL, WM_PAINT, WM_PAINT))
return;
ASSERT(msg.message == WM_PAINT);
DispatchMessage(&msg);
}
// initialize drag state
m_rectLast.SetRectEmpty();
m_sizeLast.cx = m_sizeLast.cy = 0;
m_bForceFrame = m_bFlip = m_bDitherLast = FALSE;
// get current bar location
CRect rect;
m_pBar->GetWindowRect(rect);
m_ptLast = pt;
m_ptStart = pt;
BOOL bHorz = HORZF(m_dwStyle);
// MFC includes code for flipping orientation using the shift key - I wasn't keen
// on this... (sorry) so I've left it out for now. Some references are still left
// in for it, in case I decide to implement it.
// Start by working out the possible rectangles that dragging could result in.
// These are:
// m_rectFrameDragHorz : floating frame, horizontal orientation
// m_rectFrameDragVert : floating frame, vertical orientation (not used, 'cos
// flipping not allowed)
//
// m_rectDragHorz : docking horizontally, another bar already on this row
// m_rectDragVert : docking vertically, another bar already on this row
// m_rectDragHorzAlone : docking horizontally, on a new row
// m_rectDragVertAlone : docking vertically, on a new row
// calculate dragging rects if you drag on the new row/column
//
CRect rectBorder;
m_pDockSite->RepositionBars(0,0xffff,AFX_IDW_PANE_FIRST,
CFrameWnd::reposQuery,&rectBorder);
m_pDockSite->ClientToScreen(rectBorder);
CWnd* pLeftDockBar=m_pDockSite->GetControlBar(AFX_IDW_DOCKBAR_LEFT);
if(pLeftDockBar!=NULL && pLeftDockBar->GetStyle()&WS_VISIBLE)
{
CRect rectDockBar;
pLeftDockBar->GetWindowRect(rectDockBar);
rectBorder.left-=rectDockBar.Width();
}
CWnd* pRightDockBar=m_pDockSite->GetControlBar(AFX_IDW_DOCKBAR_RIGHT);
if(pRightDockBar!=NULL && pRightDockBar->GetStyle()&WS_VISIBLE)
{
CRect rectDockBar;
pRightDockBar->GetWindowRect(rectDockBar);
rectBorder.right+=rectDockBar.Width();
}
m_rectDragHorzAlone=CRect(CPoint(rectBorder.left,rect.top),rectBorder.Size());
m_rectDragVertAlone=CRect(CPoint(rect.left,rectBorder.top),rectBorder.Size());
m_rectDragHorzAlone.bottom=m_rectDragHorzAlone.top+pSzBar->m_HorzDockSize.cy;
m_rectDragVertAlone.right=m_rectDragVertAlone.left+pSzBar->m_VertDockSize.cx;
//
//////////////////////////////////////////////////////////////
//////////////////
//
int nDockAreaWidth = rectBorder.Width();
int nDockAreaHeight = rectBorder.Height();
CSize HorzAloneSize(nDockAreaWidth, pSzBar->m_HorzDockSize.cy);
CSize VertAloneSize(pSzBar->m_VertDockSize.cx, nDockAreaHeight);
// sizes to use when docking into a row that already has some bars.
// use the stored sizes - unless they are > the max dock area -
// in which case make a guess.
if (pSzBar->m_VertDockSize.cy >= nDockAreaHeight - 16)
VertAloneSize.cy = nDockAreaHeight / 3;
else
VertAloneSize.cy = pSzBar->m_VertDockSize.cy;
if (pSzBar->m_HorzDockSize.cx >= nDockAreaWidth - 16)
HorzAloneSize.cx = nDockAreaWidth / 3;
else
HorzAloneSize.cx = pSzBar->m_HorzDockSize.cx;
m_rectDragHorz = CRect(rect.TopLeft(), HorzAloneSize);
m_rectDragVert = CRect(rect.TopLeft(), VertAloneSize);
//
///////////////////
// rectangle for the floating frame...
m_rectFrameDragVert = m_rectFrameDragHorz =
CRect(rect.TopLeft(), pSzBar->m_FloatSize);
// To work out the size we actually create a floating mini frame, and then see how big
// it is
CMiniDockFrameWnd* pFloatFrame =
m_pDockSite->CreateFloatingFrame(bHorz ? CBRS_ALIGN_TOP : CBRS_ALIGN_LEFT);
if (pFloatFrame == NULL)
AfxThrowMemoryException();
pFloatFrame->CalcWindowRect(&m_rectFrameDragHorz);
pFloatFrame->CalcWindowRect(&m_rectFrameDragVert);
// m_rectFrameDragHorz.InflateRect(-afxData.cxBorder2, -afxData.cyBorder2);
// m_rectFrameDragVert.InflateRect(-afxData.cxBorder2, -afxData.cyBorder2);
pFloatFrame->DestroyWindow();
// adjust rectangles so that point is inside
AdjustRectangle(m_rectDragHorzAlone, pt);
AdjustRectangle(m_rectDragVertAlone, pt);
AdjustRectangle(m_rectDragHorz, pt);
AdjustRectangle(m_rectDragVert, pt);
AdjustRectangle(m_rectFrameDragHorz, pt);
AdjustRectangle(m_rectFrameDragVert, pt);
// lock window update while dragging
ASSERT(m_pDC == NULL);
CWnd* pWnd = CWnd::GetDesktopWindow();
#ifndef _MAC
if (pWnd->LockWindowUpdate())
m_pDC = pWnd->GetDCEx(NULL, DCX_WINDOW|DCX_CACHE|DCX_LOCKWINDOWUPDATE);
else
#endif
m_pDC = pWnd->GetDCEx(NULL, DCX_WINDOW|DCX_CACHE);
ASSERT(m_pDC != NULL);
// initialize tracking state and enter tracking loop
m_dwOverDockStyle = CanDock();
Move(pt); // call it here to handle special keys
Track();
}
void COXDragDockContext::Move(CPoint pt)
{
CPoint ptOffset = pt - m_ptLast;
#ifdef _MAC
// prevent dragging the floating window completely under the menu bar
GDHandle hgd = _AfxFindDevice(pt.x, pt.y);
if (hgd == NULL || hgd == GetMainDevice() ||
TestDeviceAttribute(hgd, hasAuxMenuBar))
{
CRect rect;
if ((HORZF(m_dwStyle) && !m_bFlip) || (VERTF(m_dwStyle) && m_bFlip))
rect = m_rectFrameDragHorz;
else
rect = m_rectFrameDragVert;
// determine our new position
rect.OffsetRect(ptOffset);
// keep us on the screen if we were getting too close to the menu bar
int yMBarBottom = (*hgd)->gdRect.top + GetMBarHeight() + 4;
if (rect.bottom < yMBarBottom)
{
pt.y += yMBarBottom - rect.bottom;
ptOffset.y += yMBarBottom - rect.bottom;
}
}
#endif
// offset all drag rects to new position
m_rectDragHorz.OffsetRect(ptOffset);
m_rectFrameDragHorz.OffsetRect(ptOffset);
m_rectDragVert.OffsetRect(ptOffset);
m_rectFrameDragVert.OffsetRect(ptOffset);
// these rectangles only move in 1 direction
m_rectDragHorzAlone.top += ptOffset.y;
m_rectDragHorzAlone.bottom += ptOffset.y;
m_rectDragVertAlone.left += ptOffset.x;
m_rectDragVertAlone.right += ptOffset.x;
m_ptLast = pt;
// if control key is down don't dock
m_dwOverDockStyle = m_bForceFrame ? 0 : CanDock();
// update feedback
DrawFocusRect();
}
void COXDragDockContext::OnKey(int nChar, BOOL bDown)
{
if (nChar == VK_CONTROL)
UpdateState(&m_bForceFrame, bDown);
}
void COXDragDockContext::EndDrag()
{
CancelDrag();
if (m_ptStart == m_ptLast)
return;
m_dwOverDockStyle = m_bForceFrame ? 0 : CanDock();
if (m_dwOverDockStyle != 0)
{
// dockbar we're going to dock at.
CDockBar* pDockBar = GetDockBar();
ASSERT(pDockBar != NULL);
// check the original dockbar - if a valid CSizeDockBar...
// work out the row number.
CDockBar* pOrigDockBar = m_pBar->m_pDockBar;
int nOrigCheckSum = -1;
if (pOrigDockBar != NULL &&
pOrigDockBar->IsKindOf(RUNTIME_CLASS(COXSizeDockBar)))
nOrigCheckSum = ((COXSizeDockBar*)pOrigDockBar)->CheckSumBars();
// Now we're going to actually dock the window.
// Update the appropriate size in the control bar.
if (HORZF(m_dwOverDockStyle))
{
((COXSizeControlBar*)m_pBar)->m_HorzDockSize = m_rectDragDock.Size();
}
else
{
((COXSizeControlBar*)m_pBar)->m_VertDockSize = m_rectDragDock.Size();
}
m_pDockSite->DockControlBar(m_pBar, pDockBar, m_rectDragDock);
// if into a sizeable dockbar (always we be !), then adjust other bars in the same row
// to attempt to maintain size
if (pDockBar->IsKindOf(RUNTIME_CLASS(COXSizeDockBar)))
{
if (pOrigDockBar != pDockBar ||
((COXSizeDockBar*)pDockBar)->CheckSumBars() != nOrigCheckSum)
{
((COXSizeDockBar*)pDockBar)->AdjustForNewBar(m_pBar);
}
// force RecalcLayout below to adjust sizes always for the bar into
// which we have docked - this is needed as if the bar doesn't
// actually changed position in the array, but has changed size
// (because the docking algorithm above guess the size wrong, then
// we need to set it back again.
((COXSizeDockBar*)pDockBar)->m_CountBars = 0;
}
// This RecalcLayout is what will adjust the size.
m_pDockSite->RecalcLayout();
}
else
{
m_ptMRUFloatPos = m_rectFrameDragHorz.TopLeft();
m_pDockSite->FloatControlBar(m_pBar, m_rectFrameDragHorz.TopLeft(),
CBRS_ALIGN_TOP | (m_dwDockStyle & CBRS_FLOAT_MULTI));
m_pBar->SendMessage(WM_OX_APP_AFTERFLOAT_MSG);
// set flag to indicate user has moved the bar - done as a style flag on the window.
CWnd* pFrameWnd = m_pBar->GetParentFrame();
ASSERT(pFrameWnd->IsKindOf(RUNTIME_CLASS(CMiniDockFrameWnd)));
pFrameWnd->ModifyStyle(0, CBRS_MOVED_BY_USER);
}
}
void COXDragDockContext::CancelDrag()
{
DrawFocusRect(TRUE); // gets rid of focus rect
ReleaseCapture();
CWnd* pWnd = CWnd::GetDesktopWindow();
#ifndef _MAC
pWnd->UnlockWindowUpdate();
#endif
if (m_pDC != NULL)
{
pWnd->ReleaseDC(m_pDC);
m_pDC = NULL;
}
// Tell main window to clear it's status bar
CWnd* pMainWnd = AfxGetMainWnd();
ASSERT(pMainWnd != NULL);
pMainWnd->SendMessage(WM_SETMESSAGESTRING, (WPARAM)AFX_IDS_IDLEMESSAGE);
}
/////////////////////////////////////////////////////////////////////////////
// Implementation
void COXDragDockContext::DrawFocusRect(BOOL bRemoveRect)
{
ASSERT(m_pDC != NULL);
// default to thin frame
CSize size(CX_BORDER, CY_BORDER);
// determine new rect and size
CRect rect;
CBrush* pWhiteBrush =
CBrush::FromHandle((HBRUSH)::GetStockObject(WHITE_BRUSH));
CBrush* pDitherBrush = CDC::GetHalftoneBrush();
CBrush* pBrush = pWhiteBrush;
if (m_dwOverDockStyle != 0)
{
rect = m_rectDragDock;
}
else
{
#ifndef _MAC
// use thick frame instead
size.cx = GetSystemMetrics(SM_CXFRAME) - CX_BORDER;
size.cy = GetSystemMetrics(SM_CYFRAME) - CY_BORDER;
#endif
rect = m_rectFrameDragHorz;
pBrush = pDitherBrush;
}
if (bRemoveRect)
size.cx = size.cy = 0;
#if _MFC_VER < 0x0700
if ( afxData.bWin4 && (HORZF(m_dwOverDockStyle) || VERTF(m_dwOverDockStyle)))
#else
if (HORZF(m_dwOverDockStyle) || VERTF(m_dwOverDockStyle))
#endif
{
// looks better one pixel in (makes the bar look pushed down)
rect.InflateRect(-CX_BORDER, -CY_BORDER);
}
// draw it and remember last size
m_pDC->DrawDragRect(&rect, size, &m_rectLast, m_sizeLast,
pBrush, m_bDitherLast ? pDitherBrush : pWhiteBrush);
m_rectLast = rect;
m_sizeLast = size;
m_bDitherLast = (pBrush == pDitherBrush);
}
void COXDragDockContext::UpdateState(BOOL* pFlag, BOOL bNewValue)
{
if (*pFlag != bNewValue)
{
*pFlag = bNewValue;
m_dwOverDockStyle = (m_bForceFrame) ? 0 : CanDock();
DrawFocusRect();
}
}
DWORD COXDragDockContext::CanDock()
{
BOOL bStyleHorz;
DWORD dwDock = 0; // Dock Canidate
DWORD dwCurr = 0; // Current Orientation
// let's check for something in our current orientation first
// then if the shift key is not forcing our orientation then
// check for horizontal or vertical orientations as long
// as we are close enough
ASSERT(m_dwStyle != 0);
bStyleHorz = HORZF(m_dwStyle);
m_pTargetDockBar = NULL;
// can simplify this, 'cos most of the rectangles are actually the same
if (dwDock == 0 && HORZF(m_dwDockStyle)/* && m_rectDragHorzAlone.PtInRect(m_ptLast)*/)
{
dwDock = m_pDockSite->CanDock(m_rectDragHorzAlone,
m_dwDockStyle & ~CBRS_ORIENT_VERT, (CDockBar**)&m_pTargetDockBar);
}
if (dwDock == 0 && VERTF(m_dwDockStyle)/* && m_rectDragVertAlone.PtInRect(m_ptLast)*/)
{
dwDock = m_pDockSite->CanDock(m_rectDragVertAlone,
m_dwDockStyle & ~CBRS_ORIENT_HORZ, (CDockBar**)&m_pTargetDockBar);
}
if (dwDock != 0) // will dock somewhere - now look for 1/2 bars on the row...
{
if (HORZF(dwDock))
{
m_rectDragDock = m_rectDragHorzAlone;
dwCurr = m_pDockSite->CanDock(m_rectDragHorz,
m_dwDockStyle & ~CBRS_ORIENT_VERT, (CDockBar**)&m_pTargetDockBar);
if (dwCurr != 0)
{
int nBars=m_pTargetDockBar->BarsOnThisRow(m_pBar,m_rectDragHorzAlone);
if(nBars!=0)
{
m_rectDragDock = m_rectDragHorz;
// v9.3 - update 04 fixed resizing of docked item if moving to same bar - AAW 2009-03-29
if (m_pBar->m_pDockBar == m_pTargetDockBar)
{
// if we're planning to move it to the same bar, don't change our width.
CRect rct;
m_pBar->GetWindowRect(&rct);
m_rectDragDock.bottom = m_rectDragDock.right +
rct.Width();
}
else
// AAW END
{
m_rectDragDock.right = m_rectDragDock.left+
m_rectDragHorzAlone.Width()/(nBars+1);
}
}
}
}
if (VERTF(dwDock))
{
m_rectDragDock = m_rectDragVertAlone;
dwCurr = m_pDockSite->CanDock(m_rectDragVert,
m_dwDockStyle & ~CBRS_ORIENT_HORZ, (CDockBar**)&m_pTargetDockBar);
if (dwCurr != 0)
{
int nBars=m_pTargetDockBar->BarsOnThisRow(m_pBar,m_rectDragVertAlone);
if(nBars!=0)
{
m_rectDragDock = m_rectDragVert;
// v9.3 - update 04 fixed resizing of docked item if moving to same bar - AAW 2009-03-29
if (m_pBar->m_pDockBar == m_pTargetDockBar)
{ // if we're planning to move it to the same bar, don't change our height.
CRect rct;
m_pBar->GetWindowRect(&rct);
m_rectDragDock.bottom = m_rectDragDock.top +
rct.Height();
}
else
// AAW END
{
m_rectDragDock.bottom = m_rectDragDock.top+
m_rectDragVertAlone.Height()/(nBars+1);
}
}
}
}
}
return dwCurr;
}
// should have m_dwOverDockStyle and m_rectDragDock set before calling this
// routine.
CDockBar* COXDragDockContext::GetDockBar()
{
DWORD dw = 0;
CDockBar* pBar;
if (HORZF(m_dwOverDockStyle))
{
dw = m_pDockSite->CanDock(m_rectDragDock,
m_dwOverDockStyle & ~CBRS_ORIENT_VERT, &pBar);
ASSERT(dw != 0);
ASSERT(pBar != NULL);
return pBar;
}
if (VERTF(m_dwOverDockStyle))
{
dw = m_pDockSite->CanDock(m_rectDragDock,
m_dwOverDockStyle & ~CBRS_ORIENT_HORZ, &pBar);
ASSERT(dw != 0);
ASSERT(pBar != NULL);
return pBar;
}
return NULL;
}
BOOL COXDragDockContext::Track()
{
// don't handle if capture already set
if (::GetCapture() != NULL)
return FALSE;
// set capture to the window which received this message
m_pBar->SetCapture();
ASSERT(m_pBar == CWnd::GetCapture());
// get messages until capture lost or cancelled/accepted
while(CWnd::GetCapture() == m_pBar)
{
MSG msg;
#ifndef _MAC
if (!::GetMessage(&msg, NULL, 0, 0))
#else
// don't allow yielding while tracking since we don't have LockWindowUpdate
if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE|PM_NOYIELD))
continue;
if (msg.message == WM_QUIT)
#endif
{
AfxPostQuitMessage(PtrToInt(msg.wParam));
break;
}
switch (msg.message)
{
case WM_LBUTTONUP: // drag finished
EndDrag();
return TRUE;
case WM_MOUSEMOVE:
Move(msg.pt);
break;
case WM_KEYUP:
OnKey((int)msg.wParam, FALSE);
break;
case WM_KEYDOWN:
OnKey((int)msg.wParam, TRUE);
if (msg.wParam == VK_ESCAPE)
goto exit_cancel_drag;
break;
case WM_RBUTTONDOWN:
goto exit_cancel_drag;
// just dispatch rest of the messages
default:
DispatchMessage(&msg);
break;
}
}
exit_cancel_drag: // goto - can't use break as we're inside a switch()
CancelDrag();
return FALSE;
}
// Wow - another genuine virtual function in CDockContext !
// overridden to prevent windows floated in an MDI window from being double-clicked
void COXDragDockContext::ToggleDocking()
{
ASSERT_VALID(m_pBar);
ASSERT(m_pBar->IsKindOf(RUNTIME_CLASS(COXSizeControlBar)));
// check to see we're not hanging from a COXMDIFloatWnd.
// Disallow toggle docking if we are
if (m_pBar->IsFloating())
{
CFrameWnd* pFrameWnd = m_pBar->GetParentFrame();
ASSERT(pFrameWnd != NULL);
ASSERT(pFrameWnd->IsKindOf(RUNTIME_CLASS(CFrameWnd)));
if (pFrameWnd->IsKindOf(RUNTIME_CLASS(COXMDIFloatWnd)))
return; // do nothing if floating inside a COXMDIFloatWnd
}
CDockContext::ToggleDocking();
}