/////////////////////////////////////////////////////////////////////////////
// TabbedFrame.h - Base template class for supporting a frame
//   window with multiple views that you switch between using
//   a "CustomTabCtrl" (such as CDotNetTabCtrl)
//
// Written by Daniel Bowen (dbowen@es.com)
// Copyright (c) 2002 Daniel Bowen.
//
// Depends on CustomTabCtrl.h written by Bjarke Viksoe (bjarke@viksoe.dk),
//  with the modifications by Daniel Bowen
//
// CCustomTabOwnerImpl -
//   MI class that helps implement the parent of the actual custom tab control window.
//   The class doesn't have a message map itself, and is meant
//   to be inherited from along-side a CWindowImpl derived class.
//   This class handles creation of the tab window as well as
//   adding, removing, switching and renaming tabs based on an HWND.
// CTabbedFrameImpl -
//   Base template to derive your specialized frame window class from to get
//   a frame window with multiple "view" child windows that you
//   switch between using a custom tab control (such as CDotNetTabCtrl).
// CTabbedPopupFrame -
//   Simple class deriving from CTabbedFrameImpl that is suitable
//   for implementing a tabbed "popup frame" tool window, with one or more views.
// CTabbedChildWindow -
//   Simple class deriving from CTabbedFrameImpl that is suitable
//   for implementing a tabbed child window, with one or more views.
//
//   
//
// This code may be used in compiled form in any way you desire. This
// file may be redistributed by any means PROVIDING it is 
// not sold for profit without the authors written consent, and 
// providing that this notice and the authors name is included.
//
// This file is provided "as is" with no expressed or implied warranty.
// The author accepts no liability if it causes any damage to you or your
// computer whatsoever.
//
// If you find bugs, have suggestions for improvements, etc.,
// please contact the author.
//
// History (Date/Author/Description):
// ----------------------------------
//
// 2002/09/25: Daniel Bowen
// - CTabbedFrameImpl -
//   * Expose "SetTabStyles" and "GetTabStyles" so that you can change
//     the tab related styles to something different than the default
// - CTabbedPopupFrame -
//   * Expose "SetCloseCommand" and "GetCloseCommand" so that
//     instead of destroying the window when the close button
//     on the popup frame is pushed, a command ID of your choice
//     is sent to the parent (such as a menu ID that corresponds
//     to toggling the visibility of the popup frame)
//
// 2002/06/26: Daniel Bowen
// - New "CTabbedChildWindow" that derives from CTabbedFrameImpl.
//   You can use this class when you want a child window to
//   use a tab control to switch between multiple views
// - Provide "PreTranslateMessage" function in CTabbedPopupFrame
//   (and the new CTabbedChildWindow)
// - CCustomTabOwnerImpl -
//   * Rename "GetTabs" method to "GetTabCtrl"
//   * Rename member "m_tabs" to "m_TabCtrl"
//   * Rename template argument "TTab" to "TTabCtrl"
//   * Rename "ShowTabs" and "HideTabs" overrideables to "OnAddFirstTab" and "OnRemoveLastTab",
//     and change the place that calls these to live up to those new names
//   * Remove GetCurSel (just call GetTabCtrl().GetCurSel() instead)
//   * DisplayTab - 
//     + Add new parameter that says whether to use the window's icon.
//       If TRUE, the icon is requested first by sending the window WM_GETICON
//       looking for the "small" icon, then asking the window class for a small icon.
//       If no small icon is found, the same procedure is used to look for the
//       "big" icon.
//     + Call "SetCurSel" even if the tab to display has the same index
//       as the current selection
//     + Call "OnAddFirstTab" (which was "ShowTabs") only when the count
//       of tabs goes from 0 to 1.
//  
// 2002/06/12: Daniel Bowen
// - Publish codeproject article.  For history prior
//   to the release of the article, please see the article
//   and the section "Note to previous users"

#ifndef __WTL_TABBED_FRAME_H__
#define __WTL_TABBED_FRAME_H__

#pragma once

#ifndef __cplusplus
	#error TabbedFrame.h requires C++ compilation
#endif

#ifndef __ATLAPP_H__
	#error TabbedFrame.h requires atlapp.h to be included first
#endif

#ifndef __ATLWIN_H__
	#error TabbedFrame.h requires atlwin.h to be included first
#endif

#ifndef __ATLFRAME_H__
	#error TabbedFrame.h requires atlframe.h to be included first
