|
|
// File: Toolbar.cpp
#include "precomp.h"
#include "GenContainers.h"
#include "GenControls.h"
#include <windowsx.h>
// Minimum size for children;
// BUGBUG georgep; Should probably set this to 0 after debugging
const static int MinSize = 10;
// Default m_gap
const static int HGapSize = 4; // Default m_hMargin
const static int HMargin = 0; // Default m_vMargin
const static int VMargin = 0;
// Init m_uRightIndex and m_uCenterIndex to very large numbers
CToolbar::CToolbar() : m_gap(HGapSize), m_hMargin(HMargin), m_vMargin(VMargin), m_nAlignment(TopLeft), m_uRightIndex(static_cast<UINT>(-1)), m_bHasCenterChild(FALSE), m_bReverseOrder(FALSE), m_bMinDesiredSize(FALSE), m_bVertical(FALSE) { }
BOOL CToolbar::Create( HWND hWndParent, // The parent of the toolbar window
DWORD dwExStyle // The extended style of the toolbar window
) { return(CGenWindow::Create( hWndParent, // Window parent
0, // ID of the child window
TEXT("NMToolbar"), // Window name
WS_CLIPCHILDREN, // Window style; WS_CHILD|WS_VISIBLE will be added to this
dwExStyle|WS_EX_CONTROLPARENT // Extended window style
)); }
// Get the desired size for a child, and make sure it is big enough
static void GetWindowDesiredSize(HWND hwnd, SIZE *ppt) { ppt->cx = ppt->cy = 0;
IGenWindow *pWnd = CGenWindow::FromHandle(hwnd); if (NULL != pWnd) { pWnd->GetDesiredSize(ppt); }
ppt->cx = max(ppt->cx, MinSize); ppt->cy = max(ppt->cy, MinSize); }
BOOL IsChildVisible(HWND hwndChild) { return((GetWindowLong(hwndChild, GWL_STYLE)&WS_VISIBLE) == WS_VISIBLE); }
/** Get the total desired size of the child windows: max of heights and sum of
* widths or vice versa for vertical windows. * @param hwndParent The window whose children are to be examined * @param size The returned total size * @param bVertical Whether to flow vertical or horizontal * @returns The number of visible child windows */ static int GetChildTotals(HWND hwndParent, SIZE *size, BOOL bVertical) { int nChildren = 0; int xMax=0, xTot=0; int yMax=0, yTot=0;
for (HWND hwndChild=::GetWindow(hwndParent, GW_CHILD); NULL!=hwndChild; hwndChild=::GetWindow(hwndChild, GW_HWNDNEXT)) { if (!IsChildVisible(hwndChild)) { continue; } ++nChildren;
SIZE pt; GetWindowDesiredSize(hwndChild, &pt);
xTot += pt.cx; yTot += pt.cy; if (xMax < pt.cx) xMax = pt.cx; if (yMax < pt.cy) yMax = pt.cy; }
if (bVertical) { size->cx = xMax; size->cy = yTot; } else { size->cx = xTot; size->cy = yMax; }
return(nChildren); }
// Returns the total children desired size, plus the gaps and margins.
void CToolbar::GetDesiredSize(SIZE *ppt) { int nChildren = GetChildTotals(GetWindow(), ppt, m_bVertical);
if (nChildren > 1 && !m_bMinDesiredSize) { if (m_bVertical) { ppt->cy += (nChildren-1) * m_gap; } else { ppt->cx += (nChildren-1) * m_gap; } }
ppt->cx += m_hMargin * 2; ppt->cy += m_vMargin * 2;
SIZE sizeTemp; CGenWindow::GetDesiredSize(&sizeTemp); ppt->cx += sizeTemp.cx; ppt->cy += sizeTemp.cy; }
void CToolbar::AdjustPos(POINT *pPos, SIZE *pSize, UINT width) { pPos->x = pPos->y = 0;
switch (m_nAlignment) { default: case TopLeft: // Nothing to do
break;
case Center: if (m_bVertical) { pPos->x = (width - pSize->cx)/2; } else { pPos->y = (width - pSize->cy)/2; } break;
case BottomRight: if (m_bVertical) { pPos->x = (width - pSize->cx); } else { pPos->y = (width - pSize->cy); } break;
case Fill: if (m_bVertical) { pSize->cx = width; } else { pSize->cy = width; } break; } }
// Get the first child to layout
HWND CToolbar::GetFirstKid() { HWND ret = ::GetWindow(GetWindow(), GW_CHILD); if (m_bReverseOrder && NULL != ret) { ret = ::GetWindow(ret, GW_HWNDLAST); }
return(ret); }
// Get the next child to layout
HWND CToolbar::GetNextKid(HWND hwndCurrent) { return(::GetWindow(hwndCurrent, m_bReverseOrder ? GW_HWNDPREV : GW_HWNDNEXT)); }
extern HDWP SetWindowPosI(HDWP hdwp, HWND hwndChild, int left, int top, int width, int height);
// Flow child windows according to the fields
void CToolbar::Layout() { RECT rc; GetClientRect(GetWindow(), &rc);
// First see how much extra space we have
SIZE sizeTotal; int nChildren = GetChildTotals(GetWindow(), &sizeTotal, m_bVertical); if (0 == nChildren) { // No children, so nothing to layout
return; }
// Add on the margins
sizeTotal.cx += 2*m_hMargin; sizeTotal.cy += 2*m_vMargin;
if (nChildren > 1 || !m_bHasCenterChild) { // Don't layout with children overlapping
rc.right = max(rc.right , sizeTotal.cx); rc.bottom = max(rc.bottom, sizeTotal.cy); }
// Calculate the total gaps between children
int tGap = m_bVertical ? rc.bottom - sizeTotal.cy : rc.right - sizeTotal.cx; int maxGap = (nChildren-1)*m_gap; if (tGap > maxGap) tGap = maxGap; tGap = max(tGap, 0); // This can happen if only a center child
// If we fill, then children in a vertical toolbar go from the left to the
// right margin, and similar for a horizontal toolbar
int fill = m_bVertical ? rc.right-2*m_hMargin : rc.bottom-2*m_vMargin;
// Speed up layout by deferring it
HDWP hdwp = BeginDeferWindowPos(nChildren);
HWND hwndChild; UINT nChild = 0;
// Iterate through the children
UINT uCenterIndex = m_bHasCenterChild ? m_uRightIndex-1 : static_cast<UINT>(-1); // We need to keep track of whether the middle was skipped in case the
// center control or the first right-aligned control is hidden
BOOL bMiddleSkipped = FALSE;
// Do left/top-aligned children
// The starting point for laying out children
int left = m_hMargin; int top = m_vMargin;
for (hwndChild=GetFirstKid(); NULL!=hwndChild; hwndChild=GetNextKid(hwndChild), ++nChild) { if (!IsChildVisible(hwndChild)) { continue; }
SIZE size; GetWindowDesiredSize(hwndChild, &size);
if (nChild == uCenterIndex) { // Take the window size, subtract all the gaps, and subtract the
// desired size of everybody but this control. That should give
// the "extra" area in the middle
if (m_bVertical) { size.cy = rc.bottom - tGap - (sizeTotal.cy - size.cy); } else { size.cx = rc.right - tGap - (sizeTotal.cx - size.cx); }
bMiddleSkipped = TRUE; } else if (nChild >= m_uRightIndex && !bMiddleSkipped) { // Skip the "extra" room in the middle; if there is a centered
// control, then we have already done this
if (m_bVertical) { top += rc.bottom - tGap - sizeTotal.cy; } else { left += rc.right - tGap - sizeTotal.cx; }
bMiddleSkipped = TRUE; }
POINT pos; AdjustPos(&pos, &size, fill);
// Move the window
hdwp = SetWindowPosI(hdwp, hwndChild, pos.x+left, pos.y+top, size.cx, size.cy);
// calculate the gap; don't just use a "fixed" gap, since children
// would move in chunks
int gap = (nChildren<=1) ? 0 : ((tGap * (nChild+1))/(nChildren-1) - (tGap * nChild)/(nChildren-1));
// Update the pos of the next child
if (m_bVertical) { top += gap + size.cy; } else { left += gap + size.cx; } }
// Actually move all the windows now
EndDeferWindowPos(hdwp); }
LRESULT CToolbar::ProcessMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { HANDLE_MSG(hwnd, WM_COMMAND, OnCommand); }
return(CGenWindow::ProcessMessage(hwnd, message, wParam, lParam)); }
void CToolbar::OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { FORWARD_WM_COMMAND(GetParent(hwnd), id, hwndCtl, codeNotify, SendMessage); }
static HWND FindControl(HWND hwndParent, int nID) { if (GetWindowLong(hwndParent, GWL_ID) == nID) { return(hwndParent); }
for (hwndParent=GetWindow(hwndParent, GW_CHILD); NULL!=hwndParent; hwndParent=GetWindow(hwndParent, GW_HWNDNEXT)) { HWND ret = FindControl(hwndParent, nID); if (NULL != ret) { return(ret); } }
return(NULL); }
IGenWindow *CToolbar::FindControl(int nID) { HWND hwndRet = ::FindControl(GetWindow(), nID); if (NULL == hwndRet) { return(NULL); }
return(FromHandle(hwndRet)); }
CSeparator::CSeparator() : m_iStyle(Normal) { m_desSize.cx = m_desSize.cy = 2; }
BOOL CSeparator::Create( HWND hwndParent, UINT iStyle ) { m_iStyle = iStyle; return(CGenWindow::Create( hwndParent, // Window parent
0, // ID of the child window
TEXT("NMSeparator"), // Window name
WS_CLIPCHILDREN, // Window style; WS_CHILD|WS_VISIBLE will be added to this
WS_EX_CONTROLPARENT // Extended window style
)); }
void CSeparator::GetDesiredSize(SIZE *ppt) { *ppt = m_desSize;
// Make sure there's room for the child
HWND child = GetFirstChild(GetWindow()); if (NULL == child) { // Nothing to do
return; } IGenWindow *pChild = FromHandle(child); if (NULL == pChild) { // Don't know what to do
return; }
SIZE size; pChild->GetDesiredSize(&size);
ppt->cx = max(ppt->cx, size.cx); ppt->cy = max(ppt->cy, size.cy); }
void CSeparator::SetDesiredSize(SIZE *psize) { m_desSize = *psize; OnDesiredSizeChanged(); }
void CSeparator::Layout() { HWND hwnd = GetWindow();
HWND child = GetFirstChild(hwnd); if (NULL == child) { // Nothing to do
return; } IGenWindow *pChild = FromHandle(child); if (NULL == pChild) { // Don't know what to do
return; }
// Center the child horizontally and vertically
SIZE size; pChild->GetDesiredSize(&size);
RECT rcClient; GetClientRect(hwnd, &rcClient);
rcClient.left += (rcClient.right-rcClient.left-size.cx)/2; rcClient.top += (rcClient.bottom-rcClient.top-size.cy)/2;
SetWindowPos(child, NULL, rcClient.left, rcClient.top, size.cx, size.cy, SWP_NOZORDER|SWP_NOACTIVATE); }
void CSeparator::OnPaint(HWND hwnd) { PAINTSTRUCT ps; HDC hdc = BeginPaint(hwnd, &ps);
RECT rc; GetClientRect(hwnd, &rc);
int nFlags = BF_LEFT; if (rc.right < rc.bottom) { // this is a vertical separator
// center the drawing
rc.left += (rc.right-rc.left)/2 - 1; rc.right = 4; } else { // this is a horizontal separator
nFlags = BF_TOP; // center the drawing
rc.top += (rc.bottom-rc.top)/2 - 1; rc.bottom = 4; }
if (Normal == m_iStyle) { DrawEdge(hdc, &rc, EDGE_ETCHED, nFlags); }
EndPaint(hwnd, &ps); }
LRESULT CSeparator::ProcessMessage(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { HANDLE_MSG(hwnd, WM_PAINT, OnPaint); }
return(CGenWindow::ProcessMessage(hwnd, message, wParam, lParam)); }
BOOL CLayeredView::Create( HWND hwndParent, // The parent of this window
DWORD dwExStyle // The extended style
) { return(CGenWindow::Create( hwndParent, 0, TEXT("NMLayeredView"), WS_CLIPCHILDREN, dwExStyle)); }
void CLayeredView::GetDesiredSize(SIZE *psize) { CGenWindow::GetDesiredSize(psize);
HWND child = GetFirstChild(GetWindow()); if (NULL == child) { return; }
SIZE sizeContent;
IGenWindow *pChild; pChild = FromHandle(child); if (NULL != pChild) { // Make sure we can always handle the first window
pChild->GetDesiredSize(&sizeContent); }
for (child=::GetWindow(child, GW_HWNDNEXT); NULL!=child; child=::GetWindow(child, GW_HWNDNEXT)) { if (IsChildVisible(child)) { pChild = FromHandle(child); if (NULL != pChild) { SIZE sizeTemp; pChild->GetDesiredSize(&sizeTemp);
sizeContent.cx = max(sizeContent.cx, sizeTemp.cx); sizeContent.cy = max(sizeContent.cy, sizeTemp.cy);
break; } } }
psize->cx += sizeContent.cx; psize->cy += sizeContent.cy; }
void CLayeredView::Layout() { HWND hwndThis = GetWindow();
RECT rcClient; GetClientRect(hwndThis, &rcClient);
// Just move all the children
for (HWND child=GetFirstChild(hwndThis); NULL!=child; child=::GetWindow(child, GW_HWNDNEXT)) { switch (m_lStyle) { case Center: { IGenWindow *pChild = FromHandle(child); if (NULL != pChild) { SIZE size; pChild->GetDesiredSize(&size); SetWindowPos(child, NULL, (rcClient.left+rcClient.right-size.cx)/2, (rcClient.top+rcClient.bottom-size.cy)/2, size.cx, size.cy, SWP_NOZORDER|SWP_NOACTIVATE); break; } }
// Fall through
case Fill: default: SetWindowPos(child, NULL, rcClient.left, rcClient.top, rcClient.right-rcClient.left, rcClient.bottom-rcClient.top, SWP_NOZORDER|SWP_NOACTIVATE); break; } } }
|