Click here to Skip to main content
15,892,517 members
Articles / Desktop Programming / MFC

AppBar - How to implement a sliding desktop bar with a simple MFC class and a DLL

Rate me:
Please Sign up or sign in to vote.
4.58/5 (25 votes)
10 Jul 2008CPOL4 min read 154.1K   6.4K   95  
An alternative to standard Windows AppBars with minimal changes to your application.
// MainFrm.cpp : implementation of the CMainFrame class
//

#include "stdafx.h"
#include "AppBarDemo.h"
#include "MainFrm.h"
#include "math.h"

#define DISPLAY_WIDTH 11

COLORREF palette[4][5] = {
	{ RGB(255,0,0),     RGB(255,127,127), RGB(255,191,191), RGB(255,207,207), RGB(255,239,239) },
	{ RGB(0,207,0),     RGB(63,207,63),   RGB(191,255,191), RGB(199,255,199), RGB(239,255,239) },
	{ RGB(0,0,255),     RGB(127,127,255), RGB(191,191,255), RGB(207,207,255), RGB(239,239,255) },
	{ RGB(127,127,127), RGB(175,175,175), RGB(207,207,207), RGB(217,217,217), RGB(239,239,239) },
};

enum tones { tone0=0, tone127, tone191, tone207, tone239 };

/////////////////////////////////////////////////////////////////////////////
// CMainFrame

IMPLEMENT_DYNAMIC(CMainFrame, CFrameWnd)

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
	//{{AFX_MSG_MAP(CMainFrame)
	ON_WM_PAINT()
	ON_WM_ERASEBKGND()
	ON_WM_GETMINMAXINFO()
	ON_WM_SIZE()
	ON_WM_CREATE()
	ON_WM_LBUTTONDOWN()
	ON_WM_CTLCOLOR()
	ON_WM_LBUTTONDBLCLK()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CMainFrame construction/destruction

CMainFrame::CMainFrame()
{
	CTime t = CTime::GetCurrentTime();
	m_Month = t.GetMonth();
	m_Year   = t.GetYear();
	m_Operator = 0;
	m_Status = Result;
	m_Display = "0";
    m_Value = 0.0;

	m_Palette = 2;
	m_ClearColor.CreateSolidBrush(palette[m_Palette][tone239]);
}

CMainFrame::~CMainFrame()
{
}

BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
	if( !CFrameWnd::PreCreateWindow(cs) )
		return FALSE;

	return TRUE;
}

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CFrameWnd::OnCreate(lpCreateStruct) == -1)
		return -1;
	
	CRect rect;
	m_Notepad.Create(WS_VISIBLE|ES_MULTILINE|ES_AUTOVSCROLL, rect, this, 100);


	return 0;
}

void CMainFrame::OnPaint() 
{
	CPaintDC dc(this); // device context for painting
	CDC NoFlick;
	CBitmap bmp, *oldBmp;
	CRect rect;

	NoFlick.CreateCompatibleDC(&dc);
	GetClientRect(&rect);
	bmp.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height());
	oldBmp = NoFlick.SelectObject(&bmp);

	NoFlick.SelectStockObject(NULL_PEN);
	NoFlick.SelectStockObject(SYSTEM_FONT);
	NoFlick.SetBkMode(TRANSPARENT);

	EraseBackground(&NoFlick);

	NoFlick.SetTextColor(palette[m_Palette][tone0]);
	NoFlick.TextOut(10,7, "Calculator");
	DrawDisplay(&NoFlick);
	DrawButtons(&NoFlick);
	
	DrawCalendar(&NoFlick, m_Month, m_Year);
	DrawNotepad(&NoFlick);

	dc.BitBlt(0, 0, rect.right, rect.bottom, &NoFlick, 0, 0, SRCCOPY);
	NoFlick.SelectObject(*oldBmp);
}

BOOL CMainFrame::OnEraseBkgnd(CDC* pDC) 
{
	return FALSE;
}

HBRUSH CMainFrame::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) 
{
	HBRUSH hbr = CFrameWnd::OnCtlColor(pDC, pWnd, nCtlColor);

	pDC->SetBkColor(palette[m_Palette][tone239]);
	if (nCtlColor==CTLCOLOR_EDIT)
		return (HBRUSH)m_ClearColor;

	return hbr;
}

void CMainFrame::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI) 
{
	lpMMI->ptMinTrackSize = CPoint(150,480);
	lpMMI->ptMaxTrackSize = lpMMI->ptMaxSize = CPoint(150,1600);
	
	CFrameWnd::OnGetMinMaxInfo(lpMMI);
}