#endif

#ifndef __CUSTOMTABCTRL_H__
	#error TabbedFrame.h requires CustomTabCtrl.h to be included first
#endif


/////////////////////////////////////////////////////////////////////////////
//
// CCustomTabOwnerImpl
//  an MI template to help implement the owner window that uses CustomTabCtrl
//  to switch between windows / views
//
/////////////////////////////////////////////////////////////////////////////

template <class T, class TTabCtrl>
class CCustomTabOwnerImpl
{
// Member variables
protected:
	TTabCtrl m_TabCtrl;
	WTL::CImageList m_ImageList;
	int m_cxImage, m_cyImage;
	int m_nTabAreaHeight;

// Constructors
public:
	CCustomTabOwnerImpl() :
		m_cxImage(16),
		m_cyImage(16),
		m_nTabAreaHeight(24)
	{
	}

// Overrideables
public:

	void OnAddFirstTab()
	{
	}

	void OnRemoveLastTab()
	{
	}

	void SetTabAreaHeight(int nNewTabAreaHeight)
	{
		if(m_nTabAreaHeight != nNewTabAreaHeight)
		{
			m_nTabAreaHeight = nForceTabAreaHeight;

			/*
			T* pT = static_cast<T*>(this);
			pT->UpdateLayout();
			Invalidate();
			*/
		}
	}

	// A derived class might not need to override this although they can.
	// (but they will probably need to specialize SetTabAreaHeight)
	void CalcTabAreaHeight(void)
	{
		// Dynamically figure out a reasonable tab area height
		// based on the tab's font metrics

		const int nNominalHeight = 24;
		const int nNominalFontLogicalUnits = 11;	// 8 point Tahoma with 96 DPI

		// Initialize nFontLogicalUnits to the typical case
		// appropriate for CDotNetTabCtrl
		LOGFONT lfIcon = { 0 };
		::SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lfIcon), &lfIcon, 0);
		int nFontLogicalUnits = -lfIcon.lfHeight;

		// Use the actual font of the tab control
		TTabCtrl& TabCtrl = this->GetTabCtrl();
		if(TabCtrl.IsWindow())
		{
			HFONT hFont = TabCtrl.GetFont();
			if(hFont != NULL)
			{
				CDC dc = TabCtrl.GetDC();
				CFontHandle hFontOld = dc.SelectFont(hFont);
				TEXTMETRIC tm = {0};
				dc.GetTextMetrics(&tm);
				nFontLogicalUnits = tm.tmAscent;
				dc.SelectFont(hFontOld);
			}
		}

		int nNewTabAreaHeight = nNominalHeight + ( ::MulDiv(nNominalHeight, nFontLogicalUnits, nNominalFontLogicalUnits) - nNominalHeight ) / 2;

		T* pT = static_cast<T*>(this);
		pT->SetTabAreaHeight(nNewTabAreaHeight);
	}

