Windows-Server-2003/admin/pchealth/sysinfo/msconfig/exe/pageini.cpp

1026 lines
28 KiB
C++

#include "stdafx.h"
#include "PageIni.h"
#include "MSConfigFind.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CPageIni property page
IMPLEMENT_DYNCREATE(CPageIni, CPropertyPage)
CPageIni::CPageIni() : CPropertyPage(CPageIni::IDD)
{
//{{AFX_DATA_INIT(CPageIni)
// NOTE: the ClassWizard will add member initialization here
//}}AFX_DATA_INIT
m_fModified = FALSE;
}
CPageIni::~CPageIni()
{
}
void CPageIni::SetTabInfo(LPCTSTR szFilename)
{
m_strINIFile = szFilename;
}
void CPageIni::DoDataExchange(CDataExchange* pDX)
{
CPropertyPage::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CPageIni)
// NOTE: the ClassWizard will add DDX and DDV calls here
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CPageIni, CPropertyPage)
//{{AFX_MSG_MAP(CPageIni)
ON_BN_CLICKED(IDC_BUTTONINIDISABLE, OnButtonDisable)
ON_BN_CLICKED(IDC_BUTTONINIDISABLEALL, OnButtonDisableAll)
ON_BN_CLICKED(IDC_BUTTONINIENABLE, OnButtonEnable)
ON_BN_CLICKED(IDC_BUTTONINIENABLEALL, OnButtonEnableAll)
ON_BN_CLICKED(IDC_BUTTONINIMOVEDOWN, OnButtonMoveDown)
ON_BN_CLICKED(IDC_BUTTONINIMOVEUP, OnButtonMoveUp)
ON_NOTIFY(TVN_SELCHANGED, IDC_INITREE, OnSelChangedTree)
ON_BN_CLICKED(IDC_BUTTONSEARCH, OnButtonSearch)
ON_NOTIFY(NM_CLICK, IDC_INITREE, OnClickTree)
ON_BN_CLICKED(IDC_BUTTONINIEDIT, OnButtonEdit)
ON_NOTIFY(TVN_ENDLABELEDIT, IDC_INITREE, OnEndLabelEdit)
ON_BN_CLICKED(IDC_BUTTONININEW, OnButtonNew)
ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_INITREE, OnBeginLabelEditIniTree)
ON_NOTIFY(TVN_KEYDOWN, IDC_INITREE, OnKeyDownTree)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
//-------------------------------------------------------------------------
// Reads the contents of the INI file in to this class's internal
// structures.
//-------------------------------------------------------------------------
BOOL CPageIni::LoadINIFile(CStringArray & lines, int & iLastLine, BOOL fLoadBackupFile)
{
lines.RemoveAll();
// Open the specified INI file.
TCHAR szPath[MAX_PATH];
CString strINIFileLocation;
strINIFileLocation.Format(_T("%%windir%%\\%s"), m_strINIFile);
if (::ExpandEnvironmentStrings(strINIFileLocation, szPath, MAX_PATH) == 0)
return FALSE;
if (fLoadBackupFile)
{
CString strPath = GetBackupName(szPath, _T(".backup"));
// Replacing unsafe string copy: _tcscpy(szPath, strPath);
::ZeroMemory((PVOID)szPath, sizeof(szPath));
_tcsncpy(szPath, strPath, (sizeof(szPath) / sizeof(TCHAR)) - 1);
}
else
{
// If a backup of this file doesn't exist, we should make one.
BackupFile(szPath, _T(".backup"), FALSE);
}
CStdioFile inifile;
if (inifile.Open(szPath, CFile::modeRead | CFile::typeText))
{
// Estimate how big the string array will need to be (the array
// will grow if we're off). We'll estimate 15 characters/line, average.
// And we'll set the array to grow by 16 if we exceed this.
lines.SetSize(inifile.GetLength() / (15 * sizeof(TCHAR)), 16);
// Read each line and insert it into the array.
CString strLine;
m_iLastLine = -1;
while (inifile.ReadString(strLine))
{
strLine.TrimRight(_T("\r\n"));
CString strCheck(strLine);
strCheck.TrimLeft();
if (!strCheck.IsEmpty())
lines.SetAtGrow(++iLastLine, strLine);
}
inifile.Close();
}
else
return FALSE;
return TRUE;
}
//-------------------------------------------------------------------------
// Writes the contents of the array of lines out to the actual file.
//-------------------------------------------------------------------------
BOOL CPageIni::WriteINIFile(CStringArray & lines, int iLastLine, BOOL fUndoable)
{
// Open the specified INI file.
TCHAR szPath[MAX_PATH];
CString strINIFileLocation;
CString strINIFile(m_strINIFile);
strINIFileLocation.Format(_T("%%windir%%\\%s"), strINIFile);
if (::ExpandEnvironmentStrings(strINIFileLocation, szPath, MAX_PATH) == 0)
return FALSE;
CStdioFile inifile;
if (inifile.Open(szPath, CFile::modeCreate | CFile::modeWrite | CFile::typeText))
{
// We need to traverse the tree structure to get the new contents of
// the file.
HWND hwndTree = m_tree.m_hWnd;
HTREEITEM htiLine = TreeView_GetRoot(hwndTree);
TVITEM tvi;
TCHAR szBuffer[MAX_PATH];
tvi.mask = TVIF_TEXT | TVIF_IMAGE;
tvi.pszText = szBuffer;
while (htiLine)
{
tvi.hItem = htiLine;
tvi.cchTextMax = MAX_PATH;
if (TreeView_GetItem(hwndTree, &tvi))
{
CString strLine(tvi.pszText);
CString strCheck(strLine);
strCheck.TrimLeft();
if (!strCheck.IsEmpty())
{
if (!fUndoable && strLine.Find(DISABLE_STRING) != -1)
strLine.Replace(DISABLE_STRING, _T("; "));
strLine += CString(_T("\n"));
inifile.WriteString(strLine);
}
}
HTREEITEM htiNext = TreeView_GetChild(hwndTree, htiLine);
if (htiNext)
{
htiLine = htiNext;
continue;
}
htiNext = TreeView_GetNextSibling(hwndTree, htiLine);
if (htiNext)
{
htiLine = htiNext;
continue;
}
htiNext = TreeView_GetParent(hwndTree, htiLine);
if (htiNext)
{
htiNext = TreeView_GetNextSibling(hwndTree, htiNext);
if (htiNext)
{
htiLine = htiNext;
continue;
}
}
htiLine = NULL;
}
inifile.Close();
}
else
return FALSE;
return TRUE;
}
//-------------------------------------------------------------------------
// Updates the tree view to show the contents of the internal structures.
//-------------------------------------------------------------------------
void CPageIni::UpdateTreeView()
{
TreeView_DeleteAllItems(m_tree.m_hWnd);
ASSERT(m_iLastLine < m_lines.GetSize());
if (m_iLastLine > m_lines.GetSize())
return;
TVINSERTSTRUCT tvis;
tvis.hParent = TVI_ROOT;
tvis.hInsertAfter = TVI_LAST;
tvis.itemex.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
tvis.itemex.iImage = m_checkedID;
tvis.itemex.iSelectedImage = m_checkedID;
// Add each line to the tree view.
int iDisableLen = _tcslen(DISABLE_STRING);
int iDisableLenHdr = _tcslen(DISABLE_STRING_HDR);
for (int i = 0; i <= m_iLastLine; i++)
{
CString strLine = m_lines.GetAt(i);
tvis.itemex.pszText = (LPTSTR)(LPCTSTR)strLine;
if (!strLine.IsEmpty() && (_tcsnccmp((LPCTSTR)strLine, DISABLE_STRING, iDisableLen) == 0))
tvis.itemex.iImage = tvis.itemex.iSelectedImage = m_uncheckedID;
else
tvis.itemex.iImage = tvis.itemex.iSelectedImage = m_checkedID;
BOOL fSectionHeader = FALSE;
if (!strLine.IsEmpty())
{
if (strLine[0] == _T('['))
fSectionHeader = TRUE;
else if (_tcsnccmp((LPCTSTR)strLine, DISABLE_STRING_HDR, iDisableLenHdr) == 0)
fSectionHeader = TRUE;
}
if (fSectionHeader)
{
tvis.hParent = TVI_ROOT;
tvis.hParent = TreeView_InsertItem(m_tree.m_hWnd, &tvis);
}
else
TreeView_InsertItem(m_tree.m_hWnd, &tvis);
}
// Now scan the top level of the tree view. For every node which
// has children, we want to set the image appropriately.
for (HTREEITEM hti = TreeView_GetRoot(m_tree.m_hWnd); hti; hti = TreeView_GetNextSibling(m_tree.m_hWnd, hti))
if (TreeView_GetChild(m_tree.m_hWnd, hti) != NULL)
UpdateLine(hti);
UpdateControls();
}
//-------------------------------------------------------------------------
// Update the image state of the specified line, based on the text in
// the line. If the line is a bracketed section header, this will involve
// scanning the children. Returns the index of the image set for the node.
//-------------------------------------------------------------------------
int CPageIni::UpdateLine(HTREEITEM hti)
{
if (hti == NULL)
return 0;
TVITEM tvi;
tvi.hItem = hti;
int iNewImageIndex = m_checkedID;
HTREEITEM htiChild = TreeView_GetChild(m_tree.m_hWnd, hti);
if (htiChild)
{
BOOL fEnabledChild = FALSE, fDisabledChild = FALSE;
while (htiChild)
{
if (UpdateLine(htiChild) == m_checkedID)
fEnabledChild = TRUE;
else
fDisabledChild = TRUE;
htiChild = TreeView_GetNextSibling(m_tree.m_hWnd, htiChild);
}
if (fDisabledChild)
iNewImageIndex = (fEnabledChild) ? m_fuzzyID : m_uncheckedID;
}
else
{
TCHAR szBuffer[MAX_PATH]; // seems like a reasonably big value
tvi.mask = TVIF_TEXT;
tvi.pszText = szBuffer;
tvi.cchTextMax = MAX_PATH;
if (TreeView_GetItem(m_tree.m_hWnd, &tvi))
iNewImageIndex = (_tcsnccmp(tvi.pszText, DISABLE_STRING, _tcslen(DISABLE_STRING)) == 0) ? m_uncheckedID : m_checkedID;
}
tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
if (TreeView_GetItem(m_tree.m_hWnd, &tvi) && tvi.iImage != iNewImageIndex)
{
tvi.iSelectedImage = tvi.iImage = iNewImageIndex;
TreeView_SetItem(m_tree.m_hWnd, &tvi);
}
return iNewImageIndex;
}
//-------------------------------------------------------------------------
// Enable or disable a node in the tree (and its children).
//-------------------------------------------------------------------------
void CPageIni::SetEnable(BOOL fEnable, HTREEITEM htiNode, BOOL fUpdateLine, BOOL fBroadcast)
{
HTREEITEM hti = (htiNode) ? htiNode : TreeView_GetSelection(m_tree.m_hWnd);
if (hti == NULL)
return;
HTREEITEM htiChild = TreeView_GetChild(m_tree.m_hWnd, hti);
if (htiChild)
{
while (htiChild)
{
SetEnable(fEnable, htiChild, FALSE, FALSE);
htiChild = TreeView_GetNextSibling(m_tree.m_hWnd, htiChild);
}
UpdateLine(hti);
}
else
{
int iDisableLen = _tcslen(DISABLE_STRING);
TCHAR szBuffer[MAX_PATH]; // seems like a reasonably big value
TVITEM tvi;
tvi.hItem = hti;
tvi.mask = TVIF_TEXT;
tvi.pszText = &szBuffer[iDisableLen]; // leave some room to add disable string
tvi.cchTextMax = MAX_PATH + iDisableLen;
if (TreeView_GetItem(m_tree.m_hWnd, &tvi))
{
BOOL fAlreadyEnabled = (_tcsnccmp(&szBuffer[iDisableLen], DISABLE_STRING, iDisableLen) != 0);
if (fEnable != fAlreadyEnabled)
{
if (fEnable)
tvi.pszText = &szBuffer[iDisableLen * 2];
else
{
_tcsncpy(szBuffer, DISABLE_STRING, iDisableLen);
tvi.pszText = szBuffer;
}
TreeView_SetItem(m_tree.m_hWnd, &tvi);
if (fUpdateLine)
{
UpdateLine(hti);
if (TreeView_GetParent(m_tree.m_hWnd, hti))
UpdateLine(TreeView_GetParent(m_tree.m_hWnd, hti));
}
}
}
}
if (fBroadcast)
SetModified(TRUE);
}
//-------------------------------------------------------------------------
// Move the specified branch in the tree view to a new location.
//-------------------------------------------------------------------------
void CPageIni::MoveBranch(HWND hwndTree, HTREEITEM htiMove, HTREEITEM htiParent, HTREEITEM htiAfter)
{
HTREEITEM htiNew = CopyBranch(hwndTree, htiMove, htiParent, htiAfter);
if (htiNew != NULL)
{
TreeView_SelectItem(hwndTree, htiNew);
TreeView_DeleteItem(hwndTree, htiMove);
SetModified(TRUE);
}
}
HTREEITEM CPageIni::CopyBranch(HWND hwndTree, HTREEITEM htiFrom, HTREEITEM htiToParent, HTREEITEM htiToAfter)
{
TCHAR szBuffer[MAX_PATH];
TVINSERTSTRUCT tvis;
tvis.item.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_PARAM | TVIF_SELECTEDIMAGE | TVIF_TEXT | TVIF_STATE;
tvis.item.pszText = szBuffer;
tvis.item.cchTextMax = MAX_PATH;
tvis.item.hItem = htiFrom;
tvis.item.stateMask = TVIS_EXPANDED;
HTREEITEM htiNew = NULL;
if (TreeView_GetItem(hwndTree, &tvis.item))
{
tvis.hParent = htiToParent;
tvis.hInsertAfter = htiToAfter;
htiNew = TreeView_InsertItem(hwndTree, &tvis);
}
HTREEITEM htiPrevious = TVI_FIRST;
if (htiNew)
for (HTREEITEM htiChild = TreeView_GetChild(hwndTree, htiFrom); htiChild; htiChild = TreeView_GetNextSibling(hwndTree, htiChild))
htiPrevious = CopyBranch(hwndTree, htiChild, htiNew, htiPrevious);
return htiNew;
}
//-------------------------------------------------------------------------
// Update the controls to reflect the state of the selection.
//-------------------------------------------------------------------------
void CPageIni::UpdateControls()
{
BOOL fEnable = FALSE;
BOOL fDisable = FALSE;
BOOL fMoveUp = FALSE;
BOOL fMoveDown = FALSE;
HTREEITEM htiSelection = TreeView_GetSelection(m_tree.m_hWnd);
if (htiSelection)
{
fMoveUp = (TreeView_GetPrevSibling(m_tree.m_hWnd, htiSelection) != NULL);
fMoveDown = (TreeView_GetNextSibling(m_tree.m_hWnd, htiSelection) != NULL);
TVITEM tvi;
tvi.hItem = htiSelection;
tvi.mask = TVIF_IMAGE;
if (TreeView_GetItem(m_tree.m_hWnd, &tvi))
{
fEnable = (tvi.iImage != m_checkedID);
fDisable = (tvi.iImage != m_uncheckedID);
}
}
HWND hwndFocus = ::GetFocus();
CPageBase::TabState state = GetCurrentTabState();
::EnableWindow(GetDlgItemHWND(IDC_BUTTONINIDISABLEALL), (state != DIAGNOSTIC));
::EnableWindow(GetDlgItemHWND(IDC_BUTTONINIENABLEALL), (state != NORMAL));
if ((state == DIAGNOSTIC) && hwndFocus == GetDlgItemHWND(IDC_BUTTONINIDISABLEALL))
PrevDlgCtrl();
if ((state == NORMAL) && hwndFocus == GetDlgItemHWND(IDC_BUTTONINIENABLEALL))
NextDlgCtrl();
::EnableWindow(GetDlgItemHWND(IDC_BUTTONINIDISABLE), fDisable);
::EnableWindow(GetDlgItemHWND(IDC_BUTTONINIENABLE), fEnable);
::EnableWindow(GetDlgItemHWND(IDC_BUTTONINIMOVEUP), fMoveUp);
::EnableWindow(GetDlgItemHWND(IDC_BUTTONINIMOVEDOWN), fMoveDown);
if (!fMoveUp && hwndFocus == GetDlgItemHWND(IDC_BUTTONINIMOVEUP))
NextDlgCtrl();
if (!fMoveDown && hwndFocus == GetDlgItemHWND(IDC_BUTTONINIMOVEDOWN))
PrevDlgCtrl();
if (!fEnable && hwndFocus == GetDlgItemHWND(IDC_BUTTONINIENABLE))
NextDlgCtrl();
if (!fDisable && hwndFocus == GetDlgItemHWND(IDC_BUTTONINIDISABLE))
PrevDlgCtrl();
}
//-------------------------------------------------------------------------
// Get the next item in the tree. Since we know this won't be more than
// two levels deep, we don't need to have a loop.
//-------------------------------------------------------------------------
HTREEITEM CPageIni::GetNextItem(HTREEITEM hti)
{
if (hti == NULL)
return NULL;
HTREEITEM htiNext = TreeView_GetChild(m_tree.m_hWnd, hti);
if (htiNext != NULL)
return htiNext;
htiNext = TreeView_GetNextSibling(m_tree.m_hWnd, hti);
if (htiNext != NULL)
return htiNext;
htiNext = TreeView_GetParent(m_tree.m_hWnd, hti);
if (htiNext != NULL)
htiNext = TreeView_GetNextSibling(m_tree.m_hWnd, htiNext);
return htiNext;
}
/////////////////////////////////////////////////////////////////////////////
// CPageIni message handlers
BOOL CPageIni::OnInitDialog()
{
CPropertyPage::OnInitDialog();
// These items are initially disabled.
::EnableWindow(GetDlgItemHWND(IDC_BUTTONINIDISABLE), FALSE);
::EnableWindow(GetDlgItemHWND(IDC_BUTTONINIENABLE), FALSE);
::EnableWindow(GetDlgItemHWND(IDC_BUTTONINIMOVEUP), FALSE);
::EnableWindow(GetDlgItemHWND(IDC_BUTTONINIMOVEDOWN), FALSE);
m_tree.Attach(GetDlgItemHWND(IDC_INITREE));
VERIFY(m_fImageList = m_imagelist.Create(IDB_IMAGELIST, 0, 3, RGB(255, 0, 255)));
if (m_fImageList)
TreeView_SetImageList(m_tree.m_hWnd, m_imagelist, TVSIL_NORMAL);
// If we are running on an RTL system, then the bitmaps for the check boxes
// will be reversed. The imagemap includes reversed versions of the checked
// and indetermined state, so we should just use the appropriate index.
DWORD dwLayout;
BOOL fRTL = FALSE;
if (::GetProcessDefaultLayout(&dwLayout))
fRTL = ((dwLayout & LAYOUT_RTL) != 0);
m_checkedID = (fRTL) ? IMG_CHECKED_RTL : IMG_CHECKED;
m_fuzzyID = (fRTL) ? IMG_FUZZY_RTL : IMG_FUZZY;
m_uncheckedID = IMG_UNCHECKED;
if (LoadINIFile(m_lines, m_iLastLine))
UpdateTreeView();
else
{
// set controls for no file TBD
}
m_fInitialized = TRUE;
return TRUE; // return TRUE unless you set the focus to a control
}
//-------------------------------------------------------------------------
// When the user clicks on an enable or disable button, we'll modify the
// text in the tree view and update the images.
//-------------------------------------------------------------------------
void CPageIni::OnButtonDisable()
{
SetEnable(FALSE);
UpdateControls();
}
void CPageIni::OnButtonDisableAll()
{
for (HTREEITEM hti = TreeView_GetRoot(m_tree.m_hWnd); hti; hti = TreeView_GetNextSibling(m_tree.m_hWnd, hti))
SetEnable(FALSE, hti, TRUE);
UpdateControls();
}
void CPageIni::OnButtonEnable()
{
SetEnable(TRUE);
UpdateControls();
}
void CPageIni::OnButtonEnableAll()
{
for (HTREEITEM hti = TreeView_GetRoot(m_tree.m_hWnd); hti; hti = TreeView_GetNextSibling(m_tree.m_hWnd, hti))
SetEnable(TRUE, hti, TRUE);
UpdateControls();
}
//-------------------------------------------------------------------------
// Move a branch of the tree up or down.
//-------------------------------------------------------------------------
void CPageIni::OnButtonMoveDown()
{
HTREEITEM htiSelection = TreeView_GetSelection(m_tree.m_hWnd);
if (htiSelection)
{
HTREEITEM htiParent = TreeView_GetParent(m_tree.m_hWnd, htiSelection);
HTREEITEM htiNext = TreeView_GetNextSibling(m_tree.m_hWnd, htiSelection);
if (htiNext == NULL)
return;
if (htiParent == NULL)
htiParent = TVI_ROOT;
MoveBranch(m_tree.m_hWnd, htiSelection, htiParent, htiNext);
}
}
void CPageIni::OnButtonMoveUp()
{
HTREEITEM htiSelection = TreeView_GetSelection(m_tree.m_hWnd);
if (htiSelection)
{
HTREEITEM htiParent = TreeView_GetParent(m_tree.m_hWnd, htiSelection);
HTREEITEM htiPrevious = TreeView_GetPrevSibling(m_tree.m_hWnd, htiSelection);
if (htiPrevious == NULL)
return;
htiPrevious = TreeView_GetPrevSibling(m_tree.m_hWnd, htiPrevious);
if (htiPrevious == NULL)
htiPrevious = TVI_FIRST;
if (htiParent == NULL)
htiParent = TVI_ROOT;
MoveBranch(m_tree.m_hWnd, htiSelection, htiParent, htiPrevious);
}
}
void CPageIni::OnSelChangedTree(NMHDR * pNMHDR, LRESULT * pResult)
{
NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW *)pNMHDR;
UpdateControls();
*pResult = 0;
}
//-------------------------------------------------------------------------
// Search the tree view for a string (present a dialog to the user).
//-------------------------------------------------------------------------
void CPageIni::OnButtonSearch()
{
CMSConfigFind find;
find.m_strSearchFor = m_strLastSearch;
if (find.DoModal() == IDOK && !find.m_strSearchFor.IsEmpty())
{
CString strSearch(find.m_strSearchFor);
m_strLastSearch = strSearch;
strSearch.MakeLower();
HTREEITEM htiSearch;
if (find.m_fSearchFromTop)
htiSearch = TreeView_GetRoot(m_tree.m_hWnd);
else
{
htiSearch = TreeView_GetSelection(m_tree.m_hWnd);
if (htiSearch == NULL)
htiSearch = TreeView_GetRoot(m_tree.m_hWnd);
else
htiSearch = GetNextItem(htiSearch);
}
TVITEM tvi;
TCHAR szBuffer[MAX_PATH];
tvi.mask = TVIF_TEXT | TVIF_IMAGE;
tvi.pszText = szBuffer;
while (htiSearch != NULL)
{
tvi.hItem = htiSearch;
tvi.cchTextMax = MAX_PATH;
if (TreeView_GetItem(m_tree.m_hWnd, &tvi))
{
CString strItem(szBuffer);
strItem.MakeLower();
if (strItem.Find(strSearch) != -1)
{
// We found a hit. Select the node.
TreeView_SelectItem(m_tree.m_hWnd, htiSearch);
break;
}
}
htiSearch = GetNextItem(htiSearch);
}
if (htiSearch == NULL)
Message(IDS_NOFIND);
}
}
//-------------------------------------------------------------------------
// The current tab state can be found by looking through the tree view.
//-------------------------------------------------------------------------
CPageBase::TabState CPageIni::GetCurrentTabState()
{
if (!m_fInitialized)
return GetAppliedTabState();
BOOL fAllEnabled = TRUE, fAllDisabled = TRUE;
HTREEITEM hti = TreeView_GetRoot(m_tree.m_hWnd);
TVITEM tvi;
tvi.mask = TVIF_IMAGE;
while (hti)
{
tvi.hItem = hti;
if (TreeView_GetItem(m_tree.m_hWnd, &tvi))
{
if (m_uncheckedID != tvi.iImage)
fAllDisabled = FALSE;
if (m_checkedID != tvi.iImage)
fAllEnabled = FALSE;
}
hti = TreeView_GetNextSibling(m_tree.m_hWnd, hti);
}
return ((fAllEnabled) ? NORMAL : ((fAllDisabled) ? DIAGNOSTIC : USER));
}
//-------------------------------------------------------------------------
// Apply the changes by saving the INI file.
//
// The base class implementation is called to maintain the
// applied tab state.
//-------------------------------------------------------------------------
BOOL CPageIni::OnApply()
{
if (!m_fModified)
return TRUE;
WriteINIFile(m_lines, m_iLastLine);
CPageBase::SetAppliedState(GetCurrentTabState());
m_fMadeChange = TRUE;
return TRUE;
}
//-------------------------------------------------------------------------
// To commit the changes, write the INI file without the distinguishing
// comments (by calling WriteINIFile with FALSE as the last param).
//
// Then call the base class implementation.
//-------------------------------------------------------------------------
void CPageIni::CommitChanges()
{
WriteINIFile(m_lines, m_iLastLine, FALSE);
LoadINIFile(m_lines, m_iLastLine);
UpdateTreeView();
CPageBase::CommitChanges();
}
//-------------------------------------------------------------------------
// Set the overall state of the tab to normal or diagnostic.
//-------------------------------------------------------------------------
void CPageIni::SetNormal()
{
HWND hwndTree = m_tree.m_hWnd;
HTREEITEM hti = TreeView_GetRoot(hwndTree);
while (hti != NULL)
{
SetEnable(TRUE, hti, TRUE, FALSE);
hti = TreeView_GetNextSibling(hwndTree, hti);
}
SetModified(TRUE);
UpdateControls();
}
void CPageIni::SetDiagnostic()
{
HWND hwndTree = m_tree.m_hWnd;
HTREEITEM hti = TreeView_GetRoot(hwndTree);
while (hti != NULL)
{
SetEnable(FALSE, hti, TRUE, FALSE);
hti = TreeView_GetNextSibling(hwndTree, hti);
}
SetModified(TRUE);
UpdateControls();
}
//-------------------------------------------------------------------------
// We need to look at user clicks on the tree view. If it is on an item,
// and also on the item's image, then we'll need to toggle the image
// state.
//-------------------------------------------------------------------------
void CPageIni::OnClickTree(NMHDR* pNMHDR, LRESULT* pResult)
{
// Determine if this tree click is on a node, and if it is,
// if it is on the image.
TVHITTESTINFO tvhti;
DWORD dwPoint = GetMessagePos();
tvhti.pt.x = ((int)(short)LOWORD(dwPoint));
tvhti.pt.y = ((int)(short)HIWORD(dwPoint));
::ScreenToClient(m_tree.m_hWnd, &tvhti.pt);
HTREEITEM hti = TreeView_HitTest(m_tree.m_hWnd, &tvhti);
if (hti != NULL && (tvhti.flags & TVHT_ONITEMICON) != 0)
{
// This is a click that we care about. We need to get the
// current state of this node so we know which way to
// toggle the state. We'll make an arbitrary decision
// that the toggle from undetermined is to enabled.
TVITEM tvi;
tvi.hItem = hti;
tvi.mask = TVIF_IMAGE;
if (TreeView_GetItem(m_tree.m_hWnd, &tvi))
{
SetEnable(tvi.iImage != m_checkedID, hti);
UpdateControls();
}
}
}
//-------------------------------------------------------------------------
// We allow the user to edit the lines in the INI file. When the user
// is through editing, we want to make sure we notify the framework
// that a change has been made.
//-------------------------------------------------------------------------
void CPageIni::OnButtonEdit()
{
HTREEITEM hti = TreeView_GetSelection(m_tree.m_hWnd);
if (hti != NULL)
{
::SetFocus(m_tree.m_hWnd);
TreeView_EditLabel(m_tree.m_hWnd, hti);
}
}
//-------------------------------------------------------------------------
// WndProc for the edit control when editing a label in the tree (handles
// enter/esc better). Lifted from ME source.
//-------------------------------------------------------------------------
WNDPROC pOldEditProc = NULL; // save old wndproc when we subclass edit control
LRESULT TreeViewEditSubclassProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
{
switch (wm)
{
case WM_GETDLGCODE:
return DLGC_WANTALLKEYS;
// The Knowledge Base article describing the work-around for this
// this bug indicates the following handling of VK_ESCAPE & VK_RETURN
// is necessary -- however, under Memphis & OSR2 these keys are never
// received (returning DLGC_WANTALLKEYS seems to fix the problem).
// Perhaps it depends on which comctl32.dll is installed...
case WM_CHAR:
if (wp == VK_ESCAPE || wp == VK_RETURN)
{
TreeView_EndEditLabelNow(GetParent(hwnd), wp == VK_ESCAPE);
return 0;
}
break;
}
if (pOldEditProc) // better not be null
return CallWindowProc(pOldEditProc, hwnd, wm, wp, lp);
return 0;
}
//-------------------------------------------------------------------------
// The tree view doesn't handle enter and esc correctly, so when we start
// editing a label, we need to subclass the control.
//-------------------------------------------------------------------------
void CPageIni::OnBeginLabelEditIniTree(NMHDR * pNMHDR, LRESULT * pResult)
{
TV_DISPINFO * pTVDispInfo = (TV_DISPINFO *)pNMHDR;
// Disable Move Up and Down buttons while editing.
::EnableWindow(GetDlgItemHWND(IDC_BUTTONINIMOVEUP), FALSE);
::EnableWindow(GetDlgItemHWND(IDC_BUTTONINIMOVEDOWN), FALSE);
::EnableWindow(GetDlgItemHWND(IDC_BUTTONINIEDIT), FALSE);
// TreeView controls don't properly handle Esc/Enter when editing
// a label. To fix this, it's necessary to subclass the label's edit
// control and process Esc & Enter ourselves. Sigh...
HWND hWndEdit = TreeView_GetEditControl(m_tree.m_hWnd);
if (hWndEdit)
{
pOldEditProc = (WNDPROC)::GetWindowLongPtr(hWndEdit, GWLP_WNDPROC);
::SetWindowLongPtr(hWndEdit, GWLP_WNDPROC, (ULONG_PTR)(WNDPROC)&TreeViewEditSubclassProc);
}
*pResult = 0;
}
void CPageIni::OnEndLabelEdit(NMHDR* pNMHDR, LRESULT* pResult)
{
TV_DISPINFO * pTVDispInfo = (TV_DISPINFO *)pNMHDR;
// Stop subclassing the edit control.
HWND hWndEdit = TreeView_GetEditControl(m_tree.m_hWnd);
if (hWndEdit && pOldEditProc)
{
::SetWindowLongPtr(hWndEdit, GWLP_WNDPROC, (ULONG_PTR)(WNDPROC)pOldEditProc);
pOldEditProc = NULL;
}
// If the new text pointer is null, then the edit was cancelled.
// We only care if a new item was being added, in which case
// we should delete it.
if (pTVDispInfo->item.pszText == NULL)
{
TCHAR szBuffer[MAX_PATH];
TVITEM tvi;
tvi.pszText = szBuffer;
tvi.mask = TVIF_TEXT;
tvi.hItem = pTVDispInfo->item.hItem;
tvi.cchTextMax = MAX_PATH;
if (TreeView_GetItem(m_tree.m_hWnd, &tvi) && tvi.pszText && tvi.pszText[0] == _T('\0'))
{
HTREEITEM hPriorItem = TreeView_GetPrevSibling(pTVDispInfo->hdr.hwndFrom, pTVDispInfo->item.hItem);
if (hPriorItem == NULL)
hPriorItem = TreeView_GetParent(pTVDispInfo->hdr.hwndFrom, pTVDispInfo->item.hItem);
TreeView_DeleteItem(m_tree.m_hWnd, pTVDispInfo->item.hItem);
if (hPriorItem)
TreeView_SelectItem(pTVDispInfo->hdr.hwndFrom, hPriorItem);
}
*pResult = 0;
}
else
{
SetModified(TRUE);
*pResult = 1;
}
::EnableWindow(GetDlgItemHWND(IDC_BUTTONINIEDIT), TRUE);
UpdateControls();
}
//-------------------------------------------------------------------------
// If the user clicks on the new button, then add an empty tree view
// node after the currently selected one. If the selected node has
// children, add the node as the first child under the selected node.
// Then select the node for editing.
//-------------------------------------------------------------------------
void CPageIni::OnButtonNew()
{
HTREEITEM hti = TreeView_GetSelection(m_tree.m_hWnd);
if (hti == NULL)
hti = TreeView_GetRoot(m_tree.m_hWnd);
if (hti == NULL)
return;
TVINSERTSTRUCT tvis;
if (TreeView_GetChild(m_tree.m_hWnd, hti) != NULL)
{
tvis.hParent = hti;
tvis.hInsertAfter = TVI_FIRST;
}
else
{
tvis.hParent = TreeView_GetParent(m_tree.m_hWnd, hti);
tvis.hInsertAfter = hti;
}
TCHAR szBuffer[] = _T("");
tvis.itemex.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
tvis.itemex.iImage = m_checkedID;
tvis.itemex.iSelectedImage = m_checkedID;
tvis.itemex.pszText = szBuffer;
HTREEITEM htiNew = TreeView_InsertItem(m_tree.m_hWnd, &tvis);
if (htiNew != NULL)
{
TreeView_SelectItem(m_tree.m_hWnd, htiNew);
TreeView_EditLabel(m_tree.m_hWnd, htiNew);
}
}
//-------------------------------------------------------------------------
// If the user hits the space bar with an item selected in the tree, toggle
// the state of the item.
//-------------------------------------------------------------------------
void CPageIni::OnKeyDownTree(NMHDR* pNMHDR, LRESULT* pResult)
{
TV_KEYDOWN * pTVKeyDown = (TV_KEYDOWN *)pNMHDR;
if (pTVKeyDown->wVKey == VK_SPACE)
{
HTREEITEM hti = TreeView_GetSelection(m_tree.m_hWnd);
if (hti != NULL)
{
TVITEM tvi;
tvi.mask = TVIF_IMAGE;
tvi.hItem = hti;
if (TreeView_GetItem(m_tree.m_hWnd, &tvi))
{
SetEnable(tvi.iImage != m_checkedID, hti);
UpdateControls();
}
}
}
*pResult = 0;
}