// TaskbarNotifier.cpp : implementation file
// By John O'Byrne - 05 July 2002
#include "stdafx.h"
#include "TaskbarNotifier.h"
#define IDT_HIDDEN 0
#define IDT_APPEARING 1
#define IDT_WAITING 2
#define IDT_DISAPPEARING 3
#define TASKBAR_ON_TOP 1
#define TASKBAR_ON_LEFT 2
#define TASKBAR_ON_RIGHT 3
#define TASKBAR_ON_BOTTOM 4
// CTaskbarNotifier
IMPLEMENT_DYNAMIC(CTaskbarNotifier, CWnd)
CTaskbarNotifier::CTaskbarNotifier()
{
m_strCaption="";
m_pWndParent=NULL;
m_bMouseIsOver=FALSE;
m_hSkinRegion=NULL;
m_hCursor=NULL;
m_crNormalTextColor=RGB(133,146,181);
m_crSelectedTextColor=RGB(10,36,106);
m_nSkinHeight=0;
m_nSkinWidth=0;
m_dwTimeToShow=0;
m_dwTimeToLive=0;
m_dwTimeToHide=0;
m_dwDelayBetweenShowEvents=0;
m_dwDelayBetweenHideEvents=0;
m_nStartPosX=0;
m_nStartPosY=0;
m_nCurrentPosX=0;
m_nCurrentPosY=0;
m_nIncrement=2;
m_nTaskbarPlacement=0;
m_nAnimStatus=IDT_HIDDEN;
m_rcText.SetRect(0,0,0,0);
}
CTaskbarNotifier::~CTaskbarNotifier()
{
// No need to delete the HRGN, SetWindowRgn() owns it after being called
}
int CTaskbarNotifier::Create(CWnd *pWndParent)
{
m_pWndParent=pWndParent;
CString strWndClass=AfxRegisterWndClass(0,AfxGetApp()->LoadStandardCursor(IDC_ARROW),GetSysColorBrush(COLOR_WINDOW),NULL);
return CreateEx(0,strWndClass,NULL,WS_POPUP,0,0,0,0,pWndParent->m_hWnd,NULL);
}
void CTaskbarNotifier::SetTextFont(LPCTSTR szFont,int nSize,int nNormalStyle,int nSelectedStyle)
{
LOGFONT lf;
m_myNormalFont.DeleteObject();
m_myNormalFont.CreatePointFont(nSize,szFont);
m_myNormalFont.GetLogFont(&lf);
// We set the Font of the unselected ITEM
if (nNormalStyle & TN_TEXT_BOLD)
lf.lfWeight = FW_BOLD;
else
lf.lfWeight = FW_NORMAL;
if (nNormalStyle & TN_TEXT_ITALIC)
lf.lfItalic=TRUE;
else
lf.lfItalic=FALSE;
if (nNormalStyle & TN_TEXT_UNDERLINE)
lf.lfUnderline=TRUE;
else
lf.lfUnderline=FALSE;
m_myNormalFont.DeleteObject();
m_myNormalFont.CreateFontIndirect(&lf);
// We set the Font of the selected ITEM
if (nSelectedStyle & TN_TEXT_BOLD)
lf.lfWeight = FW_BOLD;
else
lf.lfWeight = FW_NORMAL;
if (nSelectedStyle & TN_TEXT_ITALIC)
lf.lfItalic=TRUE;
else
lf.lfItalic=FALSE;
if (nSelectedStyle & TN_TEXT_UNDERLINE)
lf.lfUnderline=TRUE;
else
lf.lfUnderline=FALSE;
m_mySelectedFont.DeleteObject();
m_mySelectedFont.CreateFontIndirect(&lf);
}
void CTaskbarNotifier::SetTextColor(COLORREF crNormalTextColor,COLORREF crSelectedTextColor)
{
m_crNormalTextColor=crNormalTextColor;
m_crSelectedTextColor=crSelectedTextColor;
RedrawWindow();
}
void CTaskbarNotifier::SetTextRect(RECT rcText)
{
m_rcText=rcText;
}
BOOL CTaskbarNotifier::SetSkin(UINT nBitmapID,short red,short green,short blue)
{
BITMAP bm;
m_biSkinBackground.DeleteObject();
if (!m_biSkinBackground.LoadBitmap(nBitmapID))
return FALSE;
GetObject(m_biSkinBackground.GetSafeHandle(), sizeof(bm), &bm);
m_nSkinWidth=bm.bmWidth;
m_nSkinHeight=bm.bmHeight;
m_rcText.SetRect(0,0,bm.bmWidth,bm.bmHeight);
if (red!=-1 && green!=-1 && blue!=-1)
{
// No need to delete the HRGN, SetWindowRgn() owns it after being called
m_hSkinRegion=GenerateRegion((HBITMAP)m_biSkinBackground.GetSafeHandle(),(BYTE) red,(BYTE) green,(BYTE) blue);
SetWindowRgn(m_hSkinRegion, true);
}
return TRUE;
}
BOOL CTaskbarNotifier::SetSkin(LPCTSTR szFileName,short red,short green,short blue)
{
BITMAP bm;
HBITMAP hBmp;
hBmp=(HBITMAP) ::LoadImage(AfxGetInstanceHandle(),szFileName,IMAGE_BITMAP,0,0, LR_LOADFROMFILE);
if (!hBmp)
return FALSE;
m_biSkinBackground.DeleteObject();
m_biSkinBackground.Attach(hBmp);
GetObject(m_biSkinBackground.GetSafeHandle(), sizeof(bm), &bm);
m_nSkinWidth=bm.bmWidth;
m_nSkinHeight=bm.bmHeight;
m_rcText.SetRect(0,0,bm.bmWidth,bm.bmHeight);
if (red!=-1 && green!=-1 && blue!=-1)
{
// No need to delete the HRGN, SetWindowRgn() owns it after being called
m_hSkinRegion=GenerateRegion((HBITMAP)m_biSkinBackground.GetSafeHandle(),(BYTE) red,(BYTE) green,(BYTE) blue);
SetWindowRgn(m_hSkinRegion, true);
}
return TRUE;
}
void CTaskbarNotifier::Show(LPCTSTR szCaption,DWORD dwTimeToShow,DWORD dwTimeToLive,DWORD dwTimeToHide,int nIncrement)
{
unsigned int nDesktopHeight;
unsigned int nDesktopWidth;
unsigned int nScreenWidth;
unsigned int nScreenHeight;
CRect rcDesktop;
m_strCaption=szCaption;
m_dwTimeToShow=dwTimeToShow;
m_dwTimeToLive=dwTimeToLive;
m_dwTimeToHide=dwTimeToHide;
::SystemParametersInfo(SPI_GETWORKAREA,0,&rcDesktop,0);
nDesktopWidth=rcDesktop.right-rcDesktop.left;
nDesktopHeight=rcDesktop.bottom-rcDesktop.top;
nScreenWidth=::GetSystemMetrics(SM_CXSCREEN);
nScreenHeight=::GetSystemMetrics(SM_CYSCREEN);
BOOL bTaskbarOnRight=nDesktopWidth<nScreenWidth && rcDesktop.left==0;
BOOL bTaskbarOnLeft=nDesktopWidth<nScreenWidth && rcDesktop.left!=0;
BOOL bTaskBarOnTop=nDesktopHeight<nScreenHeight && rcDesktop.top!=0;
BOOL bTaskbarOnBottom=nDesktopHeight<nScreenHeight && rcDesktop.top==0;
switch (m_nAnimStatus)
{
case IDT_HIDDEN:
ShowWindow(SW_SHOW);
if (bTaskbarOnRight)
{
m_dwDelayBetweenShowEvents=m_dwTimeToShow/(m_nSkinWidth/m_nIncrement);
m_dwDelayBetweenHideEvents=m_dwTimeToHide/(m_nSkinWidth/m_nIncrement);
m_nStartPosX=rcDesktop.right;
m_nStartPosY=rcDesktop.bottom-m_nSkinHeight;
m_nTaskbarPlacement=TASKBAR_ON_RIGHT;
}
else if (bTaskbarOnLeft)
{
m_dwDelayBetweenShowEvents=m_dwTimeToShow/(m_nSkinWidth/m_nIncrement);
m_dwDelayBetweenHideEvents=m_dwTimeToHide/(m_nSkinWidth/m_nIncrement);
m_nStartPosX=rcDesktop.left-m_nSkinWidth;
m_nStartPosY=rcDesktop.bottom-m_nSkinHeight;
m_nTaskbarPlacement=TASKBAR_ON_LEFT;
}
else if (bTaskBarOnTop)
{
m_dwDelayBetweenShowEvents=m_dwTimeToShow/(m_nSkinHeight/m_nIncrement);
m_dwDelayBetweenHideEvents=m_dwTimeToHide/(m_nSkinHeight/m_nIncrement);
m_nStartPosX=rcDesktop.right-m_nSkinWidth;
m_nStartPosY=rcDesktop.top-m_nSkinHeight;
m_nTaskbarPlacement=TASKBAR_ON_TOP;
}
else //if (bTaskbarOnBottom)
{
// Taskbar is on the bottom or Invisible
m_dwDelayBetweenShowEvents=m_dwTimeToShow/(m_nSkinHeight/m_nIncrement);
m_dwDelayBetweenHideEvents=m_dwTimeToHide/(m_nSkinHeight/m_nIncrement);
m_nStartPosX=rcDesktop.right-m_nSkinWidth;
m_nStartPosY=rcDesktop.bottom;
m_nTaskbarPlacement=TASKBAR_ON_BOTTOM;
}
m_nCurrentPosX=m_nStartPosX;
m_nCurrentPosY=m_nStartPosY;
SetTimer(IDT_APPEARING,m_dwDelayBetweenShowEvents,NULL);
break;
case IDT_WAITING:
RedrawWindow();
KillTimer(IDT_WAITING);
SetTimer(IDT_WAITING,m_dwTimeToLive,NULL);
break;
case IDT_APPEARING:
RedrawWindow();
break;
case IDT_DISAPPEARING:
KillTimer(IDT_DISAPPEARING);
SetTimer(IDT_WAITING,m_dwTimeToLive,NULL);
if (bTaskbarOnRight)
m_nCurrentPosX=rcDesktop.right-m_nSkinWidth;
else if (bTaskbarOnLeft)
m_nCurrentPosX=rcDesktop.left;
else if (bTaskBarOnTop)
m_nCurrentPosY=rcDesktop.top;
else //if (bTaskbarOnBottom)
m_nCurrentPosY=rcDesktop.bottom-m_nSkinHeight;
SetWindowPos(NULL,m_nCurrentPosX,m_nCurrentPosY,m_nSkinWidth,m_nSkinHeight,SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOACTIVATE);
RedrawWindow();
break;
}
}
void CTaskbarNotifier::Hide()
{
switch (m_nAnimStatus)
{
case IDT_APPEARING:
KillTimer(IDT_APPEARING);
break;
case IDT_WAITING:
KillTimer(IDT_WAITING);
break;
case IDT_DISAPPEARING:
KillTimer(IDT_DISAPPEARING);
break;
}
MoveWindow(0,0,0,0);
ShowWindow(SW_HIDE);
m_nAnimStatus=IDT_HIDDEN;
}
HRGN CTaskbarNotifier::GenerateRegion(HBITMAP hBitmap, BYTE red, BYTE green, BYTE blue)
{
WORD wBmpWidth,wBmpHeight;
HRGN hRgn, hTmpRgn;
// 24bit pixels from the bitmap
BYTE *pPixels = Get24BitPixels(hBitmap, &wBmpWidth, &wBmpHeight);
if (!pPixels) return NULL;
// create our working region
hRgn = CreateRectRgn(0,0,wBmpWidth,wBmpHeight);
if (!hRgn) { delete pPixels; return NULL; }
DWORD p=0;
for (WORD y=0; y<wBmpHeight; y++)
{
for (WORD x=0; x<wBmpWidth; x++)
{
BYTE jRed = pPixels[p+2];
BYTE jGreen = pPixels[p+1];
BYTE jBlue = pPixels[p+0];
if (jRed==red && jGreen==green && jBlue==blue)
{
// remove transparent color from region
hTmpRgn = CreateRectRgn(x,y,x+1,y+1);
CombineRgn(hRgn, hRgn, hTmpRgn, RGN_XOR);
DeleteObject(hTmpRgn);
}
// next pixel
p+=3;
}
}
// release pixels
delete pPixels;
// return the region
return hRgn;
}
BYTE* CTaskbarNotifier::Get24BitPixels(HBITMAP pBitmap, WORD *pwWidth, WORD *pwHeight)
{
BITMAP bmpBmp;
LPBITMAPINFO pbmiInfo;
BITMAPINFO bmiInfo;
WORD wBmpWidth, wBmpHeight;
GetObject(pBitmap, sizeof(bmpBmp),&bmpBmp);
pbmiInfo = (LPBITMAPINFO)&bmpBmp;
wBmpWidth = (WORD)pbmiInfo->bmiHeader.biWidth;
wBmpWidth -= (wBmpWidth%4);
wBmpHeight = (WORD)pbmiInfo->bmiHeader.biHeight;
*pwWidth = wBmpWidth;
*pwHeight = wBmpHeight;
BYTE *pPixels = new BYTE[wBmpWidth*wBmpHeight*3];
if (!pPixels) return NULL;
HDC hDC =::GetWindowDC(NULL);
bmiInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmiInfo.bmiHeader.biWidth = wBmpWidth;
bmiInfo.bmiHeader.biHeight = -wBmpHeight;
bmiInfo.bmiHeader.biPlanes = 1;
bmiInfo.bmiHeader.biBitCount = 24;
bmiInfo.bmiHeader.biCompression = BI_RGB;
bmiInfo.bmiHeader.biSizeImage = wBmpWidth*wBmpHeight*3;
bmiInfo.bmiHeader.biXPelsPerMeter = 0;
bmiInfo.bmiHeader.biYPelsPerMeter = 0;
bmiInfo.bmiHeader.biClrUsed = 0;
bmiInfo.bmiHeader.biClrImportant = 0;
// get pixels from the original bitmap converted to 24bits
int iRes = GetDIBits(hDC,pBitmap,0,wBmpHeight,(LPVOID)pPixels,&bmiInfo,DIB_RGB_COLORS);
// release the device context
::ReleaseDC(NULL,hDC);
// if failed, cancel the operation.
if (!iRes)
{
delete pPixels;
return NULL;
};
// return the pixel array
return pPixels;
}
BEGIN_MESSAGE_MAP(CTaskbarNotifier, CWnd)
ON_WM_CREATE()
ON_WM_MOUSEMOVE()
ON_WM_DESTROY()
ON_WM_ERASEBKGND()
ON_WM_PAINT()
ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave)
ON_MESSAGE(WM_MOUSEHOVER, OnMouseHover)
ON_WM_SETCURSOR()
ON_WM_LBUTTONUP()
ON_WM_TIMER()
END_MESSAGE_MAP()
// CTaskbarNotifier message handlers
int CTaskbarNotifier::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CWnd::OnCreate(lpCreateStruct) == -1)
return -1;
m_hCursor = ::LoadCursor(NULL, MAKEINTRESOURCE(32649));
return 0;
}
void CTaskbarNotifier::OnDestroy()
{
CWnd::OnDestroy();
// TODO: Add your message handler code here
}
void CTaskbarNotifier::OnMouseMove(UINT nFlags, CPoint point)
{
TRACKMOUSEEVENT t_MouseEvent;
t_MouseEvent.cbSize = sizeof(TRACKMOUSEEVENT);
t_MouseEvent.dwFlags = TME_LEAVE | TME_HOVER;
t_MouseEvent.hwndTrack = m_hWnd;
t_MouseEvent.dwHoverTime = 1;
::_TrackMouseEvent(&t_MouseEvent);
CWnd::OnMouseMove(nFlags, point);
}
void CTaskbarNotifier::OnLButtonUp(UINT nFlags, CPoint point)
{
m_pWndParent->PostMessage(WM_TASKBARNOTIFIERCLICKED,0,0);
CWnd::OnLButtonUp(nFlags, point);
}
LRESULT CTaskbarNotifier::OnMouseHover(WPARAM w, LPARAM l)
{
if (m_bMouseIsOver==FALSE)
{
m_bMouseIsOver=TRUE;
RedrawWindow();
}
return 0;
}
LRESULT CTaskbarNotifier::OnMouseLeave(WPARAM w, LPARAM l)
{
if (m_bMouseIsOver==TRUE)
{
m_bMouseIsOver=FALSE;
RedrawWindow();
}
return 0;
}
BOOL CTaskbarNotifier::OnEraseBkgnd(CDC* pDC)
{
CDC memDC;
CBitmap *pOldBitmap;
BITMAP bm;
memDC.CreateCompatibleDC(pDC);
GetObject(m_biSkinBackground.GetSafeHandle(), sizeof(bm), &bm);
pOldBitmap=memDC.SelectObject(&m_biSkinBackground);
pDC->BitBlt(0,0,bm.bmWidth,bm.bmHeight,&memDC,0,0,SRCCOPY);
memDC.SelectObject(pOldBitmap);
return TRUE;
}
void CTaskbarNotifier::OnPaint()
{
CPaintDC dc(this);
CRect rcClient;
CFont *pOldFont;
char *szBuffer;
if (m_bMouseIsOver)
{
dc.SetTextColor(m_crSelectedTextColor);
pOldFont=dc.SelectObject(&m_mySelectedFont);
}
else
{
dc.SetTextColor(m_crNormalTextColor);
pOldFont=dc.SelectObject(&m_myNormalFont);
}
szBuffer=new char[m_strCaption.GetLength()+10];
strcpy(szBuffer,m_strCaption);
dc.SetBkMode(TRANSPARENT);
rcClient.DeflateRect(10,20,10,20);
dc.DrawText(szBuffer,-1,m_rcText,DT_CENTER | DT_VCENTER | DT_WORDBREAK | DT_END_ELLIPSIS);
delete[] szBuffer;
dc.SelectObject(pOldFont);
}
BOOL CTaskbarNotifier::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if (nHitTest == HTCLIENT)
{
::SetCursor(m_hCursor);
return TRUE;
}
return CWnd::OnSetCursor(pWnd, nHitTest, message);
}
void CTaskbarNotifier::OnTimer(UINT nIDEvent)
{
switch (nIDEvent)
{
case IDT_APPEARING:
m_nAnimStatus=IDT_APPEARING;
switch(m_nTaskbarPlacement)
{
case TASKBAR_ON_BOTTOM:
if (m_nCurrentPosY>(m_nStartPosY-m_nSkinHeight))
m_nCurrentPosY-=m_nIncrement;
else
{
KillTimer(IDT_APPEARING);
SetTimer(IDT_WAITING,m_dwTimeToLive,NULL);
m_nAnimStatus=IDT_WAITING;
}
break;
case TASKBAR_ON_TOP:
if ((m_nCurrentPosY-m_nStartPosY)<m_nSkinHeight)
m_nCurrentPosY+=m_nIncrement;
else
{
KillTimer(IDT_APPEARING);
SetTimer(IDT_WAITING,m_dwTimeToLive,NULL);
m_nAnimStatus=IDT_WAITING;
}
break;
case TASKBAR_ON_LEFT:
if ((m_nCurrentPosX-m_nStartPosX)<m_nSkinWidth)
m_nCurrentPosX+=m_nIncrement;
else
{
KillTimer(IDT_APPEARING);
SetTimer(IDT_WAITING,m_dwTimeToLive,NULL);
m_nAnimStatus=IDT_WAITING;
}
break;
case TASKBAR_ON_RIGHT:
if (m_nCurrentPosX>(m_nStartPosX-m_nSkinWidth))
m_nCurrentPosX-=m_nIncrement;
else
{
KillTimer(IDT_APPEARING);
SetTimer(IDT_WAITING,m_dwTimeToLive,NULL);
m_nAnimStatus=IDT_WAITING;
}
break;
}
SetWindowPos(NULL,m_nCurrentPosX,m_nCurrentPosY,m_nSkinWidth,m_nSkinHeight,SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOACTIVATE);
//RedrawWindow();
break;
case IDT_WAITING:
KillTimer(IDT_WAITING);
SetTimer(IDT_DISAPPEARING,m_dwDelayBetweenHideEvents,NULL);
break;
case IDT_DISAPPEARING:
m_nAnimStatus=IDT_DISAPPEARING;
switch(m_nTaskbarPlacement)
{
case TASKBAR_ON_BOTTOM:
if (m_nCurrentPosY<m_nStartPosY)
m_nCurrentPosY+=m_nIncrement;
else
{
KillTimer(IDT_DISAPPEARING);
Hide();
}
break;
case TASKBAR_ON_TOP:
if (m_nCurrentPosY>m_nStartPosY)
m_nCurrentPosY-=m_nIncrement;
else
{
KillTimer(IDT_DISAPPEARING);
Hide();
}
break;
case TASKBAR_ON_LEFT:
if (m_nCurrentPosX>m_nStartPosX)
m_nCurrentPosX-=m_nIncrement;
else
{
KillTimer(IDT_DISAPPEARING);
Hide();
}
break;
case TASKBAR_ON_RIGHT:
if (m_nCurrentPosX<m_nStartPosX)
m_nCurrentPosX+=m_nIncrement;
else
{
KillTimer(IDT_DISAPPEARING);
Hide();
}
break;
}
SetWindowPos(NULL,m_nCurrentPosX,m_nCurrentPosY,m_nSkinWidth,m_nSkinHeight,SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOACTIVATE);
//RedrawWindow();
break;
}
CWnd::OnTimer(nIDEvent);
}