void CMainFrame::EraseBackground(CDC *pDC)
{
	CRect rect;
	GetClientRect(&rect);

	rect.top = 400;
	pDC->FillSolidRect(&rect, palette[m_Palette][tone191]);

	// Draws background in degrad�
	COLORREF tone,top,bot;				
	top = palette[m_Palette][tone239];
	bot = palette[m_Palette][tone191];

	rect.top = 0;
	rect.bottom = 2;
	for (int i=0; i<200; i++) {
		tone = RGB(
			(GetRValue(bot)-GetRValue(top))*((float)i)/200.0f+GetRValue(top),
			(GetGValue(bot)-GetGValue(top))*((float)i)/200.0f+GetGValue(top),
			(GetBValue(bot)-GetBValue(top))*((float)i)/200.0f+GetBValue(top));
		pDC->FillSolidRect(rect,tone);
		rect.OffsetRect(0,2);
	}

	// Draw closing close
	CPen pen(PS_SOLID,1,palette[m_Palette][tone191]);
	pDC->SelectObject(pen);

	GetClientRect(&rect);
	rect.DeflateRect(rect.Width()-12,4,4,rect.Height()-12);

	CPoint poly[7];
	poly[0] = rect.TopLeft();
	poly[1] = rect.BottomRight();
	poly[2] = poly[1] + CSize( 0,-1);
	poly[3] = poly[0] + CSize( 1, 0);
	poly[4] = poly[3] + CSize(-1, 1);
	poly[5] = poly[1] + CSize(-1, 0);
	poly[6] = poly[1];
	pDC->Polyline(poly, 7);
	poly[0] = CPoint(rect.left, rect.bottom);
	poly[1] = CPoint(rect.right, rect.top);
	poly[2] = poly[1] + CSize( 0, 1);
	poly[3] = poly[0] + CSize( 1, 0);
	poly[4] = poly[3] + CSize(-1,-1);
	poly[5] = poly[1] + CSize(-1, 0);
	poly[6] = poly[1];
	pDC->Polyline(poly, 7);

	pDC->SelectStockObject(NULL_PEN);
}

void CMainFrame::DrawDisplay(CDC *pDC)
{
	CRect rect;
	CBrush *oldBrush, bg(palette[m_Palette][tone207]);

	GetClientRect(&rect);
	rect.left  +=10;
	rect.right -=10;
	rect.top   +=25;
	rect.bottom = rect.top + 25;

	oldBrush = (CBrush *)pDC->SelectObject(&bg);
	pDC->RoundRect(&rect, CPoint(5,5));
	pDC->SelectObject(oldBrush);

	rect.DeflateRect(CSize(3,3));
	pDC->DrawText(m_Display, &rect, DT_RIGHT);

}

static unsigned char buttons[4][4] = {
	'7', '8', '9', 'C',
	'4', '5', '6', '/',  // Divide
	'1', '2', '3', 'x',  // Multiply
	'0',0xB1, '.', '-'   // +/-
};

void CMainFrame::DrawButtons(CDC *pDC)
{
	CString label;
	CRect rect;
	CBrush *oldBrush, bg(palette[m_Palette][tone207]);
	int row,col;

	GetClientRect(&rect);
	float w = (rect.Width()-20)/11.0f;
	
	pDC->SetTextColor(palette[m_Palette][tone127]);
	oldBrush = (CBrush *)pDC->SelectObject(&bg);
	for (row=0; row<4; row++) {
		for (col=0; col<4; col++) {
			rect = CRect(10+(int)(col*w*3.0f), 60+row*25, 10+(int)(col*w*3.0+w*2.0), 60+row*25+(int)(w*2.0));
			pDC->RoundRect(&rect, CPoint(5,5));
			
			label = buttons[row][col];
			rect.top += 2;
			pDC->DrawText(label, &rect, DT_CENTER);
		}
	}

	col = 0;
	rect = CRect(10+(int)(col*w*3.0f), 60+row*25, 10+(int)(col*w*3.0+w*5.0), 60+row*25+(int)(w*2.0));
	pDC->RoundRect(&rect, CPoint(5,5));
	rect.top += 2;
	pDC->DrawText("=", &rect, DT_CENTER);

	col = 2;
	rect = CRect(10+(int)(col*w*3.0f), 60+row*25, 10+(int)(col*w*3.0+w*5.0), 60+row*25+(int)(w*2.0));
	pDC->RoundRect(&rect, CPoint(5,5));
	rect.top += 2;
	pDC->DrawText("+", &rect, DT_CENTER);

	pDC->SelectObject(oldBrush);
}


