2025-11-27 16:46:48 +09:00

777 lines
17 KiB
C++

// ==========================================================================
// Class Implementation : COXOptionTreeCtrl
// ==========================================================================
// Header file : OXOptionTreeCtrl.h
// Version: 9.3
// This software along with its related components, documentation and files ("The Libraries")
// is © 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is
// governed by a software license agreement ("Agreement"). Copies of the Agreement are
// available at The Code Project (www.codeproject.com), as part of the package you downloaded
// to obtain this file, or directly from our office. For a copy of the license governing
// this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900.
// //////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "OXOptionTreeCtrl.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#ifndef TVM_SETBKCOLOR
#define TVM_SETBKCOLOR (TV_FIRST + 29)
#endif
/////////////////////////////////////////////////////////////////////////////
// COXOptionTreeCtrl
COXOptionTreeCtrl::COXOptionTreeCtrl() :
m_bUseHighContrast(FALSE)
{
}
COXOptionTreeCtrl::~COXOptionTreeCtrl()
{
ASSERT(m_mapItems.GetCount()==0);
ASSERT(m_mapReadOnlyItems.GetCount()==0);
}
BEGIN_MESSAGE_MAP(COXOptionTreeCtrl, CTreeCtrl)
//{{AFX_MSG_MAP(COXOptionTreeCtrl)
ON_NOTIFY_REFLECT_EX(TVN_KEYDOWN, OnKeydown)
ON_WM_CHAR()
ON_WM_LBUTTONDOWN()
ON_WM_CREATE()
ON_NOTIFY_REFLECT_EX(TVN_DELETEITEM,OnDeleteItem)
ON_WM_SETTINGCHANGE()
//}}AFX_MSG_MAP
ON_MESSAGE(TVM_SETBKCOLOR,OnSetBkColor)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// COXOptionTreeCtrl message handlers
BOOL COXOptionTreeCtrl::OnKeydown(NMHDR* pNMHDR, LRESULT* pResult)
{
TV_KEYDOWN* pTVKeyDown=(TV_KEYDOWN*)pNMHDR;
*pResult=0;
if(pTVKeyDown->wVKey==VK_SPACE)
{
HTREEITEM hItem=GetSelectedItem();
if(hItem!=NULL && !IsReadOnly(hItem))
{
ToggleItem(hItem);
}
}
return FALSE;
}
void COXOptionTreeCtrl::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
// TODO: Add your message handler code here and/or call default
// prevents beep
if(nChar!=VK_SPACE)
{
CTreeCtrl::OnChar(nChar,nRepCnt,nFlags);
}
}
void COXOptionTreeCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
HTREEITEM hItem=GetClickedOptionItem();
if(hItem!=NULL && !IsReadOnly(hItem))
{
if(::GetFocus()!=GetSafeHwnd())
SetFocus();
ToggleItem(hItem);
SelectItem(hItem);
}
else
{
CTreeCtrl::OnLButtonDown(nFlags,point);
}
}
void COXOptionTreeCtrl::OnSettingChange(UINT uFlags, LPCTSTR lpszSection)
{
CheckHightContrast();
SetImageList(GetImageList(),TVSIL_NORMAL);
CWnd::OnSettingChange(uFlags, lpszSection);
}
LRESULT COXOptionTreeCtrl::OnSetBkColor(WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(wParam);
UNREFERENCED_PARAMETER(lParam);
LRESULT lResult=Default();
CheckHightContrast();
SetImageList(GetImageList(),TVSIL_NORMAL);
return lResult;
}
void COXOptionTreeCtrl::PreSubclassWindow()
{
// TODO: Add your specialized code here and/or call the base class
_AFX_THREAD_STATE* pThreadState=AfxGetThreadState();
// hook not already in progress
if(pThreadState->m_pWndInit==NULL)
{
if(!Initialize())
{
TRACE(_T("COXOptionTreeCtrl::PreSubclassWindow: failed to initialize the control\n"));
}
}
CTreeCtrl::PreSubclassWindow();
}
int COXOptionTreeCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if(CTreeCtrl::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: Add your specialized creation code here
if(!Initialize())
{
TRACE(_T("COXOptionTreeCtrl::OnCreate: failed to initialize the control\n"));
return -1;
}
return 0;
}
BOOL COXOptionTreeCtrl::OnDeleteItem(NMHDR* pNotifyStruct, LRESULT* result)
{
LPNMTREEVIEW lpNMTV=(LPNMTREEVIEW)pNotifyStruct;
ASSERT(lpNMTV!=NULL);
UINT nID=GetIDFromItem(lpNMTV->itemOld.hItem);
m_mapItems.RemoveKey(nID);
m_mapReadOnlyItems.RemoveKey(lpNMTV->itemOld.hItem);
*result=0;
return FALSE;
}
//////////////////////////////////////////////////////////////////////////////////
void COXOptionTreeCtrl::ToggleItem(HTREEITEM hItem)
{
ASSERT(hItem!=NULL);
UpdateWindow();
if(IsCheckBox(hItem))
{
if(GetCheck(hItem)==OTITEM_CHECKED)
{
SetCheckBox(hItem,OTITEM_UNCHECKED);
}
else
{
SetCheckBox(hItem,OTITEM_CHECKED);
}
// Send MODIFIED message to parent if needed.
}
else if (IsRadioButton(hItem))
{
if(GetCheck(hItem)==OTITEM_UNCHECKED)
{
SetRadioButton(hItem,OTITEM_CHECKED);
// Send MODIFIED message to parent if needed.
}
}
// avoid unnecessary flicker
RECT rect;
GetItemRect(hItem,&rect,TRUE);
ValidateRect(&rect);
}
HTREEITEM COXOptionTreeCtrl::GetClickedOptionItem() const
{
ASSERT(::IsWindow(GetSafeHwnd()));
// Get the current cursor coordinate
CPoint pt;
::GetCursorPos(&pt);
ScreenToClient(&pt);
UINT nFlags=NULL;
HTREEITEM hItem=HitTest(pt,&nFlags);
if(nFlags&TVHT_ONITEM && (IsCheckBox(hItem) || IsRadioButton(hItem)))
{
if((GetStyle()&TVS_EDITLABELS)==TVS_EDITLABELS && (nFlags&TVHT_ONITEMLABEL))
return NULL;
return hItem;
}
else
return NULL;
}
HTREEITEM COXOptionTreeCtrl::AddControlGroup(LPCTSTR pszText,
HTREEITEM hParent/*=NULL*/,
BOOL bExpanded/*=TRUE*/,
int nImageIndex/*=-1*/,
int nSelectedImageIndex/*=-1*/,
HTREEITEM hInsertAfter/*=TVI_LAST*/)
{
TV_INSERTSTRUCT tvs;
tvs.hParent=hParent;
tvs.hInsertAfter=hInsertAfter;
tvs.item.mask=TVIF_CHILDREN|TVIF_STATE|TVIF_TEXT|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
tvs.item.state=(bExpanded ? TVIS_EXPANDED : 0);
tvs.item.stateMask=tvs.item.state;
tvs.item.cChildren=1;
tvs.item.pszText = (LPTSTR)pszText;
tvs.item.iImage=nImageIndex;
tvs.item.iSelectedImage=nSelectedImageIndex;
return InsertItem(&tvs);
}
HTREEITEM COXOptionTreeCtrl::AddCheckBox(UINT uID, LPCTSTR pszText,
HTREEITEM hParent,
int nCheck/*=OTITEM_UNCHECKED*/,
HTREEITEM hInsertAfter/*=TVI_LAST*/,
BOOL bReadOnly/*=FALSE*/)
{
if(uID==NULL || GetItemFromID(uID)!=NULL)
{
TRACE(_T("COXOptionTreeCtrl::AddCheckBox: invalid/duplicated ID\n"));
ASSERT(FALSE);// Give a chance to debug.
return NULL;
}
TV_INSERTSTRUCT tvs;
tvs.hParent=hParent;
tvs.hInsertAfter=hInsertAfter;
tvs.item.mask=TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_TEXT;
tvs.item.pszText=(LPTSTR)pszText;
tvs.item.iImage=((nCheck==OTITEM_CHECKED) ?
CHECKED_IMAGE_INDEX : UNCHECKED_IMAGE_INDEX);
tvs.item.iSelectedImage=tvs.item.iImage;
return AddOptionItem(uID,&tvs,bReadOnly);
}
HTREEITEM COXOptionTreeCtrl::AddRadioButton(UINT uID, LPCTSTR pszText,
HTREEITEM hParent,
int nCheck/*=OTITEM_UNCHECKED*/,
HTREEITEM hInsertAfter/*=TVI_LAST*/,
BOOL bReadOnly/*=FALSE*/)
{
if(uID==NULL || GetItemFromID(uID)!=NULL)
{
TRACE(_T("COXOptionTreeCtrl::AddRadioButt: invalid/duplicated ID\n"));
ASSERT(FALSE);// Give a chance to debug.
return NULL;
}
TV_INSERTSTRUCT tvs;
tvs.hParent=hParent;
tvs.hInsertAfter=hInsertAfter;
tvs.item.mask=TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_TEXT;
tvs.item.pszText=(LPTSTR)pszText;
tvs.item.iImage=((nCheck==OTITEM_CHECKED) ?
RADIO_IMAGE_INDEX : UNRADIOED_IMAGE_INDEX);
tvs.item.iSelectedImage=tvs.item.iImage;
return AddOptionItem(uID,&tvs,bReadOnly);
}
HTREEITEM COXOptionTreeCtrl::AddOptionItem(UINT uID, LPTV_INSERTSTRUCT ptvs,
BOOL bReadOnly/*=FALSE*/)
{
HTREEITEM hItem=InsertItem(ptvs);
if(hItem!=NULL)
{
m_mapItems.SetAt(uID,hItem);
m_mapReadOnlyItems.SetAt(hItem,bReadOnly);
}
return hItem;
}
HTREEITEM COXOptionTreeCtrl::GetItemFromID(UINT uID) const
{
HTREEITEM hItem=NULL;
m_mapItems.Lookup(uID, (void*&)hItem);
return hItem;
}
UINT COXOptionTreeCtrl::GetIDFromItem(HTREEITEM hItemFrom) const
{
UINT uID;
HTREEITEM hItem;
for(POSITION pos=m_mapItems.GetStartPosition(); pos!=NULL; )
{
m_mapItems.GetNextAssoc(pos,uID,(void*&)hItem);
if(hItem==hItemFrom)
return uID;
}
return NULL;
}
void COXOptionTreeCtrl::SetCheck(UINT uID, int nCheck)
{
ASSERT(nCheck==OTITEM_UNCHECKED || nCheck==OTITEM_CHECKED);
HTREEITEM hItem=GetItemFromID(uID);
if(hItem!=NULL)
{
SetCheck(hItem,nCheck);
}
else
{
TRACE(_T("COXOptionTreeCtrl::SetCheck: item with specified ID wasn't found\n"));
}
}
void COXOptionTreeCtrl::SetCheck(HTREEITEM hItem, int nCheck)
{
ASSERT(nCheck==OTITEM_UNCHECKED || nCheck==OTITEM_CHECKED);
ASSERT(hItem!=NULL);
if(GetCheck(hItem)==nCheck)
{
// Already Set. Just return.
return;
}
if(IsCheckBox(hItem))
{
SetCheckBox(hItem,nCheck);
}
else if(IsRadioButton(hItem))
{
SetRadioButton(hItem,nCheck);
}
}
void COXOptionTreeCtrl::SetCheckBox(HTREEITEM hItem, int nCheck)
{
ASSERT(hItem!=NULL);
ASSERT(IsCheckBox(hItem));
int nImage=-1;
switch(nCheck)
{
case OTITEM_UNCHECKED:
nImage=UNCHECKED_IMAGE_INDEX;
break;
case OTITEM_CHECKED:
nImage=CHECKED_IMAGE_INDEX;
break;
default:
ASSERT(FALSE);
}
int nOldCheck=GetCheck(hItem);
VERIFY(SetItemImage(hItem,nImage,nImage));
// notify parent
NotifyOptionChanged(hItem,nOldCheck,nCheck);
}
void COXOptionTreeCtrl::SetRadioButton(HTREEITEM hItem, int nCheck)
{
ASSERT(hItem!=NULL);
ASSERT(IsRadioButton(hItem));
int nImage=-1;
switch(nCheck)
{
case OTITEM_UNCHECKED:
{
nImage=UNRADIOED_IMAGE_INDEX;
break;
}
case OTITEM_CHECKED:
{
nImage=RADIO_IMAGE_INDEX;
HTREEITEM hsibling=hItem;
// Set all other radio buttons ABOVE to OFF
while((hsibling=GetPrevSiblingItem(hsibling))!=NULL)
{
if(!IsRadioButton(hsibling))
break;
if(GetCheck(hsibling)!=OTITEM_UNCHECKED)
SetRadioButton(hsibling,OTITEM_UNCHECKED);
}
hsibling=hItem;
// Set all other radio buttons BELOW to OFF
while((hsibling=GetNextSiblingItem(hsibling))!=NULL)
{
if(!IsRadioButton(hsibling))
break;
if(GetCheck(hsibling)!=OTITEM_UNCHECKED)
SetRadioButton(hsibling,OTITEM_UNCHECKED);
}
break;
}
default:
ASSERT(FALSE);
}
int nOldCheck=GetCheck(hItem);
VERIFY(SetItemImage(hItem,nImage,nImage));
// notify parent
NotifyOptionChanged(hItem,nOldCheck,nCheck);
}
int COXOptionTreeCtrl::GetCheck(UINT uID) const
{
HTREEITEM hItem=GetItemFromID(uID);
return GetCheck(hItem);
}
int COXOptionTreeCtrl::GetCheck(HTREEITEM hItem) const
{
int nImage=-1;
int nSelectedImage=-1;
VERIFY(GetItemImage(hItem,nImage,nSelectedImage));
if(nImage==UNCHECKED_IMAGE_INDEX || nImage==UNRADIOED_IMAGE_INDEX)
{
return OTITEM_UNCHECKED;
}
if(nImage==CHECKED_IMAGE_INDEX || nImage==RADIO_IMAGE_INDEX)
{
return OTITEM_CHECKED;
}
ASSERT(FALSE);
return -1;
}
BOOL COXOptionTreeCtrl::IsCheckBox(HTREEITEM hItem) const
{
ASSERT(hItem!=NULL);
int nImage=-1;
int nSelectedImage=-1;
VERIFY(GetItemImage(hItem,nImage,nSelectedImage));
if(nImage==CHECKED_IMAGE_INDEX || nImage==UNCHECKED_IMAGE_INDEX)
{
return TRUE;
}
return FALSE;
}
BOOL COXOptionTreeCtrl::IsRadioButton(HTREEITEM hItem) const
{
ASSERT(hItem!=NULL);
int nImage=-1;
int nSelectedImage=-1;
VERIFY(GetItemImage(hItem,nImage,nSelectedImage));
if(nImage==RADIO_IMAGE_INDEX || nImage==UNRADIOED_IMAGE_INDEX)
{
return TRUE;
}
return FALSE;
}
void COXOptionTreeCtrl::SetReadOnly(UINT uID, BOOL bReadOnly)
{
HTREEITEM hItem=GetItemFromID(uID);
if(hItem!=NULL)
{
SetReadOnly(hItem,bReadOnly);
}
else
{
TRACE(_T("COXOptionTreeCtrl::SetReadOnly: item with specified ID wasn't found\n"));
}
}
void COXOptionTreeCtrl::SetReadOnly(HTREEITEM hItem, BOOL bReadOnly)
{
ASSERT(hItem!=NULL);
if(IsCheckBox(hItem) || IsRadioButton(hItem))
{
m_mapReadOnlyItems.SetAt(hItem,bReadOnly);
}
}
BOOL COXOptionTreeCtrl::IsReadOnly(HTREEITEM hItem) const
{
ASSERT(hItem!=NULL);
BOOL bReadOnly=FALSE;
if(m_mapReadOnlyItems.Lookup(hItem,bReadOnly))
{
return bReadOnly;
}
else
{
ASSERT(!(IsCheckBox(hItem) || IsRadioButton(hItem)));
return FALSE;
}
}
UINT COXOptionTreeCtrl::GetCheckedRadioButton(UINT uIDFirstButton,
UINT uIDLastButton) const
{
HTREEITEM hItemFirst=GetItemFromID(uIDFirstButton);
HTREEITEM hItemLast=GetItemFromID(uIDLastButton);
ASSERT(hItemFirst!=NULL && hItemLast!=NULL);
// Just checking....
HTREEITEM hsibling = hItemFirst;
do
{
if(GetCheck(hsibling)!=OTITEM_UNCHECKED)
{
return GetIDFromItem(hsibling);
}
if (hsibling == hItemLast)
{
break;
}
hsibling = GetNextSiblingItem(hsibling);
} while(hsibling!=NULL);
return NULL;
}
void COXOptionTreeCtrl::CheckRadioButton(UINT uIDCheckButton)
{
SetCheck(uIDCheckButton,OTITEM_CHECKED);
}
BOOL COXOptionTreeCtrl::Initialize()
{
ASSERT(::IsWindow(GetSafeHwnd()));
if((HIMAGELIST)m_imageList==NULL)
{
if(!m_imageList.Create(IDB_OX_OPTIONTREE_IMAGELIST,16,0,RGB(192,192,192)))
{
TRACE(_T("COXOptionTreeCtrl::Initialize: failed to create image list\n"));
ASSERT(FALSE);
return FALSE;
}
}
if((HIMAGELIST)m_imageListHighContrast==NULL)
{
if(!m_imageListHighContrast.
Create(IDB_OX_OPTIONTREE_HIGHCONTRAST_IMAGELIST,16,0,RGB(192,192,192)))
{
TRACE(_T("COXOptionTreeCtrl::Initialize: failed to create image list\n"));
ASSERT(FALSE);
return FALSE;
}
}
CheckHightContrast();
SetImageList(GetImageList(),TVSIL_NORMAL);
return TRUE;
}
int COXOptionTreeCtrl::AddImage(LPCTSTR lpszImageResourceID,
COLORREF clrMask/*=RGB(192,192,192)*/,
LPCTSTR lpszHighContrastImageResourceID/*=NULL*/,
COLORREF clrHighContrastMask/*=RGB(192,192,192)*/)
{
CBitmap bitmap;
bitmap.LoadBitmap(lpszImageResourceID);
int nImageIndex=m_imageList.Add(&bitmap,clrMask);
if(lpszHighContrastImageResourceID==NULL)
{
VERIFY(m_imageListHighContrast.Add(&bitmap,clrMask)==nImageIndex);
}
else
{
CBitmap bitmapHighContrast;
bitmapHighContrast.LoadBitmap(lpszHighContrastImageResourceID);
VERIFY(m_imageListHighContrast.
Add(&bitmapHighContrast,clrHighContrastMask)==nImageIndex);
bitmapHighContrast.DeleteObject();
}
bitmap.DeleteObject();
return nImageIndex;
}
// Save to registry state of check boxes and radio buttons
BOOL COXOptionTreeCtrl::SaveState(LPCTSTR lpszSubKey, LPCTSTR lpszValueName) const
{
#ifndef _MAC
CWinApp* pApp=AfxGetApp();
CString sProfileName;
sProfileName.Format(_T("%s_%s"),lpszSubKey,lpszValueName);
UINT uID=NULL;
HTREEITEM hItem=NULL;
CString sItemID(_T(""));
for(POSITION pos=m_mapItems.GetStartPosition(); pos!=NULL; )
{
m_mapItems.GetNextAssoc(pos,uID,(void*&)hItem);
ASSERT(hItem!=NULL && uID!=NULL && (IsCheckBox(hItem) || IsRadioButton(hItem)));
sItemID.Format(_T("%d"),uID);
pApp->WriteProfileInt(sProfileName,sItemID,GetCheck(hItem));
}
return TRUE;
#else
return FALSE;
#endif
}
// Load from registry state of check boxes and radio buttons
BOOL COXOptionTreeCtrl::LoadState(LPCTSTR lpszSubKey, LPCTSTR lpszValueName)
{
#ifndef _MAC
CWinApp* pApp=AfxGetApp();
CString sProfileName;
sProfileName.Format(_T("%s_%s"),lpszSubKey,lpszValueName);
UINT uID=NULL;
HTREEITEM hItem=NULL;
CString sItemID(_T(""));
for(POSITION pos=m_mapItems.GetStartPosition(); pos!=NULL; )
{
m_mapItems.GetNextAssoc(pos,uID,(void*&)hItem);
ASSERT(hItem!=NULL && uID!=NULL && (IsCheckBox(hItem) || IsRadioButton(hItem)));
sItemID.Format(_T("%d"),uID);
int nCheck=pApp->GetProfileInt(sProfileName,sItemID,-1);
if(nCheck!=-1)
SetCheck(hItem,nCheck);
}
return TRUE;
#else
return FALSE;
#endif
}
LRESULT COXOptionTreeCtrl::NotifyOptionChanged(HTREEITEM hItem, int nOldCheck,
int nNewCheck)
{
ASSERT(nOldCheck==OTITEM_UNCHECKED || nOldCheck==OTITEM_CHECKED);
ASSERT(nNewCheck==OTITEM_UNCHECKED || nNewCheck==OTITEM_CHECKED);
ASSERT(hItem!=NULL);
ASSERT(IsCheckBox(hItem) || IsRadioButton(hItem));
UINT uID=GetIDFromItem(hItem);
ASSERT(uID!=NULL);
// notify parent
CWnd* pParentWnd=GetParent();
if(pParentWnd!=NULL)
{
NMOPTIONTREE nmot;
nmot.hdr.hwndFrom=GetSafeHwnd();
nmot.hdr.idFrom=GetDlgCtrlID();
nmot.hdr.code=OTN_OPTIONCHANGED;
nmot.hItem=hItem;
nmot.uItemID=uID;
nmot.nOldCheck=nOldCheck;
nmot.nNewCheck=nNewCheck;
return (pParentWnd->SendMessage(WM_NOTIFY,(WPARAM)nmot.hdr.idFrom,
(LPARAM)&nmot));
}
else
return (LRESULT)0;
}
void COXOptionTreeCtrl::CheckHightContrast()
{
COLORREF clrBack=(COLORREF)-1;
#if _MFC_VER>0x0421
if(::IsWindow(GetSafeHwnd()))
{
clrBack=GetBkColor();
}
#endif
if(clrBack==(COLORREF)-1)
{
clrBack=::GetSysColor(COLOR_WINDOW);
}
m_bUseHighContrast=(GetRValue(clrBack)+GetGValue(clrBack)+GetBValue(clrBack)<300);
}
BOOL COXOptionTreeCtrl::DeleteAllItems()
{
m_mapItems.RemoveAll();
return(CTreeCtrl::DeleteAllItems());
}