// Methods
public:
	TTabCtrl& GetTabCtrl(void)
	{
		return m_TabCtrl;
	}

	int GetTabAreaHeight(void) const
	{
		return m_nTabAreaHeight;
	}

	void CreateTabWindow(HWND hWndTabParent, RECT rcTab, DWORD dwOtherStyles = CTCS_TOOLTIPS)
	{
		if(m_TabCtrl.IsWindow())
		{
			m_TabCtrl.DestroyWindow();
		}

		BOOL bCreate = FALSE;
		bCreate = m_ImageList.Create(m_cxImage, m_cyImage, ILC_COLOR32 | ILC_MASK, 4, 4);
		if(bCreate)
		{
			m_TabCtrl.SetImageList(m_ImageList);
		}

		DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | dwOtherStyles;

		m_TabCtrl.Create(hWndTabParent, rcTab, NULL, dwStyle);

		T* pT = static_cast<T*>(this);
		pT->CalcTabAreaHeight();
	}

	BOOL DestroyTabWindow()
	{
		return m_ImageList.Destroy();
	}

	// AddBitmap (with a couple of overloaded versions)
	int AddBitmap(HBITMAP hBitmap, HBITMAP hBitmapMask = NULL)
	{
		return m_ImageList.Add(hBitmap, hBitmapMask);
	}

	int AddBitmap(HBITMAP hBitmap, COLORREF crMask)
	{
		return m_ImageList.Add(hBitmap, crMask);
	}

	int AddBitmap(_U_STRINGorID bitmap, COLORREF crMask, HMODULE hModule = _Module.GetResourceInstance())
	{
		HBITMAP hBitmap = (HBITMAP)::LoadImage(
			hModule,
			bitmap.m_lpstr,
			IMAGE_BITMAP, 0, 0, LR_SHARED);
		return hBitmap ? m_ImageList.Add(hBitmap, crMask) : -1;
	}

	int AddBitmap(_U_STRINGorID bitmap, HBITMAP hBitmapMask = NULL, HMODULE hModule = _Module.GetResourceInstance())
	{
		HBITMAP hBitmap = (HBITMAP)::LoadImage(
			hModule,
			bitmap.m_lpstr,
			IMAGE_BITMAP, 0, 0, LR_SHARED);
		return hBitmap ? m_ImageList.Add(hBitmap, hBitmapMask) : -1;
	}

	// AddIcon (with a couple of overloaded versions)
	int AddIcon(HICON hIcon)
	{
		return m_ImageList.AddIcon(hIcon);
	}

	int AddIcon(_U_STRINGorID icon, HMODULE hModule = _Module.GetResourceInstance())
	{
		HICON hIcon = (HICON)::LoadImage(
			hModule,
			icon.m_lpstr,
			IMAGE_ICON, m_cxImage, m_cyImage, LR_SHARED);
		return hIcon ? m_ImageList.AddIcon(hIcon) : -1;
	}

	// AddTabWithBitmap (with a couple of overloaded versions)
	int AddTabWithBitmap(HWND hWnd, LPCTSTR sTabText, HBITMAP hBitmap, HBITMAP hBitmapMask = NULL)
	{
		if(hWnd == NULL)
		{
			return -1;
		}

		int nImageIndex = this->AddBitmap(hBitmap, hBitmapMask);

		return this->AddTab(hWnd, sTabText, nImageIndex);
	}

	int AddTabWithBitmap(HWND hWnd, LPCTSTR sTabText, HBITMAP hBitmap, COLORREF crMask)
	{
		if(hWnd == NULL)
		{
			return -1;
		}

		int nImageIndex = this->AddBitmap(hBitmap, crMask);

		return this->AddTab(hWnd, sTabText, nImageIndex);
	}

	int AddTabWithBitmap(HWND hWnd, LPCTSTR sTabText, _U_STRINGorID bitmap, HBITMAP hBitmapMask = NULL, HMODULE hModule = _Module.GetResourceInstance())
	{
		if(hWnd == NULL)
		{
			return -1;
		}

		int nImageIndex = this->AddBitmap(bitmap, hBitmapMask, hModule);

		return this->AddTab(hWnd, sTabText, nImageIndex);
	}

	int AddTabWithBitmap(HWND hWnd, LPCTSTR sTabText, _U_STRINGorID bitmap, COLORREF crMask, HMODULE hModule = _Module.GetResourceInstance())
	{
		if(hWnd == NULL)
		{
			return -1;
		}

		int nImageIndex = this->AddBitmap(bitmap, crMask, hModule);

		return this->AddTab(hWnd, sTabText, nImageIndex);
	}

	// AddTabWithIcon (with a couple of overloaded versions)
	int AddTabWithIcon(HWND hWnd, LPCTSTR sTabText, HICON hIcon)
	{
		if(hWnd == NULL)
		{
			return -1;
		}

		int nImageIndex = this->AddIcon(hIcon);

		return this->AddTab(hWnd, sTabText, nImageIndex);
	}

	int AddTabWithIcon(HWND hWnd, LPCTSTR sTabText, _U_STRINGorID icon, HMODULE hModule = _Module.GetResourceInstance())
	{
		if(hWnd == NULL)
		{
			return -1;
		}

		int nImageIndex = this->AddIcon(icon, hModule);

		return this->AddTab(hWnd, sTabText, nImageIndex);
	}

	// AddTab - either referencing an image in the image list, or no image used
	int AddTab(HWND hWnd, LPCTSTR sTabText, int nImageIndex = -1)
	{
		if(hWnd == NULL)
		{
			return -1;
		}

		TTabCtrl::TItem* pItem = m_TabCtrl.CreateNewItem();
		if(pItem)
		{
			pItem->SetText(sTabText);
			pItem->SetImageIndex(nImageIndex);
			// NOTE: You must use a tab item class derived off of CCustomTabCtrl
			//  that tracks a view HWND, such as CTabViewTabItem
			pItem->SetTabView(hWnd);

			// The tab control takes ownership of the new item
			return m_TabCtrl.InsertItem(m_TabCtrl.GetItemCount(), pItem);
		}

		return -1;
	}

	int DisplayTab(HWND hWnd, BOOL bAddIfNotFound = TRUE, BOOL bUseIcon = FALSE)
	{
		int nTab = -1;
		if(hWnd)
		{
			size_t nOldCount = m_TabCtrl.GetItemCount();

			TTabCtrl::TItem tcItem;
			tcItem.SetTabView(hWnd);

			nTab = m_TabCtrl.FindItem(&tcItem, TTabCtrl::TItem::eCustomTabItem_TabView);
			if((bAddIfNotFound == TRUE) && (nTab < 0))
			{
				// The corresponding tab doesn't exist yet. Create it.

				LPTSTR sWindowText = NULL;
				size_t cchWindowText = ::GetWindowTextLength(hWnd);
				if(cchWindowText > 0)
				{
					sWindowText = new TCHAR[cchWindowText + 1];
					if(sWindowText != NULL)
					{
						::GetWindowText(hWnd, sWindowText, cchWindowText+1);

						HICON hIcon = NULL;
						if(bUseIcon)
						{
							if(hIcon == NULL)
							{
								hIcon = (HICON) ::SendMessage(hWnd, WM_GETICON, ICON_SMALL, 0);
							}
							if(hIcon == NULL)
							{
								hIcon = (HICON) ::GetClassLong(hWnd, GCL_HICONSM);
							}
							if(hIcon == NULL)
							{
								hIcon = (HICON) ::SendMessage(hWnd, WM_GETICON, ICON_BIG, 0);
							}
							if(hIcon == NULL)
							{
								hIcon = (HICON) ::GetClassLong(hWnd, GCL_HICON);
							}
						}

						if(hIcon == NULL)
						{
							nTab = AddTab(hWnd, sWindowText);
						}
						else
						{
							nTab = AddTabWithIcon(hWnd, sWindowText, hIcon);
						}

						delete [] sWindowText;
					}
				}

				if(nTab < 0)
				{
					// We had trouble getting the window text
					// TODO: What should we put for the text and/or icon
					//  in this case?
					ATLASSERT(0 && "Adding a tab where no name was provided");
					nTab = AddTab(hWnd, _T("Untitled"));
				}
			}

			if(nTab >= 0)
			{
				m_TabCtrl.SetCurSel(nTab);

				if((nOldCount == 0) && (m_TabCtrl.GetItemCount() == 1))
				{
					T* pT = static_cast<T*>(this);
					pT->OnAddFirstTab();
				}
			}

		}

		return nTab;
	}

	BOOL RemoveTab(HWND hWnd)
	{
		BOOL bSuccess = FALSE;

		TTabCtrl::TItem tcItem;
		tcItem.SetTabView(hWnd);

		int nTab = m_TabCtrl.FindItem(&tcItem, TTabCtrl::TItem::eCustomTabItem_TabView);
		if(nTab >= 0)
		{
			bSuccess = m_TabCtrl.DeleteItem(nTab);

			if(m_TabCtrl.GetItemCount() < 1)
			{
				T* pT = static_cast<T*>(this);
				pT->OnRemoveLastTab();
			}
		}

		return bSuccess;
	}

	BOOL UpdateTabText(HWND hWnd, LPCTSTR sText = NULL)
	{
		BOOL bSuccess = FALSE;

		TTabCtrl::TItem tcItem;
		tcItem.SetTabView(hWnd);

		int nTab = m_TabCtrl.FindItem(&tcItem, TTabCtrl::TItem::eCustomTabItem_TabView);
		if(nTab >= 0)
		{
			TTabCtrl::TItem* pItem = m_TabCtrl.GetItem(nTab);
			CString sCurrentTabText = pItem->GetText();

			if(sText != NULL)
			{
				if(sCurrentTabText != sText)
				{
					bSuccess = pItem->SetText(sText);
					m_TabCtrl.UpdateLayout();
					m_TabCtrl.Invalidate();
				}
			}
			else
			{
				LPTSTR sWindowText = NULL;
				size_t cchWindowText = ::GetWindowTextLength(hWnd);
				if(cchWindowText > 0)
				{
					sWindowText = new TCHAR[cchWindowText + 1];
					if(sWindowText != NULL)
					{
						::GetWindowText(hWnd, sWindowText, cchWindowText+1);

						if(sWindowText != NULL &&
							sCurrentTabText != sWindowText)
						{
							bSuccess = pItem->SetText(sWindowText);
							m_TabCtrl.UpdateLayout();
							m_TabCtrl.Invalidate();
						}

						delete [] sWindowText;
					}
				}
			}
		}

		return bSuccess;
	}

	BOOL UpdateTabImage(HWND hWnd, int nImageIndex = -1)
	{
		BOOL bSuccess = FALSE;

		TTabCtrl::TItem tcItem;
		tcItem.SetTabView(hWnd);

		int nTab = m_TabCtrl.FindItem(&tcItem, TTabCtrl::TItem::eCustomTabItem_TabView);
		if(nTab >= 0)
		{
			TTabCtrl::TItem* pItem = m_TabCtrl.GetItem(nTab);
			int nCurrentImageIndex = pItem->GetImageIndex();
			if(nCurrentImageIndex != nImageIndex)
			{
				bSuccess = pItem->SetImageIndex(nImageIndex);
				m_TabCtrl.UpdateLayout();
				m_TabCtrl.Invalidate();
			}
		}

		return bSuccess;
	}

	BOOL UpdateTabToolTip(HWND hWnd, LPCTSTR sToolTip = NULL)
	{
		BOOL bSuccess = FALSE;

		TTabCtrl::TItem tcItem;
		tcItem.SetTabView(hWnd);

		int nTab = m_TabCtrl.FindItem(&tcItem, TTabCtrl::TItem::eCustomTabItem_TabView);
		if(nTab >= 0)
		{
			TTabCtrl::TItem* pItem = m_TabCtrl.GetItem(nTab);
			CString sCurrentToolTip = pItem->GetToolTip();
			if(sCurrentToolTip != sToolTip)
			{
				bSuccess = pItem->SetToolTip(sToolTip);
			}
		}

		return bSuccess;
	}
};

