Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version
Go to top

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

, 28 Aug 2009
A simple Calendar utility that demonstrates basic Shell extensibility techniques: desk band, tray notification icon, locales.
Calendar.zip
Calendar
CalendarDeskBand
CalendarDeskBand.def
CalendarDeskBand.rgs
CalendarDeskBandApp.rgs
CalendarSysTray
Res
01.ico
02.ico
03.ico
04.ico
05.ico
06.ico
07.ico
08.ico
09.ico
10.ico
11.ico
12.ico
13.ico
14.ico
15.ico
16.ico
17.ico
18.ico
19.ico
20.ico
21.ico
22.ico
23.ico
24.ico
25.ico
26.ico
27.ico
28.ico
29.ico
30.ico
31.ico
Blank-16x16-16.ico
Calendar.ico
Common
Win32
Release
Calendar.exe
x64
Release
Calendar.exe
// DateFormatSettings.cpp : Implementation of CDateFormatSettings

#include "StdAfx.h"
#include "DateFormatSettings.h"

////////////////////////////////////////////////////////////////////////////////
// 
namespace
{
    CDateFormatSettings* g_This;

    const struct
    {
        int nIDButton;
        CALTYPE calDateFormat;
    } g_dateFormat[] = {
        { IDC_LONGDATE, CAL_SLONGDATE },
        { IDC_SHORTDATE, CAL_SSHORTDATE },
        { IDC_CUSTOMDATE, CAL_SCUSTOMDATE }
    };
}

///////////////////////////////////////////////////////////////////////////////
// 
BOOL CBAddString(HWND hwndCtl, LPCTSTR lpString, LPARAM data)
{
    const int itemIdx = ComboBox_AddString(hwndCtl, lpString);
    ATLASSERT(itemIdx != CB_ERR && itemIdx != CB_ERRSPACE);

    const int ret = ComboBox_SetItemData(hwndCtl, itemIdx, data);
    ATLASSERT(ret != CB_ERR);

    return (ret != CB_ERR);
}

int CBSelectItemData(HWND hwndCtl, LPARAM data)
{
    const int itemCount = ComboBox_GetCount(hwndCtl);
    int itemToSelect = CB_ERR;

    for(int itemIdx = 0; itemIdx < itemCount; ++itemIdx)
    {
        const LPARAM itemData = ComboBox_GetItemData(hwndCtl, itemIdx);

        if(itemData == data)
        {
            itemToSelect = itemIdx;
            break;
        }
    }

    if(itemToSelect != CB_ERR)
        ComboBox_SetCurSel(hwndCtl, itemToSelect);

    return itemToSelect;
}

BOOL CBAddSelectItemString(HWND hwndCtl, LPCTSTR pszString)
{
    ATLASSERT(pszString);

    int itemIdx = ComboBox_FindStringExact(hwndCtl, -1, pszString);

    if(itemIdx == CB_ERR)
    {
        if(lstrlen(pszString) > 0)
            itemIdx = ComboBox_AddString(hwndCtl, pszString);
        else
            itemIdx = 0;
    }

    itemIdx = ComboBox_SetCurSel(hwndCtl, itemIdx);
    ATLASSERT(itemIdx != CB_ERR);

    return (itemIdx != CB_ERR);
}

LPARAM CBGetCurSelData(HWND hwndCtl)
{
    const int itemIdx = ComboBox_GetCurSel(hwndCtl);
    ATLASSERT(itemIdx != CB_ERR);

    return ComboBox_GetItemData(hwndCtl, itemIdx);
}

////////////////////////////////////////////////////////////////////////////////
// CDateFormatSettings

CDateFormatSettings::CDateFormatSettings() : m_enumCalendarInfo(0)
{
    g_This = this;
}

CDateFormatSettings::~CDateFormatSettings()
{
}

LRESULT CDateFormatSettings::OnInitDialog(
    UINT /*uMsg*/,
    WPARAM /*wParam*/,
    LPARAM lParam,
    BOOL& /*bHandled*/)
{
    if(lParam)
    {
        DateFormat* pDateFormat =
            reinterpret_cast<DateFormat*>(lParam);
        m_dateFormat = *pDateFormat;
    }

    InitControls();
    UpdateSampleDate();

	return 1;  // Let the system set the focus
}

LRESULT CDateFormatSettings::OnClickedOK(
    WORD /*wNotifyCode*/,
    WORD wID,
    HWND /*hWndCtl*/,
    BOOL& /*bHandled*/)
{
    const BOOL bRes = CollectCalendarSettings();
    ATLASSERT(bRes);

	EndDialog(wID);
	return 0;
}

