1 /*
2  * PROJECT:     ReactOS Applications
3  * LICENSE:     LGPL - See COPYING in the top level directory
4  * FILE:        base/applications/msconfig_new/toolspage.cpp
5  * PURPOSE:     Tools page message handler
6  * COPYRIGHT:   Copyright 2005-2006 Christoph von Wittich <Christoph@ApiViewer.de>
7  *              Copyright 2011-2012 Hermes BELUSCA - MAITO <hermes.belusca@sfr.fr>
8  */
9 
10 #include "precomp.h"
11 #include "xmldomparser.hpp"
12 #include "utils.h"
13 #include "listview.h"
14 #include "uxthemesupp.h"
15 
16 static HWND hToolsPage     = NULL;
17 static HWND hToolsListCtrl = NULL;
18 static int  iToolsPageSortedColumn  = 0;
19 
20 struct TOOL
21 {
22     TOOL(const _bstr_t& Command,
23          const _bstr_t& DefParam,
24          const _bstr_t& AdvParam) :
25         m_Command(Command),
26         m_DefParam(DefParam),
27         m_AdvParam(AdvParam)
28     { }
29 
30     ~TOOL(void)
31     { }
32 
33     DWORD Run(BOOL bUseAdvParams)
34     {
35         return RunCommand(m_Command, bUseAdvParams ? m_AdvParam : m_DefParam, SW_SHOW);
36     }
37 
38     _bstr_t m_Command;
39     _bstr_t m_DefParam;
40     _bstr_t m_AdvParam;
41 };
42 
43 static void AddTool(IXMLDOMElement*, BOOL);
44 
45 static HRESULT
46 ParseToolsList(IXMLDOMDocument* pXMLDom, BOOL bIsStandard)
47 {
48     static const _bstr_t XMLFileTag(L"MSCONFIGTOOLFILE");
49     static const _bstr_t XMLToolsTag(L"MSCONFIGTOOLS");
50 
51     HRESULT hr = S_OK;
52 
53     IXMLDOMNode    *pIterator = NULL, *pTmp = NULL;
54     IXMLDOMElement* pEl       = NULL;
55     DOMNodeType     type;
56     _bstr_t         tagName;
57 
58     if (!pXMLDom)
59         return E_POINTER; // E_INVALIDARG
60 
61     pXMLDom->get_documentElement(&pEl);
62 
63     pEl->get_tagName(&tagName.GetBSTR());
64     _wcsupr(tagName);
65     if (tagName == XMLFileTag)
66     {
67         pEl->get_firstChild(&pIterator); SAFE_RELEASE(pEl);
68         while (pIterator)
69         {
70             pIterator->get_nodeType(&type);
71             if (type == NODE_ELEMENT)
72             {
73                 pIterator->QueryInterface(IID_PPV_ARG(IXMLDOMElement, &pEl) /* IID_PPV_ARGS(&pEl) */);
74 
75                 pEl->get_tagName(&tagName.GetBSTR());
76                 _wcsupr(tagName);
77                 if (tagName == XMLToolsTag)
78                 {
79                     pEl->get_firstChild(&pIterator); SAFE_RELEASE(pEl);
80                     while (pIterator)
81                     {
82                         pIterator->QueryInterface(IID_PPV_ARG(IXMLDOMElement, &pEl) /* IID_PPV_ARGS(&pEl) */);
83                         AddTool(pEl, bIsStandard);
84                         SAFE_RELEASE(pEl);
85 
86                         pIterator->get_nextSibling(&pTmp);
87                         SAFE_RELEASE(pIterator); pIterator = pTmp;
88                     }
89                     // SAFE_RELEASE(pIterator);
90 
91                     break;
92                 }
93 
94                 SAFE_RELEASE(pEl);
95             }
96 
97             pIterator->get_nextSibling(&pTmp);
98             SAFE_RELEASE(pIterator); pIterator = pTmp;
99         }
100         // SAFE_RELEASE(pIterator);
101     }
102     else if (tagName == XMLToolsTag)
103     {
104         pEl->get_firstChild(&pIterator); SAFE_RELEASE(pEl);
105         while (pIterator)
106         {
107             pIterator->QueryInterface(IID_PPV_ARG(IXMLDOMElement, &pEl) /* IID_PPV_ARGS(&pEl) */);
108             AddTool(pEl, bIsStandard);
109             SAFE_RELEASE(pEl);
110 
111             pIterator->get_nextSibling(&pTmp);
112             SAFE_RELEASE(pIterator); pIterator = pTmp;
113         }
114         // SAFE_RELEASE(pIterator);
115     }
116 
117     SAFE_RELEASE(pEl);
118 
119     return hr;
120 }
121 
122 static void
123 AddItem(BOOL bIsStandard, const _bstr_t& name, const _bstr_t& descr, TOOL* tool)
124 {
125     LPWSTR lpszStandard;
126     LVITEM item = {};
127 
128     assert(tool);
129 
130     item.mask   = LVIF_TEXT | LVIF_PARAM;
131     item.lParam = (LPARAM)tool;
132 
133     item.pszText  = (LPWSTR)name;
134     item.iSubItem = 0;
135     // item.iItem    = ListView_GetItemCount(hToolsListCtrl);
136 
137     ListView_InsertItem(hToolsListCtrl, &item);
138 
139     if (bIsStandard)
140     {
141         lpszStandard = LoadResourceString(hInst, IDS_YES);
142         ListView_SetItemText(hToolsListCtrl, item.iItem, 1, lpszStandard);
143         MemFree(lpszStandard);
144     }
145     else
146     {
147         lpszStandard = LoadResourceString(hInst, IDS_NO);
148         ListView_SetItemText(hToolsListCtrl, item.iItem, 1, lpszStandard);
149         MemFree(lpszStandard);
150     }
151 
152     ListView_SetItemText(hToolsListCtrl, item.iItem, 2, (LPWSTR)descr);
153 }
154 
155 static void
156 AddTool(IXMLDOMElement* pXMLTool, BOOL bIsStandard)
157 {
158     TOOL* tool;
159     _variant_t varLocID, varName, varPath,
160                varDefOpt, varAdvOpt, varHelp;
161 
162     assert(pXMLTool);
163 
164     pXMLTool->getAttribute(_bstr_t(L"_locID")     , &varLocID );
165     pXMLTool->getAttribute(_bstr_t(L"NAME")       , &varName  );
166     pXMLTool->getAttribute(_bstr_t(L"PATH")       , &varPath  );
167     pXMLTool->getAttribute(_bstr_t(L"DEFAULT_OPT"), &varDefOpt);
168     pXMLTool->getAttribute(_bstr_t(L"ADV_OPT")    , &varAdvOpt);
169     pXMLTool->getAttribute(_bstr_t(L"HELP")       , &varHelp  );
170 
171     // TODO: check if the tool really exists... ??
172 
173     tool = new TOOL(_bstr_t(varPath), _bstr_t(varDefOpt), _bstr_t(varAdvOpt));
174     AddItem(bIsStandard, _bstr_t(varName), _bstr_t(varHelp), tool);
175 }
176 
177 static void
178 FillListView(void)
179 {
180     IXMLDOMDocument* pXMLDom = NULL;
181 
182     if (!SUCCEEDED(InitXMLDOMParser()))
183         return;
184 
185     if (SUCCEEDED(CreateAndInitXMLDOMDocument(&pXMLDom)))
186     {
187         // Load the internal tools list.
188         if (LoadXMLDocumentFromResource(pXMLDom, L"MSCFGTL.XML"))
189             ParseToolsList(pXMLDom, TRUE);
190 
191         // Try to load the user-provided tools list. If it doesn't exist,
192         // then the second list-view's column "Standard" tool is removed.
193         if (LoadXMLDocumentFromFile(pXMLDom, L"MSCFGTLC.XML", TRUE))
194             ParseToolsList(pXMLDom, FALSE);
195         else
196             ListView_DeleteColumn(hToolsListCtrl, 1);
197     }
198 
199     SAFE_RELEASE(pXMLDom);
200     UninitXMLDOMParser();
201 }
202 
203 static size_t
204 BuildCommandLine(LPWSTR lpszDest, LPCWSTR lpszCmdLine, LPCWSTR lpszParam, size_t bufSize)
205 {
206     size_t numOfChars = 0; // The null character is counted in ExpandEnvironmentStrings(...).
207     // TODO: Take into account the "plus one" for numOfChars for ANSI version (see MSDN for more details).
208 
209     if (lpszCmdLine && *lpszCmdLine)
210     {
211         numOfChars += ExpandEnvironmentStringsW(lpszCmdLine, NULL, 0);
212         if (lpszDest)
213             ExpandEnvironmentStringsW(lpszCmdLine, lpszDest, (DWORD)bufSize); // TODO: size_t to DWORD conversion !
214 
215         if (lpszParam && *lpszParam)
216         {
217             ++numOfChars;
218             if (lpszDest)
219                 wcscat(lpszDest, L" ");
220         }
221     }
222 
223     if (lpszParam && *lpszParam)
224     {
225         numOfChars += wcslen(lpszParam);
226         if (lpszDest)
227             wcscat(lpszDest, lpszParam);
228     }
229 
230     return numOfChars;
231 }
232 
233 static void Update_States(int iSelectedItem)
234 {
235     TOOL* tool;
236     LVITEM item = {};
237 
238     assert(hToolsPage);
239 
240     item.mask  = LVIF_PARAM;
241     item.iItem = iSelectedItem;
242 
243     if (ListView_GetItem(hToolsListCtrl, &item)) // (item.iItem > -1) // TODO: corriger ailleurs ce genre de code...
244     {
245         LPWSTR lpszCmdLine = NULL;
246         size_t numOfChars  = 0;
247         tool = reinterpret_cast<TOOL*>(item.lParam);
248 
249         ListView_EnsureVisible(hToolsListCtrl, item.iItem, FALSE);
250 
251         Button_Enable(GetDlgItem(hToolsPage, IDC_BTN_RUN), TRUE);
252 
253         if (!*(wchar_t*)tool->m_AdvParam)
254         {
255             ShowWindow(GetDlgItem(hToolsPage, IDC_CBX_TOOLS_ADVOPT), SW_HIDE);
256             Button_Enable(GetDlgItem(hToolsPage, IDC_CBX_TOOLS_ADVOPT), FALSE);
257         }
258         else
259         {
260             Button_Enable(GetDlgItem(hToolsPage, IDC_CBX_TOOLS_ADVOPT), TRUE);
261             ShowWindow(GetDlgItem(hToolsPage, IDC_CBX_TOOLS_ADVOPT), SW_NORMAL);
262         }
263 
264         if ( (Button_IsEnabled(GetDlgItem(hToolsPage, IDC_CBX_TOOLS_ADVOPT))) &&
265              (Button_GetCheck(GetDlgItem(hToolsPage, IDC_CBX_TOOLS_ADVOPT)) == BST_CHECKED) )
266         {
267             numOfChars = BuildCommandLine(NULL, tool->m_Command, tool->m_AdvParam, 0);
268             lpszCmdLine = (LPWSTR)MemAlloc(0, numOfChars * sizeof(WCHAR));
269             BuildCommandLine(lpszCmdLine, tool->m_Command, tool->m_AdvParam, numOfChars);
270         }
271         else
272         {
273             numOfChars = BuildCommandLine(NULL, tool->m_Command, tool->m_DefParam, 0);
274             lpszCmdLine = (LPWSTR)MemAlloc(0, numOfChars * sizeof(WCHAR));
275             BuildCommandLine(lpszCmdLine, tool->m_Command, tool->m_DefParam, numOfChars);
276         }
277 
278         SendDlgItemMessage(hToolsPage, IDC_TOOLS_CMDLINE, WM_SETTEXT, 0, (LPARAM)lpszCmdLine);
279 
280         MemFree(lpszCmdLine);
281     }
282     else
283     {
284         ShowWindow(GetDlgItem(hToolsPage, IDC_CBX_TOOLS_ADVOPT), SW_HIDE);
285         Button_Enable(GetDlgItem(hToolsPage, IDC_CBX_TOOLS_ADVOPT), FALSE);
286         Button_Enable(GetDlgItem(hToolsPage, IDC_BTN_RUN), FALSE);
287     }
288 }
289 
290 static BOOL RunSelectedTool(VOID)
291 {
292     BOOL Success = FALSE;
293     BOOL bUseAdvParams;
294 
295     LVITEM item = {};
296     item.mask = LVIF_PARAM;
297     item.iItem = ListView_GetSelectionMark(hToolsListCtrl);
298     ListView_GetItem(hToolsListCtrl, &item);
299 
300     if (ListView_GetItem(hToolsListCtrl, &item)) // (item.iItem > -1) // TODO: corriger ailleurs ce genre de code...
301     {
302         if ( (Button_IsEnabled(GetDlgItem(hToolsPage, IDC_CBX_TOOLS_ADVOPT))) &&
303              (Button_GetCheck(GetDlgItem(hToolsPage, IDC_CBX_TOOLS_ADVOPT)) == BST_CHECKED) )
304             bUseAdvParams = TRUE;
305         else
306             bUseAdvParams = FALSE;
307 
308         // Values greater (strictly) than 32 indicate success (see MSDN documentation for ShellExecute(...) API).
309         Success = (reinterpret_cast<TOOL*>(item.lParam)->Run(bUseAdvParams) > 32);
310     }
311 
312     return Success;
313 }
314 
315 extern "C" {
316 
317 INT_PTR CALLBACK
318 ToolsPageWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
319 {
320     switch (message)
321     {
322         case WM_INITDIALOG:
323         {
324             hToolsPage     = hDlg;
325             hToolsListCtrl = GetDlgItem(hToolsPage, IDC_TOOLS_LIST);
326 
327             //
328             // Initialize the styles.
329             //
330             DWORD dwStyle = ListView_GetExtendedListViewStyle(hToolsListCtrl);
331             ListView_SetExtendedListViewStyle(hToolsListCtrl, dwStyle | LVS_EX_FULLROWSELECT);
332             SetWindowTheme(hToolsListCtrl, L"Explorer", NULL);
333 
334             //
335             // Initialize the application page's controls.
336             //
337             LVCOLUMN column = {};
338 
339             // First column : Tool's name.
340             column.mask = LVCF_TEXT | LVCF_WIDTH;
341             column.pszText = LoadResourceString(hInst, IDS_TOOLS_COLUMN_NAME);
342             column.cx = 150;
343             ListView_InsertColumn(hToolsListCtrl, 0, &column);
344             MemFree(column.pszText);
345 
346             // Second column : Whether the tool is a standard one or not.
347             column.mask = LVCF_TEXT | LVCF_WIDTH;
348             column.pszText = LoadResourceString(hInst, IDS_TOOLS_COLUMN_STANDARD);
349             column.cx = 60;
350             ListView_InsertColumn(hToolsListCtrl, 1, &column);
351             MemFree(column.pszText);
352 
353             // Third column : Description.
354             column.mask = LVCF_TEXT | LVCF_WIDTH;
355             column.pszText = LoadResourceString(hInst, IDS_TOOLS_COLUMN_DESCR);
356             column.cx = 500;
357             ListView_InsertColumn(hToolsListCtrl, 2, &column);
358             MemFree(column.pszText);
359 
360             //
361             // Populate and sort the list.
362             //
363             FillListView();
364             ListView_Sort(hToolsListCtrl, 0);
365 
366             // Force an update in case of an empty list (giving focus on it when empty won't emit a LVN_ITEMCHANGED message).
367             Update_States(-1 /* Wrong index to initialize all the controls with their default state (i.e. disabled) */);
368 
369             PropSheet_UnChanged(GetParent(hToolsPage), hToolsPage);
370 
371             return TRUE;
372         }
373 
374         case WM_DESTROY:
375         {
376             LVITEM lvitem = {};
377             lvitem.mask  = LVIF_PARAM;
378             lvitem.iItem = -1; // From the beginning.
379 
380             while ((lvitem.iItem = ListView_GetNextItem(hToolsListCtrl, lvitem.iItem, LVNI_ALL)) != -1)
381             {
382                 // ListView_Update();   // Updates a list-view item.
383                 // ListView_FindItem(); // peut �tre int�ressant pour faire de la recherche it�rative � partir du nom (ou partie du...) de l'item.
384 
385                 ListView_GetItem(hToolsListCtrl, &lvitem);
386 
387                 delete reinterpret_cast<TOOL*>(lvitem.lParam);
388                 lvitem.lParam = NULL;
389             }
390             ListView_DeleteAllItems(hToolsListCtrl);
391 
392             return 0;
393         }
394 
395         case WM_COMMAND:
396         {
397             switch (LOWORD(wParam))
398             {
399                 case IDC_BTN_RUN:
400                 {
401                     RunSelectedTool();
402                     return TRUE;
403                 }
404 
405                 case IDC_CBX_TOOLS_ADVOPT:
406                 {
407                     Update_States(ListView_GetSelectionMark(hToolsListCtrl));
408                     return TRUE;
409                 }
410 
411                 default:
412                     return FALSE;
413             }
414             return FALSE;
415         }
416 
417         case WM_NOTIFY:
418         {
419             if (((LPNMHDR)lParam)->hwndFrom == hToolsListCtrl)
420             {
421                 switch (((LPNMHDR)lParam)->code)
422                 {
423                     case LVN_ITEMCHANGED:
424                     {
425                         if ( (((LPNMLISTVIEW)lParam)->uChanged  & LVIF_STATE) && /* The state has changed */
426                              (((LPNMLISTVIEW)lParam)->uNewState & LVIS_SELECTED) /* The item has been (de)selected */ )
427                         {
428                             Update_States(((LPNMLISTVIEW)lParam)->iItem);
429                         }
430 
431                         return TRUE;
432                     }
433 
434                     case NM_DBLCLK:
435                     case NM_RDBLCLK:
436                     {
437                         RunSelectedTool();
438                         return TRUE;
439                     }
440 
441                     case LVN_COLUMNCLICK:
442                     {
443                         int iSortingColumn = ((LPNMLISTVIEW)lParam)->iSubItem;
444 
445                         ListView_SortEx(hToolsListCtrl, iSortingColumn, iToolsPageSortedColumn);
446                         iToolsPageSortedColumn = iSortingColumn;
447 
448                         return TRUE;
449                     }
450 
451                     default:
452                         break;
453                 }
454             }
455             else
456             {
457                 switch (((LPNMHDR)lParam)->code)
458                 {
459                     case PSN_APPLY:
460                     {
461                         // Since there are nothing to modify, applying modifications
462                         // cannot return any error.
463                         SetWindowLongPtr(hToolsPage, DWLP_MSGRESULT, PSNRET_NOERROR);
464                         PropSheet_UnChanged(GetParent(hToolsPage), hToolsPage);
465                         return TRUE;
466                     }
467 
468                     case PSN_HELP:
469                     {
470                         MessageBoxW(hToolsPage, L"Help not implemented yet!", L"Help", MB_ICONINFORMATION | MB_OK);
471                         return TRUE;
472                     }
473 
474                     case PSN_KILLACTIVE: // Is going to lose activation.
475                     {
476                         // Changes are always valid of course.
477                         SetWindowLongPtr(hToolsPage, DWLP_MSGRESULT, FALSE);
478                         return TRUE;
479                     }
480 
481                     case PSN_QUERYCANCEL:
482                     {
483                         // Allows cancellation since there are nothing to cancel...
484                         SetWindowLongPtr(hToolsPage, DWLP_MSGRESULT, FALSE);
485                         return TRUE;
486                     }
487 
488                     case PSN_QUERYINITIALFOCUS:
489                     {
490                         // Give the focus on and select the first item.
491                         ListView_SetItemState(hToolsListCtrl, 0, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
492 
493                         SetWindowLongPtr(hToolsPage, DWLP_MSGRESULT, (LONG_PTR)hToolsListCtrl);
494                         return TRUE;
495                     }
496 
497                     //
498                     // DO NOT TOUCH THESE NEXT MESSAGES, THEY ARE OK LIKE THIS...
499                     //
500                     case PSN_RESET: // Perform final cleaning, called before WM_DESTROY.
501                         return TRUE;
502 
503                     case PSN_SETACTIVE: // Is going to gain activation.
504                     {
505                         SetWindowLongPtr(hToolsPage, DWLP_MSGRESULT, 0);
506                         return TRUE;
507                     }
508 
509                     default:
510                         break;
511                 }
512             }
513 
514             return FALSE;
515         }
516 
517         default:
518             return FALSE;
519     }
520 
521     // return FALSE;
522 }
523 
524 }
525 
526 /* EOF */
527