/////////////////////////////////////////////////////////////////////////////
//
// CTabbedFrameImpl
//
/////////////////////////////////////////////////////////////////////////////

template <
	class T,
	class TTabCtrl = CDotNetTabCtrl<CTabViewTabItem>,
	class TBase = CFrameWindowImpl<T, CWindow, CFrameWinTraits> >
class CTabbedFrameImpl :
	public TBase,
	public CCustomTabOwnerImpl< CTabbedFrameImpl<T, TTabCtrl, TBase>, TTabCtrl>
{
protected:
	typedef CTabbedFrameImpl<T, TTabCtrl, TBase> thisClass;
	typedef TBase baseClass;
	typedef CCustomTabOwnerImpl< CTabbedFrameImpl<T, TTabCtrl, TBase>, TTabCtrl> customTabOwnerClass;

// Member variables
protected:
	bool m_bReflectNotifications;
	DWORD m_nTabStyles;
	HWND m_hWndActive;

// Constructors
public:
	CTabbedFrameImpl(bool bReflectNotifications = false) :
		m_bReflectNotifications(bReflectNotifications),
		m_nTabStyles(CTCS_BOTTOM | CTCS_TOOLTIPS),
		m_hWndActive(NULL)
	{
	}

// Methods
public:
	void SetReflectNotifications(bool bReflectNotifications = true)
	{
		m_bReflectNotifications = bReflectNotifications;
	}

	bool GetReflectNotifications(void) const
	{
		return m_bReflectNotifications;
	}

	void SetTabStyles(DWORD nTabStyles)
	{
		m_nTabStyles = nTabStyles;
	}

	bool GetTabStyles(void) const
	{
		return m_nTabStyles;
	}

	virtual void OnFinalMessage(HWND /*hWnd*/)
	{
		// TODO: Have support both for "new"ing an
		//  instance of this class, or having
		//  a member variable of this class.
		//  Currently, we don't support deleting our
		//  instance because someone created us with "new"
		//delete this;
	}

// Message Handling
public:
	DECLARE_FRAME_WND_CLASS(_T("TabbedFrame"), 0)

	BEGIN_MSG_MAP(thisClass)
		MESSAGE_HANDLER(WM_CREATE, OnCreate)
		MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
		MESSAGE_HANDLER(WM_SETTINGCHANGE, OnSettingChange)
		MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
		MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus)
		MESSAGE_HANDLER(WM_FORWARDMSG, OnForwardMsg)

		NOTIFY_CODE_HANDLER(CTCN_DELETEITEM, OnDeleteItem)
		NOTIFY_CODE_HANDLER(CTCN_SELCHANGING, OnSelChanging)
		NOTIFY_CODE_HANDLER(CTCN_SELCHANGE, OnSelChange)

		CHAIN_MSG_MAP(baseClass)

		// If there are key messages that haven't been handled yet,
		// pass those along to the active child window
		if(uMsg >= WM_KEYFIRST && uMsg <= WM_KEYLAST)
		{
			if(m_hWndActive != NULL && ::IsWindow(m_hWndActive))
			{
				lResult = ::SendMessage(m_hWndActive, uMsg, wParam, lParam);

				return TRUE;
			}
		}

		if(m_bReflectNotifications)
		{
			REFLECT_NOTIFICATIONS()
		}
	END_MSG_MAP()

	LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		// "baseClass::OnCreate()"
		LRESULT lRet = DefWindowProc(uMsg, wParam, lParam);
		bHandled = TRUE;

		CreateTabWindow(m_hWnd, rcDefault, m_nTabStyles);

		return 0;
	}

	LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		DestroyTabWindow();

		// Say that we didn't handle it so that anyone else
		//  interested gets to handle the message
		bHandled = FALSE;
		return 0;
	}

	LRESULT OnSettingChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		// Be sure tab gets message before we recalculate the tab area height,
		//  so that it can adjust its font metrics first.
		// NOTE: This causes the tab to get the WM_SETTINGCHANGE message twice,
		//  but that's OK.
		m_TabCtrl.SendMessage(uMsg, wParam, lParam);

		CalcTabAreaHeight();

		bHandled = FALSE;
		return 0;
	}

	LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
	{
		// Let the active view and the tabs do all the drawing
		// as flicker-free as possible.
		return 1;
	}

	LRESULT OnSetFocus(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		// NOTE: ::IsWindowVisible(m_hWndActive) will be false if
		//  the frame is maximized.  So just use "IsWindow" instead.
		if(m_hWndActive != NULL && ::IsWindow(m_hWndActive))
		{
			::SetFocus(m_hWndActive);
		}

		bHandled = FALSE;
		return 1;
	}

	LRESULT OnForwardMsg(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
	{
		//LPMSG pMsg = (LPMSG)lParam;
		//
		//if(PreTranslateMessage(pMsg))
		//	return TRUE;
		//
		//return m_view.PreTranslateMessage(pMsg);

		return ::SendMessage(m_hWndActive, WM_FORWARDMSG, 0, lParam);
	}

	LRESULT OnDeleteItem(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& bHandled)
	{
		bHandled = FALSE;
		return 0;
	}

	LRESULT OnSelChanging(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& bHandled)
	{
		bHandled = FALSE;
		return 0;
	}

	LRESULT OnSelChange(int /*idCtrl*/, LPNMHDR /*pnmh*/, BOOL& bHandled)
	{
		int nNewTab = m_TabCtrl.GetCurSel();

		if(nNewTab >= 0)
		{
			TTabCtrl::TItem* pItem = m_TabCtrl.GetItem(nNewTab);
			if(pItem->UsingTabView())
			{
				HWND hWndNew = pItem->GetTabView();
				HWND hWndOld = m_hWndActive;
				if( hWndNew != hWndOld )
				{
					m_hWndActive = hWndNew;

					//UpdateLayout is going to essentially do a
					//  "ShowWindow(hWndNew, SW_SHOW)" for us
					// (Call the most derived class's version of UpdateLayout)
					T* pT = static_cast<T*>(this);
					pT->UpdateLayout();

					if(hWndOld)
					{
						::ShowWindow(hWndOld, SW_HIDE);
					}

					::SetFocus(hWndNew);
				}
			}
		}

		bHandled = FALSE;
		return 0;
	}

// Overrides from CCustomTabOwnerImpl
public:

	void SetTabAreaHeight(int nNewTabAreaHeight)
	{
		if(m_nTabAreaHeight != nNewTabAreaHeight)
		{
			m_nTabAreaHeight = nNewTabAreaHeight;

			T* pT = static_cast<T*>(this);
			pT->UpdateLayout();
			Invalidate();
		}
	}

// Overrides from TBase
public:

	void UpdateLayout(BOOL bResizeBars = TRUE)
	{
		RECT rect;
		GetClientRect(&rect);

		// position bars and offset their dimensions
		T* pT = static_cast<T*>(this);
		pT->UpdateBarsPosition(rect, bResizeBars);

		/*
		// resize client window
		if(m_hWndClient != NULL)
			::SetWindowPos(m_hWndClient, NULL, rect.left, rect.top,
				rect.right - rect.left, rect.bottom - rect.top,
				SWP_NOZORDER | SWP_NOACTIVATE);
		*/

		int nWindowPosCount=0;
		if(m_TabCtrl) nWindowPosCount++;
		if(m_hWndActive) nWindowPosCount++;

		if(nWindowPosCount > 0)
		{
			HDWP hdwp = BeginDeferWindowPos(nWindowPosCount);
			DWORD dwStyle = (DWORD)m_TabCtrl.GetWindowLong(GWL_STYLE);
			if(CTCS_BOTTOM == (dwStyle & CTCS_BOTTOM))
			{
				if(m_TabCtrl)
				{
					::DeferWindowPos(
						hdwp,
						m_TabCtrl,
						NULL,
						rect.left, rect.bottom - m_nTabAreaHeight,
						rect.right - rect.left, m_nTabAreaHeight,
						SWP_NOZORDER | SWP_NOACTIVATE);
				}
				if(m_hWndActive)
				{
					::DeferWindowPos(
						hdwp,
						m_hWndActive,
						NULL,
						rect.left, rect.top,
						rect.right - rect.left, (rect.bottom-m_nTabAreaHeight) - rect.top,
						SWP_NOZORDER | SWP_SHOWWINDOW);
				}
			}
			else
			{
				if(m_TabCtrl)
				{
					::DeferWindowPos(
						hdwp,
						m_TabCtrl,
						NULL,
						rect.left, rect.top,
						rect.right-rect.left, m_nTabAreaHeight,
						SWP_NOZORDER | SWP_NOACTIVATE);
				}
				if(m_hWndActive)
				{
					::DeferWindowPos(
						hdwp,
						m_hWndActive,
						NULL,
						rect.left, rect.top + m_nTabAreaHeight,
						rect.right - rect.left,
						rect.bottom - (rect.top+m_nTabAreaHeight),
						SWP_NOZORDER | SWP_SHOWWINDOW);
				}
			}
			EndDeferWindowPos(hdwp);
		}

		m_TabCtrl.UpdateLayout();
	}
};

