2025-11-27 16:46:48 +09:00

780 lines
17 KiB
C++

// ==========================================================================
// Class Implementation :
// COXStatic
// ==========================================================================
// Implementation file : OXStatic.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.
////////////////
// COXStatic implements a static control which could be set to use
// user-defined color and font to draw text. Also you can show tooltip
// and you can make COXStatic automatically adjust its size to fit drawn text
//
#include "StdAfx.h"
#include "OXStatic.h"
#include "UTB64Bit.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//////////////////////////////////////////////////
//////////////////////////////////////////////////
IMPLEMENT_DYNAMIC(COXStatic, CStatic)
BEGIN_MESSAGE_MAP(COXStatic, CStatic)
//{{AFX_MSG_MAP(COXStatic)
ON_WM_CTLCOLOR_REFLECT()
ON_MESSAGE(WM_SETTEXT,OnSetText)
ON_WM_SIZE()
ON_WM_DESTROY()
ON_WM_ERASEBKGND()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
///////////////////
// Constructor
//
// --- In :
// --- Out :
// --- Returns :
// --- Effect : Constructs the object
COXStatic::COXStatic()
{
// background color
m_clrBk=::GetSysColor(COLOR_BTNFACE);
// type of filling
m_typeFillBackground=NOGRADIENT;
// text color
m_clr=::GetSysColor(COLOR_WINDOWTEXT);
m_bShowToolTip=FALSE;
m_bToolTipUserDefined=FALSE;
m_bFitToText=FALSE;
m_bCreated=FALSE;
m_bIsDefaultFontSet=FALSE;
}
void COXStatic::SetBkColor(COLORREF clr, BOOL bRedraw)
{
m_clrBk=clr;
if(bRedraw)
::InvalidateRect(GetSafeHwnd(),NULL,TRUE);
}
void COXStatic::SetBackgroundFillType(BackgroundFillType typeFillBackground,
BOOL bRedraw)
{
m_typeFillBackground=typeFillBackground;
if(bRedraw)
::InvalidateRect(GetSafeHwnd(),NULL,TRUE);
}
void COXStatic::SetTextColor(COLORREF clr, BOOL bRedraw)
{
m_clr=clr;
if(bRedraw)
::InvalidateRect(GetSafeHwnd(),NULL,TRUE);
}
// --- In : plf - pointer to LOGFONT structure
// --- Out : plf - filled the structure with the LOGFONT
// of the font that COXStatic object uses to draw text
// --- Returns: TRUE if plf was successfully populated with
// LOGINFO of the font used by COXStatic object,
// otherwise returns FALSE and plf is undefined
// --- Effect :
BOOL COXStatic::GetTextLogFont(LOGFONT* plf) const
{
if((HFONT)m_font==NULL)
{
return FALSE;
}
return m_font.GetObject(sizeof(*plf),plf);
}
// --- In :
// --- Out :
//
// --- Returns: pointer to the font used by COXStatic object if succeed,
// otherwise returns NULL
// --- Effect :
CFont* COXStatic::GetTextFont()
{
if((HFONT)m_font==NULL)
{
return NULL;
}
return &m_font;
}
// --- In : plf - pointer to LOGFONT structure
// --- Out :
// --- Returns: TRUE if font was successfully set
// --- Effect : sets the font that COXStatic object uses to draw text;
// if you call this function as SetTextLogFont(NULL) then
// text will be drawn using the font which is associated
// with CStatic window by default
BOOL COXStatic::SetTextLogFont(LOGFONT* plf, BOOL bRedraw)
{
if ((HFONT)m_font!=NULL)
{
m_font.DeleteObject();
}
BOOL bResult=TRUE;
if(plf!=NULL)
{
bResult=m_font.CreateFontIndirect(plf);
}
if(bResult)
{
if(m_bFitToText)
{
AdjustToFitText();
}
else
{
if(bRedraw)
{
::InvalidateRect(GetSafeHwnd(),NULL,TRUE);
}
}
}
return bResult;
}
// --- In : pFont - pointer to CFont
// --- Out :
// --- Returns: TRUE if font was successfully set
// --- Effect : sets the font that COXStatic object uses to draw text
BOOL COXStatic::SetTextFont(CFont* pFont, BOOL bRedraw)
{
ASSERT_VALID(pFont);
BOOL bResult=TRUE;
if(pFont!=&m_font)
{
if ((HFONT)m_font!=NULL)
m_font.DeleteObject();
LOGFONT lf;
bResult=pFont->GetLogFont(&lf);
if(bResult)
bResult=m_font.CreateFontIndirect(&lf);
}
if(bResult)
{
if(m_bFitToText)
{
AdjustToFitText();
}
else
{
if(bRedraw)
{
::InvalidateRect(GetSafeHwnd(),NULL,TRUE);
}
}
}
return bResult;
}
// --- In :
// --- Out :
// --- Returns: TRUE if default font was successfully set to the
// COXStatic object, otherwise returns FALSE
// --- Effect : make COXStatic object to use default font to draw text.
// Note that function declared as virtual, so you can put
// in any derivation of COXStatic class your own definition
// of "default font". COXStatic class uses the font which is
// associated with CStatic window as default.
BOOL COXStatic::SetDefaultTextFont()
{
if(!IsStaticText(this))
return TRUE;
CFont* pFont=GetFont();
if(pFont==NULL)
return FALSE;
ASSERT_VALID(pFont);
if((HFONT)m_font!=NULL)
m_font.DeleteObject();
LOGFONT lf;
BOOL bResult=pFont->GetObject(sizeof(lf), &lf);
if(bResult)
{
bResult=m_font.CreateFontIndirect(&lf);
if (bResult && m_bFitToText)
AdjustToFitText();
}
return bResult;
}
// if TRUE then we will adjust size of static control window
// to match the size of the text otherwise we will use original
// size of static control's window
void COXStatic::SetFitToText(BOOL bFitText)
{
m_bFitToText=bFitText;
AdjustToFitText();
}
// Sets the tooltip text. If tooltip text wasn't set and tootip control is still
// to be shown then COXStatic would use window's text as the tooltip text
void COXStatic::SetToolTipText(LPCTSTR sText)
{
m_sToolTipText=sText;
if(m_sToolTipText.IsEmpty())
{
// mark tooltip text as not userdefined to use
// window text as tooltip
m_bToolTipUserDefined=FALSE;
}
else
{
// mark tooltip text as userdefined
m_bToolTipUserDefined=TRUE;
}
FormatToolTipText();
}
// Returns tooltip text if it was set previously by SetToolTipText function.
// If bForse is TRUE then returns the tooltip text even if it wasn't set by
// SetToolTipText function, otherwise returns empty string
CString COXStatic::GetToolTipText(BOOL bForce)
{
if(!bForce && !m_bToolTipUserDefined)
{
return _T("");
}
return m_sToolTipText;
}
// if set to TRUE, the tooltip will be shown (by default TRUE)
void COXStatic::SetShowToolTip(BOOL bShowToolTip)
{
m_bShowToolTip=bShowToolTip;
if(::IsWindow(m_ttc.GetSafeHwnd()))
// activate/deactivate tooltip control
m_ttc.Activate(m_bShowToolTip);
FormatToolTipText();
}
//////////////////
// Handle reflected WM_CTLCOLOR to set custom control color and font.
// For non-text controls, do nothing.
// Note that function is declared as virtual, so any COXStatic derived
// class can define its own function to draw itself.
//
HBRUSH COXStatic::CtlColor(CDC* pDC, UINT nCtlColor)
{
UNREFERENCED_PARAMETER(nCtlColor);
ASSERT(nCtlColor==CTLCOLOR_STATIC);
DWORD dwStyle=GetStyle();
if (!(dwStyle & SS_NOTIFY))
{
// Turn on notify flag to get mouse messages and STN_CLICKED.
// Otherwise, I'll never get any mouse clicks!
::SetWindowLongPtr(m_hWnd, GWL_STYLE, dwStyle | SS_NOTIFY);
}
HBRUSH hbr = NULL;
if(IsStaticText(this))
{
// this is a text control: set up font and colors
//
if((HFONT)m_font!=NULL)
{
// use our font
pDC->SelectObject(&m_font);
}
// use our visited/unvisited colors
pDC->SetTextColor(m_clr);
pDC->SetBkMode(TRANSPARENT);
// return hollow brush to preserve parent background color
hbr=(HBRUSH)::GetStockObject(HOLLOW_BRUSH);
}
return hbr;
}
// handles WM_SETTEXT message in order to possibly adjust size of
// control's window or update tooltip text
LRESULT COXStatic::OnSetText(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(wParam);
UNREFERENCED_PARAMETER(lParam);
BOOL bResult=(BOOL)Default();
FormatToolTipText();
if(m_bFitToText)
AdjustToFitText();
RedrawWindow();
return bResult;
}
// if m_bFitToText is TRUE then adjusts the size of control's window
// to the size of the text, otherwise set size of window to its original size
void COXStatic::AdjustToFitText()
{
if(!IsStaticText(this))
{
// just return if static control is not text control
return;
}
// get the parent window
// all operations with coordinates use parent as origin
CWnd* pParent = GetParent();
if(pParent==NULL)
{
// just return if we didn't find any parent
return;
}
CRect rect;
if (!m_bFitToText)
{
// if m_bFitToText is FALSE then reset the size of control's window
// to its original size
rect=m_rectOriginal;
}
else
{
// Get the current window position
GetWindowRect(rect);
pParent->ScreenToClient(rect);
// Get the size of the window text
CString strWndText;
GetWindowText(strWndText);
CDC* pDC = GetDC();
CFont* pOldFont=NULL;
if ((HFONT)m_font!=NULL)
{
// use our font
pOldFont=pDC->SelectObject(&m_font);
}
CSize sizeText = pDC->GetTextExtent(strWndText);
if(pOldFont!=NULL)
{
pDC->SelectObject(pOldFont);
}
ReleaseDC(pDC);
// make sure the size of text is no more than the size of
// the client part of parent window
CRect rectClient;
pParent->GetClientRect(&rectClient);
if(sizeText.cx>rectClient.Width())
{
sizeText.cx=rectClient.Width();
}
if(sizeText.cy>rectClient.Height())
{
sizeText.cy=rectClient.Height();
}
// Get the text justification via the window style
DWORD dwStyle = GetStyle();
// Recalc the window size and position based on the text justification
if(dwStyle & SS_CENTERIMAGE)
{
rect.DeflateRect(0, (rect.Height() - sizeText.cy)/2);
}
else
{
rect.bottom = rect.top + sizeText.cy;
}
DWORD dwStaticStyle=GetStyle()&0x0000000f;
switch(dwStaticStyle)
{
// SS_CENTER
case 1:
rect.DeflateRect((rect.Width() - sizeText.cx)/2, 0);
break;
// SS_RIGHT
case 2:
rect.left=rect.right - sizeText.cx;
break;
// SS_LEFT
default:
rect.right=rect.left + sizeText.cx;
break;
}
}
// Change information about window's rect in tooltip control
if(::IsWindow(m_ttc.GetSafeHwnd()))
{
CToolInfo toolInfo;
if(m_ttc.GetToolInfo(toolInfo,this,ID_HLTOOLTIP))
{
toolInfo.rect.left=0;
toolInfo.rect.top=0;
toolInfo.rect.right=rect.Width();
toolInfo.rect.bottom=rect.Height();
m_ttc.SetToolInfo(&toolInfo);
}
}
// Move the window
SetWindowPos(NULL, rect.left, rect.top, rect.Width(), rect.Height(), SWP_NOZORDER);
}
// builds tooltip text
void COXStatic::FormatToolTipText()
{
// set only if tooltip is shown
if(m_bShowToolTip)
{
// if tooltip text wasn't set then set it to the window text
if(!m_bToolTipUserDefined)
{
if(IsStaticText(this))
{
GetWindowText(m_sToolTipText);
}
else
{
m_sToolTipText.Empty();
}
}
if(::IsWindow(m_ttc.GetSafeHwnd()))
m_ttc.UpdateTipText(m_sToolTipText, this, ID_HLTOOLTIP);
}
}
// handle it to pass a mouse message to a tool tip control for processing
BOOL COXStatic::PreTranslateMessage(MSG* pMsg)
{
if(::IsWindow(m_ttc.GetSafeHwnd()))
{
m_ttc.Activate(m_bShowToolTip);
m_ttc.RelayEvent(pMsg);
}
return CStatic::PreTranslateMessage(pMsg);
}
// handle it to initialize tooltip control and save the original size
// of static control window
void COXStatic::PreSubclassWindow()
{
if(!m_bCreated && !InitStatic())
{
TRACE(_T("COXStatic::PreSubclassWindow: failed to initialize static control\n"));
}
CStatic::PreSubclassWindow();
}
// handle it to update original size of window and if m_bFitToText is TRUE then
// resize it to fit to its text
void COXStatic::OnSize(UINT nType, int cx, int cy)
{
CStatic::OnSize(nType, cx, cy);
// TODO: Add your message handler code here
if(m_bFitToText)
{
AdjustToFitText();
}
else
{
// update the original size of the static control window
GetWindowRect(&m_rectOriginal);
CWnd* pParent = GetParent();
if(pParent!=NULL)
{
pParent->ScreenToClient(&m_rectOriginal);
}
// Change information about window's rect in tooltip control
if(::IsWindow(m_ttc.GetSafeHwnd()))
{
CToolInfo toolInfo;
if(m_ttc.GetToolInfo(toolInfo,this,ID_HLTOOLTIP))
{
toolInfo.rect.left=0;
toolInfo.rect.top=0;
toolInfo.rect.right=m_rectOriginal.Width();
toolInfo.rect.bottom=m_rectOriginal.Height();
m_ttc.SetToolInfo(&toolInfo);
}
}
RedrawWindow();
}
}
// helper function, defines if static control displays text or graphics
BOOL COXStatic::IsStaticText(CStatic* pStatic) const
{
DWORD dwStyle=pStatic->GetStyle();
return ((dwStyle&0xFF)>SS_RIGHT && (dwStyle&0xFF)!=SS_SIMPLE &&
(dwStyle&0xFF)!=SS_LEFTNOWORDWRAP) ? FALSE : TRUE;
}
BOOL COXStatic::InitStatic()
{
ASSERT(::IsWindow(GetSafeHwnd()));
// save the original size of the static control window
GetWindowRect(&m_rectOriginal);
CWnd* pParent = GetParent();
if(pParent!=NULL)
{
pParent->ScreenToClient(&m_rectOriginal);
}
// Create the tooltip
CRect rect;
GetClientRect(rect);
m_ttc.Create(this);
m_ttc.AddTool(this, m_sToolTipText, rect, ID_HLTOOLTIP);
FormatToolTipText();
// set up default font once!
if(!m_bIsDefaultFontSet && SetDefaultTextFont())
m_bIsDefaultFontSet=TRUE;
return TRUE;
}
BOOL COXStatic::Create(LPCTSTR lpszText, DWORD dwStyle, const RECT& rect,
CWnd* pParentWnd, UINT nID/*=0xffff*/)
{
// mark control as being created
m_bCreated=TRUE;
BOOL bResult=CStatic::Create(lpszText, dwStyle, rect, pParentWnd, nID);
if(bResult)
{
// Initialize static control
if(!InitStatic())
{
TRACE(_T("COXStatic::Create: failed to initialize static control"));
return FALSE;
}
}
return bResult;
}
void COXStatic::OnDestroy()
{
if(::IsWindow(m_ttc.GetSafeHwnd()))
{
m_ttc.DelTool(this, ID_HLTOOLTIP);
m_ttc.DestroyWindow();
}
CStatic::OnDestroy();
}
BOOL COXStatic::OnEraseBkgnd(CDC* pDC)
{
// redraw the background
if(m_clrBk!=CLR_NONE)
{
DrawBackground(pDC);
}
return TRUE;
}
void COXStatic::DrawBackground(CDC* pDC)
{
ASSERT(pDC!=NULL);
CRect rect;
GetClientRect(rect);
pDC->IntersectClipRect(rect);
switch(m_typeFillBackground)
{
case NOGRADIENT:
{
CBrush brush(m_clrBk);
ASSERT((HBRUSH)brush!=NULL);
pDC->FillRect(rect,&brush);
break;
}
case GRADIENT_LEFT:
case GRADIENT_CENTER:
case GRADIENT_RIGHT:
{
FillGradient(pDC,m_typeFillBackground,rect,GetBkColor());
break;
}
default:
ASSERT(FALSE);
}
}
void COXStatic::FillGradient(CDC* pDC, BackgroundFillType typeFillBackground,
CRect rect, COLORREF clr)
{
ASSERT(typeFillBackground!=NOGRADIENT);
// red, green and blue color vals
COLORREF clrBackground=clr;
int red=GetRValue(clrBackground);
int green=GetGValue(clrBackground);
int blue=GetBValue(clrBackground);
int cxCap=rect.Width();
int cyCap=rect.Height();
int nCurBlock;
// width of area to shade and width squared
int nWidth, nWidth_x_2;
// width of one shade band
int nDelta;
UINT nNumberShade=128;
switch(typeFillBackground)
{
case GRADIENT_LEFT:
{
nCurBlock=cxCap;
nWidth=cxCap;
nWidth_x_2=cxCap*cxCap;
nDelta=__max(nWidth/nNumberShade,1);
while(nCurBlock>0)
{
// paint bands right to left
int nRest_x_2=(nWidth-nCurBlock)*(nWidth-nCurBlock);
PaintRect(pDC, nCurBlock, 0, nDelta, cyCap,
RGB(red-(red*nRest_x_2)/nWidth_x_2,
green-(green*nRest_x_2)/nWidth_x_2,
blue-(blue*nRest_x_2)/nWidth_x_2));
// next band
nCurBlock-=nDelta;
}
// whatever's left ==> black
PaintRect(pDC,0,0,nCurBlock+nDelta,cyCap,RGB(0,0,0));
break;
}
case GRADIENT_CENTER:
{
nCurBlock=cxCap/2;
nWidth=cxCap/2;
nWidth_x_2=cxCap*cxCap/4;
nDelta=__max(nWidth/(2*nNumberShade),1);
while(nCurBlock>0)
{
// paint bands right to left
int nRest_x_2=(nWidth-nCurBlock)*(nWidth-nCurBlock);
PaintRect(pDC, nWidth+nCurBlock, 0, nDelta, cyCap,
RGB(red-(red*nRest_x_2)/nWidth_x_2,
green-(green*nRest_x_2)/nWidth_x_2,
blue-(blue*nRest_x_2)/nWidth_x_2));
// next band
nCurBlock-=nDelta;
}
// whatever's left ==> black
PaintRect(pDC,nWidth,0,nCurBlock+nDelta,cyCap,RGB(0,0,0));
nCurBlock=0;
while(nCurBlock<=nWidth)
{
// paint bands left to right
int nRest_x_2=nCurBlock*nCurBlock;
PaintRect(pDC, nCurBlock, 0, nDelta, cyCap,
RGB(red-(red*nRest_x_2)/nWidth_x_2,
green-(green*nRest_x_2)/nWidth_x_2,
blue-(blue*nRest_x_2)/nWidth_x_2));
// next band
nCurBlock+=nDelta;
}
// whatever's left ==> black
PaintRect(pDC,nCurBlock-nDelta,0,nWidth-nCurBlock+nDelta,cyCap,RGB(0,0,0));
break;
}
case GRADIENT_RIGHT:
{
nCurBlock=0;
nWidth=cxCap;
nWidth_x_2=cxCap*cxCap;
nDelta=__max(nWidth/nNumberShade,1);
while(nCurBlock<nWidth)
{
// paint bands left to right
int nRest_x_2=nCurBlock*nCurBlock;
PaintRect(pDC, nCurBlock, 0, nDelta, cyCap,
RGB(red-(red*nRest_x_2)/nWidth_x_2,
green-(green*nRest_x_2)/nWidth_x_2,
blue-(blue*nRest_x_2)/nWidth_x_2));
// next band
nCurBlock+=nDelta;
}
// whatever's left ==> black
PaintRect(pDC,nCurBlock-nDelta,0,nWidth-nCurBlock+nDelta-1,cyCap,RGB(0,0,0));
break;
}
}
}
void COXStatic::PaintRect(CDC* pDC, int x, int y, int w, int h, COLORREF color)
{
CBrush brush(color);
CBrush* pOldBrush = pDC->SelectObject(&brush);
pDC->PatBlt(x, y, w, h, PATCOPY);
pDC->SelectObject(pOldBrush);
}