http://www.codeproject.com/staticctrl/h ... roller.asp
I've ported this custom control to wxWidgets, but it still has some bugs.
If you're interested in this, please help me get rid of all bugs.
KNOWN BUGS:
When you move your mouse on one text, it sometimes put focus on other text.
header file
Code: Select all
#if !defined(AFX_MYSTATIC_H__0538365E_3D56_4CEE_8E0D_93990285B3EF__INCLUDED_)
#define AFX_MYSTATIC_H__0538365E_3D56_4CEE_8E0D_93990285B3EF__INCLUDED_
/////////////////////////////////////////////////////////////////////////////
// Class : CMyStatic
// Version : 1.0
// Origianl Author : Weiye Chen
// http://www.codeproject.com/staticctrl/hyperlinkscroller.asp
//
// wxWidgets version by : Charry Wong
//
// Email : [email protected]
//
#include "wx/wx.h"
#include "wx/timer.h"
/////////////////////////////////////////////////////////////////////////////
// CMyStatic window
class CTextFrame;
WX_DECLARE_OBJARRAY(CTextFrame*, CObArray);
WX_DECLARE_OBJARRAY(int, CArrayInt);
class CMyStatic : public wxStaticText
{
DECLARE_DYNAMIC_CLASS (CMyStatic)
// Construction
public:
// Constructor
CMyStatic();
bool Create(wxWindow* parent,
wxWindowID id,
const wxString& label,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = 0,
const wxString& name = "staticText");
// Attributes
public:
CObArray m_oTextFrames; // Array of CTextFrame objects
wxColor m_clrText; // Text color
wxColor m_clrHover; // On mouse hover color
wxColor m_clrBackground; // Control's background color
wxTimer m_Timer; // Timer for scrolling text
// Operations
public:
// Start scrolling text
bool StartScrolling(bool bRestart = true);
// Add scrolling text and its url
bool AddScrollText(wxString title, wxString url);
// Remove all previously added scrolling text
bool RemoveAllScrollText(void);
// Overrides
public:
virtual bool Destroy();
// Implementation
public:
// Destructor
virtual ~CMyStatic();
protected:
void OnEraseBackground( wxEraseEvent &event );
void OnPaint(wxPaintEvent& event);
void OnTimer(wxTimerEvent &event);
void OnMouseMove(wxMouseEvent& event);
void OnSize(wxSizeEvent& event);
void OnClicked(wxMouseEvent &event);
void OnMouseLeave(wxMouseEvent &event);
DECLARE_EVENT_TABLE()
// Operations
private:
// Delete CTextFrame objects
void DeleteTextFrames(void);
// Calculate window and text area coordinates
void CalculateWindowAndTextArea(void);
// Initialize size of text and its starting position
void PrepareTextFramesForScrolling(void);
// Set new position for current scrolling texts based on scrolling speed
void IncrementScrollingTextFramesPosition(void);
// Draw scrolling text into text area
void DrawScrollingText(wxDC*);
// Add index of new scrolling text if allowance satisfied
void AddOnNewScrollingText(void);
// Remove index of finished scrolling text if it runs out of view
void RemoveFinishedScrollingText(void);
// Check the number of same element previously in array
int NumOfSameElement(CArrayInt&, int);
// Get index of CTextFrame object in array that is being hovered
int IsTextBeingHovered(wxPoint, int*);
// Attributes
private:
UINT m_uiScrollRate; // Timer value for scrolling
UINT m_uiScrollSpeed; // Amount of pixels to scroll
UINT m_uiScrollAllowance; // Amount of pixels between 2 scrolling text
wxRect m_oTextArea; // Coordinates of text area
wxRect m_oWindowArea; // Coordinates of window area
int m_iHoveredTextFrame; // Index of current text frame that is being hovered
bool m_bOnHover; // Flag to indicate if mouse is over text
CArrayInt m_iTextFramesScrolling; // Indexes of current scrolling text frame objects
};
/////////////////////////////////////////////////////////////////////////////
class CTextFrame
{
public:
// Constructors
CTextFrame();
CTextFrame(wxString, wxString);
// Destructor
virtual ~CTextFrame();
// Attributes
public:
wxSize m_szTextSize; // Size of text
wxString m_strURL; // URL behind text;
wxString m_strText; // Text to scroll
CArrayInt m_iXOffSets; // X coordinates of text frames currently showing
};
#endif // !defined(AFX_MYSTATIC_H__0538365E_3D56_4CEE_8E0D_93990285B3EF__INCLUDED_)
implementation file
Code: Select all
// MyStatic.cpp : implementation file
//
#include "MyStatic.h"
#include <wx/arrimpl.cpp>
/////////////////////////////////////////////////////////////////////////////
// CMyStatic
#define CLR_RED wxColor(255, 0, 0) // Red color
#define CLR_BLUE wxColor(0, 0, 255) // Blue color
#define CLR_BLACK wxColor(0, 0, 0) // Black color
#define CLR_GRAY wxColor(212, 208, 200) // Gray color
#define SCROLLRATE 1 // Frequency of timer
#define SCROLLSPEED 1 // Amount of pixels to scroll
#define SCROLLALLOWANCE 30 // Amount of pixels between 2 scrolling text
#define ID_SCROLLTIMER 10001
WX_DEFINE_OBJARRAY(CArrayInt);
WX_DEFINE_OBJARRAY(CObArray);
IMPLEMENT_DYNAMIC_CLASS (CMyStatic, wxStaticText)
/////////////////////////////////////////////////////////////////////////////
// CMyStatic
CMyStatic::CMyStatic()
{
// Empty array
m_oTextFrames.Empty();
m_iTextFramesScrolling.Empty();
m_uiScrollRate = SCROLLRATE;
m_uiScrollSpeed = SCROLLSPEED;
m_uiScrollAllowance = SCROLLALLOWANCE;
m_clrText = CLR_BLACK;
m_clrHover = CLR_RED;
m_clrBackground = GetSysColor(COLOR_BTNFACE); // windows
// Set flag
m_bOnHover = false;
m_Timer.SetOwner(this, ID_SCROLLTIMER);
}
bool CMyStatic::Create(wxWindow* parent,
wxWindowID id,
const wxString& label,
const wxPoint& pos,
const wxSize& size,
long style,
const wxString& name)
{
return wxStaticText::Create(parent, id, label, pos, size, style, name);
}
CMyStatic::~CMyStatic()
{
// Delete CTextFrame objects
DeleteTextFrames();
}
BEGIN_EVENT_TABLE(CMyStatic, wxStaticText)
EVT_ERASE_BACKGROUND(CMyStatic::OnEraseBackground)
EVT_PAINT(CMyStatic::OnPaint)
EVT_TIMER(wxID_ANY, CMyStatic::OnTimer)
EVT_MOTION(CMyStatic::OnMouseMove)
EVT_SIZE(CMyStatic::OnSize)
EVT_LEAVE_WINDOW(CMyStatic::OnMouseLeave)
EVT_LEFT_DOWN(CMyStatic::OnClicked)
END_EVENT_TABLE()
/////////////////////////////////////////////////////////////////////////////
// Delete CTextFrame objects for scrolling text
void CMyStatic::DeleteTextFrames(void)
{
// Loop through every object pointers in array
for (int i=0; i<m_oTextFrames.GetCount(); ++i)
{
// Check if pointer is valid
if (m_oTextFrames.Item(i) != NULL)
{
// Free memory of object
delete m_oTextFrames.Item(i);
// Assign null pointer value
// m_oTextFrames.SetAt(i, NULL);
m_oTextFrames.Item(i) = NULL;
}
}
// Empty array
m_oTextFrames.Empty();
m_iTextFramesScrolling.Empty();
}
/////////////////////////////////////////////////////////////////////////////
// Calculate window and text area coordinates
void CMyStatic::CalculateWindowAndTextArea(void)
{
m_oWindowArea = this->GetRect();
m_oWindowArea.x = 0;
m_oWindowArea.y = 0;
m_oTextArea = m_oWindowArea;
}
/////////////////////////////////////////////////////////////////////////////
// Initialize size of text and its starting position
void CMyStatic::PrepareTextFramesForScrolling(void)
{
// Create a wxClientDC object
wxClientDC oClientDC(this);
// Select our created font into device context
wxFont font(10, wxFONTFAMILY_SWISS, wxNORMAL, wxNORMAL);
oClientDC.SetFont(font);
// Loop through every object pointers in array
for (int i=0; i<m_oTextFrames.GetCount(); ++i)
{
// Get pointer to current looping object
CTextFrame* pTextFrame = (CTextFrame*)m_oTextFrames.Item(i);
// Empty array
pTextFrame->m_iXOffSets.Empty();
// Calculate and set size of text to be drawn
// based on current device context
pTextFrame->m_szTextSize
= oClientDC.GetTextExtent(pTextFrame->m_strText);
}
// Restore original font
oClientDC.SetFont(*wxNORMAL_FONT);
// Check if there are at least 1 text frame in array
if (m_oTextFrames.GetCount() > 0)
{
// Set starting position in pixels for
// first frame to start ball rolling
((CTextFrame*)m_oTextFrames.Item(0))->m_iXOffSets.Add(m_oTextArea.GetRight());
}
}
/////////////////////////////////////////////////////////////////////////////
// Set new position for current scrolling texts based on scroll speed
void CMyStatic::IncrementScrollingTextFramesPosition(void)
{
// Make sure there are text to scroll
if (m_oTextFrames.GetCount() > 0)
{
// Loop through the indexes of current scrolling text
for (int i=0; i<m_iTextFramesScrolling.GetCount(); ++i)
{
// Get index of scrolling text frame object
int iIndex = m_iTextFramesScrolling.Item(i);
// Get pointer to the text frame object
CTextFrame* pTextFrame = (CTextFrame*)m_oTextFrames.Item(iIndex);
// Check for the number of same element if array until current position, excluding
int iXOffSetIndex = NumOfSameElement(m_iTextFramesScrolling, i);
// Get and calculate new x offset value
int iNewXOffSet = pTextFrame->m_iXOffSets.Item(iXOffSetIndex) - 1;
pTextFrame->m_iXOffSets.Item(iXOffSetIndex) = iNewXOffSet;
}
}
}
/////////////////////////////////////////////////////////////////////////////
// Set the clipping region so that text not within it will not be drawn
/////////////////////////////////////////////////////////////////////////////
// Draw scrolling text into text area
void CMyStatic::DrawScrollingText(wxDC* pDC /* Pointer to wxDC object */)
{
// Make sure pointer is valid
if (pDC != NULL)
{
// Loop through the indexes of text frame objects to draw
for (int i=0; i<m_iTextFramesScrolling.GetCount(); ++i)
{
// Get index of scrolling text frame object
int iIndex = m_iTextFramesScrolling.Item(i);
// Get pointer to the text frame object
CTextFrame* pTextFrame = (CTextFrame*)m_oTextFrames.Item(iIndex);
// Check for the number of same element if
// array until current position, excluding
int iXOffSetIndex = NumOfSameElement(m_iTextFramesScrolling, i);
// Calculate a rectangular coordinates to draw scroll text
wxRect oDrawingArea(pTextFrame->m_iXOffSets.Item(iXOffSetIndex),
m_oTextArea.GetTop(),
m_oTextArea.GetRight(),
m_oTextArea.GetBottom());
// Check if text has no associate url, then draw it as black
if (pTextFrame->m_strURL.IsEmpty() != false)
{
// Set black color text
pDC->SetTextForeground(CLR_BLACK);
}
// Text has an associate url, so draw as blue
else
{
// Set blue color text
pDC->SetTextForeground(CLR_BLUE);
}
// Draw scrolling text
pDC->DrawText(pTextFrame->m_strText,
oDrawingArea.GetLeft(),
0);
}
}
}
/////////////////////////////////////////////////////////////////////////////
// Add index of new scrolling text if allowance satisfied
void CMyStatic::AddOnNewScrollingText(void)
{
// Make sure there are text to scroll and there must be at least
// one text scrolling
if (m_oTextFrames.GetCount()>0
&& m_iTextFramesScrolling.GetCount()>0)
{
// Get index of last scrolling text frame object index
int iLastScrolling = m_iTextFramesScrolling.GetCount() - 1;
// Get index of last scrolling text frame object
int iIndex = m_iTextFramesScrolling.Item(iLastScrolling);
// Get pointer to the text frame object
CTextFrame* pTextFrame = (CTextFrame*)m_oTextFrames.Item(iIndex);
// Check if there is enough room to scroll another text behind it
if (
(pTextFrame->m_iXOffSets.Item(pTextFrame->m_iXOffSets.GetCount() - 1)
+ pTextFrame->m_szTextSize.GetWidth()
+ SCROLLALLOWANCE) < m_oTextArea.GetRight())
{
// Set new index for next scrolling text frame object in array
++iIndex;
// Check if index is not within array size
if (iIndex >= m_oTextFrames.GetCount())
{
// Reset index to start
iIndex = 0;
}
// Get pointer to the next text frame object
CTextFrame* pTextFrame = (CTextFrame*)m_oTextFrames.Item(iIndex);
// Set starting position in pixels for scrolling
pTextFrame->m_iXOffSets.Add(m_oTextArea.GetRight());
// Add new text frame index into array
m_iTextFramesScrolling.Add(iIndex);
}
}
}
/////////////////////////////////////////////////////////////////////////////
// Remove index of finished scrolling text if it runs out of view
void CMyStatic::RemoveFinishedScrollingText(void)
{
// Make sure there are text to scroll and indexes of text frame objects are there
if (m_oTextFrames.GetCount() > 0
&& m_iTextFramesScrolling.GetCount() > 0)
{
// Get index of first scrolling text frame object
int iIndex = m_iTextFramesScrolling.Item(0);
// Get pointer to the text frame object
CTextFrame* pTextFrame = (CTextFrame*)m_oTextFrames.Item(iIndex);
// Check if the first text frame has finished scrolling
if (
(pTextFrame->m_iXOffSets.Item(0)
+ pTextFrame->m_szTextSize.GetWidth()) <= m_oTextArea.GetLeft())
{
// Remove x offset from array
pTextFrame->m_iXOffSets.RemoveAt(0);
// Remove its index from array
m_iTextFramesScrolling.RemoveAt(0);
}
}
}
/////////////////////////////////////////////////////////////////////////////
// Check the number of same element previously in array
int CMyStatic::NumOfSameElement(
CArrayInt& iArray /* Reference to array */,
int iIndexToCheck /* Index of array item to be check against */)
{
// Counter to keep track of number of same element
int iCount = 0;
// Get value of array content at index position
int iValue = iArray.Item(iIndexToCheck);
// Loop through every element in array until indexed position (excluding)
for (int i=0; i<iIndexToCheck; ++i)
{
// Check if element match the value to check
if (iArray.Item(i) == iValue)
{
// Increment counter
iCount ++;
}
}
// Return counter value
return iCount;
}
/////////////////////////////////////////////////////////////////////////////
// Get index of CTextFrame object in array that is being hovered
int CMyStatic::IsTextBeingHovered(
wxPoint oCursorPos /* wxPoint object containing coordinates of cursor */,
int* ipXOffSet /* X coordinate of the text frame that is being hovered */)
{
// Make sure pointer is valid
if (ipXOffSet != NULL)
{
// Loop through every text frame indexes in array
for (int i=0; i<m_iTextFramesScrolling.GetCount(); ++i)
{
// Get index of scrolling text frame object
int iIndex = m_iTextFramesScrolling.Item(i);
// Get pointer to the text frame object
CTextFrame* pTextFrame = (CTextFrame*)m_oTextFrames.Item(iIndex);
for (int x=0; x<pTextFrame->m_iXOffSets.GetCount(); ++x)
{
// Calculate left and right x coordinate of text
int iLeftXOffSet = pTextFrame->m_iXOffSets.Item(x);
int iRightXOffSet = pTextFrame->m_iXOffSets.Item(x)
+ pTextFrame->m_szTextSize.GetWidth();
// Calculate starting y coordinate of scroll text
int iYPos = (m_oTextArea.GetHeight() -
pTextFrame->m_szTextSize.GetHeight()) / 2;
// Check if text is left partially out of area
if (iLeftXOffSet < m_oTextArea.GetLeft())
{
// Set left x offset value for later
// formation of rect coordinates
iLeftXOffSet = m_oTextArea.GetLeft();
}
// Check if text is right partially out of area
if (iRightXOffSet > m_oTextArea.GetRight())
{
// Set right x offset value for later formation
// of rect coordinates
iRightXOffSet = m_oTextArea.GetRight();
}
// Calculate a rectangular coordinates to represent
// the bounding rect around scroll text
wxRect oScrollTextArea(iLeftXOffSet,
iYPos,
pTextFrame->m_szTextSize.GetWidth(),
pTextFrame->m_szTextSize.GetHeight());
// Check if cursor over text
if (oScrollTextArea.Contains(oCursorPos) != false)
{
// Set x coordinate of text frame
*ipXOffSet = pTextFrame->m_iXOffSets.Item(x);
// Return index of text frame object in array
return iIndex;
}
}
}
}
// Cursor not on scroll text
return -1;
}
/////////////////////////////////////////////////////////////////////////////
// Start scrolling text
bool CMyStatic::StartScrolling(bool bRestart /* Flag to indicate whether to restart scrolling */)
{
// Make sure there are text to scroll
if (m_oTextFrames.GetCount() > 0)
{
m_Timer.Stop();
// Check if scrolling is to be restarted
if (bRestart == true)
{
// Empty array of indexes of current scrolling text frames
m_iTextFramesScrolling.Empty();
// Store index of the first text frame to be scrolling
m_iTextFramesScrolling.Add(0);
// Initialize size of text and its starting position
PrepareTextFramesForScrolling();
}
// Scrolling is to be continued
else
{
}
// Start timer
m_Timer.Start(SCROLLRATE);
// Indicate success
return true;
}
// Indicate failure
return false;
}
/////////////////////////////////////////////////////////////////////////////
// Add scrolling text and its url
bool CMyStatic::AddScrollText(wxString strText /* Text to scroll */,
wxString strURL /* URL behind scrolled text */)
{
strText += " "; // a bug :(
// Allocate memory for CTextFrame object
CTextFrame* pTextFrame = new CTextFrame(strText, strURL);
// Check if memory allocation successful
if (pTextFrame != NULL)
{
// Store object into array
m_oTextFrames.Add(pTextFrame);
// Assign null pointer value
pTextFrame = NULL;
// Indicate success
return true;
}
// Indicate failure
return false;
}
/////////////////////////////////////////////////////////////////////////////
// Remove all previously added scroll text
bool CMyStatic::RemoveAllScrollText(void)
{
// Remove all scroll text
DeleteTextFrames();
return true;
}
/////////////////////////////////////////////////////////////////////////////
// CMyStatic message handlers
void CMyStatic::OnEraseBackground(wxEraseEvent &event)
{
wxDC *pDC = event.GetDC();
wxBrush brush(CLR_GRAY, wxSOLID);
pDC->SetBackground(brush);
pDC->Clear();
}
void CMyStatic::OnPaint(wxPaintEvent &event)
{
wxPaintDC dc(this); // device context for painting
}
void CMyStatic::OnTimer(wxTimerEvent &event)
{
// Set new position for current scrolling texts based on scroll speed
IncrementScrollingTextFramesPosition();
// Create a CClientDC object
wxClientDC oClientDC(this);
wxFont font(10, wxFONTFAMILY_SWISS, wxNORMAL, wxNORMAL);
oClientDC.SetFont(font);
// Set background mode to opaque
oClientDC.SetBackgroundMode(wxSOLID);
// Set text background color to match control background
oClientDC.SetTextBackground(m_clrBackground);
// Draw scrolling text into text area
DrawScrollingText(&oClientDC);
// Restore original font
oClientDC.SetFont(*wxNORMAL_FONT);
// Remove index of finished scrolling text if it runs out of view
RemoveFinishedScrollingText();
// Add index of new scrolling text if allowance satisfied
AddOnNewScrollingText();
}
void CMyStatic::OnMouseMove(wxMouseEvent &event)
{
// X coordinate of hovered text frame
int iXOffSet = 0;
wxPoint point = event.GetPosition();
// Device context for client area
wxClientDC oClientDC(this);
// Get index of text frame object that is being hovered
m_iHoveredTextFrame = IsTextBeingHovered(point, &iXOffSet);
// Check if mouse is not hovering text
if (m_bOnHover == false)
{
// Check if text is being hovered
if (m_iHoveredTextFrame != -1)
{
// Stop timer
m_Timer.Stop();
// Mouse is now hovering text
m_bOnHover = true;
// Get pointer to the text frame object
CTextFrame* pTextFrame
= (CTextFrame*)m_oTextFrames.Item(m_iHoveredTextFrame);
// Check if this text has a hyperlink
if (pTextFrame->m_strURL.IsEmpty() == false)
{
// Select font for text to be drawn
wxFont font(10, wxFONTFAMILY_SWISS, wxNORMAL, wxNORMAL, true);
oClientDC.SetFont(font);
// Set background mode to opaque
oClientDC.SetBackgroundMode(wxSOLID);
// Set text color
oClientDC.SetTextBackground(CLR_GRAY);
oClientDC.SetTextForeground(CLR_RED);
// Calculate a rectangular coordinates to draw scroll text
wxRect oDrawingArea(iXOffSet, m_oTextArea.GetTop(),
m_oTextArea.GetRight(), m_oTextArea.GetBottom());
// Draw scroll text
oClientDC.DrawText(pTextFrame->m_strText,
oDrawingArea.GetLeft(), oDrawingArea.GetTop());
// Restore original font
oClientDC.SetFont(*wxNORMAL_FONT);
}
}
}
// Mouse move when text is hovered
else
{
// Check if text is not being hovered
if (m_iHoveredTextFrame == -1)
{
}
::wxSetCursor(wxCURSOR_HAND);
}
}
void CMyStatic::OnMouseLeave(wxMouseEvent &event)
{
// Mouse not hovering text
m_bOnHover = false;
// Continue scrolling text
m_Timer.Start();
}
void CMyStatic::OnSize(wxSizeEvent &event)
{
// Calculate window and text area coordinates
CalculateWindowAndTextArea();
event.Skip();
}
bool CMyStatic::Destroy()
{
// never go here, can anyone tell me why? :(
m_Timer.Stop();
return wxStaticText::Destroy();
}
void CMyStatic::OnClicked(wxMouseEvent &event)
{
// Check if mouse hovering text and index of text being hovered is valid
if (m_bOnHover != false && m_iHoveredTextFrame != -1)
{
// Get pointer to the text frame object
CTextFrame* pTextFrame
= (CTextFrame*)m_oTextFrames.Item(m_iHoveredTextFrame);
// Make sure url is not empty
if (pTextFrame->m_strURL.IsEmpty() != true)
{
// Open browser and go to url
// ShellExecute(NULL, wxT("open"), pTextFrame->m_strURL, NULL, NULL, SW_SHOW);
wxMessageBox(pTextFrame->m_strURL);
}
}
}
/////////////////////////////////////////////////////////////////////////////
// CMyStatic message handlers
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CTextFrame::CTextFrame()
{
// Initialize member data
m_szTextSize.SetWidth(0);
m_szTextSize.SetHeight(0);
m_strText = wxT("");
m_strURL = wxT("");
// Empty array
m_iXOffSets.Empty();
}
CTextFrame::CTextFrame(wxString strText /* Text of scrol */,
wxString strURL /* URL behind text */)
{
// Initialize member data
m_szTextSize.SetWidth(0);
m_szTextSize.SetHeight(0);
m_strText = strText;
m_strURL = strURL;
// Empty array
m_iXOffSets.Empty();
}
CTextFrame::~CTextFrame()
{
// Empty array
m_iXOffSets.Empty();
}
how to use it
Code: Select all
CMyStatic* ads = new CMyStatic();
ads->Create(
this, 11111,
wxT("staticText"),
wxPoint(100, 150), wxSize(200, 20), 0);
ads->AddScrollText("wxWidgets discussion forum", "http://wxwindows.org");
ads->AddScrollText("before you post your message", "http://wxwindows.org");
ads->AddScrollText("read some simple guidelines", "http://wxwindows.org");
ads->AddScrollText("this is a demo", "http://www.charry.org");
ads->AddScrollText("yahoo", "");
ads->StartScrolling();