/////////////////////////////////////////////////////////////////////////////
//
// CTabbedPopupFrame
//
/////////////////////////////////////////////////////////////////////////////

typedef CWinTraits<WS_POPUP | WS_CAPTION | WS_VISIBLE | WS_SYSMENU | WS_THICKFRAME, WS_EX_TOOLWINDOW> TabbedPopupFrameWinTraits;

template <class TTabCtrl = CDotNetTabCtrl<CTabViewTabItem> >
class CTabbedPopupFrame :
	public CTabbedFrameImpl<CTabbedPopupFrame, TTabCtrl, CFrameWindowImpl<CTabbedPopupFrame, CWindow, TabbedPopupFrameWinTraits> >
{
protected:
	typedef CTabbedPopupFrame<TTabCtrl> thisClass;
	typedef CTabbedFrameImpl<CTabbedPopupFrame, TTabCtrl, CFrameWindowImpl<CTabbedPopupFrame, CWindow, TabbedPopupFrameWinTraits> > baseClass;

// Members:
protected:
	// NOTE: If the "Close Command" is 0, than we really
	//  just let the default frame handling of "closing"
	//  happen, otherwise, we send the specified command to the parent
	WORD m_nCloseCommand;

// Constructors
public:
	CTabbedPopupFrame(bool bReflectNotifications = false) :
		baseClass(bReflectNotifications),
		m_nCloseCommand(0U)
	{
	}

// Accessors
public:
	WORD GetCloseCommand(void) const
	{
		return m_nCloseCommand;
	}

	void SetCloseCommand(WORD nCloseCommand)
	{
		m_nCloseCommand = nCloseCommand;
	}

// Message Handling
public:
	DECLARE_FRAME_WND_CLASS(_T("TabbedPopupFrame"), 0)

	BOOL PreTranslateMessage(MSG* pMsg)
	{
		if(baseClass::PreTranslateMessage(pMsg))
			return TRUE;

		//return m_view.PreTranslateMessage(pMsg);

		HWND hWndFocus = ::GetFocus();
		if(m_hWndActive != NULL && ::IsWindow(m_hWndActive) &&
			(m_hWndActive == hWndFocus || ::IsChild(m_hWndActive, hWndFocus)))
		{
			//active.PreTranslateMessage(pMsg);
			if(::SendMessage(m_hWndActive, WM_FORWARDMSG, 0, (LPARAM)pMsg))
			{
				return TRUE;
			}
		}

		return FALSE;
	}

	BEGIN_MSG_MAP(thisClass)
		if(m_nCloseCommand != 0)
		{
			if(uMsg == WM_SYSCOMMAND && wParam == SC_CLOSE)
			{
				bHandled = TRUE;
				lResult = ::SendMessage(this->GetParent(), WM_COMMAND, MAKEWPARAM(m_nCloseCommand, 0), 0);

				return TRUE;
			}
		}
		CHAIN_MSG_MAP(baseClass)
	END_MSG_MAP()
};

/////////////////////////////////////////////////////////////////////////////
//
// CTabbedChildWindow
//
/////////////////////////////////////////////////////////////////////////////

// We need CTabbedChildWindowBase because of how CTabbedFrameImpl is currently implemented -
// It's expecting that the "base" class to usually be derived from CFrameWindowImpl.
// We'll have this special class for CTabbedChildWindow to
// inherit from instead of CWindowImpl, so that we provide
// the couple of extra things that CTabbedFrameImpl would
// like to be able to depend on (currently - a message map that
// at least handles WM_SIZE and overrideable methods
// "UpdateLayout" and "UpdateBarsPosition")

typedef CWinTraits<WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, WS_EX_CLIENTEDGE> TabbedChildWindowWinTraits;

template <class T, class TBase = CWindow, class TWinTraits = TabbedChildWindowWinTraits>
class ATL_NO_VTABLE CTabbedChildWindowBase : public CWindowImpl< T, TBase, TWinTraits >
{
	typedef CTabbedChildWindowBase< T, TBase, TWinTraits >	thisClass;
	BEGIN_MSG_MAP(thisClass)
		MESSAGE_HANDLER(WM_SIZE, OnSize)
	END_MSG_MAP()

	LRESULT OnSize(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
	{
		if(wParam != SIZE_MINIMIZED)
		{
			T* pT = static_cast<T*>(this);
			pT->UpdateLayout();
		}
		bHandled = FALSE;
		return 1;
	}

// Overrideables
public:
	void UpdateLayout(BOOL bResizeBars = TRUE)
	{
	}

	void UpdateBarsPosition(RECT& rect, BOOL bResizeBars = TRUE)
	{
	}
};

template <class TTabCtrl = CDotNetTabCtrl<CTabViewTabItem> >
class CTabbedChildWindow :
public CTabbedFrameImpl<CTabbedChildWindow, TTabCtrl, CTabbedChildWindowBase<CTabbedChildWindow, CWindow, TabbedChildWindowWinTraits> >
{
protected:
	typedef CTabbedChildWindow<TTabCtrl> thisClass;
	typedef CTabbedFrameImpl<CTabbedChildWindow, TTabCtrl, CTabbedChildWindowBase<CTabbedChildWindow, CWindow, TabbedChildWindowWinTraits> > baseClass;

// Constructors
public:
	CTabbedChildWindow(bool bReflectNotifications = false) :
		baseClass(bReflectNotifications)
	{
	}

// Message Handling
public:
	DECLARE_WND_CLASS(_T("TabbedChildWindow"))

	BOOL PreTranslateMessage(MSG* pMsg)
	{
		//if(baseClass::PreTranslateMessage(pMsg))
		//	return TRUE;

		//return m_view.PreTranslateMessage(pMsg);

		HWND hWndFocus = ::GetFocus();
		if(m_hWndActive != NULL && ::IsWindow(m_hWndActive) &&
			(m_hWndActive == hWndFocus || ::IsChild(m_hWndActive, hWndFocus)))
		{
			//active.PreTranslateMessage(pMsg);
			if(::SendMessage(m_hWndActive, WM_FORWARDMSG, 0, (LPARAM)pMsg))
			{
				return TRUE;
			}
		}

		return FALSE;
	}


	BEGIN_MSG_MAP(thisClass)
		CHAIN_MSG_MAP(baseClass)
	END_MSG_MAP()
};

#endif // __WTL_TABBED_FRAME_H__