LRESULT CDateFormatSettings::OnClickedCancel(
    WORD /*wNotifyCode*/,
    WORD wID,
    HWND /*hWndCtl*/,
    BOOL& /*bHandled*/)
{
	EndDialog(wID);
	return 0;
}

LRESULT CDateFormatSettings::OnChangeLanguage(
    WORD /*wNotifyCode*/,
    WORD /*wID*/,
    HWND /*hWndCtl*/,
    BOOL& /*bHandled*/)
{
    UpdateCurrentCalendar();
    UpdateDateFormat();
    UpdateSampleDate();

    return 0;
}

LRESULT CDateFormatSettings::OnChangeCalendarType(
    WORD /*wNotifyCode*/,
    WORD /*wID*/,
    HWND /*hWndCtl*/,
    BOOL& /*bHandled*/)
{
    UpdateDateFormat();
    UpdateSampleDate();

    return 0;
}

LRESULT CDateFormatSettings::OnChangeCustomDateFormat(
    WORD /*wNotifyCode*/,
    WORD wID,
    HWND hWndCtl,
    BOOL& /*bHandled*/)
{
    // Upon CBN_SELCHANGE notification, the combo's edit box is not updated yet.
    // So, in order to get correct format string we _post_ CBN_EDITCHANGE
    // notification. On arival of CBN_EDITCHANGE, the edit box will be
    // already updated.

    PostMessage(WM_COMMAND,
        MAKEWPARAM(wID, CBN_EDITCHANGE),
        reinterpret_cast<LPARAM>(hWndCtl));

    return 0;
}

LRESULT CDateFormatSettings::OnEditCustomDateFormat(
    WORD /*wNotifyCode*/,
    WORD /*wID*/,
    HWND /*hWndCtl*/,
    BOOL& /*bHandled*/)
{
    UpdateSampleDate();

    return 0;
}

LRESULT CDateFormatSettings::OnClickedDateFormat(
    WORD /*wNotifyCode*/,
    WORD wID,
    HWND /*hWndCtl*/,
    BOOL& /*bHandled*/)
{
    const CALTYPE calType = GetSelectedCalendarType();

    if(calType != m_dateFormat.calType)
    {
        m_dateFormat.calType = calType;

        BOOL bRes = SelectDateFormat(wID);
        ATLASSERT(bRes);

        bRes = UpdateSampleDate();
        ATLASSERT(bRes);
    }
    
    return 0;
}

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

BOOL CDateFormatSettings::InitControls()
{
    BOOL bRes = FALSE;
    
    if(InitControlHwnds())
    {
        if(InitLanguages())
            bRes = UpdateDateFormat(m_dateFormat.calType);
    }

    return bRes;
}

BOOL CDateFormatSettings::InitControlHwnds()
{
    m_hwndLanguage = GetDlgItem(IDC_LANGUAGE);
    m_hwndCalendar = GetDlgItem(IDC_CALENDARTYPE);
    m_hwndFormat = GetDlgItem(IDC_DATEFORMAT);
    m_hwndSample = GetDlgItem(IDC_DATESAMPLE);

    return TRUE;
}

BOOL CDateFormatSettings::InitLanguages()
{
    BOOL bRes = FALSE;
    
    if(EnumKeyboardLayouts())
    {
        bRes = SelectLanguage();

        if(bRes)
            bRes = UpdateCurrentCalendar();
    }

    return bRes;
}



BOOL CDateFormatSettings::InitCalendars()
{
    ComboBox_ResetContent(m_hwndCalendar);

    const LCID lcid = GetSelectedLcid();

    return EnumCalendarNames(lcid);
}

BOOL CDateFormatSettings::InitDateFormat(CALTYPE calType)
{
    int nIDButton;

    switch(calType)
    {
    case CAL_SLONGDATE:
        nIDButton = IDC_LONGDATE;
        break;
    case CAL_SSHORTDATE:
        nIDButton = IDC_SHORTDATE;
        break;
    default:
        nIDButton = IDC_CUSTOMDATE;
    }

    CheckRadioButton(IDC_SHORTDATE, IDC_CUSTOMDATE, nIDButton);
    BOOL bRes = SelectDateFormat(nIDButton);

    return bRes;
}

BOOL CDateFormatSettings::InitCustomDateFormat()
{
    ComboBox_ResetContent(m_hwndFormat);
    BOOL bRes = EnumAvailableDateFormats();

    if(bRes)
        bRes = SelectCustomDateFormat();

    return bRes;
}

BOOL CDateFormatSettings::SelectLanguage()
{
    // first try preset LCID, then user default one, then first item's
    const LCID lcidToSelect[] = {
        m_dateFormat.lcId,
        ::GetUserDefaultLCID(),
        static_cast<LCID>(ComboBox_GetItemData(m_hwndLanguage, 0))
    };

    int itemIdx = CB_ERR;
    for(size_t i = 0; i < ARRAYSIZE(lcidToSelect); ++i)
    {
        itemIdx = ::CBSelectItemData(m_hwndLanguage, lcidToSelect[i]);

        if(itemIdx != CB_ERR)
            break;
    }

    return (itemIdx != CB_ERR);
}

BOOL CDateFormatSettings::SelectCurrentCalendar()
{
    // try preset calendar first, then Gregorian
    const CALID calendarToSelect[] = {
        m_dateFormat.calId,
        CAL_GREGORIAN
    };

    int itemIdx = CB_ERR;
    for(size_t i = 0; i < ARRAYSIZE(calendarToSelect); ++i)
    {
        itemIdx = ::CBSelectItemData(m_hwndCalendar, calendarToSelect[i]);

        if(itemIdx != CB_ERR)
            break;
    }

    return (itemIdx != CB_ERR);
}

BOOL CDateFormatSettings::SelectDateFormat(int nIDButton)
{
    const BOOL bEnable = (nIDButton == IDC_CUSTOMDATE);

    ::EnableWindow(m_hwndFormat, bEnable);

    BOOL bRes = TRUE;
    if(nIDButton == IDC_CUSTOMDATE)
        bRes = InitCustomDateFormat();

    return bRes;
}

BOOL CDateFormatSettings::SelectCustomDateFormat()
{
    ::CBAddSelectItemString(m_hwndFormat, m_dateFormat.dateFormat);

    return TRUE;
}

BOOL CDateFormatSettings::EnumKeyboardLayouts()
{
    const int layoutCount = ::GetKeyboardLayoutList(0, NULL);
    CAtlArray<HKL> layout;
    layout.SetCount(layoutCount);

    ::GetKeyboardLayoutList(layoutCount, layout.GetData());

    for(int i = 0; i < layoutCount; ++i)
    {
        const LANGID lid = LOWORD(layout[i]);
        const LCID lcid = MAKELCID(lid, SORT_DEFAULT);

        AddLocale(lcid);
    }

    return (ComboBox_GetCount(m_hwndLanguage) > 0);
}

BOOL CDateFormatSettings::EnumAvailableDateFormats()
{
    const LCID lcid = GetSelectedLcid();
    const CALID calid = GetSelectedCalendar();

    return EnumCalendarDateFormats(lcid, calid);
}

BOOL CDateFormatSettings::UpdateCurrentCalendar()
{
    BOOL bRes = FALSE;

    if(InitCalendars())
        bRes = SelectCurrentCalendar();

    return bRes;
}

BOOL CDateFormatSettings::UpdateDateFormat(CALTYPE calTypeHint)
{
    const CALID calid = GetSelectedCalendar();
    const BOOL bEnableCustomDate = (calid == CAL_GREGORIAN);

    ::EnableWindow(GetDlgItem(IDC_CUSTOMDATE), bEnableCustomDate);

    const CALTYPE calType = (calTypeHint == CAL_DEFAULT ? CAL_SLONGDATE : calTypeHint);

    return InitDateFormat(calType);
}

BOOL CDateFormatSettings::UpdateSampleDate()
{
    BOOL bRes = CollectCalendarSettings();
    ATLASSERT(bRes);

    SYSTEMTIME st = { 0 };
    ::GetSystemTime(&st);

    const CString& sampleDate = m_dateFormat.FormatDateString(st);
    ATLASSERT(!sampleDate.IsEmpty());
    
    ::Edit_SetText(m_hwndSample, sampleDate);

    return !sampleDate.IsEmpty();
}

BOOL CDateFormatSettings::AddLocale(LCID lcid)
{
    const int langLen = ::GetLocaleInfo(lcid, LOCALE_SLANGUAGE, NULL, 0);
    ATLASSERT(langLen > 0);

    CString layoutName;
    ::GetLocaleInfo(lcid, LOCALE_SLANGUAGE,
        layoutName.GetBufferSetLength(langLen), langLen);
    layoutName.ReleaseBuffer();

    return ::CBAddString(m_hwndLanguage, layoutName, lcid);
}

LCID CDateFormatSettings::GetSelectedLcid() const
{
    return static_cast<LCID>(::CBGetCurSelData(m_hwndLanguage));
}

CALID CDateFormatSettings::GetSelectedCalendar() const
{
    return static_cast<CALID>(::CBGetCurSelData(m_hwndCalendar));
}

CALTYPE CDateFormatSettings::GetSelectedCalendarType() const
{
    CALTYPE calDate = CAL_SCUSTOMDATE;
    for(size_t i = 0; i < ARRAYSIZE(g_dateFormat); ++i)
    {
        if(IsDlgButtonChecked(g_dateFormat[i].nIDButton) == BST_CHECKED)
        {
            calDate = g_dateFormat[i].calDateFormat;
            break;
        }
    }

    return calDate;
}

CString CDateFormatSettings::GetSelectedDateFormat() const
{
    CString dateFormat;

    const CALTYPE calDate = GetSelectedCalendarType();

    if(calDate == CAL_SCUSTOMDATE)
        dateFormat = GetSelectedCustomDateFormat();

    return dateFormat;
}

CString CDateFormatSettings::GetSelectedCustomDateFormat() const
{
    CString customFormat;

    const int textLen = ::ComboBox_GetTextLength(m_hwndFormat);
    ::ComboBox_GetText(m_hwndFormat, customFormat.GetBuffer(textLen), textLen);
    customFormat.ReleaseBuffer();

    return customFormat;
}

BOOL CDateFormatSettings::CollectCalendarSettings()
{
    m_dateFormat.lcId = GetSelectedLcid();
    m_dateFormat.calId = GetSelectedCalendar();
    m_dateFormat.calType = GetSelectedCalendarType();
    m_dateFormat.dateFormat = GetSelectedDateFormat();

    return TRUE;
}

// static
BOOL CALLBACK CDateFormatSettings::EnumCalendarInfoProcEx(
    LPTSTR lpCalendarInfoString,
    CALID Calendar)
{
    switch(g_This->m_enumCalendarInfo)
    {
    case CAL_SCALNAME:
        g_This->AddCalendarName(lpCalendarInfoString, Calendar);
        break;
    case CAL_SLONGDATE:
    case CAL_SSHORTDATE:
        g_This->AddCalendarDateFormat(lpCalendarInfoString, Calendar);
        break;
    }
    
    return TRUE;
}

BOOL CDateFormatSettings::EnumCalendarNames(LCID lcid)
{
    m_enumCalendarInfo = CAL_SCALNAME;
    const BOOL bRes = ::EnumCalendarInfoEx(
        &CDateFormatSettings::EnumCalendarInfoProcEx,
        lcid, ENUM_ALL_CALENDARS, CAL_SCALNAME);
    m_enumCalendarInfo = 0;

    return bRes;
}

BOOL CDateFormatSettings::AddCalendarName(LPCTSTR lpCalendarName, CALID Calendar)
{
    BOOL bContinue = TRUE;

    // Gregorian calendar may be listed twice for U.S. English (LCID: 0x0409) -
    // first time as CAL_GREGORIAN, second time as CAL_GREGORIAN_US.
    // We need only one of them = CAL_GREGORIAN.
    if(Calendar != CAL_GREGORIAN_US)
        bContinue = ::CBAddString(m_hwndCalendar, lpCalendarName, Calendar);

    return bContinue;
}

BOOL CDateFormatSettings::EnumCalendarDateFormats(LCID lcid, CALID Calendar)
{
    const CALTYPE dateFormat[] = { CAL_SLONGDATE, CAL_SSHORTDATE };

    BOOL bRes = TRUE;
    for(size_t i = 0; i < ARRAYSIZE(dateFormat); ++i)
    {
        m_enumCalendarInfo = dateFormat[i];
        bRes &= ::EnumCalendarInfoEx(
            &CDateFormatSettings::EnumCalendarInfoProcEx,
            lcid, Calendar, dateFormat[i]);
        m_enumCalendarInfo = 0;
    }

    return bRes;
}

BOOL CDateFormatSettings::AddCalendarDateFormat(LPCTSTR lpDateFormat, CALID /*Calendar*/)
{
    return ::CBAddString(m_hwndFormat, lpDateFormat, 0);
}

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

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)

Share

About the Author

Alex Blekhman
Software Developer
Australia Australia
More than ten years of C++ native development, and counting.
 
Smile | :)

| Advertise | Privacy | Mobile
Web02 | 2.8.140916.1 | Last Updated 28 Aug 2009
Article Copyright 2009 by Alex Blekhman
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid