Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1019 lines
28 KiB

  1. #include "stdafx.h"
  2. #include "PageIni.h"
  3. #include "MSConfigFind.h"
  4. #ifdef _DEBUG
  5. #define new DEBUG_NEW
  6. #undef THIS_FILE
  7. static char THIS_FILE[] = __FILE__;
  8. #endif
  9. /////////////////////////////////////////////////////////////////////////////
  10. // CPageIni property page
  11. IMPLEMENT_DYNCREATE(CPageIni, CPropertyPage)
  12. CPageIni::CPageIni() : CPropertyPage(CPageIni::IDD)
  13. {
  14. //{{AFX_DATA_INIT(CPageIni)
  15. // NOTE: the ClassWizard will add member initialization here
  16. //}}AFX_DATA_INIT
  17. }
  18. CPageIni::~CPageIni()
  19. {
  20. }
  21. void CPageIni::SetTabInfo(LPCTSTR szFilename)
  22. {
  23. m_strINIFile = szFilename;
  24. }
  25. void CPageIni::DoDataExchange(CDataExchange* pDX)
  26. {
  27. CPropertyPage::DoDataExchange(pDX);
  28. //{{AFX_DATA_MAP(CPageIni)
  29. // NOTE: the ClassWizard will add DDX and DDV calls here
  30. //}}AFX_DATA_MAP
  31. }
  32. BEGIN_MESSAGE_MAP(CPageIni, CPropertyPage)
  33. //{{AFX_MSG_MAP(CPageIni)
  34. ON_BN_CLICKED(IDC_BUTTONINIDISABLE, OnButtonDisable)
  35. ON_BN_CLICKED(IDC_BUTTONINIDISABLEALL, OnButtonDisableAll)
  36. ON_BN_CLICKED(IDC_BUTTONINIENABLE, OnButtonEnable)
  37. ON_BN_CLICKED(IDC_BUTTONINIENABLEALL, OnButtonEnableAll)
  38. ON_BN_CLICKED(IDC_BUTTONINIMOVEDOWN, OnButtonMoveDown)
  39. ON_BN_CLICKED(IDC_BUTTONINIMOVEUP, OnButtonMoveUp)
  40. ON_NOTIFY(TVN_SELCHANGED, IDC_INITREE, OnSelChangedTree)
  41. ON_BN_CLICKED(IDC_BUTTONSEARCH, OnButtonSearch)
  42. ON_NOTIFY(NM_CLICK, IDC_INITREE, OnClickTree)
  43. ON_BN_CLICKED(IDC_BUTTONINIEDIT, OnButtonEdit)
  44. ON_NOTIFY(TVN_ENDLABELEDIT, IDC_INITREE, OnEndLabelEdit)
  45. ON_BN_CLICKED(IDC_BUTTONININEW, OnButtonNew)
  46. ON_NOTIFY(TVN_BEGINLABELEDIT, IDC_INITREE, OnBeginLabelEditIniTree)
  47. ON_NOTIFY(TVN_KEYDOWN, IDC_INITREE, OnKeyDownTree)
  48. //}}AFX_MSG_MAP
  49. END_MESSAGE_MAP()
  50. //-------------------------------------------------------------------------
  51. // Reads the contents of the INI file in to this class's internal
  52. // structures.
  53. //-------------------------------------------------------------------------
  54. BOOL CPageIni::LoadINIFile(CStringArray & lines, int & iLastLine, BOOL fLoadBackupFile)
  55. {
  56. lines.RemoveAll();
  57. // Open the specified INI file.
  58. TCHAR szPath[MAX_PATH];
  59. CString strINIFileLocation;
  60. strINIFileLocation.Format(_T("%%windir%%\\%s"), m_strINIFile);
  61. if (::ExpandEnvironmentStrings(strINIFileLocation, szPath, MAX_PATH) == 0)
  62. return FALSE;
  63. if (fLoadBackupFile)
  64. {
  65. CString strPath = GetBackupName(szPath, _T(".backup"));
  66. _tcscpy(szPath, strPath);
  67. }
  68. else
  69. {
  70. // If a backup of this file doesn't exist, we should make one.
  71. BackupFile(szPath, _T(".backup"), FALSE);
  72. }
  73. CStdioFile inifile;
  74. if (inifile.Open(szPath, CFile::modeRead | CFile::typeText))
  75. {
  76. // Estimate how big the string array will need to be (the array
  77. // will grow if we're off). We'll estimate 15 characters/line, average.
  78. // And we'll set the array to grow by 16 if we exceed this.
  79. lines.SetSize(inifile.GetLength() / (15 * sizeof(TCHAR)), 16);
  80. // Read each line and insert it into the array.
  81. CString strLine;
  82. m_iLastLine = -1;
  83. while (inifile.ReadString(strLine))
  84. {
  85. strLine.TrimRight(_T("\r\n"));
  86. CString strCheck(strLine);
  87. strCheck.TrimLeft();
  88. if (!strCheck.IsEmpty())
  89. lines.SetAtGrow(++iLastLine, strLine);
  90. }
  91. inifile.Close();
  92. }
  93. else
  94. return FALSE;
  95. return TRUE;
  96. }
  97. //-------------------------------------------------------------------------
  98. // Writes the contents of the array of lines out to the actual file.
  99. //-------------------------------------------------------------------------
  100. BOOL CPageIni::WriteINIFile(CStringArray & lines, int iLastLine, BOOL fUndoable)
  101. {
  102. // Open the specified INI file.
  103. TCHAR szPath[MAX_PATH];
  104. CString strINIFileLocation;
  105. CString strINIFile(m_strINIFile);
  106. strINIFileLocation.Format(_T("%%windir%%\\%s"), strINIFile);
  107. if (::ExpandEnvironmentStrings(strINIFileLocation, szPath, MAX_PATH) == 0)
  108. return FALSE;
  109. CStdioFile inifile;
  110. if (inifile.Open(szPath, CFile::modeCreate | CFile::modeWrite | CFile::typeText))
  111. {
  112. // We need to traverse the tree structure to get the new contents of
  113. // the file.
  114. HWND hwndTree = m_tree.m_hWnd;
  115. HTREEITEM htiLine = TreeView_GetRoot(hwndTree);
  116. TVITEM tvi;
  117. TCHAR szBuffer[MAX_PATH];
  118. tvi.mask = TVIF_TEXT | TVIF_IMAGE;
  119. tvi.pszText = szBuffer;
  120. while (htiLine)
  121. {
  122. tvi.hItem = htiLine;
  123. tvi.cchTextMax = MAX_PATH;
  124. if (TreeView_GetItem(hwndTree, &tvi))
  125. {
  126. CString strLine(tvi.pszText);
  127. CString strCheck(strLine);
  128. strCheck.TrimLeft();
  129. if (!strCheck.IsEmpty())
  130. {
  131. if (!fUndoable && strLine.Find(DISABLE_STRING) != -1)
  132. strLine.Replace(DISABLE_STRING, _T("; "));
  133. strLine += CString(_T("\n"));
  134. inifile.WriteString(strLine);
  135. }
  136. }
  137. HTREEITEM htiNext = TreeView_GetChild(hwndTree, htiLine);
  138. if (htiNext)
  139. {
  140. htiLine = htiNext;
  141. continue;
  142. }
  143. htiNext = TreeView_GetNextSibling(hwndTree, htiLine);
  144. if (htiNext)
  145. {
  146. htiLine = htiNext;
  147. continue;
  148. }
  149. htiNext = TreeView_GetParent(hwndTree, htiLine);
  150. if (htiNext)
  151. {
  152. htiNext = TreeView_GetNextSibling(hwndTree, htiNext);
  153. if (htiNext)
  154. {
  155. htiLine = htiNext;
  156. continue;
  157. }
  158. }
  159. htiLine = NULL;
  160. }
  161. inifile.Close();
  162. }
  163. else
  164. return FALSE;
  165. return TRUE;
  166. }
  167. //-------------------------------------------------------------------------
  168. // Updates the tree view to show the contents of the internal structures.
  169. //-------------------------------------------------------------------------
  170. void CPageIni::UpdateTreeView()
  171. {
  172. TreeView_DeleteAllItems(m_tree.m_hWnd);
  173. ASSERT(m_iLastLine < m_lines.GetSize());
  174. if (m_iLastLine > m_lines.GetSize())
  175. return;
  176. TVINSERTSTRUCT tvis;
  177. tvis.hParent = TVI_ROOT;
  178. tvis.hInsertAfter = TVI_LAST;
  179. tvis.itemex.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
  180. tvis.itemex.iImage = m_checkedID;
  181. tvis.itemex.iSelectedImage = m_checkedID;
  182. // Add each line to the tree view.
  183. int iDisableLen = _tcslen(DISABLE_STRING);
  184. int iDisableLenHdr = _tcslen(DISABLE_STRING_HDR);
  185. for (int i = 0; i <= m_iLastLine; i++)
  186. {
  187. CString strLine = m_lines.GetAt(i);
  188. tvis.itemex.pszText = (LPTSTR)(LPCTSTR)strLine;
  189. if (!strLine.IsEmpty() && (_tcsnccmp((LPCTSTR)strLine, DISABLE_STRING, iDisableLen) == 0))
  190. tvis.itemex.iImage = tvis.itemex.iSelectedImage = m_uncheckedID;
  191. else
  192. tvis.itemex.iImage = tvis.itemex.iSelectedImage = m_checkedID;
  193. BOOL fSectionHeader = FALSE;
  194. if (!strLine.IsEmpty())
  195. {
  196. if (strLine[0] == _T('['))
  197. fSectionHeader = TRUE;
  198. else if (_tcsnccmp((LPCTSTR)strLine, DISABLE_STRING_HDR, iDisableLenHdr) == 0)
  199. fSectionHeader = TRUE;
  200. }
  201. if (fSectionHeader)
  202. {
  203. tvis.hParent = TVI_ROOT;
  204. tvis.hParent = TreeView_InsertItem(m_tree.m_hWnd, &tvis);
  205. }
  206. else
  207. TreeView_InsertItem(m_tree.m_hWnd, &tvis);
  208. }
  209. // Now scan the top level of the tree view. For every node which
  210. // has children, we want to set the image appropriately.
  211. for (HTREEITEM hti = TreeView_GetRoot(m_tree.m_hWnd); hti; hti = TreeView_GetNextSibling(m_tree.m_hWnd, hti))
  212. if (TreeView_GetChild(m_tree.m_hWnd, hti) != NULL)
  213. UpdateLine(hti);
  214. UpdateControls();
  215. }
  216. //-------------------------------------------------------------------------
  217. // Update the image state of the specified line, based on the text in
  218. // the line. If the line is a bracketed section header, this will involve
  219. // scanning the children. Returns the index of the image set for the node.
  220. //-------------------------------------------------------------------------
  221. int CPageIni::UpdateLine(HTREEITEM hti)
  222. {
  223. if (hti == NULL)
  224. return 0;
  225. TVITEM tvi;
  226. tvi.hItem = hti;
  227. int iNewImageIndex = m_checkedID;
  228. HTREEITEM htiChild = TreeView_GetChild(m_tree.m_hWnd, hti);
  229. if (htiChild)
  230. {
  231. BOOL fEnabledChild = FALSE, fDisabledChild = FALSE;
  232. while (htiChild)
  233. {
  234. if (UpdateLine(htiChild) == m_checkedID)
  235. fEnabledChild = TRUE;
  236. else
  237. fDisabledChild = TRUE;
  238. htiChild = TreeView_GetNextSibling(m_tree.m_hWnd, htiChild);
  239. }
  240. if (fDisabledChild)
  241. iNewImageIndex = (fEnabledChild) ? m_fuzzyID : m_uncheckedID;
  242. }
  243. else
  244. {
  245. TCHAR szBuffer[MAX_PATH]; // seems like a reasonably big value
  246. tvi.mask = TVIF_TEXT;
  247. tvi.pszText = szBuffer;
  248. tvi.cchTextMax = MAX_PATH;
  249. if (TreeView_GetItem(m_tree.m_hWnd, &tvi))
  250. iNewImageIndex = (_tcsnccmp(tvi.pszText, DISABLE_STRING, _tcslen(DISABLE_STRING)) == 0) ? m_uncheckedID : m_checkedID;
  251. }
  252. tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
  253. if (TreeView_GetItem(m_tree.m_hWnd, &tvi) && tvi.iImage != iNewImageIndex)
  254. {
  255. tvi.iSelectedImage = tvi.iImage = iNewImageIndex;
  256. TreeView_SetItem(m_tree.m_hWnd, &tvi);
  257. }
  258. return iNewImageIndex;
  259. }
  260. //-------------------------------------------------------------------------
  261. // Enable or disable a node in the tree (and its children).
  262. //-------------------------------------------------------------------------
  263. void CPageIni::SetEnable(BOOL fEnable, HTREEITEM htiNode, BOOL fUpdateLine, BOOL fBroadcast)
  264. {
  265. HTREEITEM hti = (htiNode) ? htiNode : TreeView_GetSelection(m_tree.m_hWnd);
  266. if (hti == NULL)
  267. return;
  268. HTREEITEM htiChild = TreeView_GetChild(m_tree.m_hWnd, hti);
  269. if (htiChild)
  270. {
  271. while (htiChild)
  272. {
  273. SetEnable(fEnable, htiChild, FALSE, FALSE);
  274. htiChild = TreeView_GetNextSibling(m_tree.m_hWnd, htiChild);
  275. }
  276. UpdateLine(hti);
  277. }
  278. else
  279. {
  280. int iDisableLen = _tcslen(DISABLE_STRING);
  281. TCHAR szBuffer[MAX_PATH]; // seems like a reasonably big value
  282. TVITEM tvi;
  283. tvi.hItem = hti;
  284. tvi.mask = TVIF_TEXT;
  285. tvi.pszText = &szBuffer[iDisableLen]; // leave some room to add disable string
  286. tvi.cchTextMax = MAX_PATH + iDisableLen;
  287. if (TreeView_GetItem(m_tree.m_hWnd, &tvi))
  288. {
  289. BOOL fAlreadyEnabled = (_tcsnccmp(&szBuffer[iDisableLen], DISABLE_STRING, iDisableLen) != 0);
  290. if (fEnable != fAlreadyEnabled)
  291. {
  292. if (fEnable)
  293. tvi.pszText = &szBuffer[iDisableLen * 2];
  294. else
  295. {
  296. _tcsncpy(szBuffer, DISABLE_STRING, iDisableLen);
  297. tvi.pszText = szBuffer;
  298. }
  299. TreeView_SetItem(m_tree.m_hWnd, &tvi);
  300. if (fUpdateLine)
  301. {
  302. UpdateLine(hti);
  303. if (TreeView_GetParent(m_tree.m_hWnd, hti))
  304. UpdateLine(TreeView_GetParent(m_tree.m_hWnd, hti));
  305. }
  306. }
  307. }
  308. }
  309. if (fBroadcast)
  310. SetModified(TRUE);
  311. }
  312. //-------------------------------------------------------------------------
  313. // Move the specified branch in the tree view to a new location.
  314. //-------------------------------------------------------------------------
  315. void CPageIni::MoveBranch(HWND hwndTree, HTREEITEM htiMove, HTREEITEM htiParent, HTREEITEM htiAfter)
  316. {
  317. HTREEITEM htiNew = CopyBranch(hwndTree, htiMove, htiParent, htiAfter);
  318. if (htiNew != NULL)
  319. {
  320. TreeView_SelectItem(hwndTree, htiNew);
  321. TreeView_DeleteItem(hwndTree, htiMove);
  322. SetModified(TRUE);
  323. }
  324. }
  325. HTREEITEM CPageIni::CopyBranch(HWND hwndTree, HTREEITEM htiFrom, HTREEITEM htiToParent, HTREEITEM htiToAfter)
  326. {
  327. TCHAR szBuffer[MAX_PATH];
  328. TVINSERTSTRUCT tvis;
  329. tvis.item.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_PARAM | TVIF_SELECTEDIMAGE | TVIF_TEXT | TVIF_STATE;
  330. tvis.item.pszText = szBuffer;
  331. tvis.item.cchTextMax = MAX_PATH;
  332. tvis.item.hItem = htiFrom;
  333. tvis.item.stateMask = TVIS_EXPANDED;
  334. HTREEITEM htiNew = NULL;
  335. if (TreeView_GetItem(hwndTree, &tvis.item))
  336. {
  337. tvis.hParent = htiToParent;
  338. tvis.hInsertAfter = htiToAfter;
  339. htiNew = TreeView_InsertItem(hwndTree, &tvis);
  340. }
  341. HTREEITEM htiPrevious = TVI_FIRST;
  342. if (htiNew)
  343. for (HTREEITEM htiChild = TreeView_GetChild(hwndTree, htiFrom); htiChild; htiChild = TreeView_GetNextSibling(hwndTree, htiChild))
  344. htiPrevious = CopyBranch(hwndTree, htiChild, htiNew, htiPrevious);
  345. return htiNew;
  346. }
  347. //-------------------------------------------------------------------------
  348. // Update the controls to reflect the state of the selection.
  349. //-------------------------------------------------------------------------
  350. void CPageIni::UpdateControls()
  351. {
  352. BOOL fEnable = FALSE;
  353. BOOL fDisable = FALSE;
  354. BOOL fMoveUp = FALSE;
  355. BOOL fMoveDown = FALSE;
  356. HTREEITEM htiSelection = TreeView_GetSelection(m_tree.m_hWnd);
  357. if (htiSelection)
  358. {
  359. fMoveUp = (TreeView_GetPrevSibling(m_tree.m_hWnd, htiSelection) != NULL);
  360. fMoveDown = (TreeView_GetNextSibling(m_tree.m_hWnd, htiSelection) != NULL);
  361. TVITEM tvi;
  362. tvi.hItem = htiSelection;
  363. tvi.mask = TVIF_IMAGE;
  364. if (TreeView_GetItem(m_tree.m_hWnd, &tvi))
  365. {
  366. fEnable = (tvi.iImage != m_checkedID);
  367. fDisable = (tvi.iImage != m_uncheckedID);
  368. }
  369. }
  370. HWND hwndFocus = ::GetFocus();
  371. CPageBase::TabState state = GetCurrentTabState();
  372. ::EnableWindow(GetDlgItemHWND(IDC_BUTTONINIDISABLEALL), (state != DIAGNOSTIC));
  373. ::EnableWindow(GetDlgItemHWND(IDC_BUTTONINIENABLEALL), (state != NORMAL));
  374. if ((state == DIAGNOSTIC) && hwndFocus == GetDlgItemHWND(IDC_BUTTONINIDISABLEALL))
  375. PrevDlgCtrl();
  376. if ((state == NORMAL) && hwndFocus == GetDlgItemHWND(IDC_BUTTONINIENABLEALL))
  377. NextDlgCtrl();
  378. ::EnableWindow(GetDlgItemHWND(IDC_BUTTONINIDISABLE), fDisable);
  379. ::EnableWindow(GetDlgItemHWND(IDC_BUTTONINIENABLE), fEnable);
  380. ::EnableWindow(GetDlgItemHWND(IDC_BUTTONINIMOVEUP), fMoveUp);
  381. ::EnableWindow(GetDlgItemHWND(IDC_BUTTONINIMOVEDOWN), fMoveDown);
  382. if (!fMoveUp && hwndFocus == GetDlgItemHWND(IDC_BUTTONINIMOVEUP))
  383. NextDlgCtrl();
  384. if (!fMoveDown && hwndFocus == GetDlgItemHWND(IDC_BUTTONINIMOVEDOWN))
  385. PrevDlgCtrl();
  386. if (!fEnable && hwndFocus == GetDlgItemHWND(IDC_BUTTONINIENABLE))
  387. NextDlgCtrl();
  388. if (!fDisable && hwndFocus == GetDlgItemHWND(IDC_BUTTONINIDISABLE))
  389. PrevDlgCtrl();
  390. }
  391. //-------------------------------------------------------------------------
  392. // Get the next item in the tree. Since we know this won't be more than
  393. // two levels deep, we don't need to have a loop.
  394. //-------------------------------------------------------------------------
  395. HTREEITEM CPageIni::GetNextItem(HTREEITEM hti)
  396. {
  397. if (hti == NULL)
  398. return NULL;
  399. HTREEITEM htiNext = TreeView_GetChild(m_tree.m_hWnd, hti);
  400. if (htiNext != NULL)
  401. return htiNext;
  402. htiNext = TreeView_GetNextSibling(m_tree.m_hWnd, hti);
  403. if (htiNext != NULL)
  404. return htiNext;
  405. htiNext = TreeView_GetParent(m_tree.m_hWnd, hti);
  406. if (htiNext != NULL)
  407. htiNext = TreeView_GetNextSibling(m_tree.m_hWnd, htiNext);
  408. return htiNext;
  409. }
  410. /////////////////////////////////////////////////////////////////////////////
  411. // CPageIni message handlers
  412. BOOL CPageIni::OnInitDialog()
  413. {
  414. CPropertyPage::OnInitDialog();
  415. // These items are initially disabled.
  416. ::EnableWindow(GetDlgItemHWND(IDC_BUTTONINIDISABLE), FALSE);
  417. ::EnableWindow(GetDlgItemHWND(IDC_BUTTONINIENABLE), FALSE);
  418. ::EnableWindow(GetDlgItemHWND(IDC_BUTTONINIMOVEUP), FALSE);
  419. ::EnableWindow(GetDlgItemHWND(IDC_BUTTONINIMOVEDOWN), FALSE);
  420. m_tree.Attach(GetDlgItemHWND(IDC_INITREE));
  421. VERIFY(m_fImageList = m_imagelist.Create(IDB_IMAGELIST, 0, 3, RGB(255, 0, 255)));
  422. if (m_fImageList)
  423. TreeView_SetImageList(m_tree.m_hWnd, m_imagelist, TVSIL_NORMAL);
  424. // If we are running on an RTL system, then the bitmaps for the check boxes
  425. // will be reversed. The imagemap includes reversed versions of the checked
  426. // and indetermined state, so we should just use the appropriate index.
  427. DWORD dwLayout;
  428. BOOL fRTL = FALSE;
  429. if (::GetProcessDefaultLayout(&dwLayout))
  430. fRTL = ((dwLayout & LAYOUT_RTL) != 0);
  431. m_checkedID = (fRTL) ? IMG_CHECKED_RTL : IMG_CHECKED;
  432. m_fuzzyID = (fRTL) ? IMG_FUZZY_RTL : IMG_FUZZY;
  433. m_uncheckedID = IMG_UNCHECKED;
  434. if (LoadINIFile(m_lines, m_iLastLine))
  435. UpdateTreeView();
  436. else
  437. {
  438. // set controls for no file TBD
  439. }
  440. m_fInitialized = TRUE;
  441. return TRUE; // return TRUE unless you set the focus to a control
  442. }
  443. //-------------------------------------------------------------------------
  444. // When the user clicks on an enable or disable button, we'll modify the
  445. // text in the tree view and update the images.
  446. //-------------------------------------------------------------------------
  447. void CPageIni::OnButtonDisable()
  448. {
  449. SetEnable(FALSE);
  450. UpdateControls();
  451. }
  452. void CPageIni::OnButtonDisableAll()
  453. {
  454. for (HTREEITEM hti = TreeView_GetRoot(m_tree.m_hWnd); hti; hti = TreeView_GetNextSibling(m_tree.m_hWnd, hti))
  455. SetEnable(FALSE, hti, TRUE);
  456. UpdateControls();
  457. }
  458. void CPageIni::OnButtonEnable()
  459. {
  460. SetEnable(TRUE);
  461. UpdateControls();
  462. }
  463. void CPageIni::OnButtonEnableAll()
  464. {
  465. for (HTREEITEM hti = TreeView_GetRoot(m_tree.m_hWnd); hti; hti = TreeView_GetNextSibling(m_tree.m_hWnd, hti))
  466. SetEnable(TRUE, hti, TRUE);
  467. UpdateControls();
  468. }
  469. //-------------------------------------------------------------------------
  470. // Move a branch of the tree up or down.
  471. //-------------------------------------------------------------------------
  472. void CPageIni::OnButtonMoveDown()
  473. {
  474. HTREEITEM htiSelection = TreeView_GetSelection(m_tree.m_hWnd);
  475. if (htiSelection)
  476. {
  477. HTREEITEM htiParent = TreeView_GetParent(m_tree.m_hWnd, htiSelection);
  478. HTREEITEM htiNext = TreeView_GetNextSibling(m_tree.m_hWnd, htiSelection);
  479. if (htiNext == NULL)
  480. return;
  481. if (htiParent == NULL)
  482. htiParent = TVI_ROOT;
  483. MoveBranch(m_tree.m_hWnd, htiSelection, htiParent, htiNext);
  484. }
  485. }
  486. void CPageIni::OnButtonMoveUp()
  487. {
  488. HTREEITEM htiSelection = TreeView_GetSelection(m_tree.m_hWnd);
  489. if (htiSelection)
  490. {
  491. HTREEITEM htiParent = TreeView_GetParent(m_tree.m_hWnd, htiSelection);
  492. HTREEITEM htiPrevious = TreeView_GetPrevSibling(m_tree.m_hWnd, htiSelection);
  493. if (htiPrevious == NULL)
  494. return;
  495. htiPrevious = TreeView_GetPrevSibling(m_tree.m_hWnd, htiPrevious);
  496. if (htiPrevious == NULL)
  497. htiPrevious = TVI_FIRST;
  498. if (htiParent == NULL)
  499. htiParent = TVI_ROOT;
  500. MoveBranch(m_tree.m_hWnd, htiSelection, htiParent, htiPrevious);
  501. }
  502. }
  503. void CPageIni::OnSelChangedTree(NMHDR * pNMHDR, LRESULT * pResult)
  504. {
  505. NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW *)pNMHDR;
  506. UpdateControls();
  507. *pResult = 0;
  508. }
  509. //-------------------------------------------------------------------------
  510. // Search the tree view for a string (present a dialog to the user).
  511. //-------------------------------------------------------------------------
  512. void CPageIni::OnButtonSearch()
  513. {
  514. CMSConfigFind find;
  515. find.m_strSearchFor = m_strLastSearch;
  516. if (find.DoModal() == IDOK && !find.m_strSearchFor.IsEmpty())
  517. {
  518. CString strSearch(find.m_strSearchFor);
  519. m_strLastSearch = strSearch;
  520. strSearch.MakeLower();
  521. HTREEITEM htiSearch;
  522. if (find.m_fSearchFromTop)
  523. htiSearch = TreeView_GetRoot(m_tree.m_hWnd);
  524. else
  525. {
  526. htiSearch = TreeView_GetSelection(m_tree.m_hWnd);
  527. if (htiSearch == NULL)
  528. htiSearch = TreeView_GetRoot(m_tree.m_hWnd);
  529. else
  530. htiSearch = GetNextItem(htiSearch);
  531. }
  532. TVITEM tvi;
  533. TCHAR szBuffer[MAX_PATH];
  534. tvi.mask = TVIF_TEXT | TVIF_IMAGE;
  535. tvi.pszText = szBuffer;
  536. while (htiSearch != NULL)
  537. {
  538. tvi.hItem = htiSearch;
  539. tvi.cchTextMax = MAX_PATH;
  540. if (TreeView_GetItem(m_tree.m_hWnd, &tvi))
  541. {
  542. CString strItem(szBuffer);
  543. strItem.MakeLower();
  544. if (strItem.Find(strSearch) != -1)
  545. {
  546. // We found a hit. Select the node.
  547. TreeView_SelectItem(m_tree.m_hWnd, htiSearch);
  548. break;
  549. }
  550. }
  551. htiSearch = GetNextItem(htiSearch);
  552. }
  553. if (htiSearch == NULL)
  554. Message(IDS_NOFIND);
  555. }
  556. }
  557. //-------------------------------------------------------------------------
  558. // The current tab state can be found by looking through the tree view.
  559. //-------------------------------------------------------------------------
  560. CPageBase::TabState CPageIni::GetCurrentTabState()
  561. {
  562. if (!m_fInitialized)
  563. return GetAppliedTabState();
  564. BOOL fAllEnabled = TRUE, fAllDisabled = TRUE;
  565. HTREEITEM hti = TreeView_GetRoot(m_tree.m_hWnd);
  566. TVITEM tvi;
  567. tvi.mask = TVIF_IMAGE;
  568. while (hti)
  569. {
  570. tvi.hItem = hti;
  571. if (TreeView_GetItem(m_tree.m_hWnd, &tvi))
  572. {
  573. if (m_uncheckedID != tvi.iImage)
  574. fAllDisabled = FALSE;
  575. if (m_checkedID != tvi.iImage)
  576. fAllEnabled = FALSE;
  577. }
  578. hti = TreeView_GetNextSibling(m_tree.m_hWnd, hti);
  579. }
  580. return ((fAllEnabled) ? NORMAL : ((fAllDisabled) ? DIAGNOSTIC : USER));
  581. }
  582. //-------------------------------------------------------------------------
  583. // Apply the changes by saving the INI file.
  584. //
  585. // The base class implementation is called to maintain the
  586. // applied tab state.
  587. //-------------------------------------------------------------------------
  588. BOOL CPageIni::OnApply()
  589. {
  590. WriteINIFile(m_lines, m_iLastLine);
  591. CPageBase::SetAppliedState(GetCurrentTabState());
  592. m_fMadeChange = TRUE;
  593. return TRUE;
  594. }
  595. //-------------------------------------------------------------------------
  596. // To commit the changes, write the INI file without the distinguishing
  597. // comments (by calling WriteINIFile with FALSE as the last param).
  598. //
  599. // Then call the base class implementation.
  600. //-------------------------------------------------------------------------
  601. void CPageIni::CommitChanges()
  602. {
  603. WriteINIFile(m_lines, m_iLastLine, FALSE);
  604. LoadINIFile(m_lines, m_iLastLine);
  605. UpdateTreeView();
  606. CPageBase::CommitChanges();
  607. }
  608. //-------------------------------------------------------------------------
  609. // Set the overall state of the tab to normal or diagnostic.
  610. //-------------------------------------------------------------------------
  611. void CPageIni::SetNormal()
  612. {
  613. HWND hwndTree = m_tree.m_hWnd;
  614. HTREEITEM hti = TreeView_GetRoot(hwndTree);
  615. while (hti != NULL)
  616. {
  617. SetEnable(TRUE, hti, TRUE, FALSE);
  618. hti = TreeView_GetNextSibling(hwndTree, hti);
  619. }
  620. SetModified(TRUE);
  621. UpdateControls();
  622. }
  623. void CPageIni::SetDiagnostic()
  624. {
  625. HWND hwndTree = m_tree.m_hWnd;
  626. HTREEITEM hti = TreeView_GetRoot(hwndTree);
  627. while (hti != NULL)
  628. {
  629. SetEnable(FALSE, hti, TRUE, FALSE);
  630. hti = TreeView_GetNextSibling(hwndTree, hti);
  631. }
  632. SetModified(TRUE);
  633. UpdateControls();
  634. }
  635. //-------------------------------------------------------------------------
  636. // We need to look at user clicks on the tree view. If it is on an item,
  637. // and also on the item's image, then we'll need to toggle the image
  638. // state.
  639. //-------------------------------------------------------------------------
  640. void CPageIni::OnClickTree(NMHDR* pNMHDR, LRESULT* pResult)
  641. {
  642. // Determine if this tree click is on a node, and if it is,
  643. // if it is on the image.
  644. TVHITTESTINFO tvhti;
  645. DWORD dwPoint = GetMessagePos();
  646. tvhti.pt.x = ((int)(short)LOWORD(dwPoint));
  647. tvhti.pt.y = ((int)(short)HIWORD(dwPoint));
  648. ::ScreenToClient(m_tree.m_hWnd, &tvhti.pt);
  649. HTREEITEM hti = TreeView_HitTest(m_tree.m_hWnd, &tvhti);
  650. if (hti != NULL && (tvhti.flags & TVHT_ONITEMICON) != 0)
  651. {
  652. // This is a click that we care about. We need to get the
  653. // current state of this node so we know which way to
  654. // toggle the state. We'll make an arbitrary decision
  655. // that the toggle from undetermined is to enabled.
  656. TVITEM tvi;
  657. tvi.hItem = hti;
  658. tvi.mask = TVIF_IMAGE;
  659. if (TreeView_GetItem(m_tree.m_hWnd, &tvi))
  660. {
  661. SetEnable(tvi.iImage != m_checkedID, hti);
  662. UpdateControls();
  663. }
  664. }
  665. }
  666. //-------------------------------------------------------------------------
  667. // We allow the user to edit the lines in the INI file. When the user
  668. // is through editing, we want to make sure we notify the framework
  669. // that a change has been made.
  670. //-------------------------------------------------------------------------
  671. void CPageIni::OnButtonEdit()
  672. {
  673. HTREEITEM hti = TreeView_GetSelection(m_tree.m_hWnd);
  674. if (hti != NULL)
  675. {
  676. ::SetFocus(m_tree.m_hWnd);
  677. TreeView_EditLabel(m_tree.m_hWnd, hti);
  678. }
  679. }
  680. //-------------------------------------------------------------------------
  681. // WndProc for the edit control when editing a label in the tree (handles
  682. // enter/esc better). Lifted from ME source.
  683. //-------------------------------------------------------------------------
  684. WNDPROC pOldEditProc = NULL; // save old wndproc when we subclass edit control
  685. LRESULT TreeViewEditSubclassProc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
  686. {
  687. switch (wm)
  688. {
  689. case WM_GETDLGCODE:
  690. return DLGC_WANTALLKEYS;
  691. // The Knowledge Base article describing the work-around for this
  692. // this bug indicates the following handling of VK_ESCAPE & VK_RETURN
  693. // is necessary -- however, under Memphis & OSR2 these keys are never
  694. // received (returning DLGC_WANTALLKEYS seems to fix the problem).
  695. // Perhaps it depends on which comctl32.dll is installed...
  696. case WM_CHAR:
  697. if (wp == VK_ESCAPE || wp == VK_RETURN)
  698. {
  699. TreeView_EndEditLabelNow(GetParent(hwnd), wp == VK_ESCAPE);
  700. return 0;
  701. }
  702. break;
  703. }
  704. if (pOldEditProc) // better not be null
  705. return CallWindowProc(pOldEditProc, hwnd, wm, wp, lp);
  706. return 0;
  707. }
  708. //-------------------------------------------------------------------------
  709. // The tree view doesn't handle enter and esc correctly, so when we start
  710. // editing a label, we need to subclass the control.
  711. //-------------------------------------------------------------------------
  712. void CPageIni::OnBeginLabelEditIniTree(NMHDR * pNMHDR, LRESULT * pResult)
  713. {
  714. TV_DISPINFO * pTVDispInfo = (TV_DISPINFO *)pNMHDR;
  715. // Disable Move Up and Down buttons while editing.
  716. ::EnableWindow(GetDlgItemHWND(IDC_BUTTONINIMOVEUP), FALSE);
  717. ::EnableWindow(GetDlgItemHWND(IDC_BUTTONINIMOVEDOWN), FALSE);
  718. ::EnableWindow(GetDlgItemHWND(IDC_BUTTONINIEDIT), FALSE);
  719. // TreeView controls don't properly handle Esc/Enter when editing
  720. // a label. To fix this, it's necessary to subclass the label's edit
  721. // control and process Esc & Enter ourselves. Sigh...
  722. HWND hWndEdit = TreeView_GetEditControl(m_tree.m_hWnd);
  723. if (hWndEdit)
  724. {
  725. pOldEditProc = (WNDPROC)::GetWindowLongPtr(hWndEdit, GWLP_WNDPROC);
  726. ::SetWindowLongPtr(hWndEdit, GWLP_WNDPROC, (ULONG_PTR)(WNDPROC)&TreeViewEditSubclassProc);
  727. }
  728. *pResult = 0;
  729. }
  730. void CPageIni::OnEndLabelEdit(NMHDR* pNMHDR, LRESULT* pResult)
  731. {
  732. TV_DISPINFO * pTVDispInfo = (TV_DISPINFO *)pNMHDR;
  733. // Stop subclassing the edit control.
  734. HWND hWndEdit = TreeView_GetEditControl(m_tree.m_hWnd);
  735. if (hWndEdit && pOldEditProc)
  736. {
  737. ::SetWindowLongPtr(hWndEdit, GWLP_WNDPROC, (ULONG_PTR)(WNDPROC)pOldEditProc);
  738. pOldEditProc = NULL;
  739. }
  740. // If the new text pointer is null, then the edit was cancelled.
  741. // We only care if a new item was being added, in which case
  742. // we should delete it.
  743. if (pTVDispInfo->item.pszText == NULL)
  744. {
  745. TCHAR szBuffer[MAX_PATH];
  746. TVITEM tvi;
  747. tvi.pszText = szBuffer;
  748. tvi.mask = TVIF_TEXT;
  749. tvi.hItem = pTVDispInfo->item.hItem;
  750. tvi.cchTextMax = MAX_PATH;
  751. if (TreeView_GetItem(m_tree.m_hWnd, &tvi) && tvi.pszText && tvi.pszText[0] == _T('\0'))
  752. {
  753. HTREEITEM hPriorItem = TreeView_GetPrevSibling(pTVDispInfo->hdr.hwndFrom, pTVDispInfo->item.hItem);
  754. if (hPriorItem == NULL)
  755. hPriorItem = TreeView_GetParent(pTVDispInfo->hdr.hwndFrom, pTVDispInfo->item.hItem);
  756. TreeView_DeleteItem(m_tree.m_hWnd, pTVDispInfo->item.hItem);
  757. if (hPriorItem)
  758. TreeView_SelectItem(pTVDispInfo->hdr.hwndFrom, hPriorItem);
  759. }
  760. *pResult = 0;
  761. }
  762. else
  763. {
  764. SetModified(TRUE);
  765. *pResult = 1;
  766. }
  767. ::EnableWindow(GetDlgItemHWND(IDC_BUTTONINIEDIT), TRUE);
  768. UpdateControls();
  769. }
  770. //-------------------------------------------------------------------------
  771. // If the user clicks on the new button, then add an empty tree view
  772. // node after the currently selected one. If the selected node has
  773. // children, add the node as the first child under the selected node.
  774. // Then select the node for editing.
  775. //-------------------------------------------------------------------------
  776. void CPageIni::OnButtonNew()
  777. {
  778. HTREEITEM hti = TreeView_GetSelection(m_tree.m_hWnd);
  779. if (hti == NULL)
  780. hti = TreeView_GetRoot(m_tree.m_hWnd);
  781. if (hti == NULL)
  782. return;
  783. TVINSERTSTRUCT tvis;
  784. if (TreeView_GetChild(m_tree.m_hWnd, hti) != NULL)
  785. {
  786. tvis.hParent = hti;
  787. tvis.hInsertAfter = TVI_FIRST;
  788. }
  789. else
  790. {
  791. tvis.hParent = TreeView_GetParent(m_tree.m_hWnd, hti);
  792. tvis.hInsertAfter = hti;
  793. }
  794. TCHAR szBuffer[] = _T("");
  795. tvis.itemex.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
  796. tvis.itemex.iImage = m_checkedID;
  797. tvis.itemex.iSelectedImage = m_checkedID;
  798. tvis.itemex.pszText = szBuffer;
  799. HTREEITEM htiNew = TreeView_InsertItem(m_tree.m_hWnd, &tvis);
  800. if (htiNew != NULL)
  801. {
  802. TreeView_SelectItem(m_tree.m_hWnd, htiNew);
  803. TreeView_EditLabel(m_tree.m_hWnd, htiNew);
  804. }
  805. }
  806. //-------------------------------------------------------------------------
  807. // If the user hits the space bar with an item selected in the tree, toggle
  808. // the state of the item.
  809. //-------------------------------------------------------------------------
  810. void CPageIni::OnKeyDownTree(NMHDR* pNMHDR, LRESULT* pResult)
  811. {
  812. TV_KEYDOWN * pTVKeyDown = (TV_KEYDOWN *)pNMHDR;
  813. if (pTVKeyDown->wVKey == VK_SPACE)
  814. {
  815. HTREEITEM hti = TreeView_GetSelection(m_tree.m_hWnd);
  816. if (hti != NULL)
  817. {
  818. TVITEM tvi;
  819. tvi.mask = TVIF_IMAGE;
  820. tvi.hItem = hti;
  821. if (TreeView_GetItem(m_tree.m_hWnd, &tvi))
  822. {
  823. SetEnable(tvi.iImage != m_checkedID, hti);
  824. UpdateControls();
  825. }
  826. }
  827. }
  828. *pResult = 0;
  829. }