void CMainFrame::DrawCalendar(CDC *pDC, int month, int year)
{
	static char dow[7] = { 'S', 'M', 'T', 'W', 'T', 'F', 'S' };

	CTime t(year, month+1, 1,0,0,0);
	t -= CTimeSpan::CTimeSpan(1,0,0,0);
	int totdays = t.GetDay();
	
	t = CTime(year, month, 1,0,0,0);
	int	nDay1	= t.GetDayOfWeek();
	CString sMonth = t.Format("%b");

	t = CTime::GetCurrentTime();
	int	nMonth  = t.GetMonth();
	int	nYear	= t.GetYear();
	int nDay    = t.GetDay();

	CRect rect;
	GetClientRect(&rect);
	rect.DeflateRect(10,200,10,0),
	rect.bottom = rect.top + 110;
	float w = (rect.Width()-4) / 7.0f;
	
	CPen *oldPen, pen(PS_SOLID,1,palette[m_Palette][tone127]);
	oldPen = (CPen*)pDC->SelectObject(&pen);
	pDC->SelectStockObject(NULL_BRUSH);
	pDC->RoundRect(&rect, CPoint(5,5));
	pDC->MoveTo(13,230);
	pDC->LineTo(rect.right-3,230);

	pDC->SelectObject(oldPen);

	CString label;
	label.Format("%s %i", (LPCTSTR)sMonth, year);
	rect.top += 2;
	pDC->DrawText(label, &rect, DT_CENTER);

	pDC->SelectStockObject(ANSI_VAR_FONT);
	for (int col=0; col<7; col++) {
		rect = CRect(12+(int)(col*w), 217, 12+(int)((col+1)*w), 280);
		label = dow[col];
		pDC->DrawText(label, &rect, DT_CENTER);
	}


	bool thisMonth = false;
	if (month==nMonth && year==nYear)
		thisMonth = true;

	pDC->SelectStockObject(WHITE_BRUSH);
	int row = 0;
	col = nDay1 - 1;
	for (int d=1; d<=totdays; d++) {
		rect = CRect(12+(int)(col*w), 233+row*15, 12+(int)((col+1)*w+1), 247+row*15);
	
		if (thisMonth && d==nDay)
			pDC->Rectangle(&rect);

		label.Format("%i", d);
		pDC->DrawText(label, &rect, DT_CENTER);
		col++;
		if (col>=7) {
			col = 0;
			row++;
			if (row>4)
				row = 0;
		}
	}
	pDC->SelectStockObject(SYSTEM_FONT);

	CBrush *oldBrush, brush(palette[m_Palette][tone127]);
	oldBrush = (CBrush *)pDC->SelectObject(&brush);
	CPoint arrow[3];

	GetClientRect(&rect);

	arrow[0] = CPoint(13,208);
	arrow[1] = CPoint(22,202);
	arrow[2] = CPoint(22,214);
	pDC->Polygon(arrow,3);

	arrow[0] = CPoint(rect.right-13,208);
	arrow[1] = CPoint(rect.right-22,202);
	arrow[2] = CPoint(rect.right-22,214);
	pDC->Polygon(arrow,3);

	pDC->SelectObject(oldBrush);
}

void CMainFrame::DrawNotepad(CDC *pDC)
{
	pDC->SetTextColor(palette[m_Palette][tone0]);
	pDC->TextOut(10,321,"Notepad");

	CRect rect;	
	GetClientRect(&rect);
	rect.DeflateRect(10,340,10,10);
	CBrush *oldBrush = (CBrush *)pDC->SelectObject(&m_ClearColor);
	pDC->RoundRect(&rect,CPoint(7,7));
	pDC->SelectObject(oldBrush);
}

void CMainFrame::OnSize(UINT nType, int cx, int cy) 
{
	CFrameWnd::OnSize(nType, cx, cy);

	CRect rect;	
	GetClientRect(&rect);
	rect.DeflateRect(10,340,10,10);
	rect.DeflateRect(3,3,3,3);

	m_Notepad.MoveWindow(&rect);
}

void CMainFrame::OnLButtonDown(UINT nFlags, CPoint point) 
{
	CRect rect;
	GetClientRect(&rect);
	int wt = rect.Width();
	float w = (wt-20)/11.0f;

	for (int row=0; row<4; row++) {
		for (int col=0; col<4; col++) {
			rect = CRect(10+(int)(col*w*3.0f), 60+row*25, 10+(int)(col*w*3.0+w*2.0), 60+row*25+(int)(w*2.0));

			if (rect.PtInRect(point)) {
				Operate(buttons[row][col]);
			    return;
			}
		}
	}
	
	rect = CRect(10, 160, 10+(int)(w*5.0), 160+(int)(w*2.0));
	if (rect.PtInRect(point)) {
		Operate('=');
		return;
	}

	rect = CRect(10+(int)(w*6.0f), 160, 10+(int)(w*11.0), 160+(int)(w*2.0));
	if (rect.PtInRect(point)) {
		Operate('+');
		return;
	}

	rect = CRect(13,202,22,214);
	if (rect.PtInRect(point)) {
		Operate('<');
		return;
	}
	rect = CRect(wt-22,202,wt-13,214);
	if (rect.PtInRect(point)) {
		Operate('>');
		return;
	}

	GetClientRect(&rect);
	rect.DeflateRect(rect.Width()-12,4,4,rect.Height()-12);
	if (rect.PtInRect(point))
		::AfxPostQuitMessage(0);

	CFrameWnd::OnLButtonDown(nFlags, point);
}

