Click here to Skip to main content
15,880,503 members
Articles / Desktop Programming / ATL

Shell Extensibility - Explorer Desk Band, Tray Notification Icon et al.

Rate me:
Please Sign up or sign in to vote.
4.97/5 (21 votes)
28 Aug 2009CPOL12 min read 136.5K   3.2K   71  
A simple Calendar utility that demonstrates basic Shell extensibility techniques: desk band, tray notification icon, locales.
#include "StdAfx.h"
#include "CalendarWindow.h"
#include "DateFormat.h"
#include "VisualStyle.h"
#include "Utils.h"

////////////////////////////////////////////////////////////////////////////////
// 
const UINT_PTR UPDATE_TIMER_ID = 1;

////////////////////////////////////////////////////////////////////////////////
// 
class SelectGdiObject
{
public:
    SelectGdiObject(HDC hDC, HGDIOBJ hGdiObj) :
        m_hDC(hDC),
        m_hGdiObj(hGdiObj),
        m_hSaveGdiObj(::SelectObject(hDC, hGdiObj))
    {
        ATLASSERT(m_hSaveGdiObj && m_hSaveGdiObj != HGDI_ERROR);
    }

    ~SelectGdiObject()
    {
        if(m_hSaveGdiObj && m_hSaveGdiObj != HGDI_ERROR)
            ::SelectObject(m_hDC, m_hSaveGdiObj);
    }

    operator HGDIOBJ() const { return m_hGdiObj; }

private:
    HDC m_hDC;
    HGDIOBJ m_hGdiObj;
    HGDIOBJ m_hSaveGdiObj;

private:
    // not copyable
    SelectGdiObject(const SelectGdiObject&);
    SelectGdiObject& operator=(const SelectGdiObject&);
};

class DoubleBufferPaint
{
public:
    DoubleBufferPaint(HWND hWnd, DWORD dwRop = SRCCOPY) : m_hWnd(hWnd), m_dwRop(dwRop)
    {
        m_hdc = ::BeginPaint(m_hWnd, &m_ps);

        int x, y, cx, cy;
        InitDims(&x, &y, &cx, &cy);

        m_hdcMem = ::CreateCompatibleDC(m_hdc);
        m_hbmMem = ::CreateCompatibleBitmap(m_hdc, cx, cy);
        m_hbmSave = ::SelectObject(m_hdcMem, m_hbmMem);
        ::SetWindowOrgEx(m_hdcMem, x, y, NULL);
    }

    ~DoubleBufferPaint()
    {
        int x, y, cx, cy;
        InitDims(&x, &y, &cx, &cy);

        ::BitBlt(m_hdc, x, y, cx, cy, m_hdcMem, x, y, m_dwRop);

        ::SelectObject(m_hdcMem, m_hbmSave);
        ::DeleteObject(m_hbmMem);
        ::DeleteDC(m_hdcMem);

        ::EndPaint(m_hWnd, &m_ps);
    }

    HDC GetDC() const { return m_hdcMem; }
    const RECT& GetPaintRect() const { return m_ps.rcPaint; }

private:
    void InitDims(int* px, int* py, int* pcx, int* pcy) const
    {
        *px = m_ps.rcPaint.left;
        *py = m_ps.rcPaint.top;
        *pcx = m_ps.rcPaint.right - m_ps.rcPaint.left;
        *pcy = m_ps.rcPaint.bottom - m_ps.rcPaint.top;
    }

private:
    HWND m_hWnd;
    DWORD m_dwRop;

    HDC m_hdc;
    HDC m_hdcMem;
    HGDIOBJ m_hbmMem;
    HGDIOBJ m_hbmSave;
    PAINTSTRUCT m_ps;

private:
    // not copyable
    DoubleBufferPaint(const DoubleBufferPaint&);
    DoubleBufferPaint& operator=(const DoubleBufferPaint&);
};

////////////////////////////////////////////////////////////////////////////////
// CCalendarWindow
CCalendarWindow::CCalendarWindow() :
    m_pDeskBand(NULL),
    m_fHasFocus(FALSE),
    m_ptrVisualStyle(CVisualStyle::Create()),
    m_pDateFormat(NULL)
{
    ::GetLocalTime(&m_stLocalTime);
}

CCalendarWindow::~CCalendarWindow()
{
}

////////////////////////////////////////////////////////////////////////////////
// 

BOOL CCalendarWindow::Create(
    HWND hwndParent,
    LPUNKNOWN pDeskBand,
    LPUNKNOWN pInputObjectSite,
    const DateFormat* pDateFormat)
{
    if(!__super::Create(hwndParent))
        return FALSE;

    ATLASSERT(pDeskBand);
    m_pDeskBand = pDeskBand;

    ATLASSERT(pInputObjectSite);
    m_spInputObjectSite = pInputObjectSite;

    ATLASSERT(pDateFormat);
    m_pDateFormat = pDateFormat;

    const UINT_PTR timerId = SetUpdateTimer();
    ATLASSERT(timerId != 0);

    if(!timerId)
        return FALSE;

    UpdateCaption();

    return TRUE;
}

POINTL CCalendarWindow::CalcMinimalSize() const
{
    POINTL pt = {
        ::GetSystemMetrics(SM_CXMIN),
        ::GetSystemMetrics(SM_CYMIN)
    };

    return pt;
}

