// ========================================================================== // Class Implementation : COXStaticText // ========================================================================== // Source file : OXStaticText.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. // ////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "OXStaticText.h" #include "UTBStrOp.h" double __cdecl absolute(double a) { return ((a<0)?(-a):a)+0.5; } ///////////////////////////////////////////////////////////////////////////// // Data members ------------------------------------------------------------- // public: // protected: // int m_nHorzAlignment; // --- Text horizontal alignment: left, center, right // int m_nVertAlignment; // --- Text vertical alignment: top, center, bottom // BOOL m_bDottedEdge; // --- Indicates whether the window edge is dotted. // DWORD m_dwBkColor; // --- Text background color. // DWORD m_dwMinTimeOut; // --- Minimum time out interval for text scrolling. // DWORD m_dwOffset; // --- Text 3D offset. // DWORD m_dwScrollSpeed; // --- Text scrolling speed (pixels/second). // DWORD m_dwScrollTimeOut; // --- Text scrolling time out period (ms). // DWORD m_dwTextColor; // --- Text color. // double m_dXDelta; // --- Exact text scrolling x-increment. // double m_dXExactDisplacement; // --- Exact text scrolling x-displacement. // double m_dYDelta; // --- Exact text scrolling y-increment. // double m_dYExactDisplacement; // --- Exact text scrolling y-displacement. // LOGFONT m_LogFont; // --- Structure defines the attributes of a font. // int m_nEllipseMode; // --- Ellipses replacing mode. // int m_nGraphicsMode; // --- Current graphics mode. // int m_nScrollAmount; // --- Text scrolling amount. // int m_nScrollDirection; // --- Text scrolling direction (degrees). // int m_nXCastDisplacement; // --- Text scrolling cast x-displacement. // int m_nXDisplacement; // --- Text scrolling x-displacement. // int m_nYCastDisplacement; // --- Text scrolling cast y-displacement. // int m_nYDisplacement; // --- Text scrolling y-displacement. // CEvent* m_pEventLoop; // --- When pointed event is signaled, text scrolling thread terminates. // CCriticalSection* m_pCritSecRedrawWait; // --- Pointed critical section is locked during window redrawing; // CFont* m_pObjFont; // --- Pointer to the font object. // CSingleLock* m_pRedrawThreadLock; // --- This object locks window redrawing in the special redraw thread. // CWinThread* m_pScrollingThread; // --- Points to CWinThread object that represents text scrolling thread. // CString m_sText; // --- Text string. // CString m_sTextNarrow; // --- Narrow text string (with ellipses so that the result fits in the specified rectangle). // private: UINT COXStaticText::m_nPrepareBitmap= RegisterWindowMessage(_T("_OXPREPARE_BITMAP_")); ///////////////////////////////////////////////////////////////////////////// // Member functions --------------------------------------------------------- // public: COXStaticText::COXStaticText(DWORD dwOffset, int nGraphicsMode, int nHorzAlignment, int nVertAlignment) : m_BMP(NULL) { m_bAllowRefresh = FALSE; m_dwCurrentTickDelta = 0; m_dwLastTickDelta = 0; m_nHorzAlignment = nHorzAlignment; m_nVertAlignment = nVertAlignment; m_bDottedEdge = FALSE; m_bEmbossText = FALSE; m_bEmbossRaised = FALSE; m_clrEmbossHighLight = ::GetSysColor(COLOR_BTNHIGHLIGHT); m_clrEmbossShadow = ::GetSysColor(COLOR_BTNSHADOW); m_dwBkColor = ::GetSysColor(COLOR_WINDOW); m_dwMinTimeOut = 10; m_dwOffset = dwOffset; m_dwScrollSpeed = m_dwScrollTimeOut = 0; m_dwTextColor = ::GetSysColor(COLOR_WINDOWTEXT); m_dXDelta = m_dYDelta = 0.0; ::GetObject(::GetStockObject(ANSI_VAR_FONT), sizeof(m_LogFont), &m_LogFont); m_nEllipseMode = OX_NO_ELLIPSES; m_nGraphicsMode = nGraphicsMode; m_nScrollAmount = m_nScrollDirection = 0; m_nXDisplacement = m_nYDisplacement = m_nXCastDisplacement = m_nYCastDisplacement = 0; m_pEventLoop = new CEvent(FALSE, TRUE); m_pCritSecRedrawWait = new CCriticalSection(); m_pObjFont = new CFont; m_pObjFont->CreateFontIndirect(&m_LogFont); m_pScrollingThread = NULL; m_sText = m_sTextNarrow = _T(""); m_szGapSize=CSize(0,0); m_rectViewMargins=CRect(2,0,2,0); } BOOL COXStaticText::Create(LPCTSTR lpszText, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID /* = 0xffff */) { // Look for special CStatic control styles and trace if any. #ifdef _DEBUG if ((dwStyle & ~(WS_CHILD | WS_VISIBLE | WS_DISABLED | WS_BORDER)) != 0) TRACE0("in COXStaticText::Create : dwStyle contains more styles than permitted. They will be ignored\n"); #endif dwStyle&=WS_CHILD|WS_VISIBLE|WS_DISABLED|WS_BORDER; return CStatic::Create(lpszText, dwStyle, rect, pParentWnd, nID); } COXStaticText::~COXStaticText() { m_pObjFont->DeleteObject(); delete m_pObjFont; delete m_pCritSecRedrawWait; delete m_pEventLoop; delete m_BMP; } BOOL COXStaticText::SetHorzAlignment(int nAlignment, BOOL bPrepareNow /* = FALSE */) { if(nAlignment!=OX_ALIGNHORZ_LEFT && nAlignment!=OX_ALIGNHORZ_CENTER && nAlignment!=OX_ALIGNHORZ_RIGHT) { return FALSE; } m_nHorzAlignment = nAlignment; return RestoreTextPos(bPrepareNow); } BOOL COXStaticText::SetVertAlignment(int nAlignment, BOOL bPrepareNow /* = FALSE */) { if(nAlignment!=OX_ALIGNVERT_TOP && nAlignment!=OX_ALIGNVERT_CENTER && nAlignment!=OX_ALIGNVERT_BOTTOM) { return FALSE; } m_nVertAlignment = nAlignment; return RestoreTextPos(bPrepareNow); } BOOL COXStaticText::SetBkColor(COLORREF dwBkColor, BOOL bPrepareNow /* = FALSE */) { m_dwBkColor = dwBkColor; return PrepareBitmap(bPrepareNow); } BOOL COXStaticText::SetTextColor(COLORREF dwTextColor, BOOL bPrepareNow /* = FALSE */) { m_dwTextColor = dwTextColor; return PrepareBitmap(bPrepareNow); } BOOL COXStaticText::SetEmboss(BOOL bEmboss /* = TRUE */, BOOL bRaised /* = FALSE */, BOOL bPrepareNow /* = FALSE */, COLORREF clrHLight /* = ::GetSysColor(COLOR_BTNHIGHLIGHT) */, COLORREF clrShadow /* = ::GetSysColor(COLOR_BTNSHADOW) */) { m_bEmbossText = bEmboss; m_bEmbossRaised = bRaised; m_clrEmbossHighLight = clrHLight; m_clrEmbossShadow = clrShadow; return PrepareBitmap(bPrepareNow); } BOOL COXStaticText::SetWindowText(LPCTSTR psText, BOOL bPrepareNow /* = FALSE */) { m_sText = m_sTextNarrow = psText; m_bAllowRefresh = FALSE; return PrepareBitmap(bPrepareNow); } BOOL COXStaticText::SetGraphicsMode(int nGraphicsMode, BOOL bPrepareNow /* = FALSE */) { m_nGraphicsMode = nGraphicsMode; return PrepareBitmap(bPrepareNow); } BOOL COXStaticText::SetEllipseMode(int nEllipseMode, BOOL bPrepareNow /* = FALSE */) { if ( GetEllipseMode() == nEllipseMode ) m_nEllipseMode = OX_NO_ELLIPSES; else m_nEllipseMode = nEllipseMode; return PrepareBitmap(bPrepareNow); } BOOL COXStaticText::Set3Doffset(DWORD dwOffset, BOOL bPrepareNow /* = FALSE */) { BOOL bSuccess = FALSE; if ( dwOffset <= OX_MAX_3DOFFSET ) { m_dwOffset = dwOffset; bSuccess = PrepareBitmap(bPrepareNow); } return bSuccess; } BOOL COXStaticText::SetFontAttr(int nAttr, BOOL bSet /* = TRUE */, BOOL bPrepareNow /* = FALSE */) { switch ( nAttr ) { case OX_BOLD_FONT: m_LogFont.lfWeight = bSet ? FW_BOLD : FW_NORMAL; break; case OX_ITALIC_FONT: m_LogFont.lfItalic = (BYTE)bSet; break; case OX_UNDERLINED_FONT: m_LogFont.lfUnderline = (BYTE)bSet; break; case OX_STRIKED_OUT_FONT: m_LogFont.lfStrikeOut = (BYTE)bSet; break; } BOOL bSuccess = RebuildFont(); if (bSuccess) bSuccess = PrepareBitmap(bPrepareNow); return bSuccess; } BOOL COXStaticText::IsBold() const { if ( m_LogFont.lfWeight == FW_BOLD ) return TRUE; else return FALSE; } BOOL COXStaticText::SetStringAngle(int nAngle, BOOL bPrepareNow /* = FALSE */) { m_LogFont.lfEscapement = nAngle; BOOL bSuccess = RebuildFont(); if ( bSuccess ) bSuccess = PrepareBitmap(bPrepareNow); return bSuccess; } BOOL COXStaticText::SetCharAngle(int nAngle, BOOL bPrepareNow /* = FALSE */) { m_LogFont.lfOrientation = nAngle; BOOL bSuccess = RebuildFont(); if ( bSuccess ) bSuccess = PrepareBitmap(bPrepareNow); return bSuccess; } BOOL COXStaticText::SetCharSet(int nCharSet, BOOL bPrepareNow /* = FALSE */) { m_LogFont.lfCharSet = (BYTE)nCharSet; BOOL bSuccess = RebuildFont(); if ( bSuccess ) bSuccess = PrepareBitmap(bPrepareNow); return bSuccess; } BOOL COXStaticText::SetFontHeight(int nHeight, BOOL bPrepareNow /* = FALSE */) { m_LogFont.lfHeight = nHeight; BOOL bSuccess = RebuildFont(); if ( bSuccess ) bSuccess = PrepareBitmap(bPrepareNow); return bSuccess; } BOOL COXStaticText::SetFontWidth(int nWidth, BOOL bPrepareNow /* = FALSE */) { m_LogFont.lfWidth = nWidth; BOOL bSuccess = RebuildFont(); if ( bSuccess ) bSuccess = PrepareBitmap(bPrepareNow); return bSuccess; } BOOL COXStaticText::SetFontName(LPCTSTR sName, BOOL bPrepareNow /* = FALSE */) { BOOL bSuccess = FALSE; if ( _tcslen(sName) <= LF_FACESIZE ) { UTBStr::tcscpy(m_LogFont.lfFaceName, LF_FACESIZE, sName); bSuccess = RebuildFont(); if ( bSuccess ) bSuccess = PrepareBitmap(bPrepareNow); } return bSuccess; } // returns TRUE if succeed and sets plf to the LOGINFO of the font used to draw text, // otherwise FALSE and plf is undefined BOOL COXStaticText::GetLogFont(LOGFONT* plf) const { if (m_pObjFont==NULL) { return FALSE; } return m_pObjFont->GetObject(sizeof(*plf),plf); } // returns TRUE if succeed and creates font used to draw text // from plf, otherwise FALSE BOOL COXStaticText::SetLogFont(LOGFONT* plf, BOOL bPrepareNow/* = FALSE*/) { CFont font; BOOL bSuccess=font.CreateFontIndirect(plf); if(bSuccess) { bSuccess = font.GetObject(sizeof(m_LogFont),&m_LogFont); if(bSuccess) { bSuccess = RebuildFont(); if(bSuccess) { bSuccess = PrepareBitmap(bPrepareNow); } } } return bSuccess; } BOOL COXStaticText::SetPlainBorder(BOOL bSet /* = TRUE */) { if (bSet) { m_bDottedEdge = FALSE; // This odd line is to force the NC area to be invalidated ModifyStyleEx(0, WS_EX_STATICEDGE | WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE, 0); ModifyStyle(0, WS_BORDER, 0); ModifyStyleEx(WS_EX_STATICEDGE | WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE, 0, SWP_FRAMECHANGED); } else ModifyStyle(WS_BORDER, 0, SWP_FRAMECHANGED); return TRUE; } BOOL COXStaticText::SetStaticEdge(BOOL bSet /* = TRUE */) { if (bSet) { m_bDottedEdge = FALSE; ModifyStyle(0, WS_BORDER, 0); ModifyStyleEx(WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE, WS_EX_STATICEDGE, SWP_FRAMECHANGED); } else ModifyStyleEx(WS_EX_STATICEDGE, 0, SWP_FRAMECHANGED); return TRUE; } BOOL COXStaticText::SetClientEdge(BOOL bSet /* = TRUE */) { if (bSet) { m_bDottedEdge = FALSE; ModifyStyle(0, WS_BORDER, 0); ModifyStyleEx(WS_EX_STATICEDGE | WS_EX_WINDOWEDGE, WS_EX_CLIENTEDGE, SWP_FRAMECHANGED); } else ModifyStyleEx(WS_EX_CLIENTEDGE, 0, SWP_FRAMECHANGED); return TRUE; } BOOL COXStaticText::SetRaisedEdge(BOOL bSet /* = TRUE */) { if (bSet) { m_bDottedEdge = FALSE; ModifyStyle(0, WS_BORDER, 0); ModifyStyleEx(WS_EX_STATICEDGE | WS_EX_CLIENTEDGE, WS_EX_WINDOWEDGE, SWP_FRAMECHANGED); } else ModifyStyleEx(WS_EX_WINDOWEDGE, 0, SWP_FRAMECHANGED); return TRUE; } BOOL COXStaticText::SetDottedEdge(BOOL bSet /* = TRUE */) { m_bDottedEdge = bSet; if (bSet) { // This odd line is to force the NC area to be invalidated ModifyStyleEx(0, WS_EX_STATICEDGE | WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE, 0); ModifyStyle(0, WS_BORDER, 0); ModifyStyleEx(WS_EX_STATICEDGE | WS_EX_CLIENTEDGE | WS_EX_WINDOWEDGE, 0, SWP_FRAMECHANGED); } return TRUE; } void COXStaticText::SetScrollDirection(int nDirection, BOOL bPrepareNow /* = FALSE */) { CSingleLock RedrawLock(m_pCritSecRedrawWait); RedrawLock.Lock(); // Correct direction angle to keep it in [0 - 360] interval. nDirection %= 360; if ( nDirection < 0 ) nDirection += 360; m_nScrollDirection = nDirection; RestoreTextPos(bPrepareNow); ScrollAmountRecalc(); RedrawLock.Unlock(); } void COXStaticText::SetScrollSpeed(DWORD dwScrollSpeed) { CSingleLock RedrawLock(m_pCritSecRedrawWait); RedrawLock.Lock(); DWORD dwTimeOut, dwAmount; SpeedCalc(dwScrollSpeed, &dwTimeOut, &dwAmount); m_dwScrollSpeed = dwScrollSpeed; m_dwScrollTimeOut = dwTimeOut; m_nScrollAmount = dwAmount; ScrollAmountRecalc(); RedrawLock.Unlock(); } void COXStaticText::StartScrolling(BOOL bStart) { if(GetSafeHwnd()==NULL) return; if ( IsScrollingStarted() ) { m_pEventLoop->SetEvent(); DWORD dwWaitResult; BOOL bEnd = FALSE; BOOL bPostQuit = FALSE; while (!bEnd) { dwWaitResult = MsgWaitForMultipleObjects(1, &m_pScrollingThread->m_hThread, FALSE, INFINITE, QS_ALLINPUT); if (dwWaitResult == (WAIT_OBJECT_0 + 1)) { MSG msg; // making sure there is a msg in the queue // we don't want PumpMessage() to hang if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) bPostQuit = (!AfxGetThread()->PumpMessage()) || bPostQuit; } else { //if (dwWaitResult != WAIT_OBJECT_0) // TerminateThread(h_thread, 0); bEnd = TRUE; } } if (bPostQuit) { AfxPostQuitMessage(0); Sleep(200); // Give the terminating thread the time to terminate } m_pEventLoop->ResetEvent(); delete m_pScrollingThread; m_pScrollingThread = NULL; } if ( bStart ) { m_pScrollingThread = AfxBeginThread((AFX_THREADPROC)TextScrollingThreadFunction, this); m_pScrollingThread->m_bAutoDelete = FALSE; } } BOOL COXStaticText::IsScrollingStarted() const { if ( m_pScrollingThread && ::WaitForSingleObject(m_pScrollingThread->m_hThread, 0) == WAIT_TIMEOUT ) return TRUE; else return FALSE; } void COXStaticText::SetMinTimeOut(DWORD dwMinTimeOut) { if ( dwMinTimeOut != m_dwMinTimeOut && dwMinTimeOut >= 1 && dwMinTimeOut <= 500 ) { DWORD dwScrollSpeed = GetScrollSpeed(); m_dwMinTimeOut = dwMinTimeOut; SetScrollSpeed(dwScrollSpeed); } } void COXStaticText::SetGapSize(CSize& szGapSize, BOOL bPrepareNow /* = FALSE */) { m_szGapSize = szGapSize; PrepareBitmap(bPrepareNow); } void COXStaticText::SetViewMargins(CRect& rectViewMargins, BOOL bPrepareNow /* = FALSE */) { m_rectViewMargins = rectViewMargins; PrepareBitmap(bPrepareNow); } BOOL COXStaticText::RestoreTextPos(BOOL bPrepareNow /* = TRUE */) { CRect textRect; GetClientRect(&textRect); GetInitialDisplacement(textRect); return PrepareBitmap(bPrepareNow); } BEGIN_MESSAGE_MAP(COXStaticText, CStatic) //{{AFX_MSG_MAP(COXStaticText) ON_WM_PAINT() ON_WM_DESTROY() ON_WM_ERASEBKGND() ON_WM_NCPAINT() ON_WM_SIZE() //}}AFX_MSG_MAP ON_MESSAGE(WM_DISPLAYCHANGE, OnDisplayChange) ON_REGISTERED_MESSAGE(m_nPrepareBitmap, OnPrepareBitmap) END_MESSAGE_MAP() // protected: void COXStaticText::OnNcPaint() { if (!m_bDottedEdge) { Default(); return; } // Prepare for drawing the non-client area with a dotted border CWindowDC dc(this); CRect rect; GetWindowRect(&rect); rect.OffsetRect(-rect.left, -rect.top); CRect Temp; GetClientRect(&Temp); CPen DottedPen(PS_DOT, 1, RGB(0, 0, 0)); CBrush HollowBrush; HollowBrush.CreateStockObject(HOLLOW_BRUSH); CPen* pOldPen = dc.SelectObject(&DottedPen); CBrush* pOldBrush = dc.SelectObject(&HollowBrush); dc.Rectangle(rect); if (pOldBrush != NULL) dc.SelectObject(pOldBrush); if (pOldPen != NULL) dc.SelectObject(pOldPen); } void COXStaticText::OnSize(UINT nType, int cx, int cy) { CStatic::OnSize(nType,cx,cy); if(IsScrollingStarted()) StartScrolling(TRUE); PrepareBitmap(TRUE); } UINT COXStaticText::TextScrollingThreadFunction(LPVOID pParam) // --- In : pParam : Pointer to this COXStaticText object. It makes possible to // work with non-static class members in this static function. // --- Out : // --- Returns : Exit code = 0. // --- Effect : Controlling function for text scrolling worker thread. { COXStaticText* pStaText = (COXStaticText*)pParam; CSingleLock LoopLock(pStaText->m_pEventLoop), RedrawLock(pStaText->m_pCritSecRedrawWait); BOOL bRedraw; pStaText->PrepareBitmap(TRUE); CRect textRect; pStaText->GetClientRect(&textRect); /// textRect+=pStaText->m_szGapSize; /// while (TRUE) { if(LoopLock.Lock(pStaText->m_dwScrollTimeOut)) return 0; bRedraw = FALSE; RedrawLock.Lock(); double nXDisplacement = pStaText->m_nXDisplacement, nYDisplacement = pStaText->m_nYDisplacement; pStaText->m_nXDisplacement = (pStaText->m_dXDelta + pStaText->m_nXDisplacement); pStaText->m_nYDisplacement = (pStaText->m_dYDelta + pStaText->m_nYDisplacement); if ( pStaText->m_nXDisplacement != nXDisplacement || pStaText->m_nYDisplacement != nYDisplacement ) { if(pStaText->m_nXDisplacement-pStaText->m_szTextSize.cx/2>=textRect.right || pStaText->m_nXDisplacement+pStaText->m_szTextSize.cx/2<0 || pStaText->m_nYDisplacement-pStaText->m_szTextSize.cy/2>=textRect.bottom || pStaText->m_nYDisplacement+pStaText->m_szTextSize.cy/2<0) { pStaText->m_nXDisplacement += pStaText->m_nXCastDisplacement; pStaText->m_nYDisplacement += pStaText->m_nYCastDisplacement; if(pStaText->m_nXDisplacement-pStaText->m_szTextSize.cx/2>=textRect.right || pStaText->m_nXDisplacement+pStaText->m_szTextSize.cx/2<0 || pStaText->m_nYDisplacement-pStaText->m_szTextSize.cy/2>=textRect.bottom || pStaText->m_nYDisplacement+pStaText->m_szTextSize.cy/2<0) { pStaText->m_nXDisplacement -= pStaText->m_nXCastDisplacement; pStaText->m_nYDisplacement -= pStaText->m_nYCastDisplacement; } } bRedraw = TRUE; } RedrawLock.Unlock(); if (bRedraw) pStaText->RedrawWindow(); } return 0; } void COXStaticText::EmbossText(CDC* pMemDC, RECT BmpRect) { const DWORD PSDPxax = 0x00B8074A; COLORREF clrShadow = m_clrEmbossShadow; COLORREF clrHighlight = m_clrEmbossHighLight; if(!m_bEmbossRaised) { // Swap the highlight and shadow color clrShadow = m_clrEmbossHighLight; clrHighlight = m_clrEmbossShadow; } // We create two monochrome bitmaps. One of them will contain the // highlighted edge and the other will contain the shadow. These // bitmaps are then used to paint the highlight and shadow on the // background image. CDC* pDC = GetDC(); // Create compatible DCs CDC MonoDC; MonoDC.CreateCompatibleDC(pDC); CDC* pMonoDC = &MonoDC; int BmpWidth = BmpRect.right - BmpRect.left; int BmpHeight = BmpRect.bottom - BmpRect.top; // Create the monochrome and compatible color bitmaps CBitmap bmShadow; bmShadow.CreateBitmap(BmpWidth, BmpHeight, 1, 1, NULL); CBitmap bmHighlight; bmHighlight.CreateBitmap(BmpWidth, BmpHeight, 1, 1, NULL); // Set background color of bitmap for mono conversion // We assume that the pixel in the top left corner has the background color COLORREF oldBkColor=CLR_NONE; if(m_dwBkColor!=CLR_NONE) oldBkColor = pMemDC->SetBkColor(pMemDC->GetNearestColor(m_dwBkColor)); // Create the highlight bitmap. CBitmap* pbmOldHighlight = pMonoDC->SelectObject(&bmHighlight); pMonoDC->PatBlt(0, 0, BmpWidth, BmpHeight, WHITENESS ); pMonoDC->BitBlt(0, 0, BmpWidth - 1, BmpHeight - 1, pMemDC, 1, 1, SRCCOPY ); pMonoDC->BitBlt(0, 0, BmpWidth, BmpHeight, pMemDC, BmpRect.left, BmpRect.top, MERGEPAINT ); pbmOldHighlight = pMonoDC->SelectObject(pbmOldHighlight); // create the shadow bitmap CBitmap* pbmOldShadow = pMonoDC->SelectObject(&bmShadow); pMonoDC->PatBlt(0, 0, BmpWidth, BmpHeight, WHITENESS ); pMonoDC->BitBlt(0, 0, BmpWidth - 1, BmpHeight - 1, pMemDC, 1, 1, SRCCOPY ); pMonoDC->BitBlt(0, 0, BmpWidth, BmpHeight, pMemDC, BmpRect.left, BmpRect.top, MERGEPAINT ); pbmOldShadow = pMonoDC->SelectObject(pbmOldShadow); // Now let's start working on the final image // Set the background and foreground color for the raster operations pMemDC->SetBkColor(pMemDC->GetNearestColor(RGB(255,255,255))); pMemDC->SetTextColor(pMemDC->GetNearestColor(RGB(0,0,0))); // blt the highlight edge CBrush brHighPat(pMemDC->GetNearestColor(clrHighlight)); CBrush* pOldbrHighPat = pMemDC->SelectObject(&brHighPat); pbmOldHighlight = pMonoDC->SelectObject(pbmOldHighlight); pMemDC->BitBlt(BmpRect.left, BmpRect.top, BmpWidth, BmpHeight, pMonoDC, 0, 0, PSDPxax); pMemDC->SelectObject(pOldbrHighPat); pMonoDC->SelectObject(pbmOldHighlight); // blt the shadow edge CBrush brShwPat(clrShadow); CBrush* pOldbrShwPat = pMemDC->SelectObject(&brShwPat); pbmOldShadow = pMonoDC->SelectObject(pbmOldShadow); pMemDC->BitBlt(BmpRect.left, BmpRect.top, BmpWidth, BmpHeight, pMonoDC, 0, 0, PSDPxax); pMemDC->SelectObject(pOldbrShwPat); pMonoDC->SelectObject(pbmOldShadow); bmShadow.DeleteObject(); bmHighlight.DeleteObject(); ReleaseDC(pDC); pMemDC->SetBkColor(oldBkColor); } void COXStaticText::TextOutput(CDC* pMemDC, RECT rect) // --- In : pMemDC : Pointer to compatible memory device context to text output. // rect : Text output rectangle. // --- Out : // --- Returns : // --- Effect : Performs text output to the compatible memory device context. { rect.left+=m_rectViewMargins.left; rect.top+=m_rectViewMargins.top; rect.right-=m_rectViewMargins.right; rect.bottom-=m_rectViewMargins.bottom; TEXTMETRIC tm; pMemDC->GetTextMetrics(&tm); double alpha = ((GetGraphicsMode()==GM_ADVANCED) ? GetCharAngle() : GetStringAngle())/10; alpha = alpha * 3.1415926/180.0; double alphaAdvanced = GetStringAngle()/10; alphaAdvanced = alphaAdvanced * 3.1415926/180.0; CSize sizeCorr(0,0); sizeCorr.cx = (tm.tmAscent - tm.tmDescent)/2; sizeCorr.cy = (tm.tmAscent - tm.tmDescent)/2; sizeCorr.cx = (int)(((double)sizeCorr.cx)*sin(alpha)+(sin(alpha)>0 ? -0.5 : 0.5)); sizeCorr.cy = (int)(((double)sizeCorr.cy)*cos(alpha)+(cos(alpha)>0 ? -0.5 : 0.5)); if(GetGraphicsMode()==GM_ADVANCED) { } pMemDC->SetTextAlign(TA_CENTER | TA_BASELINE | TA_NOUPDATECP); pMemDC->SetTextColor(pMemDC->GetNearestColor(RGB(GetRValue(m_dwTextColor)/2, GetGValue(m_dwTextColor)/2, GetBValue(m_dwTextColor)/2))); for ( DWORD dwCount = 0; dwCount < m_dwOffset; dwCount++ ) { pMemDC->TextOut( (rect.left+rect.right)/2+sizeCorr.cx, (rect.top+rect.bottom)/2+sizeCorr.cy, m_sTextNarrow); CRect rectOutput((rect.left+rect.right)/2+sizeCorr.cx, (rect.top+rect.bottom)/2+sizeCorr.cy, (rect.left+rect.right)/2+sizeCorr.cx+rect.right-rect.left, (rect.top+rect.bottom)/2+sizeCorr.cy+rect.bottom-rect.top); ScreenToClient(rectOutput); pMemDC->DrawText(m_sTextNarrow, &rectOutput,DT_SINGLELINE); OffsetRect( &rect, 1,1); } pMemDC->SetTextColor(pMemDC->GetNearestColor(m_dwTextColor)); pMemDC->TextOut( (rect.left+rect.right)/2+sizeCorr.cx, (rect.top+rect.bottom)/2+sizeCorr.cy, m_sTextNarrow); if (m_bEmbossText) EmbossText(pMemDC, rect); } void COXStaticText::EllipsesReplace(CDC* pMemDC, LPRECT lpRect) // --- In : pMemDC : Pointer to compatible memory device context to text output. // lpRect : Pointer to the text output rectangle. // --- Out : // --- Returns : // --- Effect : If it's necessary, replaces text with ellipses so that // the result fits in the specified rectangle { BOOL bContinue = TRUE; int nRemoved = 0, nRem1 = 0, nRem2 = 0; CString sEllipses = _T("..."); CSize textSize; m_sTextNarrow = m_sText; int nLimit=lpRect->right-(int)(lpRect->right*1/4* sin((double)(GetStringAngle()/10)*OX_PI/180.0)); while ( bContinue && m_sText.GetLength() > nRemoved && ::GetTextExtentPoint32(pMemDC->m_hDC, (LPCTSTR)m_sTextNarrow, m_sTextNarrow.GetLength(), &textSize) ) { if(textSize.cx>=nLimit) { nRemoved++; switch(m_nEllipseMode) { case OX_BEGIN_ELLIPSES: m_sTextNarrow = sEllipses + m_sText.Right(m_sText.GetLength() - nRemoved); break; case OX_MIDDLE_ELLIPSES: nRem1 = m_sText.GetLength() - nRemoved; if ( nRem1 % 2 ) { nRem1 /= 2; nRem2 = nRem1 + 1; } else nRem2 = nRem1 /= 2; m_sTextNarrow = m_sText.Left(nRem1) + sEllipses + m_sText.Right(nRem2); break; case OX_END_ELLIPSES: m_sTextNarrow = m_sText.Left(m_sText.GetLength() - nRemoved) + sEllipses; break; default: bContinue = FALSE; break; } } else bContinue = FALSE; } } void COXStaticText::ScrollAmountRecalc() // --- In : // --- Out : // --- Returns : // --- Effect : Calculates the text scrolling x- and y- offsets. { double dRadDirection = (double)m_nScrollDirection * OX_PI / 180.0; m_dXDelta = (double)m_nScrollAmount * cos(dRadDirection); m_dYDelta = -(double)m_nScrollAmount * sin(dRadDirection); RECT textRect; GetClientRect(&textRect); textRect.right = __max( textRect.right, m_szTextSize.cx)+m_szGapSize.cx; textRect.bottom = __max( textRect.bottom, m_szTextSize.cy)+m_szGapSize.cy; double angle = atan( ((double)textRect.bottom)/((double)textRect.right)); angle = (angle*180.0)/OX_PI; if ( m_nScrollDirection <= angle ) { m_nXCastDisplacement = -textRect.right; m_nYCastDisplacement = ((double)textRect.right * tan(dRadDirection)); } else if ( m_nScrollDirection > angle && m_nScrollDirection <= 90 ) { m_nXCastDisplacement = -((double)textRect.bottom / tan(dRadDirection)); m_nYCastDisplacement = textRect.bottom; } else if ( m_nScrollDirection > 90 && m_nScrollDirection <= 180 - angle ) { m_nXCastDisplacement = ((double)textRect.bottom / tan(OX_PI - dRadDirection)); m_nYCastDisplacement = textRect.bottom; } else if ( m_nScrollDirection > 180-angle && m_nScrollDirection <= 180 ) { m_nXCastDisplacement = textRect.right; m_nYCastDisplacement = ((double)textRect.right * tan(OX_PI - dRadDirection)); } else if ( m_nScrollDirection > 180 && m_nScrollDirection <= 180 + angle ) { m_nXCastDisplacement = textRect.right; m_nYCastDisplacement = -((double)textRect.right * tan(dRadDirection)); } else if ( m_nScrollDirection > 180+angle && m_nScrollDirection <= 270 ) { m_nXCastDisplacement = ((double)textRect.bottom / tan(dRadDirection)); m_nYCastDisplacement = -textRect.bottom; } else if ( m_nScrollDirection > 270 && m_nScrollDirection <= 360-angle ) { m_nXCastDisplacement = -((double)textRect.bottom / tan(OX_PI - dRadDirection)); m_nYCastDisplacement = -textRect.bottom; } else { m_nXCastDisplacement = -textRect.right; m_nYCastDisplacement = -((double)textRect.right * tan(OX_PI - dRadDirection)); } } BOOL COXStaticText::RebuildFont() // --- In : // --- Out : // --- Returns : TRUE - if the function succeeds; otherwise FALSE. // --- Effect : Rebuilds the font object. { if((HFONT)*m_pObjFont!=NULL) m_pObjFont->DeleteObject(); return m_pObjFont->CreateFontIndirect(&m_LogFont); } BOOL COXStaticText::OnEraseBkgnd(CDC* pDC) { UNREFERENCED_PARAMETER(pDC); return TRUE; } void COXStaticText::OnPaint() // --- In : // --- Out : // --- Returns : // --- Effect : The framework calls this member function when Windows or an application // makes a request to repaint a portion of an application's window. { CSingleLock RedrawLock(m_pCritSecRedrawWait); RedrawLock.Lock(); m_dwBeginTickCount = ::GetTickCount(); CPaintDC dc(this); // Device context for painting. CDC memDC; // Compatible memory device context. CRect textRect; // Client coordinates of the window client area. CDC drawDC; CBitmap drawBMP; GetClientRect(&textRect); if (!memDC.CreateCompatibleDC(&dc)) // Create compatible memory device context { TRACE(_T("OnPaint() - memDC.CreateCompatibleDC() failed\n")); RedrawLock.Unlock(); return; } int nSavedDC=memDC.SaveDC(); memDC.SelectObject(m_BMP); if (!drawDC.CreateCompatibleDC(&dc)) // Create compatible memory device context { RedrawLock.Unlock(); return; } int nSavedDC2=drawDC.SaveDC(); if (drawBMP.CreateCompatibleBitmap( &dc, textRect.Width(), textRect.Height())) { CBitmap* pOldDrawBitmap = drawDC.SelectObject(&drawBMP); if(m_dwBkColor!=CLR_NONE) drawDC.FillSolidRect(&textRect, drawDC.GetNearestColor(m_dwBkColor)); else SendMessage(WM_ERASEBKGND,(WPARAM)((HDC)drawDC),NULL); drawDC.BitBlt( (int)(m_nXDisplacement-m_szTextSize.cx/2+0.5), (int)(m_nYDisplacement-m_szTextSize.cy/2+0.5), m_szTextSize.cx, m_szTextSize.cy, &memDC, 0, 0, SRCCOPY); CPoint secPt; if( (m_nXDisplacement + m_szTextSize.cx/2 >= textRect.right+m_szGapSize.cx) || (m_nXDisplacement - m_szTextSize.cx/2 < textRect.left-m_szGapSize.cx) || (m_nYDisplacement + m_szTextSize.cy/2 >= textRect.bottom+m_szGapSize.cy) || (m_nYDisplacement - m_szTextSize.cy/2 < textRect.top-m_szGapSize.cy)) { secPt.x = (long)(m_nXDisplacement + m_nXCastDisplacement + 0.5); secPt.y = (long)(m_nYDisplacement + m_nYCastDisplacement + 0.5); drawDC.BitBlt( secPt.x-m_szTextSize.cx/2, secPt.y-m_szTextSize.cy/2, m_szTextSize.cx, m_szTextSize.cy, &memDC, 0, 0, SRCCOPY); } dc.BitBlt( 0, 0, textRect.Width(), textRect.Height(), &drawDC, 0, 0, SRCCOPY); // Clean Up if (pOldDrawBitmap != NULL) drawDC.SelectObject(pOldDrawBitmap); } else { TRACE(_T("OnPaint() - drawDC.CreateCompatibleBitmap() failed\n")); } // Clean Up if(nSavedDC2) drawDC.RestoreDC(nSavedDC2); drawBMP.DeleteObject(); if(nSavedDC) memDC.RestoreDC(nSavedDC); m_dwEndTickCount = ::GetTickCount(); m_dwLastTickDelta = m_dwCurrentTickDelta; m_dwCurrentTickDelta = m_dwEndTickCount - m_dwBeginTickCount; if (m_bAllowRefresh && !(m_dwCurrentTickDelta == 0 && m_dwLastTickDelta == 0) && (m_dwCurrentTickDelta >= (2 * m_dwLastTickDelta) || m_dwLastTickDelta >= (2 * m_dwCurrentTickDelta))) { RefreshBitmap(); m_bAllowRefresh = FALSE; } else m_bAllowRefresh = TRUE; RedrawLock.Unlock(); } void COXStaticText::OnDestroy() // --- In : // --- Out : // --- Returns : // --- Effect : The framework calls this member function to inform the CWnd object that it is // being destroyed. OnDestroy is called after the CWnd object is removed from the screen. // This function stops text scrolling and terminates the window redrawing thread. { CStatic::OnDestroy(); StartScrolling(FALSE); } LRESULT COXStaticText::OnPrepareBitmap(WPARAM wParam, LPARAM lParam) { // --- In : // --- Out : // --- Returns : TRUE is successful // --- Effect : Prepares the text bitmap for drawing UNREFERENCED_PARAMETER(lParam); BOOL bNow = (BOOL)wParam; if (!bNow) return TRUE; CSingleLock RedrawLock(m_pCritSecRedrawWait); RedrawLock.Lock(); CClientDC dc(this); if(m_dwBkColor==CLR_NONE) SendMessage(WM_ERASEBKGND,(WPARAM)((HDC)dc),NULL); CRect textRect; CDC memDC; GetClientRect(&textRect); if (!memDC.CreateCompatibleDC(&dc)) // Create compatible memory device context { TRACE(_T("OnPrepareBitmap() - memDC.CreateCompatibleDC() failed\n")); RedrawLock.Unlock(); return FALSE; } int nSavedDC=memDC.SaveDC(); memDC.OffsetViewportOrg(0, 0); ::SetGraphicsMode(memDC.m_hDC, m_nGraphicsMode); CFont* pOldFont = memDC.SelectObject(m_pObjFont); memDC.SetBkMode(TRANSPARENT); if (m_sTextNarrow.IsEmpty()) GetWindowText(m_sText); EllipsesReplace(&memDC, &textRect); m_szTextSize = CalcRectSizes(&memDC); CRect rc(CPoint(0,0),m_szTextSize); m_szTextSize.cx += m_dwOffset; m_szTextSize.cy += m_dwOffset; CRect rcText( CPoint( 0, 0), m_szTextSize); if(m_BMP != NULL) { delete m_BMP; m_BMP=NULL; } try { m_BMP = new CBitmap(); } catch(CMemoryException* e) { delete e; m_BMP=NULL; TRACE(_T("OnPrepareBitmap() - m_BMP = new CBitmap() failed\n")); RedrawLock.Unlock(); return FALSE; } if(!m_BMP->CreateCompatibleBitmap( &dc, m_szTextSize.cx, m_szTextSize.cy)) { delete m_BMP; m_BMP = NULL; TRACE(_T("OnPrepareBitmap() - m_BMP->CreateCompatibleBitmap failed\n")); RedrawLock.Unlock(); return FALSE; } CBitmap* pOldBMP= memDC.SelectObject(m_BMP); if(m_dwBkColor!=CLR_NONE) memDC.FillSolidRect(&rcText, memDC.GetNearestColor(m_dwBkColor)); else SendMessage(WM_ERASEBKGND,(WPARAM)((HDC)memDC),NULL); TextOutput(&memDC, rc); if (pOldBMP != NULL) memDC.SelectObject(pOldBMP); if (pOldFont != NULL) memDC.SelectObject(pOldFont); if(nSavedDC) memDC.RestoreDC(nSavedDC); GetInitialDisplacement(textRect); ScrollAmountRecalc(); RedrawLock.Unlock(); RedrawWindow(); return TRUE; } BOOL COXStaticText::RefreshBitmap() { // --- In : // --- Out : // --- Returns : TRUE is successful // --- Effect : Prepares the text bitmap for drawing CClientDC dc(this); if(m_dwBkColor==CLR_NONE) SendMessage(WM_ERASEBKGND,(WPARAM)((HDC)dc),NULL); CRect textRect; CDC memDC; GetClientRect(&textRect); if (!memDC.CreateCompatibleDC(&dc)) // Create compatible memory device context { TRACE(_T("RefreshBitmap() - memDC.CreateCompatibleDC() failed\n")); return FALSE; } int nSavedDC=memDC.SaveDC(); memDC.OffsetViewportOrg(0, 0); ::SetGraphicsMode(memDC.m_hDC, m_nGraphicsMode); CFont* pOldFont = memDC.SelectObject(m_pObjFont); memDC.SetBkMode(TRANSPARENT); if (m_sTextNarrow.IsEmpty()) GetWindowText(m_sText); EllipsesReplace(&memDC, &textRect); m_szTextSize = CalcRectSizes(&memDC); CRect rc(CPoint(0,0),m_szTextSize); m_szTextSize.cx += m_dwOffset; m_szTextSize.cy += m_dwOffset; CRect rcText( CPoint( 0, 0), m_szTextSize); if(m_BMP != NULL) { delete m_BMP; m_BMP=NULL; } try { m_BMP = new CBitmap(); } catch(CMemoryException* e) { delete e; m_BMP=NULL; TRACE(_T("RefreshBitmap() - m_BMP = new CBitmap() failed\n")); return FALSE; } if(!m_BMP->CreateCompatibleBitmap( &dc, m_szTextSize.cx, m_szTextSize.cy)) { delete m_BMP; m_BMP = NULL; TRACE(_T("RefreshBitmap() - m_BMP->CreateCompatibleBitmap failed\n")); return FALSE; } CBitmap* pOldBMP= memDC.SelectObject(m_BMP); if(m_dwBkColor!=CLR_NONE) memDC.FillSolidRect(&rcText, memDC.GetNearestColor(m_dwBkColor)); else SendMessage(WM_ERASEBKGND,(WPARAM)((HDC)memDC),NULL); TextOutput(&memDC, rc); if (pOldBMP != NULL) memDC.SelectObject(pOldBMP); if (pOldFont != NULL) memDC.SelectObject(pOldFont); if(nSavedDC) memDC.RestoreDC(nSavedDC); return TRUE; } LRESULT COXStaticText::OnDisplayChange(WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(wParam); UNREFERENCED_PARAMETER(lParam); return PrepareBitmap(TRUE); } void COXStaticText::SpeedCalc(DWORD dwScrollSpeed, DWORD* pdwTimeOut, DWORD* pdwAmount) // --- In : dwScrollSpeed - text scrolling speed (pixels per second). // --- Out : pdwTimeOut - pointer to the scroll time out interval (ms). // pdwAmount - pointer to the scroll amount (pixels). // --- Returns : // --- Effect : Calculates optimal time out interval and scroll amount values. // These values give amount/time_out ratio that is nearest to specified speed. { DWORD dwSpeed, dwSpeedBest = dwScrollSpeed, dwTimeOut, dwTimeOutBest = 0, dwAmount, dwAmountBest = 0; for ( dwAmount = 1; dwAmount < dwScrollSpeed; dwAmount++ ) { for ( dwTimeOut = m_dwMinTimeOut; dwTimeOut < 1000 ; dwTimeOut++ ) { dwSpeed = dwScrollSpeed - 1000 * dwAmount / dwTimeOut; if ( dwSpeed < dwSpeedBest ) { dwSpeedBest = dwSpeed; dwTimeOutBest = dwTimeOut; dwAmountBest = dwAmount; } } } if ( !dwTimeOutBest || !dwAmountBest || dwSpeedBest > dwScrollSpeed / 6 ) { dwTimeOutBest = 1000; dwAmountBest = dwScrollSpeed; } *pdwTimeOut = dwTimeOutBest; *pdwAmount = dwAmountBest; } BOOL COXStaticText::PrepareBitmap(BOOL bNow) // --- In : // --- Out : // --- Returns : TRUE is successful // --- Effect : Sends message to CWnd object to prepare the text bitmap for drawing { return ((BOOL)SendMessage(m_nPrepareBitmap,(WPARAM)(bNow),NULL)); } CSize COXStaticText::CalcRectSizes(CDC* pDC) { CSize size; ::GetTextExtentPoint32(pDC->m_hDC, (LPCTSTR)m_sTextNarrow, m_sTextNarrow.GetLength(), &size); double x1, x2, y1, y2; double alpha = ((GetGraphicsMode()==GM_ADVANCED) ? GetCharAngle() : GetStringAngle())/10; alpha = alpha * 3.1415926/180.0; x1= size.cx * cos(alpha); x2= size.cy * sin(alpha); y1= size.cx * sin(alpha); y2= size.cy * cos(alpha); CSize szRet; szRet.cx = (int)(absolute(x1) + absolute(x2) + 0.5) + m_rectViewMargins.left + m_rectViewMargins.right; szRet.cy = (int)(absolute(y1) + absolute(y2) + 0.5) + m_rectViewMargins.top + m_rectViewMargins.bottom; return szRet; } void COXStaticText::GetInitialDisplacement(CRect& textRect) { switch(m_nHorzAlignment) { case OX_ALIGNHORZ_LEFT: { m_nXDisplacement = m_szTextSize.cx/2; break; } case OX_ALIGNHORZ_CENTER: { m_nXDisplacement = textRect.Width()/2; break; } case OX_ALIGNHORZ_RIGHT: { m_nXDisplacement = textRect.Width() - m_szTextSize.cx/2 - 1; break; } } switch(m_nVertAlignment) { case OX_ALIGNVERT_TOP: { m_nYDisplacement = m_szTextSize.cy/2; break; } case OX_ALIGNVERT_CENTER: { m_nYDisplacement = textRect.Height()/2; break; } case OX_ALIGNVERT_BOTTOM: { m_nYDisplacement = textRect.Height() - m_szTextSize.cy/2 - 1; break; } } }