void CMainFrame::OnLButtonDblClk(UINT nFlags, CPoint point) 
{
	CRect rect;
	GetClientRect(&rect);
	int wt=rect.Width();

	rect.DeflateRect(25,202,25,0);
	rect.bottom = 214;

	if (rect.PtInRect(point)) {
		CTime t = CTime::GetCurrentTime();
		m_Month = t.GetMonth();
		m_Year   = t.GetYear();
		Invalidate(TRUE);
		return;
	}

	rect = CRect(13,202,22,214);
	if (rect.PtInRect(point)) {
		Operate('<');
		return;
	}
	rect = CRect(wt-22,202,wt-13,214);
	if (rect.PtInRect(point)) {
		Operate('>');
		return;
	}
	
	CFrameWnd::OnLButtonDblClk(nFlags, point);
}


void CMainFrame::Operate(unsigned char button)
{
	switch (button) {
		case '0':
			if (m_Display=="0")
				break;
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
			if (m_Status==Result || m_Status==Operator) {
				m_Display = button;
				m_Value = atof(m_Display);
				m_Status = Number;
				m_bPoint = false;
			} else if (m_Status==Number) {
				if (m_Display.GetLength()<DISPLAY_WIDTH) {
					m_Display += button;
					m_Value = atof(m_Display);
				}
			}
			break;
		case '.':
			if (m_Status==Result || m_Status==Operator) {
				m_Display = button;
				m_Status = Number;
				m_bPoint = true;
			} else if (m_Status==Number && !m_bPoint) {
				if (m_Display.GetLength()<DISPLAY_WIDTH) {
					m_Display += button;
					m_bPoint = true;
				}
			}
			break;
		case '+':
		case '-':
		case 'x':
		case '/':
			if (m_Status==Number && m_Operator)
				MakeCalculus();
			m_FirstNumber = m_Value;
			m_Operator = button;
			m_Status = Operator;
			NormalizeDisplay();
			break;
		case 0xB1:  // +/-
			m_Value = -m_Value;
			NormalizeDisplay();
			break;
		case '=':
			if (m_Status==Number && m_Operator)
				MakeCalculus();
			else if (m_Status==Operator && m_Operator) {
				m_FirstNumber = m_Value;
				MakeCalculus();
			}
			m_Operator = 0;
			m_Status = Result;
			break;
		case 'C':
			m_Operator = 0;
			m_Status = Result;
			m_Display = "0";
			m_Value = 0.0;
			break;
		case '<':
			m_Month--;
			if (!m_Month) {
				m_Month = 12;
				m_Year--;
			}
			Invalidate(TRUE);
			break;
		case '>':
			m_Month++;
			if (m_Month>12) {
				m_Month = 1;
				m_Year++;
			}
			Invalidate(TRUE);
			break;
	}

	Invalidate(TRUE);
}

void CMainFrame::MakeCalculus()
{	
	double result;

	switch (m_Operator) {
		case '+':
			result = m_FirstNumber + m_Value;
			break;
		case '-':
			result = m_FirstNumber - m_Value;
			break;
		case 'x':   
			result = m_FirstNumber * m_Value;
			break;
		case '/':   // Divide
			if (m_Value==0.0) {
				m_Display = "Error";
				m_Status  = Result;
				m_Value   = 0.0;
				return;
			}
			result = m_FirstNumber / m_Value;
			break;
		default:
			return;
	}

	m_Value = result;
	NormalizeDisplay();
}

void CMainFrame::NormalizeDisplay()
{
	if (log10(fabs(m_Value))>10.0 || log10(fabs(m_Value))<-10.0)
		m_Display.Format("%e", m_Value);
	else
		m_Display.Format("%.10f", m_Value);
	m_Display.TrimRight('0');
	m_Display.TrimRight('.');

	if (m_Display.GetLength()>13)
		m_Display = m_Display.Left(12);
}

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
Architect
Peru Peru


Computer Electronics professional, Software Architect and senior Windows C++ and C# developer with experience in many other programming languages, platforms and application areas including communications, simulation systems, PACS/DICOM (radiology), GIS, 3D graphics and HTML5-based web applications.
Currently intensively working with Visual Studio and TFS.

Comments and Discussions