POINTL CCalendarWindow::CalcIdealSize() const
{
    if(!IsWindow()) return CalcMinimalSize();

    m_sDateString = m_pDateFormat->FormatDateString(m_stLocalTime);

    HDC hic = ::CreateIC(_T("DISPLAY"), NULL, NULL, NULL);

    SIZE size = { 0 };
    {
        SelectGdiObject gdiFont(hic, m_ptrVisualStyle->GetFont());
    
        const BOOL bRes = ::GetTextExtentPoint32(hic,
            m_sDateString, m_sDateString.GetLength(), &size);
        ATLASSERT(bRes);
    }

    ::DeleteDC(hic);

    const POINTL pt = { size.cx, size.cy };

    return pt;
}

BOOL CCalendarWindow::HasFocus() const
{
    return m_fHasFocus;
}

void CCalendarWindow::UpdateCaption()
{
    ATLASSERT(IsWindow());
    m_sDateString = m_pDateFormat->FormatDateString(m_stLocalTime);
    Invalidate();
}

////////////////////////////////////////////////////////////////////////////////
// Message handlers

LRESULT CCalendarWindow::OnFocus(
    UINT uMsg,
    WPARAM /*wParam*/,
    LPARAM /*lParam*/,
    BOOL& /*bHandled*/)
{
    m_fHasFocus = (uMsg == WM_SETFOCUS);

    if(m_spInputObjectSite)
        m_spInputObjectSite->OnFocusChangeIS(m_pDeskBand, m_fHasFocus);

    return 0L;
}

LRESULT CCalendarWindow::OnDestroy(
    UINT /*uMsg*/,
    WPARAM /*wParam*/,
    LPARAM /*lParam*/,
    BOOL& /*bHandled*/)
{
    KillTimer(UPDATE_TIMER_ID);

    return 0;
}

LRESULT CCalendarWindow::OnEraseBackground(
    UINT /*uMsg*/,
    WPARAM /*wParam*/,
    LPARAM /*lParam*/,
    BOOL& /*bHandled*/)
{
    return 1;
}

LRESULT CCalendarWindow::OnPaint(
    UINT /*uMsg*/,
    WPARAM /*wParam*/,
    LPARAM /*lParam*/,
    BOOL& /*bHandled*/)
{
    DoubleBufferPaint dbuffPaint(m_hWnd);
    
    Paint(dbuffPaint.GetDC(), dbuffPaint.GetPaintRect());

    return 0;
}

LRESULT CCalendarWindow::OnPowerBroadcast(
    UINT /*uMsg*/,
    WPARAM wParam,
    LPARAM /*lParam*/,
    BOOL& /*bHandled*/)
{
    if(wParam == PBT_APMRESUMECRITICAL ||
       wParam == PBT_APMRESUMESUSPEND ||
       wParam == PBT_APMRESUMESTANDBY)
    {
        // re-enable timer
        ATLVERIFY(SetUpdateTimer() != 0);
    }

    return 0;
}

LRESULT CCalendarWindow::OnTimer(
    UINT /*uMsg*/,
    WPARAM /*wParam*/,
    LPARAM /*lParam*/,
    BOOL& /*bHandled*/)
{
    SYSTEMTIME stNow = { 0 };
    ::GetLocalTime(&stNow);

    if(stNow.wDay != m_stLocalTime.wDay)
    {
        m_stLocalTime = stNow;
        UpdateCaption();
    }

    return 0;
}

LRESULT CCalendarWindow::OnTimeChange(
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam,
    BOOL& bHandled)
{
    return OnTimer(uMsg, wParam, lParam, bHandled);
}

LRESULT CCalendarWindow::OnThemeChanged(
    UINT /*uMsg*/,
    WPARAM /*wParam*/,
    LPARAM /*lParam*/,
    BOOL& /*bHandled*/)
{
    // re-create theme style
    m_ptrVisualStyle.Free();
    m_ptrVisualStyle.Attach(CVisualStyle::Create());

    return 0;
}

void CCalendarWindow::Paint(HDC hdc, const RECT& rcPaint) const
{
    m_ptrVisualStyle->DrawBackground(m_hWnd, hdc, rcPaint);

    RECT rcClient = { 0 };
    GetClientRect(&rcClient);

    SelectGdiObject gdiFont(hdc, m_ptrVisualStyle->GetFont());
    ::SetBkMode(hdc, TRANSPARENT);

    ::SetTextColor(hdc, m_ptrVisualStyle->GetTextColor());

    ::DrawText(hdc, m_sDateString, m_sDateString.GetLength(), &rcClient,
        DT_CENTER | DT_SINGLELINE | DT_VCENTER);
}

UINT_PTR CCalendarWindow::SetUpdateTimer()
{
    // Wake up every 10 seconds.
    UINT_PTR nTimerId = SetTimer(UPDATE_TIMER_ID, 10 * 1000);

    ATLASSERT(nTimerId != 0);

    return nTimerId;
}

////////////////////////////////////////////////////////////////////////////////

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
Australia Australia
More than ten years of C++ native development, and counting.

Smile | :)

Comments and Discussions