Click here to Skip to main content
15,894,180 members
Articles / Desktop Programming / MFC

An MFC Chart Control with Enhanced User Interface

Rate me:
Please Sign up or sign in to vote.
4.92/5 (102 votes)
17 Jun 2013CPOL112 min read 441K   98.4K   390  
An MFC linear chart control with enhanced appearance.
////////////////////////////////////////////////////////////////////////////////
// 
// SlidrGDI.h : header file
// Declaration of the classes CTipWnd and CSliderGdiCtrl
// Declaration of the template<typename T> class CSliderCtrlT
// 
// Programmer: geoyar
// License: CPOL
// The code is provided "as is" without express or implied warranty.
//
// First version: 04/10/2011
// Last Update:   04/10/2011
//     
////////////////////////////////////////////////////////////////////////////////

using namespace std;
using namespace Gdiplus;

#pragma once

namespace SliderGdi
{

// This is code from Andrei Alexandresku book "Modern C++ Design", ch. 2
// Copyright (c) 2001 by Andrei Alexandrescu
// This code accompanies the book:
// Alexandrescu, Andrei. "Modern C++ Design: Generic Programming and Design 
//     Patterns Applied". Copyright (c) 2001. Addison-Wesley.
// Permission to use, copy, modify, distribute and sell this software for any 
//     purpose is hereby granted without fee, provided that the above copyright 
//     notice appear in all copies and that both that copyright notice and this 
//     permission notice appear in supporting documentation.

class NullType;

// Typelist
template <class T, class U>
struct TypeList
{
  typedef T Head;
  typedef U Tail;
};

#define TYPELIST_1(T1) TypeList<T1, NullType>
#define TYPELIST_2(T1, T2) TypeList<T1, TYPELIST_1(T2)>
#define TYPELIST_3(T1, T2, T3) TypeList<T1, TYPELIST_2(T2, T3)>
#define TYPELIST_4(T1, T2, T3, T4) TypeList<T1, TYPELIST_3(T2, T3, T4)>
#define TYPELIST_5(T1, T2, T3, T4, T5) TypeList<T1, TYPELIST_4(T2, T3, T4, T5)>
#define TYPELIST_6(T1, T2, T3, T4, T5, T6) TypeList<T1, TYPELIST_5(T2, T3, T4, T5, T6)>
#define TYPELIST_7(T1, T2, T3, T4, T5, T6, T7) TypeList<T1, TYPELIST_6(T2, T3, T4, T5, T6, T7)>
#define TYPELIST_8(T1, T2, T3, T4, T5, T6, T7, T8) TypeList<T1, TYPELIST_7(T2, T3, T4, T5, T6, T7, T8)>
#define TYPELIST_9(T1, T2, T3, T4, T5, T6, T7, T8, T9) \
                                                TypeList<T1, TYPELIST_8(T2, T3, T4, T5, T6, T7, T8, T9)>

// IndexOf
template <class TList, typename T> struct IndexOf;

template <typename T> 
struct IndexOf<NullType, T>
{
  enum { value = -1};
};

template <class Tail, typename T>
struct IndexOf<TypeList<T, Tail>, T>
{
  enum {value = 0};
};

template <class Head, class Tail, typename T>
struct IndexOf<TypeList<Head, Tail>, T>
{
private:
  enum {temp = IndexOf<Tail, T>::value};
public:
  enum {value = temp == -1 ? -1 : 1 +temp};
};

// This static assert code is from Loki static_check.h (<int> changed to <bool>
template<bool> struct CompileTimeError;
template<> struct CompileTimeError<true> {};

#define STATIC_CHECK(expr, msg) \
{ CompileTimeError<((expr) != 0)> ERROR_##msg; (void)ERROR_##msg; } 



// End of Andrei's code

// List of 'lega'l PODs
typedef TYPELIST_9(byte, short, unsigned short, int, unsigned 
                    int, long, unsigned long, float, double) SL_TYPELIST;

// Global Helper Functions
  void CreateRoundedRect(GraphicsPath& grPath, RectF& rectF, float radius);
  CRect RectFToRect(const RectF& rectF);
  RectF RectToRectF(const CRect& rect);
  PointF GetRectFCenter(const RectF& rectF);
  CPoint PointFToPoint(const PointF& pntF);
  double round_number(const double val, const int precision = 0);

///////////////////////////////////////////////////////////////////////////////
// Class CTipWnd

class CTipWnd : public CWnd
{
  DECLARE_DYNAMIC(CTipWnd)

public:
	CTipWnd();
	virtual ~CTipWnd();

protected:
	DECLARE_MESSAGE_MAP()

public:
  BOOL CreateTipWnd(const CRect parentrRect, const std::wstring& maxStr, int strNmb);
  BOOL RecalcTipWnd(const CRect parentRect, const std::wstring& maxStr, int strNmb);
  void CaptionStr(const std::wstring& szCaption) { m_szCaption = szCaption; }
  void ValStr(const std::wstring& szValStr) { m_szValStr = szValStr;}
  void UpdateTipWnd(const std::wstring& szValStr, const float posX, bool bShow);
private:
  CRect SetTipWndRects(Gdiplus::Graphics* grPtr, const CRect& parentRect, const std::wstring& maxStr,
                                                                                           int strNmb);
  Gdiplus::SizeF GetTextBoxSize(Gdiplus::Graphics* grPtr, const float tipWndWidth, 
                                                               const std::wstring& maxStr, int strNmb);
  void DrawTipWnd(Gdiplus::Graphics* grPtr);
  void DrawTipText(Gdiplus::Graphics* grPtr);

private:
  bool m_bAbove;                        // Tip above or below the slider
  std::wstring m_szCaption;
  std::wstring m_szValStr;
  Gdiplus::RectF m_textRectF;
  Gdiplus::RectF m_tipWndRectF;
  Gdiplus::PointF m_tipPntF;            // Balloon tip point
  Gdiplus::StringFormat m_strFormat;
  std::wstring m_fontFamilyStr;
  float m_fontSize;                     // Tip point size, in points
  Gdiplus::SizeF m_textBoxSF;

public:
  afx_msg void OnPaint();
};

///////////////////////////////////////////////////////////////////////////////
// CSliderGdiCtrl

#define MIN_FONTH 8 
#define MAX_FONTH 18

// Data Label values
#define EMSIZE_WW 0.3f
#define LAB_W 1.5f

#define LAB_INFLW 2.0f
#define LAB_INFLH 3.0f

class CSliderGdiCtrl : public CWnd
{
	DECLARE_DYNAMIC(CSliderGdiCtrl)

public:
  enum SLIDE_STATUS{ UNINIT, IDLE, LBTNDOWN, HOVERING};

public:
  CSliderGdiCtrl(int typeID = IndexOf<SL_TYPELIST, double>::value); 

	virtual ~CSliderGdiCtrl();

public:
  BOOL CreateSliderGdiCtrl(DWORD dwStyle, const CRect slRect, CWnd* pParent, UINT slID);

private: // Layout and drawing functions
  void InitSliderCtl(Gdiplus::Graphics& gr);
  void DrawBar(Gdiplus::Graphics* grPtr, Gdiplus::GraphicsPath& grPath);
  void DrawValLabel(Gdiplus::Graphics* grPtr, Gdiplus::GraphicsPath& grPath);
  void DrawMoveArea(Gdiplus::Graphics* grPtr, Gdiplus::GraphicsPath& grPath);
  void DrawThumb(Gdiplus::Graphics* grPtr, Gdiplus::GraphicsPath& grPath);

// Access functions
public:
  void SetBarColorDefault(bool bRedraw = false);
  Gdiplus::Color BarColor(void) const { return m_barFirstCol;}
  void BarColor(Gdiplus::Color barFirstCol, bool bRedraw = false); 

  Gdiplus::StringAlignment GetValStrAlignmentH() const { return m_valStrFormat.GetAlignment();}
  Gdiplus::StringAlignment GetValStrAlignmentV() const { return m_valStrFormat.GetLineAlignment();}
  void SetSliderStrFormat(Gdiplus::StringAlignment chAl, Gdiplus::StringAlignment lineAl, bool bRedraw = false);
  Gdiplus::FontStyle GetSliderFontStyle() const {return m_fontStyle;}
  std::wstring GetSliderFontFamilyName() const { return m_fontFamilyStr;}
  const WCHAR* GetSliderFontFamilyStr() const { return m_fontFamilyStr.c_str();}
  float GetSliderFontSize() const {return m_emSize;}
  void SetSliderFontAttributes(Gdiplus::FontStyle  fontStyle, std::wstring& strFontFamilyName, 
                                                                                  bool bRedraw = false);
  void SetSliderFontAttributes(Gdiplus::FontStyle  fontStyle, WCHAR* strFontFamilyName, bool bRedraw = false); 

  std::wstring ValString() const {return m_strValue;}   // Read-only member

private:
  bool IsSliderKeyDown(void); 

  bool ValToPos(double fVal, float& fPos);
  bool PosToVal(float fPos, double& fVal);              // To raw, not rounded val

  std::pair<int, int> GetFixedStrLen(double val, int precision);
  std::pair<int, int> GetSciStrLen(double val, int precision);

  void GetMaxValStr(std::wstring& maxLenStr);
  int GetMaxTipStr(std::wstring& maxString);
  void GetCombCapValString(std::wstring& comboStr);

  std::wstring SetValStr(double val, bool bRound = true);

  bool CheckWStrBounds(Gdiplus::Graphics& gr, const Gdiplus::RectF& rLayoutF, Gdiplus::RectF& rBoundF, 
    const float emSize, const Gdiplus::FontStyle fStyle, const int lines, const std::wstring& str);

// More public access functions
public:
  std::wstring GetCaption() { return m_strCaption;}
  void SetCaption(std::wstring& caption, bool bRedraw = false); 
  void SetCaption(WCHAR* caption, bool bRedraw = false); 
//                              { SetCaption(std::wstring(caption),bRedraw);}

  int Precision(void) const {return m_precision;}
  bool SetPrecision(int precision, bool bRedraw = false);
  double GetKeyStep (void) const;
  
// Access to thumb position (value)
  double GetCurrValue(void) const ;
  bool SetCurrValue(double value, bool bRedraw = false);

// Get min and max vals
  double GetMinVal(void) const;   // Output the rounded min and max values
  double GetMaxVal(void) const;

// Set Min and Max values
  bool SetMinMaxVal(double minVal, double maxVal, bool bAdjustCurrVal = false, bool bRedraw = false);

// Helpers
public:
// Set all init values and check for sanity
  bool SetInitVals(double minVal, double maxVal, int precision, double currVal, 
                                                                          bool bRedraw = false);
  int GetMaxPrecision(double value) const;
private:
  void SetEmSizeLims(Gdiplus::Graphics& grPtr, int minH, int maxH);
  float CalcEmSize(float rectH);
  float CalcDataLabAndMoveRectsF(Gdiplus::Graphics& gr, const Gdiplus::RectF& clRF, 
                                Gdiplus::RectF& labRF, Gdiplus::RectF& moveRF);
  bool IsThumbCenterVisible(void);
  bool RecalcTipWnd(void);
  void UpdateTipWnd(bool bShow = true);
  bool DestroyTipWnd(void);

public:
  bool IsSliderFullyVisible(int dx, int dy, int dW, int dH,CRect* visibleRectPtr);

protected:
	DECLARE_MESSAGE_MAP()

// Message Handlers
public:
  LRESULT SendNotification(UINT code = TB_THUMBPOSITION);

  afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
  afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
  afx_msg void OnRButtonDown(UINT nFlags, CPoint point);

  afx_msg void OnMouseMove(UINT nFlags, CPoint point);
  afx_msg LRESULT OnMouseLeave(WPARAM, LPARAM);

  afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
  afx_msg void OnKeyUp(UINT nChar, UINT nRepCnt, UINT nFlags);
  afx_msg void OnKillFocus(CWnd* pNewWnd);

  afx_msg void OnMove(int x, int y);
  afx_msg void OnSize(UINT nType, int cx, int cy);

  afx_msg void OnPaint();

private:
  float m_maxEmSize;      // Size of 18-pnt font for given screen
  float m_minEmSize;      // Size of 8-pnt font

  int m_typeID;           // Index of the slider data type: -1 if not supported, 
                          // 0 -6 for int, 7 for float, 8 for double
  SLIDE_STATUS m_slStat;  // For right painting
  std::wstring m_strCaption; 
  std::wstring  m_strValue; 

  double m_fPosScale;    // For thumb position to value and vice versa
  double m_fMinVal;
  double m_fMaxVal;
  double m_fCurrValue;

// Slider geometry  
  Gdiplus::RectF m_rBarF;       // Slider window rect
  Gdiplus::RectF m_rValLabelF;  // Data label
  Gdiplus::RectF m_rSelectF;    // Range of position of the thumb's center
  Gdiplus::RectF m_rThumbF;     // Thumb rect
  Gdiplus::RectF m_rMoveF;      // Range the thumb is moving in

  Gdiplus::Color m_barFirstCol; // First color of the bar's linear brush

// String related members
  Gdiplus::StringFormat m_valStrFormat;
  Gdiplus::FontStyle m_fontStyle;
  std::wstring m_fontFamilyStr;
  float m_emSize;

// Flag for WM_MOUSELEAVE
  BOOL m_bMouseTracking;
  int m_precision;              // Number of digits right of floating point
                                // if positive; left non-significant digit (zeros) if negative
  int m_digitNmb;               // Number of significant digits, defines range of precision 
  CTipWnd* m_tipWndPtr;         // Pointer to the popup tip window 

};

///////////////////////////////////////////////////////////////////////////////
// Class CSliderGdiCtrlT - templated wrapper for CSliderGdiCtrl

template <typename T>
class CSliderGdiCtrlT:public CSliderGdiCtrl
{
public:
  CSliderGdiCtrlT(void):CSliderGdiCtrl(IndexOf<SL_TYPELIST, T>::value) {}
  virtual ~CSliderGdiCtrlT() {}

public:
  template <typename U>
  bool IsRightSlType(void ) const {return (m_typeID == IndexOf<SL_TYPELIST, U>::value);}

// Access to m_fCurrValue of CSliderGdiCtlr
   T GetCurrValue(void) const {return static_cast<T>(CSliderGdiCtrl::GetCurrValue());}

// Set Curr Value - always clip value out of range max - min to max or min; return false if 
// value was clipped 
  template <typename T1>
  bool SetCurrValue(T1 value, bool bRedraw = false) 
  { 
    STATIC_CHECK((IndexOf<SL_TYPELIST, T>::value == IndexOf<SL_TYPELIST, T1>::value), SetCurrValue);

    return CSliderGdiCtrl::SetCurrValue(value, bRedraw);
  }

// Get min and max vals
  T GetMinVal(void) const {return static_cast<T>(CSliderGdiCtrl::GetMinVal());}
  T GetMaxVal(void) const {return static_cast<T>(CSliderGdiCtrl::GetMaxVal());}

// Set Min and Max values (Changes nothing and returns false if constraints on min 
//                         and max are violated or bAdjustCurrVal = false
//                         and m_currValue is out of new selection range. Throws a 
//                         compile time assert if T1 and/or T2 are different from T) 
  template <typename T1, typename T2>
  bool SetMinMaxVal(T1 minVal, T2 maxVal, bool bAdjustCurrVal = false, bool bRedraw = false)
  { 
    STATIC_CHECK(((IndexOf<SL_TYPELIST, T>::value == IndexOf<SL_TYPELIST, T1>::value)&&
        (IndexOf<SL_TYPELIST, T>::value == IndexOf<SL_TYPELIST, T2>::value)), 
         Wrong_Argument_Types_In_SetMinMaxVal);

    return CSliderGdiCtrl::SetMinMaxVal(minVal, maxVal, bAdjustCurrVal, bRedraw);
  }
// Throws a compile time assert if any of T1, T2, and T3 types is not T.
//  Changes nothing and returns false if any of constraints on params are violated
  template <typename T1, typename T2, typename T3>
  bool SetInitVals(T1 minVal, T2 maxVal, int precision, T3 currVal, bool bRedraw = false)
  { 
    STATIC_CHECK(((IndexOf<SL_TYPELIST, T>::value == IndexOf<SL_TYPELIST, T1>::value)&&
                       (IndexOf<SL_TYPELIST, T>::value == IndexOf<SL_TYPELIST, T2>::value) &&
                       (IndexOf<SL_TYPELIST, T>::value == IndexOf<SL_TYPELIST, T3>::value)), 
                        Wrong_Argument_Types_In_SetInitVals);

    return CSliderGdiCtrl::SetInitVals(minVal, maxVal, precision, currVal, bRedraw);
  }
};

} // End namespace SliderGdi

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Verizon Internet Services
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions