diff foosdk/wtl/Include/atlribbon.h @ 1:20d02a178406 default tip

*: check in everything else yay
author Paper <paper@tflc.us>
date Mon, 05 Jan 2026 02:15:46 -0500
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/foosdk/wtl/Include/atlribbon.h	Mon Jan 05 02:15:46 2026 -0500
@@ -0,0 +1,3488 @@
+// Windows Template Library - WTL version 10.0
+// Copyright (C) Microsoft Corporation, WTL Team. All rights reserved.
+//
+// This file is a part of the Windows Template Library.
+// The use and distribution terms for this software are covered by the
+// Microsoft Public License (http://opensource.org/licenses/MS-PL)
+// which can be found in the file MS-PL.txt at the root folder.
+
+#ifndef __ATLRIBBON_H__
+#define __ATLRIBBON_H__
+
+#pragma once
+
+#if (_MSC_VER < 1500)
+	#error atlribbon.h requires Visual C++ 2008 compiler or higher
+#endif
+
+#ifndef _UNICODE
+	#error atlribbon.h requires the Unicode character set
+#endif
+
+#if !defined(NTDDI_WIN7) || (NTDDI_VERSION < NTDDI_WIN7)
+	#error atlribbon.h requires the Windows 7 SDK or higher
+#endif
+
+#ifndef __ATLAPP_H__
+	#error atlribbon.h requires atlapp.h to be included first
+#endif
+
+#include <atlmisc.h>    // for RecentDocumentList classes
+#include <atlframe.h>   // for Frame and UpdateUI classes
+#include <atlctrls.h>   // required for atlctrlw.h
+#include <atlctrlw.h>   // for CCommandBarCtrl
+
+#ifndef __ATLSTR_H__
+  #pragma warning(push)
+  #pragma warning(disable: 4530)   // unwind semantics not enabled
+  #include <string>
+  #pragma warning(pop)
+#endif
+
+#include <dwmapi.h>
+#pragma comment(lib, "dwmapi.lib")
+
+#include "UIRibbon.h"
+#include "UIRibbonPropertyHelpers.h"
+#pragma comment(lib, "propsys.lib")
+
+#include <Richedit.h>   // for CHARFORMAT2
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Classes in this file:
+//
+// CRibbonUpdateUI : Automatic mapping of ribbon UI elements
+//
+// RibbonUI::Text
+// RibbonUI::CharFormat
+// RibbonUI::ICtrl
+// RibbonUI::CtrlImpl
+// RibbonUI::CommandCtrlImpl
+// RibbonUI::ItemProperty
+// RibbonUI::CollectionImplBase
+// RibbonUI::CollectionImpl
+// RibbonUI::TextCollectionImpl
+// RibbonUI::ItemCollectionImpl
+// RibbonUI::ComboCollectionImpl
+// RibbonUI::CommandCollectionImpl
+// RibbonUI::ToolbarCollectionImpl
+// RibbonUI::SimpleCollectionImpl
+// RibbonUI::CollectionCtrlImpl
+// RibbonUI::ToolbarGalleryCtrlImpl
+// RibbonUI::SimpleCollectionCtrlImpl
+// RibbonUI::RecentItemsCtrlImpl
+// RibbonUI::FontCtrlImpl
+// RibbonUI::ColorCtrlImpl
+// RibbonUI::SpinnerCtrlImpl
+//
+// RibbonUI::CRibbonImpl
+//	 CRibbonImpl::CRibbonComboCtrl
+//	 CRibbonImpl::CRibbonItemGalleryCtrl
+//	 CRibbonImpl::CRibbonCommandGalleryCtrl
+//	 CRibbonImpl::CRibbonToolbarGalleryCtrl
+//	 CRibbonImpl::CRibbonSimpleComboCtrl
+//	 CRibbonImpl::CRibbonSimpleGalleryCtrl
+//	 CRibbonImpl::CRibbonRecentItemsCtrl
+//	 CRibbonImpl::CRibbonColorCtrl
+//	 CRibbonImpl::CRibbonFontCtrl
+//	 CRibbonImpl::CRibbonSpinnerCtrl
+//	 CRibbonImpl::CRibbonFloatSpinnerCtrl
+//	 CRibbonImpl::CRibbonCommandCtrl
+//
+// CRibbonFrameWindowImplBase
+// CRibbonFrameWindowImpl
+// CRibbonMDIFrameWindowImpl
+// CRibbonPersist
+//
+// Global functions:
+//   RibbonUI::SetPropertyVal()
+//   RibbonUI::GetImage()
+
+
+// Constants
+
+#ifndef RIBBONUI_MAX_TEXT
+  #define RIBBONUI_MAX_TEXT 128
+#endif
+
+#define TWIPS_PER_POINT 20   // For font size
+
+
+namespace WTL
+{
+
+///////////////////////////////////////////////////////////////////////////////
+// CRibbonUpdateUI : Automatic mapping of ribbon UI elements
+
+template <class T>
+class CRibbonUpdateUI : public CAutoUpdateUI<T>
+{
+public:
+	enum
+	{
+		UPDUI_RIBBON = 0x0080, 
+		UPDUI_PERSIST = 0x0020
+	};
+
+	bool IsRibbonElement(const CUpdateUIBase::_AtlUpdateUIMap& UIMap)
+	{
+		return (UIMap.m_wType & UPDUI_RIBBON) != 0;
+	}
+
+	bool IsRibbonID(UINT nID)
+	{
+		for(int i = 0; i < this->m_arrUIMap.GetSize(); i++)
+		{
+			if(this->m_arrUIMap[i].m_nID == nID)
+				return IsRibbonElement(this->m_arrUIMap[i]);
+		}
+
+		return false;
+	}
+
+// Element
+	bool UIAddRibbonElement(UINT nID)
+	{
+		return this->UIAddElement<UPDUI_RIBBON>(nID);
+	}
+
+	bool UIRemoveRibbonElement(UINT nID)
+	{
+		return this->UIRemoveElement<UPDUI_RIBBON>(nID);
+	}
+
+	bool UIPersistElement(UINT nID, bool bPersist = true)
+	{
+		return bPersist ?
+			this->UIAddElement<UPDUI_PERSIST>(nID) :
+			this->UIRemoveElement<UPDUI_PERSIST>(nID);
+	}
+
+// methods for Ribbon elements
+	BOOL UISetText(int nID, LPCWSTR sText, BOOL bForceUpdate = FALSE)
+	{
+		T* pT = static_cast<T*>(this);
+		BOOL bRes = CUpdateUIBase::UISetText(nID, sText, bForceUpdate);
+		if (pT->IsRibbonUI() && IsRibbonID(nID))
+			bRes = SUCCEEDED(pT->InvalidateProperty(nID, UI_PKEY_Label));
+		return bRes;
+	}
+
+	BOOL UISetText(int nID, UINT uIdResource, BOOL bForceUpdate = FALSE)
+	{
+		ATL::CTempBuffer<WCHAR> sText(RIBBONUI_MAX_TEXT);
+		int nRet = ATL::AtlLoadString(uIdResource, sText, RIBBONUI_MAX_TEXT);
+		if(nRet > 0)
+			UISetText(nID, sText, bForceUpdate);
+		return (nRet > 0) ? TRUE : FALSE;
+	}
+
+	LPCTSTR UIGetText(int nID)
+	{
+		T* pT = static_cast<T*>(this);
+		LPCTSTR sUI = CAutoUpdateUI<T>::UIGetText(nID);
+		
+		// replace 'tab' by 'space' for RibbonUI elements
+		if (sUI && pT->IsRibbonUI() && IsRibbonID(nID) && wcschr(sUI, L'\t'))
+		{
+			static WCHAR sText[RIBBONUI_MAX_TEXT] = {};
+			wcscpy_s(sText, sUI);
+			WCHAR* pch = wcschr(sText, L'\t');
+			if (pch != NULL)
+				*pch = L' ';
+			return sText;
+		}
+		else
+		{
+			return sUI;
+		}
+	}
+
+	BOOL UIEnable(int nID, BOOL bEnable, BOOL bForceUpdate = FALSE)
+	{
+		T* pT = static_cast<T*>(this);
+		BOOL bRes = CUpdateUIBase::UIEnable(nID, bEnable, bForceUpdate);
+		if (pT->IsRibbonUI() && IsRibbonID(nID))
+			bRes = SUCCEEDED(pT->SetProperty((WORD)nID, UI_PKEY_Enabled, bEnable));
+		return bRes;
+	}
+
+	BOOL UISetCheck(int nID, INT nCheck, BOOL bForceUpdate = FALSE)
+	{
+		if ((nCheck == 0) || (nCheck == 1))
+			return UISetCheck(nID, nCheck != 0, bForceUpdate);
+		else
+			return CUpdateUIBase::UISetCheck(nID, nCheck, bForceUpdate);
+	}
+
+	BOOL UISetCheck(int nID, bool bCheck, BOOL bForceUpdate = FALSE)
+	{
+		T* pT = static_cast<T*>(this);
+		BOOL bRes = CUpdateUIBase::UISetCheck(nID, bCheck, bForceUpdate);
+		if (bRes && pT->IsRibbonUI() && IsRibbonID(nID))
+			bRes = SUCCEEDED(pT->SetProperty((WORD)nID, UI_PKEY_BooleanValue, bCheck));
+		return bRes;
+	}
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// RibbonUI namespace
+//
+
+namespace RibbonUI
+{
+
+// Minimal string allocation support for various PROPERTYKEY values
+#ifdef __ATLSTR_H__
+  typedef ATL::CString Text;
+#else
+  class Text : public std::wstring
+  {
+  public:
+	Text(std::wstring& s) : std::wstring(s)
+	{ }
+	Text(LPCWSTR s) : std::wstring(s)
+	{ }
+	Text()
+	{ }
+	bool IsEmpty()
+	{
+		return empty();
+	}
+	operator LPCWSTR()
+	{
+		return c_str();
+	}
+	Text& operator =(LPCWSTR s)
+	{
+		return static_cast<Text&>(std::wstring::operator =(s));
+	}
+  };
+#endif // __ATLSTR_H__
+
+// PROPERTYKEY enum and helpers
+enum k_KEY
+{
+	// state
+	k_Enabled = 1, k_BooleanValue = 200, 
+	// text properties
+	k_LabelDescription = 2, k_Keytip = 3, k_Label = 4, k_TooltipDescription = 5, k_TooltipTitle = 6, 
+	// image properties
+	k_LargeImage = 7, k_LargeHighContrastImage = 8, k_SmallImage = 9, k_SmallHighContrastImage = 10,
+	// collection properties
+	k_ItemsSource = 101, k_Categories = 102, k_SelectedItem = 104,
+	// collection item properties
+	k_CommandId = 100, k_CategoryId = 103, k_CommandType = 105, k_ItemImage = 106,
+	// combo control property
+	k_StringValue = 202,
+	// spinner control properties
+	k_DecimalValue = 201, k_MaxValue = 203, k_MinValue, k_Increment, k_DecimalPlaces, k_FormatString, k_RepresentativeString = 208,
+	// font control properties
+	k_FontProperties = 300, k_FontProperties_Family, k_FontProperties_Size, k_FontProperties_Bold, k_FontProperties_Italic = 304, 
+	k_FontProperties_Underline = 305, k_FontProperties_Strikethrough, k_FontProperties_VerticalPositioning, k_FontProperties_ForegroundColor = 308, 
+	k_FontProperties_BackgroundColor = 309, k_FontProperties_ForegroundColorType, k_FontProperties_BackgroundColorType, k_FontProperties_ChangedProperties = 312, 
+	k_FontProperties_DeltaSize = 313, 
+	// recent items properties
+	k_RecentItems = 350, k_Pinned = 351,
+	// color control properties
+	k_Color = 400, k_ColorType = 401, k_ColorMode, 
+	k_ThemeColorsCategoryLabel = 403, k_StandardColorsCategoryLabel, k_RecentColorsCategoryLabel = 405, k_AutomaticColorLabel = 406, 
+	k_NoColorLabel = 407, k_MoreColorsLabel = 408, 
+	k_ThemeColors = 409, k_StandardColors = 410, k_ThemeColorsTooltips = 411, k_StandardColorsTooltips = 412,
+	// Ribbon state
+	k_Viewable = 1000, k_Minimized = 1001, k_QuickAccessToolbarDock = 1002, k_ContextAvailable = 1100,
+	// Ribbon UI colors
+	k_GlobalBackgroundColor = 2000, k_GlobalHighlightColor, k_GlobalTextColor = 2002
+};
+
+inline k_KEY k_(REFPROPERTYKEY key)
+{
+	return (k_KEY)key.fmtid.Data1;
+}
+
+// PROPERTYKEY value assignment and specializations
+//
+template <typename V>
+HRESULT SetPropertyVal(REFPROPERTYKEY key, V val, PROPVARIANT* ppv)
+{
+	switch (k_(key))
+	{
+	case k_Enabled:
+	case k_BooleanValue:
+		return InitPropVariantFromBoolean(val, ppv);
+	default:
+		return UIInitPropertyFromUInt32(key, val, ppv);
+	}
+}
+
+inline HRESULT SetPropertyVal(REFPROPERTYKEY key, DOUBLE val, PROPVARIANT* ppv)
+{
+	return SetPropertyVal(key, (LONG)val, ppv);
+}
+
+inline HRESULT SetPropertyVal(REFPROPERTYKEY key, IUIImage* val, PROPVARIANT* ppv)
+{
+	HRESULT hr = UIInitPropertyFromImage(key, val, ppv);
+	ATLVERIFY(val->Release() == 1);
+	return hr;
+}
+
+inline HRESULT SetPropertyVal(REFPROPERTYKEY key, IUnknown* val, PROPVARIANT* ppv)
+{
+	return UIInitPropertyFromInterface(key, val, ppv);
+}
+
+inline HRESULT SetPropertyVal(REFPROPERTYKEY key, IPropertyStore* val, PROPVARIANT* ppv)
+{
+	return UIInitPropertyFromInterface(key, val, ppv);
+}
+
+inline HRESULT SetPropertyVal(REFPROPERTYKEY key, SAFEARRAY* val, PROPVARIANT* ppv)
+{
+	return UIInitPropertyFromIUnknownArray(key, val, ppv);
+}
+
+inline HRESULT SetPropertyVal(REFPROPERTYKEY key, DECIMAL* val, PROPVARIANT* ppv)
+{
+	return UIInitPropertyFromDecimal(key, *val, ppv);
+}
+
+inline HRESULT SetPropertyVal(REFPROPERTYKEY key, bool val, PROPVARIANT* ppv)
+{
+	return UIInitPropertyFromBoolean(key, val, ppv);
+}
+
+inline HRESULT SetPropertyVal(REFPROPERTYKEY key, LPCWSTR val, PROPVARIANT* ppv)
+{
+	return UIInitPropertyFromString(key, val, ppv);
+}
+
+// CharFormat helper struct for RibbonUI font control
+//
+struct CharFormat : CHARFORMAT2
+{
+	// Default constructor
+	CharFormat()
+	{
+		cbSize = sizeof(CHARFORMAT2);
+		Reset();
+	}
+
+	// Copy constructor
+	CharFormat(const CharFormat& cf)
+	{
+		::CopyMemory(this, &cf, sizeof(CHARFORMAT2));
+	}
+
+	// Assign operator
+	CharFormat& operator =(const CharFormat& cf)
+	{
+		::CopyMemory(this, &cf, sizeof(CHARFORMAT2));
+		return (*this);
+	}
+
+	void Reset()
+	{
+		uValue = dwMask = dwEffects = 0;
+		PropVariantInit(&propvar);
+	}
+
+	void operator <<(IPropertyStore* pStore)
+	{
+		if (pStore == NULL)
+		{
+			ATLASSERT(FALSE);
+			return;
+		}
+
+		static void (CharFormat::*Getk_[])(IPropertyStore*) = 
+		{
+			&CharFormat::Getk_Family, 
+			&CharFormat::Getk_FontProperties_Size, 
+			&CharFormat::Getk_MaskEffectBold,
+			&CharFormat::Getk_MaskEffectItalic,
+			&CharFormat::Getk_MaskEffectUnderline,
+			&CharFormat::Getk_MaskEffectStrikeout,
+			&CharFormat::Getk_VerticalPositioning,
+			&CharFormat::Getk_Color, 
+			&CharFormat::Getk_ColorBack, 
+			&CharFormat::Getk_ColorType,
+			&CharFormat::Getk_ColorTypeBack,
+		};
+
+		DWORD nProps = 0;
+		Reset();
+
+		ATLVERIFY(SUCCEEDED(pStore->GetCount(&nProps)));
+		for (DWORD iProp = 0; iProp < nProps; iProp++)
+		{
+			PROPERTYKEY key;	
+			ATLVERIFY(SUCCEEDED(pStore->GetAt(iProp, &key)));
+			ATLASSERT(k_(key) >= k_FontProperties_Family);
+
+			if (k_(key) <= k_FontProperties_BackgroundColorType)
+				(this->*Getk_[k_(key) - k_FontProperties_Family])(pStore);
+		}
+	}
+
+	void operator >>(IPropertyStore* pStore)
+	{
+		if (pStore == NULL)
+		{
+			ATLASSERT(FALSE);
+			return;
+		}
+
+		PutFace(pStore);
+		PutSize(pStore);
+		PutMaskEffect(CFM_BOLD, CFE_BOLD, UI_PKEY_FontProperties_Bold, pStore);
+		PutMaskEffect(CFM_ITALIC, CFE_ITALIC, UI_PKEY_FontProperties_Italic, pStore);
+		PutMaskEffect(CFM_UNDERLINE, CFE_UNDERLINE, UI_PKEY_FontProperties_Underline, pStore);
+		PutMaskEffect(CFM_STRIKEOUT, CFE_STRIKEOUT, UI_PKEY_FontProperties_Strikethrough, pStore);
+		PutVerticalPos(pStore);
+		PutColor(pStore);
+		PutBackColor(pStore);
+	}
+
+private:
+	PROPVARIANT propvar;
+	UINT uValue;
+
+	// Getk_ functions
+	void Getk_Family(IPropertyStore* pStore)
+	{
+		if (SUCCEEDED(pStore->GetValue(UI_PKEY_FontProperties_Family, &propvar)))
+		{
+			PropVariantToString(propvar, szFaceName, LF_FACESIZE);
+			if (*szFaceName)
+				dwMask |= CFM_FACE;
+		}
+	}
+
+	void Getk_FontProperties_Size(IPropertyStore* pStore)
+	{
+		if (SUCCEEDED(pStore->GetValue(UI_PKEY_FontProperties_Size, &propvar)))
+		{
+			DECIMAL decSize = {};
+			UIPropertyToDecimal(UI_PKEY_FontProperties_Size, propvar, &decSize);
+			DOUBLE dSize = 0;
+			VarR8FromDec(&decSize, &dSize);
+			if (dSize > 0)
+			{
+				dwMask |= CFM_SIZE;
+				yHeight = (LONG)(dSize * TWIPS_PER_POINT);
+			}
+		}
+	}
+
+	void Getk_MaskEffectBold(IPropertyStore* pStore)
+	{
+		Getk_MaskEffectAll(pStore, CFM_BOLD, CFE_BOLD, UI_PKEY_FontProperties_Bold);
+	}
+
+	void Getk_MaskEffectItalic(IPropertyStore* pStore)
+	{
+		Getk_MaskEffectAll(pStore, CFM_ITALIC, CFE_ITALIC, UI_PKEY_FontProperties_Italic);
+	}
+
+	void Getk_MaskEffectUnderline(IPropertyStore* pStore)
+	{
+		Getk_MaskEffectAll(pStore, CFM_UNDERLINE, CFE_UNDERLINE, UI_PKEY_FontProperties_Underline);
+	}
+
+	void Getk_MaskEffectStrikeout(IPropertyStore* pStore)
+	{
+		Getk_MaskEffectAll(pStore, CFM_STRIKEOUT, CFE_STRIKEOUT, UI_PKEY_FontProperties_Strikethrough);
+	}
+
+	void Getk_MaskEffectAll(IPropertyStore* pStore, DWORD _dwMask, DWORD _dwEffects, REFPROPERTYKEY key)
+	{
+		if (SUCCEEDED(pStore->GetValue(key, &propvar)))
+		{
+			UIPropertyToUInt32(key, propvar, &uValue);
+			if ((UI_FONTPROPERTIES)uValue != UI_FONTPROPERTIES_NOTAVAILABLE)
+			{
+				dwMask |= _dwMask;
+				dwEffects |= ((UI_FONTPROPERTIES)uValue == UI_FONTPROPERTIES_SET) ? _dwEffects : 0;
+			}
+		}
+	}
+
+	void Getk_VerticalPositioning(IPropertyStore* pStore)
+	{
+		if (SUCCEEDED(pStore->GetValue(UI_PKEY_FontProperties_VerticalPositioning, &propvar)))
+		{
+			UIPropertyToUInt32(UI_PKEY_FontProperties_VerticalPositioning, propvar, &uValue);
+			UI_FONTVERTICALPOSITION uVerticalPosition = (UI_FONTVERTICALPOSITION) uValue;
+			if ((uVerticalPosition != UI_FONTVERTICALPOSITION_NOTAVAILABLE))
+			{
+				dwMask |= (CFM_SUPERSCRIPT | CFM_SUBSCRIPT);
+				if (uVerticalPosition != UI_FONTVERTICALPOSITION_NOTSET)
+				{
+					dwEffects |= (uVerticalPosition == UI_FONTVERTICALPOSITION_SUPERSCRIPT) ? CFE_SUPERSCRIPT : CFE_SUBSCRIPT;
+				}
+			}
+		}
+	}
+
+	void Getk_Color(IPropertyStore* pStore)
+	{
+		Getk_ColorAll(pStore, CFM_COLOR, UI_PKEY_FontProperties_ForegroundColor);
+	}
+
+	void Getk_ColorBack(IPropertyStore* pStore)
+	{
+		Getk_ColorAll(pStore, CFM_BACKCOLOR, UI_PKEY_FontProperties_BackgroundColor);
+	}
+		
+	void Getk_ColorAll(IPropertyStore* pStore, DWORD _dwMask, REFPROPERTYKEY key)
+	{
+		UINT32 color = 0;
+		if (SUCCEEDED(pStore->GetValue(key, &propvar)))
+		{
+			UIPropertyToUInt32(key, propvar, &color);
+			dwMask |= _dwMask;
+
+			if (_dwMask == CFM_COLOR)
+				crTextColor = color;
+			else
+				crBackColor = color;
+		}
+	}
+
+	void Getk_ColorType(IPropertyStore* pStore)
+	{
+		Getk_ColorTypeAll(pStore, CFM_COLOR, CFE_AUTOCOLOR, UI_SWATCHCOLORTYPE_AUTOMATIC, UI_PKEY_FontProperties_ForegroundColor);
+
+	}
+
+	void Getk_ColorTypeBack(IPropertyStore* pStore)
+	{
+		Getk_ColorTypeAll(pStore, CFM_BACKCOLOR, CFE_AUTOBACKCOLOR, UI_SWATCHCOLORTYPE_NOCOLOR, UI_PKEY_FontProperties_BackgroundColor);
+	}
+
+	void Getk_ColorTypeAll(IPropertyStore* pStore, DWORD _dwMask, DWORD _dwEffects, UI_SWATCHCOLORTYPE _type, REFPROPERTYKEY key)
+	{
+		if (SUCCEEDED(pStore->GetValue(key, &propvar)))
+		{
+			UIPropertyToUInt32(key, propvar, &uValue);
+			if (_type == (UI_SWATCHCOLORTYPE)uValue)
+			{
+				dwMask |= _dwMask;
+				dwEffects |= _dwEffects;
+			}
+		}
+	}
+
+	// Put functions
+	void PutMaskEffect(WORD dwMaskVal, WORD dwEffectVal, REFPROPERTYKEY key, IPropertyStore* pStore)
+	{
+		PROPVARIANT var;
+		UI_FONTPROPERTIES uProp = UI_FONTPROPERTIES_NOTAVAILABLE;
+		if ((dwMask & dwMaskVal) != 0)
+			uProp = dwEffects & dwEffectVal ? UI_FONTPROPERTIES_SET : UI_FONTPROPERTIES_NOTSET;
+		SetPropertyVal(key, uProp, &var);
+		pStore->SetValue(key, var);
+	}
+
+	void PutVerticalPos(IPropertyStore* pStore)
+	{
+		PROPVARIANT var;
+		UI_FONTVERTICALPOSITION uProp = UI_FONTVERTICALPOSITION_NOTAVAILABLE;
+
+		if ((dwMask & CFE_SUBSCRIPT) != 0)
+		{
+			if ((dwMask & CFM_SUBSCRIPT) && (dwEffects & CFE_SUBSCRIPT))
+				uProp = UI_FONTVERTICALPOSITION_SUBSCRIPT;
+			else
+				uProp = UI_FONTVERTICALPOSITION_SUPERSCRIPT;
+		}
+		else if ((dwMask & CFM_OFFSET) != 0)
+		{
+			if (yOffset > 0)
+				uProp = UI_FONTVERTICALPOSITION_SUPERSCRIPT;
+			else if (yOffset < 0)
+				uProp = UI_FONTVERTICALPOSITION_SUBSCRIPT;
+		}
+
+		SetPropertyVal(UI_PKEY_FontProperties_VerticalPositioning, uProp, &var);
+		pStore->SetValue(UI_PKEY_FontProperties_VerticalPositioning, var);
+	}
+
+	void PutFace(IPropertyStore* pStore)
+	{
+		PROPVARIANT var;
+		SetPropertyVal(UI_PKEY_FontProperties_Family, 
+			dwMask & CFM_FACE ? szFaceName : L"", &var);
+		pStore->SetValue(UI_PKEY_FontProperties_Family, var);
+	}
+
+	void PutSize(IPropertyStore* pStore)
+	{
+		PROPVARIANT var;
+		DECIMAL decVal;
+
+		if ((dwMask & CFM_SIZE) != 0)
+			VarDecFromR8((DOUBLE)yHeight / TWIPS_PER_POINT, &decVal);
+		else
+			VarDecFromI4(0, &decVal);
+
+		SetPropertyVal(UI_PKEY_FontProperties_Size, &decVal, &var);
+		pStore->SetValue(UI_PKEY_FontProperties_Size, var);
+	}
+
+	void PutColor(IPropertyStore* pStore)
+	{
+		if ((dwMask & CFM_COLOR) != 0)
+		{
+			if ((dwEffects & CFE_AUTOCOLOR) == 0)
+			{
+				SetPropertyVal(UI_PKEY_FontProperties_ForegroundColorType, UI_SWATCHCOLORTYPE_RGB, &propvar);
+				pStore->SetValue(UI_PKEY_FontProperties_ForegroundColorType, propvar);
+				
+				SetPropertyVal(UI_PKEY_FontProperties_ForegroundColor, crTextColor, &propvar);
+				pStore->SetValue(UI_PKEY_FontProperties_ForegroundColor, propvar);
+			}
+			else
+			{
+				SetPropertyVal(UI_PKEY_FontProperties_ForegroundColorType, UI_SWATCHCOLORTYPE_AUTOMATIC, &propvar);
+				pStore->SetValue(UI_PKEY_FontProperties_ForegroundColorType, propvar);
+			}
+		}
+	}
+
+	void PutBackColor(IPropertyStore* pStore)
+	{
+		if (((dwMask & CFM_BACKCOLOR) != 0) && ((dwEffects & CFE_AUTOBACKCOLOR) == 0))
+		{
+			SetPropertyVal(UI_PKEY_FontProperties_BackgroundColorType, UI_SWATCHCOLORTYPE_RGB, &propvar);
+			pStore->SetValue(UI_PKEY_FontProperties_BackgroundColorType, propvar);
+				
+			SetPropertyVal(UI_PKEY_FontProperties_BackgroundColor, crBackColor, &propvar);
+			pStore->SetValue(UI_PKEY_FontProperties_BackgroundColor, propvar);
+		}
+		else
+		{
+			SetPropertyVal(UI_PKEY_FontProperties_BackgroundColorType, UI_SWATCHCOLORTYPE_NOCOLOR, &propvar);
+			pStore->SetValue(UI_PKEY_FontProperties_BackgroundColorType, propvar);
+		}
+	}
+};
+
+// IUIImage helper
+//
+inline IUIImage* GetImage(HBITMAP hbm, UI_OWNERSHIP owner)
+{
+	ATLASSERT(hbm);
+	IUIImage* pIUII = NULL;
+	ATL::CComPtr<IUIImageFromBitmap> pIFB;
+
+	if SUCCEEDED(pIFB.CoCreateInstance(CLSID_UIRibbonImageFromBitmapFactory))
+		ATLVERIFY(SUCCEEDED(pIFB->CreateImage(hbm, owner, &pIUII)));
+
+	return pIUII;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Ribbon control classes
+
+// RibbonUI::ICtrl abstract interface of RibbonUI::CRibbonImpl and all RibbonUI control classes
+//
+struct ICtrl
+{
+	virtual HRESULT DoExecute(UINT nCmdID, UI_EXECUTIONVERB verb, 
+	                          const PROPERTYKEY* key, const PROPVARIANT* ppropvarValue,
+	                          IUISimplePropertySet* pCommandExecutionProperties) = 0;
+
+	virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, 
+	                                 const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue) = 0;
+};
+
+// RibbonUI::CtrlImpl base class for all ribbon controls
+//
+template <class T, UINT t_ID>
+class ATL_NO_VTABLE CtrlImpl : public ICtrl
+{
+protected:
+	T* m_pWndRibbon;
+
+public:
+	typedef T WndRibbon;
+
+	CtrlImpl() : m_pWndRibbon(T::pWndRibbon)
+	{ }
+
+	virtual ~CtrlImpl()
+	{ }
+
+	WndRibbon& GetWndRibbon()
+	{
+		return *m_pWndRibbon;
+	}
+
+	static WORD GetID()
+	{
+		return t_ID;
+	}
+
+	Text m_sTxt[5];
+
+	// Operations
+	HRESULT Invalidate()
+	{
+		return GetWndRibbon().InvalidateCtrl(GetID());
+	}
+
+	HRESULT Invalidate(REFPROPERTYKEY key, UI_INVALIDATIONS flags = UI_INVALIDATIONS_PROPERTY)
+	{
+		return GetWndRibbon().InvalidateProperty(GetID(), key, flags);
+	}
+
+	HRESULT SetText(REFPROPERTYKEY key, LPCWSTR sTxt, bool bUpdate = false)
+	{
+		ATLASSERT((k_(key) <= k_TooltipTitle) && (k_(key) >= k_LabelDescription));
+
+		m_sTxt[k_(key) - k_LabelDescription] = sTxt;
+
+		return bUpdate ?
+			GetWndRibbon().InvalidateProperty(GetID(), key) :
+			S_OK;
+	}
+
+	// Implementation
+	template <typename V>
+	HRESULT SetProperty(REFPROPERTYKEY key, V val)
+	{
+		return GetWndRibbon().SetProperty(GetID(), key, val);
+	}
+
+	HRESULT OnGetText(REFPROPERTYKEY key, PROPVARIANT* ppv)
+	{
+		ATLASSERT((k_(key) <= k_TooltipTitle) && (k_(key) >= k_LabelDescription));
+
+		const INT iText = k_(key) - k_LabelDescription;
+		if (m_sTxt[iText].IsEmpty())
+			if (LPCWSTR sText = GetWndRibbon().OnRibbonQueryText(GetID(), key))
+				m_sTxt[iText] = sText;
+
+		return !m_sTxt[iText].IsEmpty() ?
+			SetPropertyVal(key, (LPCWSTR)m_sTxt[iText], ppv) :
+			S_OK;
+	}
+
+	virtual HRESULT DoExecute(UINT nCmdID, UI_EXECUTIONVERB verb, 
+	                          const PROPERTYKEY* key, const PROPVARIANT* ppropvarValue,
+	                          IUISimplePropertySet* pCommandExecutionProperties)
+	{
+		ATLASSERT(nCmdID == t_ID);
+		return GetWndRibbon().DoExecute(nCmdID, verb, key, ppropvarValue, pCommandExecutionProperties);
+	}
+
+	virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, 
+	                                 const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue)
+	{
+		ATLASSERT(nCmdID == t_ID);
+
+		const INT iMax = k_TooltipTitle - k_LabelDescription;
+		const INT iVal = k_(key) - k_LabelDescription;
+
+		return (iVal <= iMax) && (iVal >= 0) ?
+			OnGetText(key, ppropvarNewValue) :
+			GetWndRibbon().DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue);
+	}
+};
+
+// CommandCtrlImpl base class for most ribbon controls
+//
+template <class T, UINT t_ID>
+class CommandCtrlImpl : public CtrlImpl<T, t_ID>
+{
+public:
+	CBitmap m_hbm[4];
+
+	HRESULT SetImage(REFPROPERTYKEY key, HBITMAP hbm, bool bUpdate = false)
+	{
+		ATLASSERT((k_(key) <= k_SmallHighContrastImage) && (k_(key) >= k_LargeImage));
+			
+		m_hbm[k_(key) - k_LargeImage].Attach(hbm);
+
+		return bUpdate ?
+			this->GetWndRibbon().InvalidateProperty(this->GetID(), key) :
+			S_OK;
+	}
+
+	HRESULT OnGetImage(REFPROPERTYKEY key, PROPVARIANT* ppv)
+	{
+		ATLASSERT((k_(key) <= k_SmallHighContrastImage) && (k_(key) >= k_LargeImage));
+
+		const INT iImage = k_(key) - k_LargeImage;
+
+		if (m_hbm[iImage].IsNull())
+			m_hbm[iImage] = this->GetWndRibbon().OnRibbonQueryImage(this->GetID(), key);
+
+		return m_hbm[iImage].IsNull() ?
+			E_NOTIMPL :
+			SetPropertyVal(key, GetImage(m_hbm[iImage], UI_OWNERSHIP_COPY), ppv);
+	}
+
+	virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, 
+	                                 const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue)
+	{
+		ATLASSERT (nCmdID == this->GetID());
+
+		return (k_(key) <= k_SmallHighContrastImage) && (k_(key) >= k_LargeImage) ?
+			OnGetImage(key, ppropvarNewValue) :
+			CtrlImpl<T, t_ID>::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue);
+	}
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Ribbon collection base classes
+
+// ItemProperty class: ribbon callback for each item in a collection
+//
+
+#pragma warning(push)
+#pragma warning(disable: 4512)   // assignment operator could not be generated
+
+template <class TCollection>
+class ItemProperty : public IUISimplePropertySet
+{
+public:
+	ItemProperty(UINT i, TCollection* pCollection) : m_Index(i), m_pCollection(pCollection)
+	{ }
+
+	const UINT m_Index;
+	TCollection* m_pCollection;
+
+	// IUISimplePropertySet method.
+	STDMETHODIMP GetValue(REFPROPERTYKEY key, PROPVARIANT *value)
+	{
+		return m_pCollection->OnGetItem(m_Index, key, value);
+	}
+
+	// IUnknown methods.
+	STDMETHODIMP_(ULONG) AddRef()
+	{
+		return 1;
+	}
+
+	STDMETHODIMP_(ULONG) Release()
+	{
+		return 1;
+	}
+
+	STDMETHODIMP QueryInterface(REFIID iid, void** ppv)
+	{
+		if ((iid == __uuidof(IUnknown)) || (iid == __uuidof(IUISimplePropertySet)))
+		{
+			*ppv = this;
+			return S_OK;
+		}
+		else
+		{
+			return E_NOINTERFACE;
+		}
+	}
+};
+
+#pragma warning(pop)
+
+
+// CollectionImplBase: base class for all RibbonUI collections
+//
+template <class TCollection, size_t t_size>
+class CollectionImplBase
+{
+	typedef CollectionImplBase<TCollection, t_size> thisClass;
+
+public:
+	CollectionImplBase()
+	{
+		for (int i = 0; i < t_size; i++)
+			m_apItems[i] = new ItemProperty<TCollection>(i, static_cast<TCollection*>(this));
+	}
+
+	~CollectionImplBase()
+	{
+		for (int i = 0; i < t_size; i++)
+			delete m_apItems[i];
+	}
+
+// Data members
+	ItemProperty<TCollection>* m_apItems[t_size];
+};
+
+// CollectionImpl: handles categories and collecton resizing
+//
+template <class TCtrl, size_t t_items, size_t t_categories>
+class CollectionImpl : public CollectionImplBase<CollectionImpl<TCtrl, t_items, t_categories>, t_items + t_categories>
+{
+	typedef CollectionImpl<TCtrl, t_items, t_categories> thisClass;
+public:
+	typedef thisClass Collection;
+
+	CollectionImpl() : m_size(t_items)
+	{
+		::FillMemory(m_auItemCat, sizeof(m_auItemCat), 0xff); // UI_COLLECTION_INVALIDINDEX
+	}
+
+	UINT32 m_auItemCat[t_items];
+	Text m_asCatName[__max(t_categories, 1)];
+	size_t m_size;
+
+// Operations
+	HRESULT SetItemCategory(UINT uItem, UINT uCat, bool bUpdate = false)
+	{
+		ATLASSERT((uItem < t_items) && (uCat < t_categories));
+
+		m_auItemCat[uItem] = uCat;
+
+		return bUpdate ? InvalidateItems() : S_OK;
+	}
+
+	HRESULT SetCategoryText(UINT uCat, LPCWSTR sText, bool bUpdate = false)
+	{
+		ATLASSERT(uCat < t_categories);
+
+		m_asCatName[uCat] = sText;
+
+		return bUpdate ? InvalidateCategories() : S_OK;
+	}
+
+	HRESULT Resize(size_t size, bool bUpdate = false)
+	{
+		ATLASSERT(size <= t_items);
+
+		m_size = size;
+
+		return bUpdate ? InvalidateItems() : S_OK;
+	}
+
+// Implementation
+	HRESULT OnGetItem(UINT uIndex, REFPROPERTYKEY key, PROPVARIANT *value)
+	{
+		ATLASSERT(uIndex < t_items + t_categories);
+		TCtrl* pCtrl = static_cast<TCtrl*>(this);
+
+		return uIndex < t_items ?
+			pCtrl->DoGetItem(uIndex, key, value) :
+			pCtrl->DoGetCategory(uIndex - t_items, key, value);
+	}
+
+	HRESULT DoGetItem(UINT uItem, REFPROPERTYKEY key, PROPVARIANT *value)
+	{
+		ATLASSERT(k_(key) == k_CategoryId);
+		UINT32 uCat = UI_COLLECTION_INVALIDINDEX;
+
+		if (t_categories != 0)
+		{
+			if (m_auItemCat[uItem] == UI_COLLECTION_INVALIDINDEX)
+			{
+				typename TCtrl::WndRibbon& ribbon = static_cast<TCtrl*>(this)->GetWndRibbon();
+				m_auItemCat[uItem] = ribbon.OnRibbonQueryItemCategory(TCtrl::GetID(), uItem);
+			}
+			uCat = m_auItemCat[uItem];
+		}
+
+		return SetPropertyVal(key, uCat, value);
+	}
+
+	HRESULT DoGetCategory(UINT uCat, REFPROPERTYKEY key, PROPVARIANT *value)
+	{
+		HRESULT hr = S_OK;
+
+		switch (k_(key))
+		{
+		case k_Label:
+			if (m_asCatName[uCat].IsEmpty())
+			{
+				typename TCtrl::WndRibbon& ribbon = static_cast<TCtrl*>(this)->GetWndRibbon();
+				m_asCatName[uCat] = ribbon.OnRibbonQueryCategoryText(TCtrl::GetID(), uCat);
+			}
+			hr = SetPropertyVal(key, (LPCWSTR)m_asCatName[uCat], value);
+			break;
+		case k_CategoryId:
+			hr = SetPropertyVal(key, uCat, value);
+			break;
+		default:
+			ATLASSERT(FALSE);
+			break;
+		}
+
+		return hr;
+	}
+
+	HRESULT InvalidateItems()
+	{
+		return static_cast<TCtrl*>(this)->Invalidate(UI_PKEY_ItemsSource);
+	}
+
+	HRESULT InvalidateCategories()
+	{
+		return static_cast<TCtrl*>(this)->Invalidate(UI_PKEY_Categories);
+	}
+
+	HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, 
+	                         const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* /*ppropvarNewValue*/)
+	{
+		ATLASSERT(nCmdID == TCtrl::GetID());
+		(void)nCmdID;   // avoid level 4 warning
+
+		HRESULT hr = E_NOTIMPL;
+		switch (k_(key))
+		{
+		case k_ItemsSource:
+			{
+				ATL::CComQIPtr<IUICollection> pIUICollection(ppropvarCurrentValue->punkVal);
+				ATLASSERT(pIUICollection);
+				hr = pIUICollection->Clear();
+				for (UINT i = 0; i < m_size; i++)
+				{
+					if FAILED(hr = pIUICollection->Add(this->m_apItems[i]))
+						break;
+				}
+				ATLASSERT(SUCCEEDED(hr));
+			}
+			break;
+		case k_Categories:
+			if (t_categories != 0)
+			{
+				ATL::CComQIPtr<IUICollection> pIUICategory(ppropvarCurrentValue->punkVal);
+				ATLASSERT(pIUICategory.p);
+				hr = pIUICategory->Clear();
+				for (UINT i = t_items; i < (t_items + t_categories); i++)
+				{
+					if FAILED(hr = pIUICategory->Add(this->m_apItems[i]))
+						break;
+				}
+				ATLASSERT(SUCCEEDED(hr));
+			}
+			break;
+		}
+
+		return hr;
+	}
+};
+
+// TextCollectionImpl: handles item labels and selection
+//
+template <class TCtrl, size_t t_items, size_t t_categories = 0>
+class TextCollectionImpl : public CollectionImpl<TCtrl, t_items, t_categories>
+{
+	typedef TextCollectionImpl<TCtrl, t_items, t_categories> thisClass;
+public:
+	typedef thisClass TextCollection;
+
+	TextCollectionImpl() : m_uSelected(UI_COLLECTION_INVALIDINDEX)
+	{ }
+
+	Text m_asText[t_items];
+	UINT m_uSelected;
+
+	// Operations
+	HRESULT SetItemText(UINT uItem, LPCWSTR sText, bool bUpdate = false)
+	{
+		ATLASSERT(uItem < t_items);
+
+		m_asText[uItem] = sText;
+
+		return bUpdate ? this->InvalidateItems() : S_OK;
+	}
+
+	UINT GetSelected()
+	{
+		return m_uSelected;
+	}
+
+	HRESULT Select(UINT uItem, bool bUpdate = false)
+	{
+		ATLASSERT((uItem < t_items) || (uItem == UI_COLLECTION_INVALIDINDEX));
+
+		m_uSelected = uItem;
+
+		typename TCtrl::WndRibbon& ribbon = static_cast<TCtrl*>(this)->GetWndRibbon();
+		return bUpdate ?
+			ribbon.SetProperty(TCtrl::GetID(), UI_PKEY_SelectedItem, uItem) : 
+			S_OK;
+	}
+
+// Implementation
+ 	HRESULT DoGetItem(UINT uItem, REFPROPERTYKEY key, PROPVARIANT *value)
+	{
+		ATLASSERT(uItem < t_items);
+
+		if (k_(key) == k_Label)
+		{
+			if (m_asText[uItem].IsEmpty())
+			{
+				typename TCtrl::WndRibbon& ribbon = static_cast<TCtrl*>(this)->GetWndRibbon();
+				m_asText[uItem] = ribbon.OnRibbonQueryItemText(TCtrl::GetID(), uItem);
+			}
+			return SetPropertyVal(key, (LPCWSTR)m_asText[uItem], value);
+		}
+		else
+		{
+			return CollectionImpl<TCtrl, t_items, t_categories>::Collection::DoGetItem(uItem, key, value);
+		}
+	}
+
+	HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, 
+	                         const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue)
+	{
+		ATLASSERT(nCmdID == TCtrl::GetID());
+
+		if (k_(key) == k_SelectedItem)
+		{
+			typename TCtrl::WndRibbon& ribbon = static_cast<TCtrl*>(this)->GetWndRibbon();
+			UINT uSel = UI_COLLECTION_INVALIDINDEX;
+			if ((m_uSelected == UI_COLLECTION_INVALIDINDEX) &&
+			    ribbon.OnRibbonQuerySelectedItem(TCtrl::GetID(), uSel))
+				m_uSelected = uSel;
+
+			return SetPropertyVal(key, m_uSelected, ppropvarNewValue);
+		}
+		else
+		{
+			return CollectionImpl<TCtrl, t_items, t_categories>::Collection::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue);
+		}
+	}
+};
+
+// ItemCollectionImpl: handles item image
+//
+template <class TCtrl, size_t t_items, size_t t_categories = 0>
+class ItemCollectionImpl : public TextCollectionImpl<TCtrl, t_items, t_categories>
+{
+	typedef ItemCollectionImpl<TCtrl, t_items, t_categories> thisClass;
+public:
+	typedef thisClass ItemCollection;
+	
+	ItemCollectionImpl()
+	{
+		::ZeroMemory(m_aBitmap, sizeof(m_aBitmap));
+	}
+
+	CBitmap m_aBitmap[t_items];
+
+	// Operations
+	HRESULT SetItemImage(UINT uIndex, HBITMAP hbm, bool bUpdate = false)
+	{
+		ATLASSERT(uIndex < t_items);
+
+		m_aBitmap[uIndex] = hbm;
+
+		return bUpdate ? this->InvalidateItems() : S_OK;
+	}
+
+// Implementation
+	HRESULT DoGetItem(UINT uItem, REFPROPERTYKEY key, PROPVARIANT *value)
+	{
+		ATLASSERT(uItem < t_items);
+
+		if (k_(key) == k_ItemImage)
+		{
+			if (m_aBitmap[uItem].IsNull())
+			{
+				typename TCtrl::WndRibbon& ribbon = static_cast<TCtrl*>(this)->GetWndRibbon();
+				m_aBitmap[uItem] = ribbon.OnRibbonQueryItemImage(TCtrl::GetID(), uItem);
+			}
+			return m_aBitmap[uItem].IsNull() ?
+				E_NOTIMPL :
+				SetPropertyVal(key, GetImage(m_aBitmap[uItem], UI_OWNERSHIP_COPY), value);
+		}
+		else
+		{
+			return TextCollectionImpl<TCtrl, t_items, t_categories>::TextCollection::DoGetItem(uItem, key, value);
+		}
+	}
+};
+
+// ComboCollectionImpl: handles combo text
+//
+template <class TCtrl, size_t t_items, size_t t_categories = 0>
+class ComboCollectionImpl : public ItemCollectionImpl<TCtrl, t_items, t_categories>
+{
+	typedef ComboCollectionImpl<TCtrl, t_items, t_categories> thisClass;
+public:
+	typedef thisClass ComboCollection;
+
+	// Operations
+	HRESULT SetComboText(LPCWSTR sText)
+	{
+		typename TCtrl::WndRibbon& ribbon = static_cast<TCtrl*>(this)->GetWndRibbon();
+		return ribbon.IsRibbonUI() ? 
+			ribbon.SetProperty(TCtrl::GetID(), UI_PKEY_StringValue, sText) : 
+			S_OK;
+	}
+
+	LPCWSTR GetComboText()
+	{
+		static WCHAR sCombo[RIBBONUI_MAX_TEXT] = {};
+		typename TCtrl::WndRibbon& ribbon = static_cast<TCtrl*>(this)->GetWndRibbon();
+		PROPVARIANT var;
+		if (ribbon.IsRibbonUI())
+		{
+			HRESULT hr = ribbon.GetIUIFrameworkPtr()->GetUICommandProperty(TCtrl::GetID(), UI_PKEY_StringValue, &var);
+			hr = PropVariantToString(var, sCombo, RIBBONUI_MAX_TEXT);
+			return sCombo;
+		}
+		return NULL;
+	}
+};
+
+// CommandCollectionImpl: handles RibbonUI command collection controls
+//
+template <class TCtrl, size_t t_items, size_t t_categories = 0>
+class CommandCollectionImpl : public CollectionImpl<TCtrl, t_items, t_categories>
+{
+	typedef CommandCollectionImpl<TCtrl, t_items, t_categories> thisClass;
+public:
+	typedef thisClass CommandCollection;
+
+	CommandCollectionImpl()
+	{
+		::ZeroMemory(m_auCmd, sizeof(m_auCmd));
+		::ZeroMemory(m_aCmdType, sizeof(m_aCmdType));
+	}
+
+	UINT32 m_auCmd[t_items];
+	BYTE m_aCmdType[t_items];
+
+	// Operations
+	HRESULT SetItemCommand(UINT uItem, UINT32 uCommandID, bool bUpdate = false)
+	{
+		ATLASSERT(uItem < t_items);
+
+		if (uCommandID == m_auCmd[uItem])
+			return S_OK;
+
+		typename TCtrl::WndRibbon& ribbon = static_cast<TCtrl*>(this)->GetWndRibbon();
+
+		m_auCmd[uItem] = uCommandID;
+		if (uCommandID != 0)
+			ribbon.UIAddRibbonElement(uCommandID);
+
+		return bUpdate ? this->InvalidateItems() : S_OK;
+	}
+
+	HRESULT SetItemCommandType(UINT uItem, UI_COMMANDTYPE type, bool bUpdate = false)
+	{
+		ATLASSERT(uItem < t_items);
+
+		m_aCmdType[uItem] = (BYTE)type;
+
+		return bUpdate ? this->InvalidateItems() : S_OK;
+	}
+
+// Implementation
+ 	HRESULT DoGetItem(UINT uItem, REFPROPERTYKEY key, PROPVARIANT *value)
+	{
+		ATLASSERT(uItem < t_items);
+		typename TCtrl::WndRibbon& ribbon = static_cast<TCtrl*>(this)->GetWndRibbon();
+
+		HRESULT hr = E_FAIL;
+		switch (k_(key))
+		{
+		case k_CommandId:
+			if (m_auCmd[uItem] == 0)
+				SetItemCommand(uItem, ribbon.OnRibbonQueryItemCommand(TCtrl::GetID(), uItem));
+			hr = SetPropertyVal(key, m_auCmd[uItem], value);
+			break;
+		case k_CommandType:
+			if (m_aCmdType[uItem] == UI_COMMANDTYPE_UNKNOWN)
+				SetItemCommandType(uItem, ribbon.OnRibbonQueryItemCommandType(TCtrl::GetID(), uItem));
+			hr = SetPropertyVal(key, UINT32(m_aCmdType[uItem]), value);
+			break;
+		case k_CategoryId:
+		default:
+			hr = CollectionImpl<TCtrl, t_items, t_categories>::Collection::DoGetItem(uItem, key, value);
+			break;
+		}
+
+		return hr;
+	}
+
+	HRESULT Select(UINT /*uItem*/, bool /*bUpdate*/ = false)
+	{
+		ATLASSERT(FALSE);
+		return S_OK;
+	}
+};
+
+// SimpleCollectionImpl: collection class for ribbon simple collection controls
+//
+template <class TCtrl, size_t t_size, UI_COMMANDTYPE t_CommandType = UI_COMMANDTYPE_ACTION>
+class SimpleCollectionImpl : public CollectionImplBase<SimpleCollectionImpl<TCtrl, t_size>, t_size>
+{
+	typedef SimpleCollectionImpl<TCtrl, t_size, t_CommandType> thisClass;
+public:
+	typedef CollectionImplBase<thisClass, t_size> CollectionBase;
+	typedef thisClass SimpleCollection;
+
+// Implementation
+	HRESULT OnGetItem(UINT uItem, REFPROPERTYKEY key, PROPVARIANT *value)
+	{
+		ATLASSERT(uItem < t_size);
+		typename TCtrl::WndRibbon& ribbon = static_cast<TCtrl*>(this)->GetWndRibbon();
+
+		HRESULT hr = E_NOTIMPL;
+		switch (k_(key))
+		{
+		case k_ItemImage:
+			if (HBITMAP hbm = ribbon.DefRibbonQueryItemImage(TCtrl::GetID(), uItem))
+				hr = SetPropertyVal(key, GetImage(hbm, UI_OWNERSHIP_TRANSFER), value);
+			break;
+		case k_Label:
+			if (LPCWSTR sText = ribbon.DefRibbonQueryItemText(TCtrl::GetID(), uItem))
+				hr = SetPropertyVal(key, (LPCWSTR)sText, value);
+			break;
+		case k_CommandType:
+			hr = SetPropertyVal(key, t_CommandType, value);
+			break;
+		case k_CommandId:
+			hr = SetPropertyVal(key, ribbon.DefRibbonQueryItemCommand(TCtrl::GetID(), uItem), value);
+			break;
+		case k_CategoryId:
+			hr = SetPropertyVal(key, UI_COLLECTION_INVALIDINDEX, value);
+			break;
+		default:
+			ATLASSERT(FALSE);
+			break;
+		}
+
+		return hr;
+	}
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Ribbon collection control classes
+
+// CollectionCtrlImpl: specializable class for ribbon collection controls
+//
+template <class T, UINT t_ID, class TCollection>
+class CollectionCtrlImpl : public CommandCtrlImpl<T, t_ID>, public TCollection
+{
+	typedef CollectionCtrlImpl<T, t_ID, TCollection> thisClass;
+public:
+	typedef CommandCtrlImpl<T, t_ID> CommandCtrl;
+	typedef TCollection Collection;
+
+	// Implementation
+	virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, 
+	                                 const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue)
+	{
+		ATLASSERT(nCmdID == this->GetID());
+		ATLASSERT(ppropvarNewValue);
+
+		HRESULT hr = Collection::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue);
+		if FAILED(hr)
+			hr = CommandCtrl::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue);
+
+		return hr;
+	}
+
+	virtual HRESULT DoExecute(UINT nCmdID, UI_EXECUTIONVERB verb, 
+	                          const PROPERTYKEY* key, const PROPVARIANT* ppropvarValue,
+	                          IUISimplePropertySet* /*pCommandExecutionProperties*/)
+	{
+		ATLASSERT (nCmdID == this->GetID());
+		(void)nCmdID; // avoid level4 warning
+
+		if (key == NULL) // gallery button pressed
+		{
+			this->GetWndRibbon().OnRibbonItemSelected(this->GetID(), UI_EXECUTIONVERB_EXECUTE, UI_COLLECTION_INVALIDINDEX);
+			return S_OK;
+		}
+
+		ATLASSERT(k_(*key) == k_SelectedItem);
+		ATLASSERT(ppropvarValue);
+
+		HRESULT hr = S_OK;
+		UINT32 uSel = 0xffff;
+		hr = UIPropertyToUInt32(*key, *ppropvarValue, &uSel);
+
+		if (SUCCEEDED(hr))
+		{
+			if (this->GetWndRibbon().OnRibbonItemSelected(this->GetID(), verb, uSel))
+				TCollection::Select(uSel);
+		}
+
+		return hr;
+	}
+};
+
+// ToolbarGalleryCtrlImpl: base class for ribbon toolbar gallery controls
+//
+template <class T, UINT t_ID, UINT t_idTB, size_t t_size>
+class ToolbarGalleryCtrlImpl : public CollectionCtrlImpl<T, t_ID, CommandCollectionImpl<ToolbarGalleryCtrlImpl<T, t_ID, t_idTB, t_size>, t_size>>
+{
+public:
+	ToolbarGalleryCtrlImpl()
+	{
+		CResource tbres;
+		ATLVERIFY(tbres.Load(RT_TOOLBAR, t_idTB));
+		_AtlToolBarData* pData = (_AtlToolBarData*)tbres.Lock();
+		ATLASSERT(pData);
+		ATLASSERT(pData->wVersion == 1);
+
+		WORD* pItems = pData->items();
+		INT j = 0;
+		for (int i = 0; (i < pData->wItemCount) && (j < t_size); i++)
+		{
+			if (pItems[i] != 0)
+			{
+				this->m_aCmdType[j] = UI_COMMANDTYPE_ACTION;
+				this->m_auCmd[j++] = pItems[i];
+			}
+		}
+
+		if (j < t_size)
+			this->Resize(j);
+	}
+
+ 	HRESULT DoGetItem(UINT uItem, REFPROPERTYKEY key, PROPVARIANT *value)
+	{
+		ATLASSERT(uItem < this->m_size);
+		ATLASSERT(this->m_auCmd[uItem]);
+
+		HRESULT hr = E_FAIL;
+		switch (k_(key))
+		{
+		case k_CommandId:
+			hr = SetPropertyVal(key, this->m_auCmd[uItem], value);
+			break;
+		case k_CommandType:
+			hr = SetPropertyVal(key, UINT32(this->m_aCmdType[uItem]), value);
+			break;
+		case k_CategoryId:
+			hr = SetPropertyVal(key, UI_COLLECTION_INVALIDINDEX, value);
+			break;
+		default:
+			ATLASSERT(FALSE);
+			break;
+		}
+
+		return hr;
+	}
+};
+
+
+// SimpleCollectionCtrlImpl: base class for simple gallery and listbox controls
+//
+template <class T, UINT t_ID, size_t t_size, UI_COMMANDTYPE t_CommandType = UI_COMMANDTYPE_ACTION>
+class SimpleCollectionCtrlImpl : 
+		public CommandCtrlImpl<T, t_ID>,
+		public SimpleCollectionImpl<SimpleCollectionCtrlImpl<T, t_ID, t_size, t_CommandType>, t_size, t_CommandType>
+{
+	typedef SimpleCollectionCtrlImpl<T, t_ID, t_size, t_CommandType> thisClass;
+public:
+	typedef thisClass SimpleCollection;
+
+	SimpleCollectionCtrlImpl() : m_uSelected(0)
+	{ }
+
+	UINT m_uSelected;
+
+	HRESULT Select(UINT uItem, bool bUpdate = false)
+	{
+		ATLASSERT((uItem < t_size) || (uItem == UI_COLLECTION_INVALIDINDEX));
+
+		m_uSelected = uItem;
+
+		return bUpdate ? 
+			this->GetWndRibbon().SetProperty(this->GetID(), UI_PKEY_SelectedItem, uItem) :
+			S_OK;
+	}
+
+	// Implementation
+	virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, 
+	                                 const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue)
+	{
+		ATLASSERT(nCmdID == this->GetID());
+		ATLASSERT(ppropvarNewValue != NULL);
+
+		HRESULT hr = S_OK;
+		switch (k_(key))
+		{
+		case k_ItemsSource:
+			{
+				ATL::CComQIPtr<IUICollection> pIUICollection(ppropvarCurrentValue->punkVal);
+				ATLASSERT(pIUICollection.p);
+				hr = pIUICollection->Clear();
+				for (UINT i = 0; i < t_size; i++)
+				{
+					if FAILED(hr = pIUICollection->Add(this->m_apItems[i]))
+						break;
+				}
+				ATLASSERT(SUCCEEDED(hr));
+			}
+			break;
+		case k_SelectedItem:
+			hr = SetPropertyVal(UI_PKEY_SelectedItem, m_uSelected, ppropvarNewValue);
+			break;
+		default:
+			hr = CommandCtrlImpl<T, t_ID>::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue);
+			break;
+		}
+
+		return hr;
+	}
+
+	virtual HRESULT DoExecute(UINT nCmdID, UI_EXECUTIONVERB verb, 
+	                          const PROPERTYKEY* key, const PROPVARIANT* ppropvarValue,
+	                          IUISimplePropertySet* /*pCommandExecutionProperties*/)
+	{
+		ATLASSERT (nCmdID == this->GetID());
+		(void)nCmdID;   // avoid level 4 warning
+		
+		HRESULT hr = S_OK;
+		if (key == NULL) // gallery button pressed
+		{
+			this->GetWndRibbon().OnRibbonItemSelected(this->GetID(), UI_EXECUTIONVERB_EXECUTE, UI_COLLECTION_INVALIDINDEX);
+			return hr;
+		}
+		ATLASSERT(k_(*key) == k_SelectedItem);
+		ATLASSERT(ppropvarValue);
+
+		if SUCCEEDED(hr = UIPropertyToUInt32(*key, *ppropvarValue, &m_uSelected))
+			this->GetWndRibbon().OnRibbonItemSelected(this->GetID(), verb, m_uSelected);
+
+		return hr;
+	}
+};
+
+// RecentItemsCtrlImpl
+//
+template <class T, UINT t_ID, class TDocList = CRecentDocumentList>
+class RecentItemsCtrlImpl : 
+		public CtrlImpl<T, t_ID>,
+		public CollectionImplBase<RecentItemsCtrlImpl<T, t_ID, TDocList>, TDocList::m_nMaxEntries_Max>,
+		public TDocList
+{
+	typedef RecentItemsCtrlImpl<T, t_ID, TDocList> thisClass;
+public:
+	typedef thisClass RecentItems;
+
+	// Implementation
+	HRESULT OnGetItem(UINT uItem, REFPROPERTYKEY key, PROPVARIANT *value)
+	{
+		ATLASSERT((INT)uItem < this->GetMaxEntries());
+
+		LPCWSTR sPath = this->m_arrDocs[uItem].szDocName;
+		HRESULT hr = E_NOTIMPL;
+		switch (k_(key))
+		{
+		case k_Label:
+			hr = SetPropertyVal(key, this->GetWndRibbon().OnRibbonQueryRecentItemName(sPath), value);
+			break;
+		case k_LabelDescription:
+			hr = SetPropertyVal(key, sPath, value);
+			break;
+		default:
+			ATLASSERT(FALSE);
+			break;
+		}
+
+		return hr;
+	}
+
+	virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, 
+	                                 const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue)
+	{
+		ATLASSERT(nCmdID == this->GetID());
+		ATLASSERT(ppropvarNewValue);
+
+		HRESULT hr = S_OK;
+		switch (k_(key))
+		{
+		case k_RecentItems:
+			if (SAFEARRAY* psa = SafeArrayCreateVector(VT_UNKNOWN, 0, this->m_arrDocs.GetSize()))
+			{
+				const int iLastIndex = this->m_arrDocs.GetSize() - 1;
+				for (LONG i = 0; i <= iLastIndex; i++)
+					SafeArrayPutElement(psa, &i, this->m_apItems[iLastIndex - i]); // reverse order
+
+				hr = SetPropertyVal(key, psa, ppropvarNewValue);
+				SafeArrayDestroy(psa);
+			}
+			break;
+		default:
+			hr = CtrlImpl<T, t_ID>::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue);
+			break;
+		}
+
+		return hr;
+	}
+
+	virtual HRESULT DoExecute(UINT nCmdID, UI_EXECUTIONVERB verb, 
+	                          const PROPERTYKEY* key, const PROPVARIANT* ppropvarValue,
+	                          IUISimplePropertySet* /*pCommandExecutionProperties*/)
+	{
+		ATLASSERT(nCmdID == this->GetID());
+		(void)nCmdID;   // avoid level 4 warning
+		ATLASSERT(verb == UI_EXECUTIONVERB_EXECUTE);
+		(void)verb;   // avoid level 4 warning
+		ATLASSERT((key) && (k_(*key) == k_SelectedItem));
+		ATLASSERT(ppropvarValue);
+
+		UINT32 uSel = 0xffff;
+		HRESULT hr = UIPropertyToUInt32(*key, *ppropvarValue, &uSel);
+		if SUCCEEDED(hr)
+		{
+			ATLASSERT(uSel < (UINT)this->GetMaxEntries());
+			this->GetWndRibbon().DefCommandExecute(ID_FILE_MRU_FIRST + uSel);
+		}
+
+		return hr;
+	}
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Ribbon stand-alone control classes
+
+// FontCtrlImpl
+//
+template <class T, UINT t_ID>
+class FontCtrlImpl : public CtrlImpl<T, t_ID>
+{
+public:
+
+	CharFormat m_cf;
+
+// Implementation
+	virtual HRESULT DoExecute(UINT nCmdID, UI_EXECUTIONVERB verb, 
+	                          const PROPERTYKEY* key, const PROPVARIANT* ppropvarValue,
+	                          IUISimplePropertySet* pCommandExecutionProperties)
+	{
+		ATLASSERT (nCmdID == this->GetID());
+		(void)nCmdID;   // avoid level 4 warning
+		ATLASSERT ((key) && (k_(*key) == k_FontProperties));
+		(void)key;   // avoid level 4 warning
+
+		HRESULT hr = E_INVALIDARG;
+		switch (verb)
+		{
+			case UI_EXECUTIONVERB_PREVIEW:
+			case UI_EXECUTIONVERB_EXECUTE:
+				ATLASSERT(pCommandExecutionProperties);
+				PROPVARIANT propvar;
+
+				if (SUCCEEDED(hr = pCommandExecutionProperties->GetValue(UI_PKEY_FontProperties_ChangedProperties, &propvar)))
+					m_cf << ATL::CComQIPtr<IPropertyStore>(propvar.punkVal);
+				break;
+
+			case UI_EXECUTIONVERB_CANCELPREVIEW:
+				ATLASSERT(ppropvarValue);
+				ATL::CComPtr<IPropertyStore> pStore;
+
+				if (SUCCEEDED(hr = UIPropertyToInterface(UI_PKEY_FontProperties, *ppropvarValue, &pStore)))
+					m_cf << pStore;
+				break;
+		}
+
+		if (SUCCEEDED(hr))
+			this->GetWndRibbon().OnRibbonFontCtrlExecute(this->GetID(), verb, &m_cf);
+		else
+			ATLASSERT(FALSE);
+
+		return hr;
+	}
+
+	virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, 
+	                                 const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue)
+	{
+		if ((k_(key) == k_FontProperties) && (this->GetWndRibbon().OnRibbonQueryFont(t_ID, m_cf)))
+		{
+			ATL::CComQIPtr<IPropertyStore> pStore(ppropvarCurrentValue->punkVal);
+			m_cf >> pStore;
+			return SetPropertyVal(key, pStore.p, ppropvarNewValue);
+		}
+		else
+		{
+			return CtrlImpl<T, t_ID>::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue);
+		}
+	}
+};
+
+// ColorCtrlImpl
+//
+template <class T, UINT t_ID>
+class ColorCtrlImpl : public CommandCtrlImpl<T, t_ID>
+{
+public:
+	ColorCtrlImpl() : m_colorType(UI_SWATCHCOLORTYPE_NOCOLOR), m_color(0x800080) /*MAGENTA*/
+	{ }
+
+	UINT32 m_colorType; // value in UI_SWATCHCOLORTYPE
+	COLORREF m_color;
+	Text m_sLabels[6]; // k_MoreColorsLabel to k_ThemeColorsCategoryLabel
+	ATL::CSimpleArray<COLORREF> m_aColors[2];
+	ATL::CSimpleArray<LPCWSTR> m_aTooltips[2];
+
+	// Operations
+	HRESULT SetColor(COLORREF color, bool bUpdate = false)
+	{
+		if (m_colorType != UI_SWATCHCOLORTYPE_RGB)
+			SetColorType(UI_SWATCHCOLORTYPE_RGB, bUpdate);
+		m_color = color;
+		return bUpdate ? this->SetProperty(UI_PKEY_Color, color) : S_OK;
+	}
+
+	HRESULT SetColorType(UI_SWATCHCOLORTYPE type, bool bUpdate = false)
+	{
+		m_colorType = type;
+		return bUpdate ? this->SetProperty(UI_PKEY_ColorType, type) : S_OK;
+	}
+
+	HRESULT SetColorLabel(REFPROPERTYKEY key, LPCWSTR sLabel, bool bUpdate = false)
+	{
+		ATLASSERT((k_(key) >= k_ThemeColorsCategoryLabel) && (k_(key) <= k_MoreColorsLabel));
+		m_sLabels[k_(key) - k_ThemeColorsCategoryLabel] = sLabel;
+		return bUpdate ? this->SetProperty(key, sLabel) : S_OK;
+	}
+
+	HRESULT SetColorArray(REFPROPERTYKEY key, COLORREF* pColor, bool bUpdate = false)
+	{
+		ATLASSERT((k_(key) == k_ThemeColors) || (k_(key) == k_StandardColors));
+
+		const INT ic = k_(key) - k_ThemeColors;
+		m_aColors[ic].RemoveAll();
+		while (*pColor != 0x800080) /*MAGENTA*/
+			m_aColors[ic].Add(*pColor++);
+
+		if (bUpdate)
+		{
+			PROPVARIANT var;
+			if SUCCEEDED(InitPropVariantFromUInt32Vector(m_aColors[ic].GetData(), m_aColors[ic].GetSize(), &var))
+				return this->SetProperty(key, var);
+			else
+				return E_INVALIDARG;
+		}
+		else
+		{
+			return S_OK;
+		}
+	}
+
+	HRESULT SetColorTooltips(REFPROPERTYKEY key, LPCWSTR* ppsTT, bool bUpdate = false)
+	{
+		ATLASSERT((k_(key) == k_ThemeColorsTooltips) || (k_(key) == k_StandardColorsTooltips));
+
+		const INT ic = k_(key) - k_ThemeColorsTooltips;
+		m_aTooltips[ic].RemoveAll();
+		while (*ppsTT)
+			m_aTooltips[ic].Add(*ppsTT++);
+
+		if (bUpdate)
+		{
+			PROPVARIANT var;
+			if SUCCEEDED(InitPropVariantFromStringVector(m_aTooltips[ic].GetData(), m_aTooltips[ic].GetSize(), &var))
+				return this->SetProperty(key, var);
+			else
+				return E_INVALIDARG;
+		}
+		else
+		{
+			return S_OK;
+		}
+	}
+
+	// Implementation
+	virtual HRESULT DoExecute(UINT nCmdID, UI_EXECUTIONVERB verb, 
+	                          const PROPERTYKEY* key, const PROPVARIANT* ppropvarValue,
+	                          IUISimplePropertySet* pCommandExecutionProperties)
+	{
+		ATLASSERT (nCmdID == this->GetID());
+		(void)nCmdID;   // avoid level 4 warning
+		ATLASSERT (key && (k_(*key) == k_ColorType));
+		(void)key;   // avoid level 4 warning
+		ATLASSERT (ppropvarValue);
+
+		HRESULT hr = PropVariantToUInt32(*ppropvarValue, &m_colorType);
+		ATLASSERT(SUCCEEDED(hr));
+
+		if (SUCCEEDED(hr) && (m_colorType == UI_SWATCHCOLORTYPE_RGB))
+		{
+			ATLASSERT(pCommandExecutionProperties);
+			PROPVARIANT var;
+			if SUCCEEDED(hr = pCommandExecutionProperties->GetValue(UI_PKEY_Color, &var))
+				hr = PropVariantToUInt32(var, &m_color);
+		}
+
+		if SUCCEEDED(hr)
+			this->GetWndRibbon().OnRibbonColorCtrlExecute(this->GetID(), verb, (UI_SWATCHCOLORTYPE)m_colorType/*uType*/, m_color);
+		else
+			ATLASSERT(FALSE); // something was wrong
+
+		return hr;
+	}
+
+	virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, 
+	                                 const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue)
+	{
+		ATLASSERT (nCmdID == this->GetID());
+
+		HRESULT hr = E_NOTIMPL;
+
+		switch (k_(key))
+		{
+		case k_ColorType:
+			hr = SetPropertyVal(key, m_colorType, ppropvarNewValue);
+			break;
+		case k_Color:
+			if (m_color == 0x800080) /*MAGENTA*/
+				m_color = this->GetWndRibbon().OnRibbonQueryColor(this->GetID());
+			hr = SetPropertyVal(key, m_color, ppropvarNewValue);
+			break;
+		case k_ColorMode:
+			break;
+		case k_ThemeColorsCategoryLabel:
+		case k_StandardColorsCategoryLabel:
+		case k_RecentColorsCategoryLabel:
+		case k_AutomaticColorLabel:
+		case k_NoColorLabel:
+		case k_MoreColorsLabel:
+			{
+				const UINT iLabel = k_(key) - k_ThemeColorsCategoryLabel;
+				if (m_sLabels[iLabel].IsEmpty())
+					if (LPCWSTR psLabel = this->GetWndRibbon().OnRibbonQueryColorLabel(this->GetID(), key))
+						m_sLabels[iLabel] = psLabel;
+				if (!m_sLabels[iLabel].IsEmpty())
+					hr = SetPropertyVal(key, (LPCWSTR)m_sLabels[iLabel], ppropvarNewValue);
+			}
+			break;
+		case k_ThemeColors:
+		case k_StandardColors:
+			{
+				const INT ic = k_(key) - k_ThemeColors;
+				if (!m_aColors[ic].GetSize())
+					if (COLORREF* pColor = this->GetWndRibbon().OnRibbonQueryColorArray(this->GetID(), key))
+						SetColorArray(key, pColor);
+				if (INT iMax = m_aColors[ic].GetSize())
+					hr = InitPropVariantFromUInt32Vector(m_aColors[ic].GetData(), iMax, ppropvarNewValue);
+			}
+			break;
+		case k_ThemeColorsTooltips:
+		case k_StandardColorsTooltips:
+			{
+				const INT ic = k_(key) - k_ThemeColorsTooltips;
+				if (m_aTooltips[ic].GetSize() == 0)
+					if (LPCWSTR* ppsTT = this->GetWndRibbon().OnRibbonQueryColorTooltips(this->GetID(), key))
+						SetColorTooltips(key, ppsTT);
+				if (INT iMax = m_aTooltips[ic].GetSize())
+					hr = InitPropVariantFromStringVector(m_aTooltips[ic].GetData(), iMax, ppropvarNewValue);
+			}
+			break;
+		default:
+			hr = CommandCtrlImpl<T, t_ID>::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue);
+			break;
+		}
+
+		return hr;
+	}
+};
+
+// SpinnerCtrlImpl
+//
+template <class T, UINT t_ID, typename V = LONG>
+class SpinnerCtrlImpl : public CtrlImpl<T, t_ID>
+{
+public:
+	SpinnerCtrlImpl()
+	{
+		m_Values[0] = m_Values[2] = m_Values[4] = 0;
+		m_Values[1] = 100;
+		m_Values[3] = 1;
+	}
+
+	V m_Values[5];
+		// k_DecimalValue = 201, k_MaxValue = 203, k_MinValue, k_Increment, k_DecimalPlaces
+		
+	Text m_FormatString;
+	Text m_RepresentativeString;
+
+	// Operations
+	HRESULT SetDecimalPlaces(V vPlaces, bool bUpdate = false)
+	{
+		return SetValue(UI_PKEY_DecimalPlaces, vPlaces, bUpdate);
+	}
+
+	HRESULT SetMin(V vMin, bool bUpdate = false)
+	{
+		return SetValue(UI_PKEY_MinValue, vMin, bUpdate);
+	}
+
+	HRESULT SetMax(V vMax, bool bUpdate = false)
+	{
+		return SetValue(UI_PKEY_MaxValue, vMax, bUpdate);
+	}
+
+	HRESULT SetVal(V vVal, bool bUpdate = false)
+	{
+		return SetValue(UI_PKEY_DecimalValue, vVal, bUpdate);
+	}
+
+	HRESULT SetIncrement(V vIncrement, bool bUpdate = false)
+	{
+		return SetValue(UI_PKEY_Increment, vIncrement, bUpdate);
+	}
+
+	HRESULT SetFormatString(LPCWSTR sFormat, bool bUpdate = false)
+	{
+		return SetText(UI_PKEY_FormatString, sFormat, bUpdate);
+	}
+
+	HRESULT SetRepresentativeString(LPCWSTR sRepresentative, bool bUpdate = false)
+	{
+		return SetText(UI_PKEY_RepresentativeString, sRepresentative, bUpdate);
+	}
+
+	// Implementation
+	HRESULT SetText(REFPROPERTYKEY key, LPCWSTR sText, bool bUpdate = false)
+	{
+		switch (k_(key))
+		{
+		case k_FormatString:
+			m_FormatString = sText;
+			break;
+		case k_RepresentativeString:
+			m_RepresentativeString = sText;
+			break;
+		default:
+			return CtrlImpl::SetText(key, sText, bUpdate);
+		}
+
+		return bUpdate ?
+			this->GetWndRibbon().InvalidateProperty(this->GetID(), key) :
+			S_OK;
+	}
+
+	HRESULT SetValue(REFPROPERTYKEY key, V val, bool bUpdate = false)
+	{
+		ATLASSERT((k_(key) <= k_DecimalPlaces) && (k_(key) >= k_DecimalValue));
+
+		const INT iVal = k_(key) == k_DecimalValue ? 0 : k_(key) - k_StringValue;
+		m_Values[iVal] = val;
+
+		if (bUpdate)
+		{
+			if(k_(key) == k_DecimalValue)
+			{
+				DECIMAL decVal;
+				InitDecimal(val, &decVal);
+				return this->SetProperty(key, &decVal);
+			}
+			else
+			{
+				return this->GetWndRibbon().InvalidateProperty(this->GetID(), key);
+			}
+		}
+		else
+		{
+			return S_OK;
+		}
+	}
+
+	HRESULT QueryValue(REFPROPERTYKEY key, LONG* plVal)
+	{
+		return this->GetWndRibbon().OnRibbonQuerySpinnerValue(this->GetID(), key, plVal) ? S_OK : S_FALSE;
+	}
+
+	HRESULT QueryValue(REFPROPERTYKEY key, DOUBLE* pdVal)
+	{
+		return this->GetWndRibbon().OnRibbonQueryFloatSpinnerValue(this->GetID(), key, pdVal) ? S_OK : S_FALSE;
+	}
+
+	HRESULT OnGetValue(REFPROPERTYKEY key, PROPVARIANT* ppv)
+	{
+		ATLASSERT((k_(key) <= k_DecimalPlaces) && (k_(key) >= k_DecimalValue));
+
+		const INT iVal = k_(key) == k_DecimalValue ? 0 : k_(key) - k_StringValue;
+
+		QueryValue(key, m_Values + iVal);
+
+		if (k_(key) == k_DecimalPlaces)
+		{
+			return SetPropertyVal(key, m_Values[iVal], ppv);
+		}
+		else
+		{
+			DECIMAL decVal;
+			InitDecimal(m_Values[iVal], &decVal);
+			return SetPropertyVal(key, &decVal, ppv);
+		}
+	}
+
+	HRESULT OnGetText(REFPROPERTYKEY key, Text& sVal, PROPVARIANT* ppv)
+	{
+		if (LPCWSTR sNew = this->GetWndRibbon().OnRibbonQueryText(this->GetID(), key))
+			sVal = sNew;
+		return SetPropertyVal(key, (LPCWSTR)sVal, ppv);
+	}
+
+	virtual HRESULT DoExecute(UINT nCmdID, UI_EXECUTIONVERB verb, 
+	                          const PROPERTYKEY* key, const PROPVARIANT* ppropvarValue,
+	                          IUISimplePropertySet* /*pCommandExecutionProperties*/)
+	{
+		ATLASSERT (nCmdID == this->GetID());
+		(void)nCmdID;   // avoid level 4 warning
+		ATLASSERT (key && (k_(*key) == k_DecimalValue));
+		(void)key;   // avoid level 4 warning
+		ATLASSERT (verb == UI_EXECUTIONVERB_EXECUTE);
+		(void)verb;   // avoid level 4 warning
+
+		DECIMAL decVal;
+
+		HRESULT hr = UIPropertyToDecimal(UI_PKEY_DecimalValue, *ppropvarValue, &decVal);
+		hr = InitVal(m_Values[0], &decVal);
+
+		this->GetWndRibbon().OnRibbonSpinnerCtrlExecute(this->GetID(), &m_Values[0]);
+
+		return hr;
+	}
+
+	virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, 
+	                                 const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue)
+	{
+		ATLASSERT (nCmdID == this->GetID());
+
+		HRESULT hr = E_NOTIMPL;
+		switch (k_(key))
+		{
+		case k_DecimalPlaces:
+		case k_DecimalValue:
+		case k_Increment:
+		case k_MaxValue:
+		case k_MinValue:
+			hr = OnGetValue(key, ppropvarNewValue);
+			break;
+		case k_FormatString:
+			if (m_FormatString.IsEmpty())
+				return OnGetText(key, m_FormatString, ppropvarNewValue);
+			break;
+		case k_RepresentativeString:
+			if (m_RepresentativeString.IsEmpty())
+				return OnGetText(key, m_RepresentativeString, ppropvarNewValue);
+			break;
+		default:
+			hr = CtrlImpl<T, t_ID>::DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue);
+			break;
+		}
+
+		return hr;
+	}
+
+	// decimal conversion helpers
+	static HRESULT InitDecimal(LONG& val, DECIMAL* pDecimal)
+	{
+		return ::VarDecFromI4(val, pDecimal);
+	}
+
+	static HRESULT InitDecimal(DOUBLE& val, DECIMAL* pDecimal)
+	{
+		return ::VarDecFromR8(val, pDecimal);
+	}
+
+	static HRESULT InitVal(LONG& val, const DECIMAL* pDecimal)
+	{
+		return ::VarI4FromDec(pDecimal, &val);
+	}
+
+	static HRESULT InitVal(DOUBLE& val, const DECIMAL* pDecimal)
+	{
+		return ::VarR8FromDec(pDecimal, &val);
+	}
+};
+
+// CRibbonImpl Ribbon implementation class
+//
+template <class T>
+class CRibbonImpl : 
+		public CRibbonUpdateUI<T>,
+		public ICtrl,
+		public IUIApplication,
+		public IUICommandHandler
+{
+	typedef CRibbonImpl<T> thisClass;
+public:
+	typedef thisClass Ribbon;
+	typedef T WndRibbon;
+
+	CRibbonImpl() : m_bRibbonUI(false), m_hgRibbonSettings(NULL)
+	{
+#ifdef _DEBUG
+		m_cRef = 1;
+#endif
+		pWndRibbon = static_cast<T*>(this);
+		HRESULT hr = ::CoInitialize(NULL);
+		if(SUCCEEDED(hr))
+			if (RunTimeHelper::IsRibbonUIAvailable())
+				hr = m_pIUIFramework.CoCreateInstance(CLSID_UIRibbonFramework);
+			else
+				ATLTRACE2(atlTraceUI, 0, _T("Ribbon UI not available\n"));
+
+		if FAILED(hr)
+			ATLTRACE2(atlTraceUI, 0, _T("Ribbon construction failed\n"));
+
+		ATLASSERT(SUCCEEDED(hr));
+	}
+
+	virtual ~CRibbonImpl()
+	{
+		::GlobalFree(m_hgRibbonSettings);
+		m_pIUIFramework.Release();
+		::CoUninitialize();
+	}
+
+	ICtrl& GetRibbonCtrl(UINT)
+	{
+		return static_cast<ICtrl&>(*this);
+	}
+
+	ATL::CComPtr<IUIFramework> m_pIUIFramework;
+	bool m_bRibbonUI;
+	HGLOBAL m_hgRibbonSettings;
+
+	bool IsRibbonUI()
+	{
+		return m_bRibbonUI;
+	}
+
+	IUIFramework* GetIUIFrameworkPtr()
+	{
+		return m_pIUIFramework;
+	}
+
+	template <typename I>
+	I* GetRibbonViewPtr(UINT32 uID)
+	{
+		ATLASSERT(m_pIUIFramework);
+		ATL::CComPtr<I> pI;
+		return m_pIUIFramework->GetView(uID, __uuidof(I), (void**) &pI) == S_OK ?
+			pI : 
+			NULL;
+	}
+
+	IUIRibbon* GetRibbonPtr()
+	{
+		return GetRibbonViewPtr<IUIRibbon>(0);
+	}
+
+	IUIContextualUI* GetMenuPtr(UINT32 uID)
+	{
+		ATLASSERT(uID);
+		return GetRibbonViewPtr<IUIContextualUI>(uID);
+	}
+
+	UINT GetRibbonHeight()
+	{
+		ATLASSERT(IsRibbonUI());
+
+		UINT32 cy = 0;
+		if (ATL::CComPtr<IUIRibbon> pIUIRibbon = GetRibbonPtr())
+			pIUIRibbon->GetHeight(&cy);
+		return cy;
+	}
+
+	HRESULT CreateRibbon(LPCWSTR sResName = L"APPLICATION_RIBBON")
+	{
+		T* pT = static_cast<T*>(this);
+		ATLASSERT(GetIUIFrameworkPtr() && !IsRibbonUI());
+		ATLASSERT(pT->IsWindow());
+
+		HRESULT hr = m_pIUIFramework->Initialize(pT->m_hWnd, this);
+
+		if (hr == S_OK)
+			hr = m_pIUIFramework->LoadUI(ModuleHelper::GetResourceInstance(), sResName);
+			
+		return hr;
+	}
+
+	HRESULT DestroyRibbon()
+	{
+		T* pT = static_cast<T*>(this);
+		ATLASSERT(GetIUIFrameworkPtr() && IsRibbonUI());
+		ATLASSERT(pT->IsWindow());
+
+		HRESULT hRes = m_pIUIFramework->Destroy();
+		if (!RunTimeHelper::IsWin7())
+			pT->SetWindowRgn(NULL, TRUE); // Vista Basic bug workaround
+		return hRes;
+	}
+
+// Ribbon persistency
+	HRESULT operator >>(IStream* pIStream)
+	{
+		ATLASSERT(GetIUIFrameworkPtr());
+		ATLASSERT(pIStream);
+
+		HRESULT hr = E_FAIL;
+		if (ATL::CComPtr<IUIRibbon> pIUIRibbon = GetRibbonPtr())
+		{
+			const LARGE_INTEGER li0 = {};
+			pIStream->Seek(li0, STREAM_SEEK_SET, NULL);
+			hr = pIUIRibbon->SaveSettingsToStream(pIStream);
+			pIStream->Commit(STGC_DEFAULT);
+		}
+
+		return hr;
+	}
+
+	HRESULT operator <<(IStream* pIStream)
+	{
+		ATLASSERT(GetIUIFrameworkPtr());
+		ATLASSERT(pIStream);
+
+		HRESULT hr = E_FAIL;
+		if (ATL::CComPtr<IUIRibbon> pIUIRibbon = GetRibbonPtr())
+		{
+			const LARGE_INTEGER li0 = {};
+			pIStream->Seek(li0, STREAM_SEEK_SET, NULL);
+			hr = pIUIRibbon->LoadSettingsFromStream(pIStream);
+		}
+
+		return hr;
+	}
+
+	void ResetRibbonSettings()
+	{
+		if (m_hgRibbonSettings != NULL)
+		{
+			::GlobalFree(m_hgRibbonSettings);
+			m_hgRibbonSettings = NULL;
+		}
+	}
+
+	HRESULT SaveRibbonSettings()
+	{
+		ATLASSERT(GetIUIFrameworkPtr());
+		ATLASSERT(static_cast<T*>(this)->IsWindow());
+
+		HRESULT hr = E_FAIL;
+		ATL::CComPtr<IStream> pIStream;
+
+		if SUCCEEDED(hr = ::CreateStreamOnHGlobal(m_hgRibbonSettings, FALSE, &pIStream))
+			hr = *this >> pIStream;
+
+		if (SUCCEEDED(hr) && (m_hgRibbonSettings == NULL))
+			hr = ::GetHGlobalFromStream(pIStream, &m_hgRibbonSettings);
+
+		if FAILED(hr)
+			ResetRibbonSettings();
+
+		return hr;
+	}
+
+	HRESULT RestoreRibbonSettings()
+	{
+		ATLASSERT(GetIUIFrameworkPtr());
+		ATLASSERT(m_hgRibbonSettings);
+		ATLASSERT(static_cast<T*>(this)->IsWindow());
+
+		HRESULT hr = E_FAIL;
+		ATL::CComPtr<IStream> pIStream;
+
+		if SUCCEEDED(hr = ::CreateStreamOnHGlobal(m_hgRibbonSettings, FALSE, &pIStream))
+			hr = *this << pIStream;
+
+		if FAILED(hr)
+			ResetRibbonSettings();
+
+		return hr;
+	}
+
+// QAT dock states
+	UI_CONTROLDOCK GetQATDock()
+	{
+		ATLASSERT(GetIUIFrameworkPtr());
+		ATLASSERT(IsRibbonUI());
+
+		UINT32 uDock = 0;
+		PROPVARIANT propvar;
+		ATL::CComQIPtr<IPropertyStore>pIPS(GetRibbonPtr());
+
+		if ((pIPS != NULL) && SUCCEEDED(pIPS->GetValue(UI_PKEY_QuickAccessToolbarDock, &propvar)) &&
+			SUCCEEDED(UIPropertyToUInt32(UI_PKEY_QuickAccessToolbarDock, propvar, &uDock)))
+				return (UI_CONTROLDOCK)uDock;
+
+		ATLASSERT(FALSE); // something was wrong
+		return (UI_CONTROLDOCK)0;
+	}
+
+	bool SetQATDock(UI_CONTROLDOCK dockState)
+	{
+		ATLASSERT(GetIUIFrameworkPtr());
+		ATLASSERT(IsRibbonUI());
+
+		PROPVARIANT propvar;
+		ATLVERIFY(SUCCEEDED(SetPropertyVal(UI_PKEY_QuickAccessToolbarDock, dockState, &propvar)));
+
+		ATL::CComQIPtr<IPropertyStore>pIPS(GetRibbonPtr());
+		if ((pIPS != NULL) && SUCCEEDED(pIPS->SetValue(UI_PKEY_QuickAccessToolbarDock, propvar)))
+		{
+			pIPS->Commit();
+			return true;
+		}
+
+		ATLASSERT(FALSE); // something was wrong
+		return false;
+	}
+
+// Ribbon display states
+	bool GetRibbonDisplayState(REFPROPERTYKEY key)
+	{
+		ATLASSERT(GetIUIFrameworkPtr());
+		ATLASSERT(IsRibbonUI());
+		ATLASSERT((k_(key) == k_Viewable) || (k_(key) == k_Minimized));
+
+		PROPVARIANT propvar;
+		ATL::CComQIPtr<IPropertyStore>pIPS(GetRibbonPtr());
+
+		if ((pIPS != NULL) && SUCCEEDED(pIPS->GetValue(key, &propvar)))
+		{
+			BOOL bState = FALSE;
+			if SUCCEEDED(UIPropertyToBoolean(key, propvar, &bState))
+				return (bState != FALSE);
+		}
+
+		ATLASSERT(FALSE); // something was wrong
+		return false;
+	}
+
+	bool SetRibbonDisplayState(REFPROPERTYKEY key, bool bState = true)
+	{
+		ATLASSERT(GetIUIFrameworkPtr());
+		ATLASSERT(IsRibbonUI());
+		ATLASSERT((k_(key) == k_Viewable) || (k_(key) == k_Minimized));
+
+		PROPVARIANT propvar;
+		ATLVERIFY(SUCCEEDED(SetPropertyVal(key, bState, &propvar)));
+
+		ATL::CComQIPtr<IPropertyStore>pIPS(GetRibbonPtr());
+
+		if ((pIPS != NULL) && SUCCEEDED(pIPS->SetValue(key, propvar)))
+		{
+			pIPS->Commit();
+			return true;
+		}
+
+		ATLASSERT(FALSE); // something was wrong
+		return false;
+	}
+
+	bool IsRibbonMinimized()
+	{
+		return GetRibbonDisplayState(UI_PKEY_Minimized);
+	}
+
+	bool MinimizeRibbon(bool bMinimize = true)
+	{
+		return SetRibbonDisplayState(UI_PKEY_Minimized, bMinimize);
+	}
+
+	bool IsRibbonHidden()
+	{
+		return !GetRibbonDisplayState(UI_PKEY_Viewable);
+	}
+
+	bool HideRibbon(bool bHide = true)
+	{
+		return SetRibbonDisplayState(UI_PKEY_Viewable, !bHide);
+	}
+
+// Ribbon colors
+	UI_HSBCOLOR GetRibbonColor(REFPROPERTYKEY key)
+	{
+		ATLASSERT(GetIUIFrameworkPtr());
+		ATLASSERT(IsRibbonUI());
+		ATLASSERT((k_(key) >= k_GlobalBackgroundColor) && (k_(key) <= k_GlobalTextColor));
+
+		PROPVARIANT propvar;
+		ATL::CComQIPtr<IPropertyStore>pIPS(GetIUIFrameworkPtr());
+
+		if ((pIPS != NULL) && SUCCEEDED(pIPS->GetValue(key, &propvar)))
+		{
+			UINT32 color = 0;
+			if SUCCEEDED(UIPropertyToUInt32(key, propvar, &color))
+				return color;
+		}
+
+		ATLASSERT(FALSE); // something was wrong
+		return 0;
+	}
+
+	bool SetRibbonColor(REFPROPERTYKEY key, UI_HSBCOLOR color)
+	{
+		ATLASSERT(GetIUIFrameworkPtr());
+		ATLASSERT(IsRibbonUI());
+		ATLASSERT((k_(key) >= k_GlobalBackgroundColor) && (k_(key) <= k_GlobalTextColor));
+
+		PROPVARIANT propvar;
+		ATLVERIFY(SUCCEEDED(SetPropertyVal(key, color, &propvar)));
+
+		ATL::CComQIPtr<IPropertyStore>pIPS(GetIUIFrameworkPtr());
+
+		if ((pIPS != NULL) && SUCCEEDED(pIPS->SetValue(key, propvar)))
+		{
+			pIPS->Commit();
+			return true;
+		}
+
+		ATLASSERT(FALSE); // something was wrong
+		return false;
+	}
+
+// Ribbon modes
+	HRESULT SetRibbonModes(INT32 iModes)
+	{
+		ATLASSERT(IsRibbonUI());
+		return GetIUIFrameworkPtr()->SetModes(iModes);
+	}
+
+// Ribbon contextual tab
+	UI_CONTEXTAVAILABILITY GetRibbonContextAvail(UINT32 uID)
+	{
+		ATLASSERT(GetIUIFrameworkPtr());
+
+		PROPVARIANT propvar;
+		if (IsRibbonUI() && 
+		    SUCCEEDED(GetIUIFrameworkPtr()->GetUICommandProperty(uID, UI_PKEY_ContextAvailable, &propvar)))
+		{
+			UINT uav;
+			if (SUCCEEDED(PropVariantToUInt32(propvar, &uav)))
+			{
+				CUpdateUIBase::UIEnable(uID, uav != UI_CONTEXTAVAILABILITY_NOTAVAILABLE);
+				CUpdateUIBase::UISetCheck(uID, uav == UI_CONTEXTAVAILABILITY_ACTIVE);
+				return (UI_CONTEXTAVAILABILITY)uav;
+			}
+		}
+
+		return UI_CONTEXTAVAILABILITY_NOTAVAILABLE;
+	}
+
+	HRESULT SetRibbonContextAvail(UINT32 uID, UI_CONTEXTAVAILABILITY cav)
+	{
+		CUpdateUIBase::UIEnable(uID, cav != UI_CONTEXTAVAILABILITY_NOTAVAILABLE);
+		CUpdateUIBase::UISetCheck(uID, cav == UI_CONTEXTAVAILABILITY_ACTIVE);
+		
+		return SetProperty((WORD)uID, UI_PKEY_ContextAvailable, UINT32(cav));
+	}
+
+// Ribbon context menu
+	bool HasRibbonMenu(UINT32 uID)
+	{
+		ATL::CComPtr<IUIContextualUI> pI = GetMenuPtr(uID);
+		return pI != NULL;
+	}
+
+	HRESULT TrackRibbonMenu(UINT32 uID, INT32 x, INT32 y)
+	{
+		ATLASSERT(HasRibbonMenu(uID));
+
+		return IsRibbonUI() ?
+			ATL::CComPtr<IUIContextualUI>(GetMenuPtr(uID))->ShowAtLocation(x, y) :
+			E_FAIL;
+	}
+
+	HRESULT TrackRibbonMenu(UINT32 uID, LPARAM lParam)
+	{
+		return TrackRibbonMenu(uID, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
+	}
+
+// Overrideables
+	HBITMAP OnRibbonQueryImage(UINT nCmdID, REFPROPERTYKEY /*key*/)
+	{
+		return DefRibbonQueryImage(nCmdID);
+	}
+
+	LPCWSTR OnRibbonQueryText(UINT nCmdID, REFPROPERTYKEY key)
+	{
+		return DefRibbonQueryText(nCmdID, key);
+	}
+
+	bool OnRibbonQueryState(UINT nCmdID, REFPROPERTYKEY key)
+	{
+		return DefRibbonQueryState(nCmdID, key);
+	}
+
+	UI_CONTEXTAVAILABILITY OnRibbonQueryTabAvail(UINT nCmdID)
+	{
+		DWORD dwState = this->UIGetState(nCmdID);
+		return ((dwState & CUpdateUIBase::UPDUI_DISABLED) == CUpdateUIBase::UPDUI_DISABLED) ?
+			UI_CONTEXTAVAILABILITY_NOTAVAILABLE :
+			(((dwState & CUpdateUIBase::UPDUI_CHECKED) == CUpdateUIBase::UPDUI_CHECKED) ?
+				UI_CONTEXTAVAILABILITY_ACTIVE : 
+				UI_CONTEXTAVAILABILITY_AVAILABLE);
+	}
+
+	LPCWSTR OnRibbonQueryComboText(UINT32 /*uCtrlID*/)
+	{
+		return NULL;
+	}
+
+	LPCWSTR OnRibbonQueryCategoryText(UINT32 /*uCtrlID*/, UINT32 /*uCat*/)
+	{
+		return L"Category";
+	}
+
+	UINT32 OnRibbonQueryItemCategory(UINT32 /*uCtrlID*/, UINT32 /*uItem*/)
+	{
+		return 0;
+	}
+
+	LPCWSTR OnRibbonQueryItemText(UINT32 uCtrlID, UINT32 uItem)
+	{
+		return DefRibbonQueryItemText(uCtrlID, uItem);
+	}
+
+	bool OnRibbonQuerySelectedItem(UINT32 /*uCtrlID*/, UINT32& /*uSel*/)
+	{
+		return false;
+	}
+
+	HBITMAP OnRibbonQueryItemImage(UINT32 uCtrlID, UINT32 uItem)
+	{
+		return DefRibbonQueryItemImage(uCtrlID, uItem);
+	}
+
+	UINT32 OnRibbonQueryItemCommand(UINT32 uCtrlID, UINT32 uItem)
+	{
+		return DefRibbonQueryItemCommand(uCtrlID, uItem);
+	}
+
+	UI_COMMANDTYPE OnRibbonQueryItemCommandType(UINT32 /*uCtrlID*/, UINT32 /*uItem*/)
+	{
+		return UI_COMMANDTYPE_ACTION;
+	}
+
+	LPCWSTR OnRibbonQueryRecentItemName(LPCWSTR sPath)
+	{
+		return ::PathFindFileName(sPath);
+	}
+
+	bool OnRibbonQueryFont(UINT /*nId*/, CHARFORMAT2& /*cf*/)
+	{
+		return false;
+	}
+
+	bool OnRibbonQuerySpinnerValue(UINT /*nCmdID*/, REFPROPERTYKEY /*key*/, LONG* /*pVal*/)
+	{
+		return false;
+	}
+
+	bool OnRibbonQueryFloatSpinnerValue(UINT /*nCmdID*/, REFPROPERTYKEY /*key*/, DOUBLE* /*pVal*/)
+	{
+		return false;
+	}
+
+	COLORREF OnRibbonQueryColor(UINT /*nCmdID*/)
+	{
+		return 0x800080; /*MAGENTA*/
+	}
+
+	LPCWSTR OnRibbonQueryColorLabel(UINT /*nCmdID*/, REFPROPERTYKEY /*key*/)
+	{
+		return NULL;
+	}
+
+	COLORREF* OnRibbonQueryColorArray(UINT /*nCmdID*/, REFPROPERTYKEY /*key*/)
+	{
+		return NULL;
+	}
+
+	LPCWSTR* OnRibbonQueryColorTooltips(UINT /*nCmdID*/, REFPROPERTYKEY /*key*/)
+	{
+		return NULL;
+	}
+
+	bool OnRibbonItemSelected(UINT32 uCtrlID, UI_EXECUTIONVERB verb, UINT32 uItem)
+	{
+		DefCommandExecute(MAKELONG(uCtrlID, verb), uItem);
+		return true;
+	}
+
+	void OnRibbonColorCtrlExecute(UINT32 uCtrlID, UI_EXECUTIONVERB verb, UI_SWATCHCOLORTYPE uType, COLORREF color)
+	{
+		DefRibbonColorCtrlExecute(uCtrlID, verb, uType, color);
+	}
+
+	void OnRibbonFontCtrlExecute(UINT32 uCtrlID, UI_EXECUTIONVERB verb, CHARFORMAT2* pcf)
+	{
+		DefCommandExecute(MAKELONG(uCtrlID, verb), (LPARAM)pcf);
+	}
+
+	void OnRibbonSpinnerCtrlExecute(UINT32 uCtrlID, LONG* pVal)
+	{
+		DefCommandExecute(uCtrlID, *pVal);
+	}
+
+	void OnRibbonSpinnerCtrlExecute(UINT32 uCtrlID, DOUBLE* pVal)
+	{
+		DefCommandExecute(uCtrlID, (LPARAM)pVal);
+	}
+
+	void OnRibbonCommandExecute(UINT32 uCmdID)
+	{
+		DefCommandExecute(uCmdID);
+	}
+
+// Default implementations
+	HBITMAP DefRibbonQueryImage(UINT nCmdID)
+	{
+		return AtlLoadBitmapImage(nCmdID, LR_CREATEDIBSECTION);
+	}
+
+	bool DefRibbonQueryState(UINT nCmdID, REFPROPERTYKEY key)
+	{
+		DWORD dwState = this->UIGetState(nCmdID);
+		bool bRet = false;
+		switch (k_(key))
+		{
+		case k_BooleanValue:
+			bRet = (dwState & CUpdateUIBase::UPDUI_CHECKED) == CUpdateUIBase::UPDUI_CHECKED;
+			break;
+		case k_Enabled:
+			bRet = (dwState & CUpdateUIBase::UPDUI_DISABLED) != CUpdateUIBase::UPDUI_DISABLED;
+			break;
+		default:
+			ATLASSERT(FALSE);
+			break;
+		}
+
+		return bRet;
+	}
+
+	LPCTSTR DefRibbonQueryText(UINT nCmdID, REFPROPERTYKEY key)
+	{
+		static WCHAR sText[RIBBONUI_MAX_TEXT] = {};
+
+		if (k_(key) == k_Label)
+			 return this->UIGetText(nCmdID);
+
+		if (ATL::AtlLoadString(nCmdID, sText, RIBBONUI_MAX_TEXT))
+		{
+			PWCHAR pTitle = wcschr(sText, L'\n');
+			switch (k_(key))
+			{
+			case k_Keytip:
+				if (PWCHAR pAmp = wcschr(sText, L'&'))
+					pTitle = pAmp;
+				if (pTitle != NULL)
+					*(pTitle + 2) = NULL; // fall through
+			case k_TooltipTitle:
+				return pTitle ? ++pTitle : NULL;
+			case k_TooltipDescription:
+			case k_LabelDescription:
+				if (pTitle != NULL)
+					*pTitle = NULL;
+				return sText;
+			}
+		}
+
+		return NULL;
+	}
+
+	LPCWSTR DefRibbonQueryItemText(UINT32 uCtrlID, UINT32 uItem)
+	{
+		return DefRibbonQueryText(uCtrlID + 1 + uItem, UI_PKEY_LabelDescription);
+	}
+
+	HBITMAP DefRibbonQueryItemImage(UINT32 uCtrlID, UINT32 uItem)
+	{
+		return DefRibbonQueryImage(uCtrlID + 1 + uItem);
+	}
+
+	UINT32 DefRibbonQueryItemCommand(UINT32 uCtrlID, UINT32 uItem)
+	{
+		return uCtrlID + 1 + uItem;
+	}
+
+	void DefRibbonColorCtrlExecute(UINT32 uCtrlID, UI_EXECUTIONVERB verb, UI_SWATCHCOLORTYPE uType, COLORREF color)
+	{
+		switch(uType)
+		{
+		case UI_SWATCHCOLORTYPE_RGB:
+			break;
+		case UI_SWATCHCOLORTYPE_AUTOMATIC:
+			color = ::GetSysColor(COLOR_WINDOWTEXT);
+			break;
+		case UI_SWATCHCOLORTYPE_NOCOLOR:
+			color = ::GetSysColor(COLOR_WINDOW);
+			break;
+		default:
+			ATLASSERT(FALSE);
+			break;
+		}
+
+		DefCommandExecute(MAKELONG(uCtrlID, verb), color);
+	}
+
+	void DefCommandExecute(UINT32 uCmd, LPARAM lParam = 0)
+	{
+		static_cast<T*>(this)->PostMessage(WM_COMMAND, uCmd, lParam);
+	}
+
+// Elements setting helpers
+	HRESULT InvalidateCtrl(UINT32 nID)
+	{
+		return IsRibbonUI() ?
+			GetIUIFrameworkPtr()->InvalidateUICommand(nID, UI_INVALIDATIONS_ALLPROPERTIES, NULL) :
+			E_FAIL;
+	}
+
+	HRESULT InvalidateProperty(UINT32 nID, REFPROPERTYKEY key, UI_INVALIDATIONS flags = UI_INVALIDATIONS_PROPERTY)
+	{
+		return IsRibbonUI() ?
+			GetIUIFrameworkPtr()->InvalidateUICommand(nID, flags, &key) :
+			E_FAIL;
+	}
+
+	template <typename V>
+	HRESULT SetProperty(WORD wID, REFPROPERTYKEY key, V val)
+	{
+		if (IsRibbonUI())
+		{
+			PROPVARIANT var;
+			if (SUCCEEDED(RibbonUI::SetPropertyVal(key, val, &var)))
+			{
+				return SetProperty(wID, key, var);
+			}
+			return E_INVALIDARG;
+		}
+		else
+		{
+			return E_FAIL;
+		}
+	}
+
+	template <>
+	HRESULT SetProperty(WORD nID, REFPROPERTYKEY key, PROPVARIANT var)
+	{
+		return IsRibbonUI() ?
+			GetIUIFrameworkPtr()->SetUICommandProperty(nID, key, var) :
+			E_FAIL;
+	}
+
+// Interfaces
+	// IUIApplication
+	STDMETHODIMP OnViewChanged(UINT32, UI_VIEWTYPE, IUnknown*, UI_VIEWVERB verb, INT32)
+	{
+		switch (verb)
+		{			
+		case UI_VIEWVERB_CREATE:
+			m_bRibbonUI = true;
+			if (m_hgRibbonSettings != NULL)
+				RestoreRibbonSettings();
+			break;
+		case UI_VIEWVERB_SIZE:
+			static_cast<T*>(this)->UpdateLayout(FALSE);
+			break;
+		case UI_VIEWVERB_DESTROY:
+			SaveRibbonSettings();
+			m_bRibbonUI = false;
+			break;
+		}
+
+		return S_OK;
+	}
+
+	STDMETHODIMP OnCreateUICommand(UINT32 nCmdID, UI_COMMANDTYPE typeID, IUICommandHandler** ppCommandHandler)
+	{
+		this->UIAddRibbonElement(nCmdID);
+		if (typeID == UI_COMMANDTYPE_CONTEXT)
+			CUpdateUIBase::UIEnable(nCmdID, false);
+		*ppCommandHandler = this;
+		return S_OK;
+	}
+
+	STDMETHODIMP OnDestroyUICommand(UINT32 nCmdID, UI_COMMANDTYPE, IUICommandHandler*)
+	{
+		this->UIRemoveRibbonElement(nCmdID);
+		return S_OK;
+	}
+
+	// IUICommandHandler
+	STDMETHODIMP Execute(UINT nCmdID,
+		UI_EXECUTIONVERB verb, 
+		const PROPERTYKEY* key,
+		const PROPVARIANT* ppropvarValue,
+		IUISimplePropertySet* pCommandExecutionProperties)
+	{
+		T* pT =static_cast<T*>(this);
+		return pT->GetRibbonCtrl(nCmdID).DoExecute(nCmdID, verb, key, ppropvarValue, pCommandExecutionProperties);	
+	}
+
+	STDMETHODIMP UpdateProperty(UINT nCmdID, REFPROPERTYKEY key, 
+	                            const PROPVARIANT* ppropvarCurrentValue, PROPVARIANT* ppropvarNewValue)
+	{
+		T* pT =static_cast<T*>(this);
+		return pT->GetRibbonCtrl(nCmdID).DoUpdateProperty(nCmdID, key, ppropvarCurrentValue, ppropvarNewValue);	
+	}
+
+#ifdef _DEBUG
+	// IUnknown methods (heavyweight)
+	STDMETHODIMP_(ULONG) AddRef()
+	{
+		return InterlockedIncrement(&m_cRef);
+	}
+
+	STDMETHODIMP_(ULONG) Release()
+	{
+		LONG cRef = InterlockedDecrement(&m_cRef);
+		if (cRef == 0) // NoOp for breakpoint
+		{
+			cRef = 0;
+		}
+
+		return cRef;
+	}
+
+	STDMETHODIMP QueryInterface(REFIID iid, void** ppv)
+	{
+		if (ppv == NULL)
+		{
+			return E_POINTER;
+		}
+		else if ((iid == __uuidof(IUnknown)) ||
+		         (iid == __uuidof(IUICommandHandler)) ||
+		         (iid == __uuidof(IUIApplication)))
+		{
+			*ppv = this;
+			AddRef();
+			return S_OK;
+		}
+		else
+		{
+			return E_NOINTERFACE;
+		}
+	}
+
+	LONG m_cRef;
+#else
+	// IUnknown methods (lightweight)
+	STDMETHODIMP QueryInterface(REFIID iid, void** ppv)
+	{
+		if ((iid == __uuidof(IUnknown)) ||
+		    (iid == __uuidof(IUICommandHandler)) ||
+		    (iid == __uuidof(IUIApplication)))
+		{
+			*ppv = this;
+			return S_OK;
+		}
+		return E_NOINTERFACE;
+	}
+	ULONG STDMETHODCALLTYPE AddRef()
+	{
+		return 1;
+	}
+	ULONG STDMETHODCALLTYPE Release()
+	{
+		return 1;
+	}
+#endif
+
+// CRibbonImpl ICtrl implementation
+	virtual HRESULT DoExecute(UINT nCmdID, UI_EXECUTIONVERB verb, 
+	                          const PROPERTYKEY* key, const PROPVARIANT* ppropvarValue,
+	                          IUISimplePropertySet* /*pCommandExecutionProperties*/)
+	{
+		if (key != NULL)
+		{
+			if(k_(*key) != k_BooleanValue)
+			{
+				ATLTRACE2(atlTraceUI, 0, _T("Control ID %d is not handled\n"), nCmdID);
+				return E_NOTIMPL;
+			}
+			BOOL bChecked = FALSE;
+			ATLVERIFY(SUCCEEDED(PropVariantToBoolean(*ppropvarValue, &bChecked)));
+			CUpdateUIBase::UISetCheck(nCmdID, bChecked);
+		}
+
+		ATLASSERT(verb == UI_EXECUTIONVERB_EXECUTE);
+		(void)verb;   // avoid level 4 warning
+
+		static_cast<T*>(this)->OnRibbonCommandExecute(nCmdID);
+		
+		return S_OK;
+	}
+
+	virtual HRESULT DoUpdateProperty(UINT nCmdID, REFPROPERTYKEY key, 
+		const PROPVARIANT* /*ppropvarCurrentValue*/, PROPVARIANT* ppropvarNewValue)
+	{
+		T* pT = static_cast<T*>(this);
+		HRESULT hr = E_NOTIMPL;
+		switch (k_(key))
+		{
+		case k_LargeImage:
+		case k_LargeHighContrastImage:
+		case k_SmallImage:
+		case k_SmallHighContrastImage:
+			if (HBITMAP hbm = pT->OnRibbonQueryImage(nCmdID, key))
+				hr = SetPropertyVal(key, GetImage(hbm, UI_OWNERSHIP_TRANSFER), ppropvarNewValue);
+			break;
+		case k_Label:
+		case k_Keytip:
+		case k_TooltipTitle:
+		case k_TooltipDescription:
+		case k_LabelDescription:
+			if (LPCWSTR sText = pT->OnRibbonQueryText(nCmdID, key))
+				hr = SetPropertyVal(key, sText, ppropvarNewValue);
+			break;
+		case k_BooleanValue:
+		case k_Enabled:
+			hr = SetPropertyVal(key, pT->OnRibbonQueryState(nCmdID, key), ppropvarNewValue);
+			break;
+		case k_ContextAvailable:
+			hr = SetPropertyVal(key, pT->OnRibbonQueryTabAvail(nCmdID), ppropvarNewValue);
+			break;
+		}
+
+		return hr;
+	}
+
+// CRibbonImpl::CRibbonXXXCtrl specialized classes
+	 //CRibbonComboCtrl
+	template <UINT t_ID, size_t t_items, size_t t_categories = 0>
+	class CRibbonComboCtrl : public CollectionCtrlImpl<T, t_ID, ComboCollectionImpl<CRibbonComboCtrl<t_ID, t_items, t_categories>, t_items, t_categories>>
+	{
+	public:
+		CRibbonComboCtrl()
+		{ }
+	};
+
+	// CRibbonItemGalleryCtrl
+	template <UINT t_ID, size_t t_items, size_t t_categories = 0>
+	class CRibbonItemGalleryCtrl : public CollectionCtrlImpl<T, t_ID, ItemCollectionImpl<CRibbonItemGalleryCtrl<t_ID, t_items, t_categories>, t_items, t_categories>>
+	{
+	public:
+		CRibbonItemGalleryCtrl()
+		{ }
+	};
+
+	// CRibbonCommandGalleryCtrl
+	template <UINT t_ID, size_t t_items, size_t t_categories = 0>
+	class CRibbonCommandGalleryCtrl : public CollectionCtrlImpl<T, t_ID, CommandCollectionImpl<CRibbonCommandGalleryCtrl<t_ID, t_items, t_categories>, t_items, t_categories>>
+	{
+	public:
+		CRibbonCommandGalleryCtrl()
+		{ }
+	};
+
+	// CRibbonToolbarGalleryCtrl
+	template <UINT t_ID, UINT t_idTB, size_t t_size>
+	class CRibbonToolbarGalleryCtrl : public ToolbarGalleryCtrlImpl<T, t_ID, t_idTB, t_size>
+	{ };
+
+	// CRibbonSimpleComboCtrl
+	template <UINT t_ID, size_t t_size>
+	class CRibbonSimpleComboCtrl : public SimpleCollectionCtrlImpl<T, t_ID, t_size>
+	{ };
+
+	// CRibbonSimpleGalleryCtrl
+	template <UINT t_ID, size_t t_size, UI_COMMANDTYPE t_CommandType = UI_COMMANDTYPE_ACTION>
+	class CRibbonSimpleGalleryCtrl : public SimpleCollectionCtrlImpl<T, t_ID, t_size, t_CommandType>
+	{ };
+
+	//CRibbonRecentItemsCtrl
+	template <UINT t_ID, class TDocList = CRecentDocumentList>
+	class CRibbonRecentItemsCtrl : public RecentItemsCtrlImpl<T, t_ID, TDocList>
+	{
+	public:
+		CRibbonRecentItemsCtrl()
+		{ }
+	};
+
+	// CRibbonColorCtrl
+	template <UINT t_ID>
+	class CRibbonColorCtrl : public ColorCtrlImpl<T, t_ID>
+	{
+	public:
+		CRibbonColorCtrl()
+		{ }
+	};
+
+	 //CRibbonFontCtrl
+	template <UINT t_ID>
+	class CRibbonFontCtrl : public FontCtrlImpl<T, t_ID>
+	{
+	public:
+		CRibbonFontCtrl()
+		{ }
+	};
+
+	// CRibbonSpinnerCtrl
+	template <UINT t_ID>
+	class CRibbonSpinnerCtrl : public SpinnerCtrlImpl<T, t_ID, LONG>
+	{
+	public:
+		CRibbonSpinnerCtrl()
+		{ }
+	};
+
+	// CRibbonFloatSpinnerCtrl
+	template <UINT t_ID>
+	class CRibbonFloatSpinnerCtrl : public SpinnerCtrlImpl<T, t_ID, DOUBLE>
+	{
+	public:
+		CRibbonFloatSpinnerCtrl()
+		{
+			this->m_Values[4] = 1; // 1 decimal
+		}
+	};
+
+	// CRibbonCommandCtrl
+	template <UINT t_ID>
+	class CRibbonCommandCtrl : public CommandCtrlImpl<T, t_ID>
+	{
+	public:
+		CRibbonCommandCtrl()
+		{ }
+	};
+
+// Control classes access to T instance (re-initialized in constructor)
+	static T* pWndRibbon;
+};
+
+template <class T>
+__declspec(selectany) T* CRibbonImpl<T>::pWndRibbon;
+
+// Control map element
+#pragma warning(push)
+#pragma warning(disable: 4510 610 4512)   // missing default constructor, can't be instatiated, assignment operator could not be generated
+typedef struct
+{
+	UINT uID;
+	ICtrl& ctrl;
+} _ribbonCtrl;
+#pragma warning(pop)
+
+} // namespace RibbonUI
+
+
+///////////////////////////////////////////////////////////////////////////////
+// RibbonUI Control map
+
+// Control map macros
+#define BEGIN_RIBBON_CONTROL_MAP(theClass) \
+	WTL::RibbonUI::ICtrl& GetRibbonCtrl(UINT id) \
+	{ \
+		WTL::RibbonUI::_ribbonCtrl _ctrls[] = \
+		{
+
+#define RIBBON_CONTROL(member) {member.GetID(), static_cast<WTL::RibbonUI::ICtrl&>(member)},
+
+#define END_RIBBON_CONTROL_MAP() \
+		{0, *this} \
+	}; \
+	int i = 0; \
+	for(; i < _countof(_ctrls) - 1; i++) \
+		if (_ctrls[i].uID == id) \
+			break; \
+	return _ctrls[i].ctrl; \
+}
+
+// Control message map macros
+#define RIBBON_GALLERY_CONTROL_HANDLER(id, func) \
+	if((uMsg == WM_COMMAND) && (id == LOWORD(wParam))) \
+	{ \
+		bHandled = TRUE; \
+		lResult = func((UI_EXECUTIONVERB)HIWORD(wParam), LOWORD(wParam), (UINT)lParam, bHandled); \
+		if(bHandled) \
+			return TRUE; \
+	}
+
+#define RIBBON_COMBO_CONTROL_HANDLER(id, func) \
+	RIBBON_GALLERY_CONTROL_HANDLER(id, func)	
+
+#define RIBBON_FONT_CONTROL_HANDLER(id, func) \
+	if((uMsg == WM_COMMAND) && (id == LOWORD(wParam))) \
+	{ \
+		bHandled = TRUE; \
+		lResult = func((UI_EXECUTIONVERB)HIWORD(wParam), LOWORD(wParam), (CHARFORMAT2*)lParam, bHandled); \
+		if(bHandled) \
+			return TRUE; \
+	}
+
+#define RIBBON_COLOR_CONTROL_HANDLER(id, func) \
+	if((uMsg == WM_COMMAND) && (id == LOWORD(wParam))) \
+	{ \
+		bHandled = TRUE; \
+		lResult = func((UI_EXECUTIONVERB)HIWORD(wParam), LOWORD(wParam), (COLORREF)lParam, bHandled); \
+		if(bHandled) \
+			return TRUE; \
+	}
+
+#define RIBBON_SPINNER_CONTROL_HANDLER(id, func) \
+	if((uMsg == WM_COMMAND) && (id == wParam)) \
+	{ \
+		bHandled = TRUE; \
+		lResult = func((WORD)wParam, (LONG)lParam, bHandled); \
+		if(bHandled) \
+			return TRUE; \
+	}
+
+#define RIBBON_FLOATSPINNER_CONTROL_HANDLER(id, func) \
+	if((uMsg == WM_COMMAND) && (id == wParam)) \
+	{ \
+		bHandled = TRUE; \
+		lResult = func((WORD)wParam, (DOUBLE*)lParam, bHandled); \
+		if(bHandled) \
+			return TRUE; \
+	}
+
+// Handler prototypes
+/*
+	LRESULT OnRibbonGalleryCtrl(UI_EXECUTIONVERB verb, WORD wID, UINT uSel, BOOL& bHandled);
+	LRESULT OnRibbonComboCtrl(UI_EXECUTIONVERB verb, WORD wID, UINT uSel, BOOL& bHandled);
+	LRESULT OnRibbonFontCtrl(UI_EXECUTIONVERB verb, WORD wID, CHARFORMAT2* pcf, BOOL& bHandled);
+	LRESULT OnRibbonColorCtrl(UI_EXECUTIONVERB verb, WORD wID, COLORREF color, BOOL& bHandled);
+	LRESULT OnRibbonSpinnerCtrl(WORD wID, LONG lVal, BOOL& bHandled);
+	LRESULT OnRibbonFloatSpinnerCtrl(WORD wID, DOUBLE* pdVal, BOOL& bHandled);
+*/
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Ribbon frame classes
+
+// CRibbonFrameWindowImplBase
+//
+template <class T, class TFrameImpl>
+class ATL_NO_VTABLE CRibbonFrameWindowImplBase : public TFrameImpl, public RibbonUI::CRibbonImpl<T>
+{
+	typedef TFrameImpl baseFrame;
+	bool m_bUseCommandBarBitmaps;
+	bool m_bWin7Fix;
+
+public:
+// Construction
+	CRibbonFrameWindowImplBase(bool bUseCommandBarBitmaps = true) : 
+			m_bUseCommandBarBitmaps(bUseCommandBarBitmaps), m_bWin7Fix(false)
+	{
+		__if_not_exists(T::m_CmdBar)
+		{
+			m_bUseCommandBarBitmaps = false;
+		}
+	}
+
+// Win7 Aero fix helpers
+	void ResetFrame()
+	{
+		const MARGINS margins = { 0, 0, 0, 0 };
+		::DwmExtendFrameIntoClientArea(this->m_hWnd, &margins);
+	}
+
+	INT CalcWin7Fix()
+	{
+		ResetFrame();
+		RECT rc = {};
+		::AdjustWindowRectEx(&rc, T::GetWndStyle(0), this->GetMenu() != NULL, T::GetWndExStyle(0));
+		return -rc.top;
+	}
+
+	bool NeedWin7Fix()
+	{
+		BOOL bComp = FALSE;
+		return m_bWin7Fix && RunTimeHelper::IsWin7() && SUCCEEDED(DwmIsCompositionEnabled(&bComp)) && bComp;
+	}
+
+// Operations
+	bool UseCommandBarBitmaps(bool bUse)
+	{
+		__if_exists(T::m_CmdBar)
+		{
+			return m_bUseCommandBarBitmaps = bUse;
+		}
+		__if_not_exists(T::m_CmdBar)
+		{
+			(void)bUse;   // avoid level 4 warning
+			return false;
+		}
+	}
+
+	bool ShowRibbonUI(bool bShow, INT32 imodes = UI_MAKEAPPMODE(0), LPCWSTR sResName = L"APPLICATION_RIBBON")
+	{
+		if (!RunTimeHelper::IsRibbonUIAvailable())
+			return false;
+
+		ATLASSERT(this->GetIUIFrameworkPtr());
+
+		if (this->IsRibbonUI() == bShow)
+			return bShow;
+
+		bool bVisible = (this->IsWindowVisible() != FALSE);
+		if(bVisible && !bShow)
+			this->SetRedraw(FALSE);
+
+		if (bShow && ::IsWindow(this->m_hWndToolBar))
+		{
+			::ShowWindow(this->m_hWndToolBar, SW_HIDE);
+			UpdateLayout();
+		}
+
+		m_bWin7Fix = !bShow;
+
+		HRESULT hr = bShow ? this->CreateRibbon(sResName) : this->DestroyRibbon();
+
+		m_bWin7Fix = SUCCEEDED(hr) && !bShow;
+
+		if (SUCCEEDED(hr))
+		{
+			if(::IsWindow(this->m_hWndToolBar) && !bShow)
+			{
+				::ShowWindow(this->m_hWndToolBar, SW_SHOWNA);
+				UpdateLayout(); 
+			}
+			else if (bShow)
+			{
+				this->PostMessage(WM_SIZE);
+				this->SetRibbonModes(imodes);
+			}
+		}
+
+		if(bVisible && !bShow)
+		{
+			this->SetRedraw(TRUE);
+			this->RedrawWindow(NULL, NULL, RDW_FRAME | RDW_ERASE | RDW_INVALIDATE | RDW_UPDATENOW | RDW_ALLCHILDREN);
+		}
+
+		return SUCCEEDED(hr) ? bShow : !bShow;
+	}
+
+// Overrideables
+	HBITMAP OnRibbonQueryImage(UINT nCmdID, REFPROPERTYKEY key)
+	{
+		if ((key == UI_PKEY_SmallImage) && m_bUseCommandBarBitmaps)
+		{
+			if (HBITMAP hbm = GetCommandBarBitmap(nCmdID))
+				return (HBITMAP)::CopyImage(hbm, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
+		}
+
+		return this->DefRibbonQueryImage(nCmdID);
+	}
+
+	BEGIN_MSG_MAP(CRibbonFrameWindowImplBase)
+		if (!this->IsRibbonUI() && NeedWin7Fix())
+		{
+			MESSAGE_HANDLER(WM_SIZING, OnSizing)
+			MESSAGE_HANDLER(WM_SIZE, OnSize)
+			MESSAGE_HANDLER(WM_ACTIVATE, OnActivate)
+			MESSAGE_HANDLER(WM_NCCALCSIZE, OnNCCalcSize)
+		}
+		CHAIN_MSG_MAP(CRibbonUpdateUI<T>)
+		CHAIN_MSG_MAP(baseFrame)
+	END_MSG_MAP()
+
+// Message handlers for Win7 Aero
+	LRESULT OnSizing(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
+	{
+		switch (wParam)
+		{		
+		case WMSZ_TOP:
+		case WMSZ_TOPLEFT:
+		case WMSZ_TOPRIGHT:
+			this->SetWindowPos(NULL, (LPRECT)lParam, SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED);
+			break;
+		default:
+			this->DefWindowProc();
+			break;
+		}
+
+		return 1; // handled
+	}
+
+	LRESULT OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
+	{
+		if (wParam != SIZE_MINIMIZED)
+			this->SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+
+		bHandled = FALSE;
+		return 1;
+	}
+
+	LRESULT OnActivate(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
+	{
+		if(wParam != WA_INACTIVE)
+			this->SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
+
+		bHandled = FALSE;
+		return 1;
+	}
+
+	LRESULT OnNCCalcSize(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& /*bHandled*/)
+	{
+		ATLASSERT(!this->IsRibbonUI() && NeedWin7Fix());
+
+		LRESULT lRet = this->DefWindowProc();
+
+		if(wParam)
+		{
+			LPNCCALCSIZE_PARAMS pParams = (LPNCCALCSIZE_PARAMS)lParam;
+			pParams->rgrc[0].top = pParams->rgrc[1].top + CalcWin7Fix();
+		}
+
+		return lRet;
+	}
+
+// Overrides
+	void UpdateLayout(BOOL bResizeBars = TRUE)
+	{
+		RECT rect = {};
+		this->GetClientRect(&rect);
+
+		if (this->IsRibbonUI() && !this->IsRibbonHidden())
+		{
+			rect.top += this->GetRibbonHeight();
+		}
+		else if (!this->IsRibbonUI() && NeedWin7Fix())
+		{
+			ResetFrame();
+		}
+
+		// position bars and offset their dimensions
+		this->UpdateBarsPosition(rect, bResizeBars);
+
+		// resize client window
+		if(this->m_hWndClient != NULL)
+			::SetWindowPos(this->m_hWndClient, NULL, rect.left, rect.top,
+				rect.right - rect.left, rect.bottom - rect.top,
+				SWP_NOZORDER | SWP_NOACTIVATE);
+	}
+
+	// Implementation
+	HBITMAP GetCommandBarBitmap(UINT nCmdID)
+	{
+		__if_exists (T::m_CmdBar)
+		{
+			ATLASSERT(RunTimeHelper::IsVista());
+			T* pT =static_cast<T*>(this);
+			int nIndex = pT->m_CmdBar.m_arrCommand.Find((WORD&)nCmdID);
+			return (nIndex == -1) ? NULL : pT->m_CmdBar.m_arrVistaBitmap[nIndex];
+		}
+		__if_not_exists (T::m_CmdBar)
+		{
+			(void)nCmdID;   // avoid level 4 warning
+			return NULL;
+		}
+	}
+};
+
+// CRibbonFrameWindowImpl
+//
+template <class T, class TBase = ATL::CWindow, class TWinTraits = ATL::CFrameWinTraits>
+class ATL_NO_VTABLE CRibbonFrameWindowImpl : public CRibbonFrameWindowImplBase<T, CFrameWindowImpl<T, TBase, TWinTraits>>
+{ };
+
+// CRibbonMDIFrameWindowImpl
+//
+template <class T, class TBase = CMDIWindow, class TWinTraits = ATL::CFrameWinTraits>
+class ATL_NO_VTABLE CRibbonMDIFrameWindowImpl : public CRibbonFrameWindowImplBase<T, CMDIFrameWindowImpl<T, TBase, TWinTraits>>
+{ };
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CRibbonPersist helper for RibbonUI persistency
+
+class CRibbonPersist
+{
+public:
+	CRibbonPersist(LPCWSTR sAppKey)
+	{
+		ATLASSERT(sAppKey && *sAppKey);
+		m_Key.Create(HKEY_CURRENT_USER, sAppKey);
+		ATLASSERT(m_Key.m_hKey);
+	}
+
+	ATL::CRegKey m_Key;
+
+	LONG Save(bool bRibbonUI, HGLOBAL hgSettings = NULL)
+	{
+		ATL::CRegKey key;
+		const DWORD dwUI = bRibbonUI;
+
+		LONG lRet = key.Create(m_Key, L"Ribbon");
+		if(lRet != ERROR_SUCCESS)
+			return lRet;
+		
+		lRet = key.SetDWORDValue(L"UI", dwUI);
+		if(lRet != ERROR_SUCCESS)
+			return lRet;
+
+		if (hgSettings != NULL)
+		{
+			LPBYTE pVal = (LPBYTE)::GlobalLock(hgSettings);
+			if (pVal != NULL)
+			{
+				lRet = key.SetBinaryValue(L"Settings", pVal, (ULONG)::GlobalSize(hgSettings));
+				::GlobalUnlock(hgSettings);
+			}
+			else
+			{
+				lRet = GetLastError();
+			}
+		}
+
+		return lRet;
+	}
+
+	LONG Restore(bool& bRibbonUI, HGLOBAL& hgSettings)
+	{
+		ATLASSERT(hgSettings == NULL);
+
+		ATL::CRegKey key;
+
+		LONG lRet = key.Open(m_Key, L"Ribbon");
+		if(lRet != ERROR_SUCCESS)
+			return lRet;
+		
+		DWORD dwUI = 0xffff;
+		lRet = key.QueryDWORDValue(L"UI", dwUI);
+		if(lRet == ERROR_SUCCESS)
+			bRibbonUI = dwUI == 1;
+		else
+			return lRet;
+
+		ULONG ulSize = 0;
+		lRet = key.QueryBinaryValue(L"Settings", NULL, &ulSize);
+		if (lRet == ERROR_SUCCESS)
+		{
+			ATLASSERT(ulSize != 0);
+			
+			hgSettings = ::GlobalAlloc(GHND, ulSize);
+			if (hgSettings != NULL)
+			{
+				LPBYTE pData = (LPBYTE)::GlobalLock(hgSettings);
+				if (pData != NULL)
+				{
+					lRet = key.QueryBinaryValue(L"Settings", pData, &ulSize);
+				}
+				else
+				{
+					lRet = GetLastError();
+					::GlobalFree(hgSettings);
+					hgSettings = NULL;
+				}
+			}
+			else
+			{
+				lRet = GetLastError();
+			}
+		}
+		return lRet;
+	}
+
+	LONG Delete()
+	{
+		return m_Key.DeleteSubKey(L"Ribbon");
+	}
+};
+
+} // namespace WTL
+
+#endif // __ATLRIBBON_H__