// DialogScroll.cpp : Implements the CDialogScroll class
//
#include "stdafx.h"
#include "DialogScroll.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#ifndef UNUSED_ALWAYS
#define UNUSED_ALWAYS(var)
#endif
IMPLEMENT_DYNAMIC(CDialogScroll, CDialog)
// scroll amount that corresponds to a "line"
static const int LINE_AMOUNT=20;
CPtrList CDialogScroll::m_listIdle;
///////////////////////////////////////////////////////////////////////////////
//
//
//
///////////////////////////////////////////////////////////////////////////////
CDialogScroll::CDialogScroll()
{
m_bEmbedded = TRUE;
Construct(0, 0, 0);
}
///////////////////////////////////////////////////////////////////////////////
//
//
//
///////////////////////////////////////////////////////////////////////////////
CDialogScroll::CDialogScroll(int nWidth, int nHeight)
{
m_bEmbedded = FALSE;
Construct(nWidth, nHeight, 0);
}
///////////////////////////////////////////////////////////////////////////////
//
//
//
///////////////////////////////////////////////////////////////////////////////
CDialogScroll::CDialogScroll(UINT nIDTemplate, int nWidth, int nHeight, CWnd* pParentWnd /*= NULL*/) :
CDialog(nIDTemplate, pParentWnd)
{
m_bEmbedded = FALSE;
Construct(nWidth, nHeight, 0);
}
///////////////////////////////////////////////////////////////////////////////
//
//
//
///////////////////////////////////////////////////////////////////////////////
CDialogScroll::CDialogScroll(UINT nIDTemplate, int nWidth, int nHeight, UINT nDlgBarIDTemplate, CWnd* pParentWnd /*= NULL*/) :
CDialog(nIDTemplate, pParentWnd)
{
m_bEmbedded = FALSE;
Construct(nWidth, nHeight, nDlgBarIDTemplate);
}
BEGIN_MESSAGE_MAP(CDialogScroll, CDialog)
//{{AFX_MSG_MAP(CDialogScroll)
ON_WM_CREATE()
ON_WM_SIZE()
ON_WM_HSCROLL()
ON_WM_VSCROLL()
ON_WM_MOVE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
///////////////////////////////////////////////////////////////////////////////
//
// Called by all constructors
//
///////////////////////////////////////////////////////////////////////////////
void CDialogScroll::Construct(int nWidth, int nHeight, UINT nDlgBarIDTemplate)
{
m_bInit = FALSE;
m_nWidth = nWidth;
m_nHeight = nHeight;
m_nDlgBarIDTemplate = nDlgBarIDTemplate;
m_pDlgBar = NULL;
}
///////////////////////////////////////////////////////////////////////////////
//
//
//
///////////////////////////////////////////////////////////////////////////////
CDialogScroll::~CDialogScroll()
{
CEnvironment* pNode = (CEnvironment*)m_listIdle.RemoveHead();
::UnhookWindowsHookEx(pNode->m_hOldIdleProc);
::UnhookWindowsHookEx(pNode->m_hOldKeyboardProc);
// ::UnhookWindowsHookEx(pNode->m_hOldMessageProc);
delete pNode;
if (NULL != m_pDlgBar)
{
m_pDlgBar->DestroyWindow();
delete m_pDlgBar;
}
}
/////////////////////////////////////////////////////////////////////////////
// CTestScrollDlg message handlers
///////////////////////////////////////////////////////////////////////////////
//
//
//
///////////////////////////////////////////////////////////////////////////////
int CDialogScroll::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CDialog::OnCreate(lpCreateStruct) == -1)
return -1;
return 0;
}
///////////////////////////////////////////////////////////////////////////////
//
//
//
///////////////////////////////////////////////////////////////////////////////
BOOL CDialogScroll::OnInitDialog()
{
CDialog::OnInitDialog();
CRect rect;
GetClientRect(&rect);
m_cxOrig = rect.Width();
m_cyOrig = rect.Height();
m_bVertSBVisible = FALSE;
m_bHorzSBVisible = FALSE;
m_bInit = TRUE;
{
CEnvironment* pNode = new CEnvironment;
pNode->m_pThis = this;
pNode->m_pCtrl = NULL;
//
// if dialog is embedded within another dialog and we will set
// the m_pThisParent to the parent of the embedded dialog
// otherwise we will set it to the this pointer
//
if (m_bEmbedded)
pNode->m_pThisParent = this->GetParent();
else
pNode->m_pThisParent = this;
// hook processing
pNode->m_hOldIdleProc = ::SetWindowsHookEx(WH_FOREGROUNDIDLE,
IdleProc,
AfxGetApp()->m_hInstance,
AfxGetThread()->m_nThreadID);
pNode->m_hOldKeyboardProc = ::SetWindowsHookEx(WH_KEYBOARD,
KeyboardProc,
AfxGetApp()->m_hInstance,
AfxGetThread()->m_nThreadID);
// MessageProc() functionality not currently needed
// pNode->m_hOldMessageProc = ::SetWindowsHookEx(WH_MSGFILTER,
// MessageProc,
// AfxGetApp()->m_hInstance,
// AfxGetThread()->m_nThreadID);
m_listIdle.AddHead(pNode);
}
// adjust window size, if desired by caller
if (0 != m_nWidth || 0 != m_nHeight)
{
CRect rect;
int nSBVWidth = ::GetSystemMetrics(SM_CXVSCROLL);
int nSBHHeight = ::GetSystemMetrics(SM_CYHSCROLL);
GetWindowRect(&rect);
int nWidth = (0 == m_nWidth ? rect.Width() + nSBVWidth : m_nWidth);
int nHeight = (0 == m_nHeight ? rect.Height() + nSBHHeight : m_nHeight);
MoveWindow(rect.TopLeft().x, rect.TopLeft().y, nWidth, nHeight);
}
ASSERT(NULL == m_pDlgBar);
if (0 != m_nDlgBarIDTemplate)
{
// create modeless dialog bar
m_pDlgBar = new CDlgBar(*this);
m_pDlgBar->Create(m_nDlgBarIDTemplate, this);
PositionDlgBar();
}
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
///////////////////////////////////////////////////////////////////////////////
//
//
//
///////////////////////////////////////////////////////////////////////////////
void CDialogScroll::OnMove(int x, int y)
{
CDialog::OnMove(x, y);
PositionDlgBar();
}
///////////////////////////////////////////////////////////////////////////////
//
//
//
///////////////////////////////////////////////////////////////////////////////
void CDialogScroll::OnSize(UINT nType, int cx, int cy)
{
CDialog::OnSize(nType, cx, cy);
if (! m_bInit)
return;
if (SIZE_RESTORED == nType)
{
if (cy < m_cyOrig)
{
// make scrollbar visible, if necessary
if (! m_bVertSBVisible)
{
/* THIS DOESN'T WORK; window does not stay widened
// widen the window
{
CRect rect;
int nSBWidth = ::GetSystemMetrics(SM_CXVSCROLL);
GetWindowRect(&rect);
MoveWindow(rect.TopLeft().x, rect.TopLeft().y,
rect.Width()+nSBWidth, rect.Height());
}
*/
ShowScrollBar(SB_VERT, TRUE);
m_bVertSBVisible = TRUE;
}
int nPos = GetScrollPos(SB_VERT);
// check for need to scroll window
if (nPos + cy > m_cyOrig)
{
ScrollWindow(0, (nPos + cy) - m_cyOrig);
nPos = m_cyOrig - cy;
}
// set scrollbar parameters
{
SCROLLINFO scrollinfo;
scrollinfo.cbSize = sizeof(scrollinfo);
scrollinfo.fMask = SIF_PAGE | SIF_RANGE;
scrollinfo.nMin = 0;
scrollinfo.nMax = m_cyOrig - 1;
scrollinfo.nPage = cy;
SetScrollInfo(SB_VERT, &scrollinfo);
}
// adjust scroll position
SetScrollPos(SB_VERT, nPos);
}
else
{
// make scrollbar invisible, if necessary
if (m_bVertSBVisible)
{
ShowScrollBar(SB_VERT, FALSE);
m_bVertSBVisible = FALSE;
/* THIS DOESN'T WORK; window does not stay narrowed
// narrow the window
{
CRect rect;
int nSBWidth = ::GetSystemMetrics(SM_CXVSCROLL);
GetWindowRect(&rect);
MoveWindow(rect.TopLeft().x, rect.TopLeft().y,
rect.Width()-nSBWidth, rect.Height());
}
*/
}
}
if (cx < m_cxOrig)
{
// make scrollbar visible, if necessary
if (!m_bHorzSBVisible)
{
ShowScrollBar(SB_HORZ, TRUE);
m_bHorzSBVisible = TRUE;
}
int nPos = GetScrollPos(SB_HORZ);
// check for need to scroll window
if (nPos + cx > m_cxOrig)
{
ScrollWindow((nPos + cx) - m_cxOrig, 0);
nPos = m_cxOrig - cx;
}
// set scrollbar parameters
{
SCROLLINFO scrollinfo;
scrollinfo.cbSize = sizeof(scrollinfo);
scrollinfo.fMask = SIF_PAGE | SIF_RANGE;
scrollinfo.nMin = 0;
scrollinfo.nMax = m_cxOrig-1;
scrollinfo.nPage = cx;
SetScrollInfo(SB_HORZ, &scrollinfo);
}
// adjust scroll position
SetScrollPos(SB_HORZ, nPos);
}
else
{
// make scrollbar invisible, if necessary
if (m_bHorzSBVisible)
{
ShowScrollBar(SB_HORZ, FALSE);
m_bHorzSBVisible = FALSE;
}
}
}
PositionDlgBar();
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void CDialogScroll::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
pScrollBar; // fix warning
if (! m_bHorzSBVisible)
// nothing; no scrollbar
return;
CRect rect;
int nMin;
int nMax;
int nOrig = GetScrollPos(SB_HORZ);
int nNew;
GetClientRect(&rect);
GetScrollRange(SB_HORZ, &nMin, &nMax);
switch (nSBCode)
{
case SB_BOTTOM: // Scroll to bottom.
nNew = nMax;
break;
case SB_LINEDOWN: //�Scroll one line down.
nNew = nOrig + LINE_AMOUNT;
break;
case SB_LINEUP: // Scroll one line up.
nNew = nOrig - LINE_AMOUNT;
break;
case SB_PAGEDOWN: //�Scroll one page down.
nNew = nOrig + rect.Width();
break;
case SB_PAGEUP: //�Scroll one page up.
nNew = nOrig - rect.Width();;
break;
case SB_THUMBTRACK: //�Drag scroll box to specified position. The current position is provided in nPos.
nNew = nPos;
break;
case SB_TOP: //���Scroll to top.
nNew = 0;
break;
default:
nNew = nOrig;
break;
}
if (nNew < 0)
nNew = 0;
else
{
int nLimit = nMax - rect.Width() + 1;
if (nNew > nLimit)
nNew = nLimit;
}
if (nNew != nOrig)
{
ScrollWindow(nOrig-nNew, 0);
SetScrollPos(SB_HORZ, nNew);
}
}
///////////////////////////////////////////////////////////////////////////////
//
//
//
///////////////////////////////////////////////////////////////////////////////
void CDialogScroll::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
UNUSED_ALWAYS(pScrollBar);
if (! m_bVertSBVisible)
// nothing; no scrollbar
return;
CRect rect;
int nMin;
int nMax;
int nOrig = GetScrollPos(SB_VERT);
int nNew;
GetClientRect(&rect);
GetScrollRange(SB_VERT, &nMin, &nMax);
switch (nSBCode)
{
case SB_BOTTOM: nNew = nMax; break;
case SB_LINEDOWN: nNew = nOrig + LINE_AMOUNT; break;
case SB_LINEUP: nNew = nOrig - LINE_AMOUNT; break;
case SB_PAGEDOWN: nNew = nOrig + rect.Height(); break;
case SB_PAGEUP: nNew = nOrig - rect.Height(); break;
//�Drag scroll box to specified position.
// The current position is provided in nPos.
case SB_THUMBTRACK: nNew = nPos; break;
case SB_TOP: nNew = 0; break;
default:
nNew = nOrig;
break;
}
if (nNew < 0)
nNew = 0;
else
{
int nLimit = nMax - rect.Height() + 1;
if (nNew > nLimit)
nNew = nLimit;
}
if (nNew != nOrig)
{
ScrollWindow(0, nOrig - nNew);
SetScrollPos(SB_VERT, nNew);
}
}
///////////////////////////////////////////////////////////////////////////////
// Internal helper functions
///////////////////////////////////////////////////////////////////////////////
BOOL CDialogScroll::CreateAsChild(UINT nTemplateID, UINT nWndToReplaceID, BOOL bBorder, CWnd* pParent)
{
ASSERT(pParent);
CRect rect;
CWnd* tempWnd; // wnd that dialog will be placed at
tempWnd = pParent->GetDlgItem(nWndToReplaceID);
tempWnd->GetWindowRect(&rect);
pParent->ScreenToClient(&rect);
if (! (Create(nTemplateID, pParent)))
return FALSE;
// add border if needed
if (bBorder)
ModifyStyle(0, WS_BORDER);
SetWindowPos(tempWnd, rect.left, rect.top, rect.Width(), rect.Height(), SWP_SHOWWINDOW);
// destroy window that we are replacing
tempWnd->DestroyWindow();
return TRUE;
}
///////////////////////////////////////////////////////////////////////////////
//
//
//
///////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK CDialogScroll::IdleProc(int nCode, WPARAM wParam, LPARAM lParam)
{
CEnvironment* pNode = (CEnvironment*)m_listIdle.GetHead();
ASSERT(NULL != pNode);
if (nCode >= 0 && HC_ACTION == nCode) // MUST return DefHookProc
{
CDialogScroll* pThis = pNode->m_pThis; // to reduce indirection
ASSERT(NULL != pThis);
HWND foreGround_hWnd = pThis->GetForegroundWindow()->m_hWnd;
HWND embedded_hWnd = pNode->GetEmbeddedWnd()->m_hWnd;
if (foreGround_hWnd == embedded_hWnd)
{
CWnd* pCtrl = pThis->GetFocus();
if (pCtrl->GetParent()->m_hWnd == pThis->m_hWnd)
{
if (pCtrl != pNode->m_pCtrl)
{
pNode->m_pCtrl = pCtrl;
if (NULL != pCtrl)
{
RECT rectDlg;
RECT rectCtrl;
BOOL bScrolled = FALSE;
pThis->GetClientRect(&rectDlg);
pThis->ClientToScreen(&rectDlg);
pCtrl->GetWindowRect(&rectCtrl);
// bring the control into view if it has scrolled out of view
if (rectCtrl.bottom > rectDlg.bottom)
{
pThis->ScrollWindow(0, rectDlg.bottom - rectCtrl.bottom);
pThis->SetScrollPos(SB_VERT, pThis->GetScrollPos(SB_VERT) + (rectCtrl.bottom - rectDlg.bottom));
bScrolled = TRUE;
}
else if (rectCtrl.top < rectDlg.top)
{
pThis->ScrollWindow(0, rectDlg.top - rectCtrl.top);
pThis->SetScrollPos(SB_VERT, pThis->GetScrollPos(SB_VERT) + (rectCtrl.top - rectDlg.top));
bScrolled = TRUE;
}
// else nothing
if (rectCtrl.right > rectDlg.right)
{
pThis->ScrollWindow(rectDlg.right - rectCtrl.right, 0);
pThis->SetScrollPos(SB_HORZ, pThis->GetScrollPos(SB_HORZ) + (rectCtrl.right - rectDlg.right));
bScrolled = TRUE;
}
else if (rectCtrl.left < rectDlg.left)
{
pThis->ScrollWindow(rectDlg.left - rectCtrl.left, 0);
pThis->SetScrollPos(SB_HORZ, pThis->GetScrollPos(SB_HORZ) + (rectCtrl.left - rectDlg.left));
bScrolled = TRUE;
}
// else nothing
if (bScrolled)
{
// We scrolled. If control is a ComboBox and box is dropped,
// need to hide it since it probably got drawn in the wrong place.
TCHAR szClassName[100];
HWND hWnd = pCtrl->m_hWnd;
::GetClassName(hWnd, szClassName, sizeof(szClassName));
if (CString(_T("ComboBox")) == szClassName)
{
BOOL bDropped = ::SendMessage(hWnd, CB_GETDROPPEDSTATE, 0, 0);
// undrop
::PostMessage(hWnd, CB_SHOWDROPDOWN, FALSE, 0);
if (bDropped)
{
// Redrop
::PostMessage(hWnd, CB_SHOWDROPDOWN, TRUE, 0);
}
}
}
}
}
// else nothing; focus did not change
// handled it
return 1L;
}
}
// else nothing; ours is not the foreground window
}
// did not handle it
return CallNextHookEx(pNode->m_hOldIdleProc, nCode, wParam, lParam);
}
///////////////////////////////////////////////////////////////////////////////
//
//
//
///////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK CDialogScroll::KeyboardProc(int nCode, WPARAM wVKeyCode, LPARAM lFlags)
{
CEnvironment* pNode = (CEnvironment*)m_listIdle.GetHead();
ASSERT(NULL != pNode);
if (nCode >= 0 && HC_ACTION == nCode)
{
CDialogScroll* pThis = pNode->m_pThis; // to reduce indirection
ASSERT(NULL != pThis);
HWND foreGround_hWnd = pThis->GetForegroundWindow()->m_hWnd;
HWND embedded_hWnd = pNode->GetEmbeddedWnd()->m_hWnd;
if (foreGround_hWnd == embedded_hWnd)
{
CWnd* pCtrl = pThis->GetFocus();
if (pCtrl->GetParent()->m_hWnd == pThis->m_hWnd)
{
if (0 == (HIWORD(lFlags) & (KF_UP | KF_ALTDOWN | KF_MENUMODE)))
{
// key pressed that we may be interested in
switch (wVKeyCode)
{
case VK_PRIOR:
pThis->OnVScroll(SB_PAGEUP, 0, NULL);
return 1L; // handled
break;
case VK_NEXT:
pThis->OnVScroll(SB_PAGEDOWN, 0, NULL);
return 1L; // handled
break;
default:
// do nothing
break;
}
}
}
}
// else nothing; ours is not the foreground window
}
// did not handle it
return CallNextHookEx(pNode->m_hOldKeyboardProc, nCode, wVKeyCode, lFlags);
}
///////////////////////////////////////////////////////////////////////////////
//
//
//
///////////////////////////////////////////////////////////////////////////////
LRESULT CALLBACK CDialogScroll::MessageProc(int nCode, WPARAM wUnused, LPARAM lMsg)
{
CEnvironment* pNode = (CEnvironment*)m_listIdle.GetHead();
ASSERT(NULL != pNode);
/* MessageProc() functionality not currently needed
if (nCode >= 0) // MUST return DefHookProc
{
if (MSGF_DIALOGBOX == nCode)
{
CDialogScroll* pThis = pNode->m_pThis; // to reduce indirection
ASSERT(NULL != pThis);
MSG* pMsg = (MSG*)lMsg;
HWND hDlgBar = pThis->m_pDlgBar->m_hWnd;
if (pMsg->hwnd == hDlgBar)
{
if (WM_SETFOCUS == pMsg->message)
{
// prevent dlg bar from getting the focus
pThis->SetFocus();
// handled
return 1L;
}
}
// else nothing; not a message for the dlg bar
}
// else nothing; not HC_ACTION
}
// else nothing; nCode < 0
*/
// did not handle it
return CallNextHookEx(pNode->m_hOldMessageProc, nCode, wUnused, lMsg);
}
///////////////////////////////////////////////////////////////////////////////
//
//
//
///////////////////////////////////////////////////////////////////////////////
void CDialogScroll::PositionDlgBar()
{
if (NULL == m_pDlgBar)
//nothing; no dlg bar
return;
CRect rectParent;
CRect rectDlgBar;
GetClientRect(&rectParent);
ClientToScreen(&rectParent);
m_pDlgBar->GetWindowRect(&rectDlgBar);
rectDlgBar.OffsetRect(rectParent.right - rectDlgBar.right,
rectParent.top - rectDlgBar.top);
m_pDlgBar->MoveWindow(&rectDlgBar);
}
//
//
// CDlgBar functions
//
//
///////////////////////////////////////////////////////////////////////////////
//
//
//
///////////////////////////////////////////////////////////////////////////////
CDialogScroll::CDlgBar::CDlgBar(CDialogScroll& DialogScroll)
: m_DialogScroll(DialogScroll)
{
}
///////////////////////////////////////////////////////////////////////////////
//
//
//
///////////////////////////////////////////////////////////////////////////////
BOOL CDialogScroll::CDlgBar::OnCommand(WPARAM wParam, LPARAM lParam )
{
// redirect message:
return m_DialogScroll.OnCommand(wParam, lParam);
}