// DragFrame.cpp: implementation of the CDragFrame class.
//
// Author : David Shepherd
// Copyright (c) 2002, DaeDoe-Software
//
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "DragFrame.h"
// drag handle size (the width and height are the same)
#define DRAG_HANDLE_SIZE 6
/////////////////////////////////////////////////////////////////////////////
// CDragFrame
CDragFrame::CDragFrame()
{
// initialise everything
m_BackColor=MAKE_OLE_COLOR(COLOR_HIGHLIGHTTEXT);
m_ForeColor=MAKE_OLE_COLOR(COLOR_HIGHLIGHT);
m_pNotifySink=NULL;
m_EnabledDragHandles=DragHandleAll;
m_AllowMove=TRUE;
m_Hollow=FALSE;
m_Action=ActionNone;
m_ActionDragHandle=DragHandleNone;
m_ActionOrigin=CPoint(0,0);
m_ActionInLimbo=FALSE;
}
CDragFrame::~CDragFrame()
{
// clean up
}
BOOL CDragFrame::IsDragHandleEnabled(DragHandle Handle) const
{
// determine if the the specified drag handle is enabled
return (m_EnabledDragHandles & Handle) ? TRUE : FALSE;
}
CRect CDragFrame::GetDragHandleRect(DragHandle Handle) const
{
// get the client rect
CRect ClientRect(0,0,0,0);
(void)GetClientRect(ClientRect);
// calculate the right middle and bottom middle drag handle locations
long dxRight=(ClientRect.Width()-DRAG_HANDLE_SIZE);
long dxMiddle=dxRight/2;
long dyBottom=(ClientRect.Height()-DRAG_HANDLE_SIZE);
long dyMiddle=dyBottom/2;
// return the specified drag handle rectangle
CRect Rect(0,0,DRAG_HANDLE_SIZE,DRAG_HANDLE_SIZE);
switch(Handle)
{
// top left
case DragHandleTopLeft:
Rect.OffsetRect(0,0);
break;
// top
case DragHandleTop:
Rect.OffsetRect(dxMiddle,0);
break;
// top right
case DragHandleTopRight:
Rect.OffsetRect(dxRight,0);
break;
// right
case DragHandleRight:
Rect.OffsetRect(dxRight,dyMiddle);
break;
// bottom right
case DragHandleBottomRight:
Rect.OffsetRect(dxRight,dyBottom);
break;
// bottom
case DragHandleBottom:
Rect.OffsetRect(dxMiddle,dyBottom);
break;
// bottom left
case DragHandleBottomLeft:
Rect.OffsetRect(0,dyBottom);
break;
// left
case DragHandleLeft:
Rect.OffsetRect(0,dyMiddle);
break;
// unknown
default:
ATLASSERT(FALSE);
break;
}
return Rect;
}
CDragFrame::DragHandle CDragFrame::HitTest(const CPoint &Pos) const
{
// determine which drag handle (if any) is located at the passed position
DragHandle DragHandles[]= {
DragHandleTopLeft,
DragHandleTop,
DragHandleTopRight,
DragHandleRight,
DragHandleBottomRight,
DragHandleBottom,
DragHandleBottomLeft,
DragHandleLeft };
DragHandle Handle=DragHandleNone; // assume none
for(long l=0; l<NUM_ELEMENTS(DragHandles,DragHandle); l++)
{
if(GetDragHandleRect(DragHandles[l]).PtInRect(Pos))
{
Handle=DragHandles[l];
break; // done
}
}
return Handle;
}
void CDragFrame::SetColors(OLE_COLOR BackColor,OLE_COLOR ForeColor)
{
// if the colors are changing a redraw will be required
if(m_BackColor!=BackColor or m_ForeColor!=ForeColor)
{
if(IsWindow())
{
(void)Invalidate(FALSE);
}
}
// set the colors
m_BackColor=BackColor;
m_ForeColor=ForeColor;
}
void CDragFrame::GetColors(OLE_COLOR &BackColor,OLE_COLOR &ForeColor) const
{
// return the colors
BackColor=m_BackColor;
ForeColor=m_ForeColor;
}
long CDragFrame::GetFrameSize() const
{
// return the drag frame size (or thickness)
return DRAG_HANDLE_SIZE;
}
BOOL CDragFrame::Create(const CWindow &Parent,const CWindow &AssociatedWindow,
CDragFrameNotifySink *pNotifySink,DWORD EnabledDragHandles/*=DragHandleAll*/,
BOOL AllowMove/*=TRUE*/,BOOL Hollow/*=FALSE*/)
{
// check parameters
ATLASSERT(::IsWindow(Parent));
ATLASSERT(::IsWindow(AssociatedWindow));
ATLASSERT(pNotifySink!=NULL);
// save settings
m_AssociatedWindow=AssociatedWindow;
m_pNotifySink=pNotifySink;
m_EnabledDragHandles=EnabledDragHandles;
m_AllowMove=AllowMove;
m_Hollow=Hollow;
// create the drag frame
ATLASSERT(IsWindow()==FALSE);
if(CWindowImpl<CDragFrame>::Create(Parent,CRect(0,0,0,0),
_T("DragFrame"),WS_CHILD|WS_VISIBLE,WS_EX_TRANSPARENT)==NULL)
{
return FALSE;
}
AutoPositionAndSize(); // auto position and size
(void)Invalidate(FALSE); // required for transparent windows
return TRUE;
}
void CDragFrame::AutoPositionAndSize()
{
// get the associated window rectangle
CRect Rect(0,0,0,0);
(void)m_AssociatedWindow.GetWindowRect(Rect);
// convert to our parents coordinates
CWindow Parent(GetParent());
(void)Parent.ScreenToClient(Rect);
// auto position and size the drag frame to hug the associated window
Rect.InflateRect(DRAG_HANDLE_SIZE,DRAG_HANDLE_SIZE);
(void)SetWindowPos(NULL,Rect,SWP_NOZORDER);
}
void CDragFrame::EnableDragHandles(DWORD EnabledDragHandles)
{
// enable the specified drag handles
if(m_EnabledDragHandles!=EnabledDragHandles)
{
m_EnabledDragHandles=EnabledDragHandles;
(void)Invalidate(FALSE);
}
}
void CDragFrame::ExternalActivate()
{
// get the cursor position
CPoint CursorPos(0,0);
(void)GetCursorPos(&CursorPos);
// convert to our client coordinates
(void)ScreenToClient(&CursorPos);
// package into an lparam
LPARAM lParam=MAKELPARAM(CursorPos.x,CursorPos.y);
// simulate a set cursor message
// todo : set all parameters correctly when required
BOOL Dummy=FALSE;
(void)OnSetCursor(WM_SETCURSOR,0,0,Dummy);
// simulate a left button down message
// todo : set all parameters correctly when required
(void)OnLButtonDown(WM_LBUTTONDOWN,0,lParam,Dummy);
}
LRESULT CDragFrame::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
// get the paint device context
CPaintDC dc(*this);
if(dc==NULL)
{
return 0;
}
// get the client rect
CRect ClientRect(0,0,0,0);
if(GetClientRect(ClientRect)==FALSE)
{
return 0;
}
// set background color
COLORREF RGBBackColor=RGB(0,0,0);
if(!SUCCEEDED(OleTranslateColor(m_BackColor,NULL,&RGBBackColor)))
{
return 0;
}
if(dc.SetBkColor(RGBBackColor)==CLR_INVALID)
{
return 0;
}
// set foreground color
COLORREF RGBForeColor=RGB(0,0,0);
if(!SUCCEEDED(OleTranslateColor(m_ForeColor,NULL,&RGBForeColor)))
{
return 0;
}
if(dc.SetTextColor(RGBForeColor)==CLR_INVALID)
{
return 0;
}
// create background brush
CBrush BackBrush;
if(BackBrush.CreateSolidBrush(RGBBackColor)==NULL)
{
return 0;
}
// create foreground brush
CBrush ForeBrush;
if(ForeBrush.CreateSolidBrush(RGBForeColor)==NULL)
{
return 0;
}
// create halftone brush
CBrush HalfToneBrush=dc.GetHalftoneBrush();
if(HalfToneBrush==NULL)
{
return 0;
}
// create foreground pen
CPen ForePen;
if(ForePen.CreatePen(PS_SOLID,0,RGBForeColor)==NULL)
{
return 0;
}
// initialise the dc
CPen OldPen=dc.SelectPen(ForePen);
if(OldPen==NULL)
{
return 0;
}
// fill in the frame background
// left
CRect LeftBorder(0,0,DRAG_HANDLE_SIZE,ClientRect.Height());
dc.FillSolidRect(LeftBorder,RGBBackColor);
// top
CRect TopBorder(0,0,ClientRect.Width(),DRAG_HANDLE_SIZE);
dc.FillSolidRect(TopBorder,RGBBackColor);
// right
CRect RightBorder(LeftBorder);
RightBorder.OffsetRect(ClientRect.Width()-DRAG_HANDLE_SIZE,0);
dc.FillSolidRect(RightBorder,RGBBackColor);
// bottom
CRect BottomBorder(TopBorder);
BottomBorder.OffsetRect(0,ClientRect.Height()-DRAG_HANDLE_SIZE);
dc.FillSolidRect(BottomBorder,RGBBackColor);
// fill in the frame center
long Deflation=(DRAG_HANDLE_SIZE-/*center width*/2)/2;
// left
CRect LeftCenter(LeftBorder);
LeftCenter.DeflateRect(Deflation,Deflation);
(void)dc.FillRect(LeftCenter,HalfToneBrush);
// top
CRect TopCenter(TopBorder);
TopCenter.DeflateRect(Deflation,Deflation);
(void)dc.FillRect(TopCenter,HalfToneBrush);
// right
CRect RightCenter(RightBorder);
RightCenter.DeflateRect(Deflation,Deflation);
(void)dc.FillRect(RightCenter,HalfToneBrush);
// bottom
CRect BottomCenter(BottomBorder);
BottomCenter.DeflateRect(Deflation,Deflation);
(void)dc.FillRect(BottomCenter,HalfToneBrush);
// draw the drag handles
DragHandle DragHandles[]= {
DragHandleTopLeft,
DragHandleTop,
DragHandleTopRight,
DragHandleRight,
DragHandleBottomRight,
DragHandleBottom,
DragHandleBottomLeft,
DragHandleLeft };
// this is done in reverse order since the drag handle with the
// highest priority should be at the top
for(long l=NUM_ELEMENTS(DragHandles,DragHandle)-1; l>=0; l--)
{
// draw the drag handle
CBrushHandle OldBrush=dc.SelectBrush(
IsDragHandleEnabled(DragHandles[l]) ? ForeBrush : BackBrush);
if(OldBrush!=NULL)
{
(void)dc.Rectangle(GetDragHandleRect(DragHandles[l]));
(void)dc.SelectBrush(OldBrush);
}
}
// clean up the dc
if(OldPen!=NULL)
{
(void)dc.SelectPen(OldPen);
}
return 0;
}
LRESULT CDragFrame::OnEraseBkgnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
// to prevent flicker do not erase the background
return TRUE;
}
LRESULT CDragFrame::OnSetCursor(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
// get the cursor position
CPoint CursorPos(0,0);
(void)GetCursorPos(&CursorPos);
// convert to our client coordinates
(void)ScreenToClient(&CursorPos);
// get the cursor to display
HCURSOR hCursor=NULL;
DragHandle Handle=HitTest(CursorPos);
switch(Handle)
{
// top left / bottom right
case DragHandleTopLeft:
case DragHandleBottomRight:
hCursor=LoadCursor(NULL,IDC_SIZENWSE);
break;
// top / bottom
case DragHandleTop:
case DragHandleBottom:
hCursor=LoadCursor(NULL,IDC_SIZENS);
break;
// top right / bottom left
case DragHandleTopRight:
case DragHandleBottomLeft:
hCursor=LoadCursor(NULL,IDC_SIZENESW);
break;
// right / left
case DragHandleRight:
case DragHandleLeft:
hCursor=LoadCursor(NULL,IDC_SIZEWE);
break;
// anything else
default:
hCursor=LoadCursor(NULL,(m_AllowMove) ? IDC_SIZEALL : IDC_ARROW);
break;
}
// if the cursor is over a disabled drag handle
if(Handle!=DragHandleNone and IsDragHandleEnabled(Handle)==FALSE)
{
hCursor=LoadCursor(NULL,(m_AllowMove) ? IDC_SIZEALL : IDC_ARROW);
}
// set the cursor
if(hCursor!=NULL)
{
(void)SetCursor(hCursor);
}
return TRUE;
}
LRESULT CDragFrame::OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
// get the cursor position
CPoint CursorPos(GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam));
// initialise action settings
ATLASSERT(m_Action==ActionNone);
m_ActionDragHandle=HitTest(CursorPos);
m_ActionOrigin=CursorPos;
m_ActionInLimbo=TRUE;
// determine which action to perform
if(m_ActionDragHandle==DragHandleNone or
IsDragHandleEnabled(m_ActionDragHandle)==FALSE)
{
// initiate a move action (if allowed)
m_Action=m_AllowMove ? ActionMove : ActionNone;
}
else
{
// initiate a size action
m_Action=ActionSize;
}
// capture the cursor
(void)SetCapture();
return 0;
}
LRESULT CDragFrame::OnLButtonUP(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
try // catch all errors from the sink
{
// if the action is not in limbo
if(m_ActionInLimbo==FALSE)
{
// end the action
if(m_Action==ActionMove)
{
m_pNotifySink->DFNS_EndMove();
}
else if (m_Action==ActionSize)
{
m_pNotifySink->DFNS_EndSize();
}
}
}
catch(...)
{}
// clean up action settings
m_Action=ActionNone;
m_ActionDragHandle=DragHandleNone;
m_ActionOrigin=CPoint(0,0);
m_ActionInLimbo=FALSE;
// release the cursor
// we need to be careful here since ReleaseCapture() calls
// OnCaptureChanged() which in turn calls us back just incase
// the capture was taken from under our feet
if(GetCapture()==*this)
{
(void)ReleaseCapture();
}
return 0;
}
LRESULT CDragFrame::OnRButtonUp(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
// initiate the context menu
try { m_pNotifySink->DFNS_ContextMenu(); }
catch(...)
{}
return 0;
}
LRESULT CDragFrame::OnMouseMove(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
// get the cursor position
CPoint CursorPos(GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam));
// get the action cursor delta
CPoint CursorDelta=CursorPos-m_ActionOrigin;
// determine if we can release the action from limbo
BOOL ReleaseActionFromLimbo=FALSE;
if(m_Action!=ActionNone and m_ActionInLimbo)
{
if( abs(CursorDelta.x) > GetSystemMetrics(SM_CXDRAG) or
abs(CursorDelta.y) > GetSystemMetrics(SM_CYDRAG))
{
ReleaseActionFromLimbo=TRUE; // release it
}
}
try // catch all errors from the sink
{
// update the action
if(m_Action==ActionMove)
{
if(m_ActionInLimbo and ReleaseActionFromLimbo)
{
m_pNotifySink->DFNS_BeginMove(); // begin the move action
m_ActionInLimbo=FALSE;
}
if(m_ActionInLimbo==FALSE)
{
m_pNotifySink->DFNS_Move( // update the move action
CursorDelta.x,CursorDelta.y);
}
}
else if(m_Action==ActionSize)
{
if(m_ActionInLimbo and ReleaseActionFromLimbo)
{
m_pNotifySink->DFNS_BeginSize(); // begin the size action
m_ActionInLimbo=FALSE;
}
if(m_ActionInLimbo==FALSE)
{
m_pNotifySink->DFNS_Size( // update the size action
m_ActionDragHandle,CursorDelta.x,CursorDelta.y);
}
}
}
catch(...)
{}
return 0;
}
LRESULT CDragFrame::OnCaptureChanged(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
// simulate a left button up message
BOOL Dummy=FALSE;
(void)OnLButtonUP(WM_LBUTTONUP,0,0,Dummy);
return 0;
}
LRESULT CDragFrame::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
// get client dimensions
long cx=LOWORD(lParam);
long cy=HIWORD(lParam);
// if the drag frame is hollow we need to create our window region
if(m_Hollow)
{
// outer rectangle
CRect OuterRect(0,0,cx,cy);
// outer region
CRgn OuterRgn;
if(OuterRgn.CreateRectRgnIndirect(OuterRect)==NULL)
{
return 0;
}
// inner rectangle
CRect InnerRect(OuterRect);
InnerRect.InflateRect(-DRAG_HANDLE_SIZE,-DRAG_HANDLE_SIZE);
// inner region
CRgn InnerRgn;
if(InnerRgn.CreateRectRgnIndirect(InnerRect)==NULL)
{
return 0;
}
// by combining the outer and inner regions together
// we come up with the final window region
CRgn WindowRgn;
if(WindowRgn.CreateRectRgn(0,0,0,0)==NULL)
{
return 0;
}
if(WindowRgn.CombineRgn(OuterRgn,InnerRgn,RGN_DIFF)==ERROR)
{
return 0;
}
(void)SetWindowRgn(WindowRgn.Detach(),FALSE);
}
return 0;
}