1 /*
2  * PROJECT:     ReactOS Applications
3  * LICENSE:     LGPL - See COPYING in the top level directory
4  * FILE:        base/applications/msconfig/treeview.c
5  * PURPOSE:     Tree-View helper functions.
6  * COPYRIGHT:   Copyright 2011-2012 Hermes BELUSCA - MAITO <hermes.belusca@sfr.fr>
7  */
8 
9 // For TVIF_EXPANDEDIMAGE and TVIF_STATEEX (are they really useful ?)
10 #if !defined(_WIN32_IE) || (_WIN32_IE < 0x0600)
11     #define _WIN32_IE 0x0600
12 #endif
13 
14 // Fake _WIN32_WINNT to 0x0600 in order to get Vista+ style flags
15 #undef _WIN32_WINNT
16 #define _WIN32_WINNT 0x0600
17 
18 #include "precomp.h"
19 #include "treeview.h"
20 
21 #include <wingdi.h> // For RGB macro
22 
23 
24 void TreeView_Set3StateCheck(HWND hTree)
25 {
26     LONG_PTR lStyle;
27     DWORD dwExStyle;
28 
29     DWORD Major, Minor, Build;
30     GetComCtl32Version(&Major, &Minor, &Build);
31 
32     /*
33      * Choose the best way to handle 3-state TreeView checkboxes
34      * according to the version of comctl32.dll we are running against.
35      *
36      * Starting version comctl32 version 6.10 (Vista+, via SxS)
37      * we have native 3-state checkboxes available.
38      * Only when comctl32 version 5.82 (no SxS) is available,
39      * use its build number to know whether we should use 2k3-style
40      * or Vista+ style check-boxes.
41      */
42     if (Major > 6 || (Major == 6 && Minor >= 10))
43     {
44         /*
45          * NOTE: As explained in the following link:
46          * http://stackoverflow.com/questions/31488233/treeview-setextendedstyle-does-not-apply-certain-styles-what-am-i-doing-wrong
47          * the TreeView control should have the extended check-box style set
48          * *BEFORE* actually setting the check-box window style, because it is
49          * only at that step that the TreeView control builds its image list
50          * containing the three check-box states. Indeed, if the extended
51          * check-box style was applied after setting the window style, then
52          * the image list would be already built with the default two states
53          * and would not be updated.
54          *
55          * The MSDN documentation is not very clear on that point.
56          *
57          * Let me also take this opportunity to document what those
58          * extended check-box state styles look like on Windows Vista+ :
59          *
60          * - TVS_EX_DIMMEDCHECKBOXES creates a grey tone version of the normal checked box state.
61          * - TVS_EX_EXCLUSIONCHECKBOXES creates a red 'X'-style cross check-box state.
62          * - TVS_EX_PARTIALCHECKBOXES creates a filled box.
63          */
64         dwExStyle = TreeView_GetExtendedStyle(hTree);
65         TreeView_SetExtendedStyle(hTree, dwExStyle | TVS_EX_PARTIALCHECKBOXES, 0);
66 
67         lStyle = GetWindowLongPtr(hTree, GWL_STYLE);
68         SetWindowLongPtr(hTree, GWL_STYLE, lStyle | TVS_CHECKBOXES);
69     }
70     else
71     {
72         lStyle = GetWindowLongPtr(hTree, GWL_STYLE);
73         SetWindowLongPtr(hTree, GWL_STYLE, lStyle | TVS_CHECKBOXES);
74 
75         // TODO: Implement this function which should build at runtime
76         // the image list with either two or three check-box states
77         // (as it is done by the real common control TreeView), instead
78         // of storing resource bitmaps.
79         //
80         // hCheckImageList = CreateCheckBoxImagelist(NULL, TRUE, TRUE, FALSE);
81         TreeView_SetImageList(hTree,
82                               ImageList_LoadBitmap(hInst, (Build >= 6000 ? MAKEINTRESOURCEW(IDB_V7CHECK) : MAKEINTRESOURCEW(IDB_2K3CHECK)), 16, 4, RGB(255, 255, 255)),
83                               TVSIL_STATE);
84     }
85 }
86 
87 void TreeView_Cleanup(HWND hTree)
88 {
89     // FIXME: Should we do it always, or only when the custom image list was set?
90     ImageList_Destroy(TreeView_GetImageList(hTree, TVSIL_STATE));
91 }
92 
93 
94 HTREEITEM
95 InsertItem(HWND hTree,
96            LPCWSTR szName,
97            HTREEITEM hParent,
98            HTREEITEM hInsertAfter)
99 {
100     TVINSERTSTRUCTW tvis;
101     SecureZeroMemory(&tvis, sizeof(tvis));
102 
103     tvis.hParent        = hParent;
104     tvis.hInsertAfter   = hInsertAfter;
105     tvis.itemex.mask    = TVIF_TEXT;
106     tvis.itemex.pszText = (LPWSTR)szName;
107 
108     return (tvis.itemex.hItem = TreeView_InsertItem(hTree, &tvis));
109 }
110 
111 UINT TreeView_GetRealSubtreeState(HWND hTree, HTREEITEM htiSubtreeItem)
112 {
113 #define OP(a, b) ((a) == (b) ? (a) : 2)
114 
115     HTREEITEM htiIterator       = TreeView_GetChild(hTree, htiSubtreeItem);
116     UINT      uRealSubtreeState = TreeView_GetCheckState(hTree, htiIterator);
117     /*
118     while (htiIterator)
119     {
120         UINT temp = TreeView_GetCheckState(hTree, htiIterator);
121         uRealSubtreeState = OP(uRealSubtreeState, temp);
122 
123         htiIterator = TreeView_GetNextSibling(hTree, htiIterator);
124     }
125     */
126     while ( htiIterator && ( (htiIterator = TreeView_GetNextSibling(hTree, htiIterator)) != NULL ) )
127     {
128         UINT temp = TreeView_GetCheckState(hTree, htiIterator);
129         uRealSubtreeState = OP(uRealSubtreeState, temp);
130     }
131 
132     return uRealSubtreeState;
133 }
134 
135 void TreeView_PropagateStateOfItemToParent(HWND hTree, HTREEITEM htiItem)
136 {
137     HTREEITEM htiParent;
138     UINT uGlobalSiblingsCheckState;
139 
140     if (!hTree || !htiItem /* || htiItem == TVI_ROOT */)
141         return;
142 
143     htiParent = TreeView_GetParent(hTree, htiItem);
144     if (!htiParent)
145         return;
146 
147     uGlobalSiblingsCheckState = TreeView_GetRealSubtreeState(hTree, htiParent);
148     TreeView_SetItemState(hTree, htiParent, INDEXTOSTATEIMAGEMASK(uGlobalSiblingsCheckState + 1), TVIS_STATEIMAGEMASK);
149     TreeView_PropagateStateOfItemToParent(hTree, htiParent);
150 
151     return;
152 }
153 
154 HTREEITEM Tree_Item_Copy(HWND hTree, HTREEITEM hSourceItem, HTREEITEM hParent, HTREEITEM hInsertAfter)
155 {
156     HTREEITEM htiIterator;
157     TVINSERTSTRUCTW tvis;
158     WCHAR label[MAX_VALUE_NAME] = L"";
159 
160     if (!hTree || !hSourceItem || !hInsertAfter)
161         return NULL;
162 
163     // 1- Retrieve properties.
164     SecureZeroMemory(&tvis, sizeof(tvis));
165 
166     tvis.itemex.hItem = hSourceItem; // Handle of the item to be retrieved.
167     tvis.itemex.mask  = TVIF_HANDLE | TVIF_TEXT | TVIF_STATE |
168                         TVIF_CHILDREN | TVIF_DI_SETITEM | TVIF_EXPANDEDIMAGE |
169                         TVIF_IMAGE | TVIF_INTEGRAL | TVIF_PARAM | TVIF_SELECTEDIMAGE | TVIF_STATEEX;
170     tvis.itemex.pszText    = label;
171     tvis.itemex.cchTextMax = MAX_VALUE_NAME;
172     TreeView_GetItem(hTree, &tvis.itemex);
173 
174     // 2- Now, copy to destination.
175     tvis.hParent          = hParent;
176     tvis.hInsertAfter     = hInsertAfter;
177     tvis.itemex.stateMask = tvis.itemex.state;
178     tvis.itemex.hItem     = TreeView_InsertItem(hTree, &tvis);
179 
180     for (htiIterator = TreeView_GetChild(hTree, hSourceItem) ; htiIterator ; htiIterator = TreeView_GetNextSibling(hTree, htiIterator))
181         Tree_Item_Copy(hTree, htiIterator, tvis.itemex.hItem, TVI_LAST);
182 
183     return tvis.itemex.hItem;
184 }
185 
186 void TreeView_DownItem(HWND hTree, HTREEITEM htiItemToDown)
187 {
188     HTREEITEM htiNextItem, htiNewItem;
189 
190     if (!hTree || !htiItemToDown)
191         return;
192 
193     htiNextItem = TreeView_GetNextSibling(hTree, htiItemToDown);
194     if (!htiNextItem)
195         htiNextItem = TVI_LAST;
196 
197     htiNewItem = Tree_Item_Copy(hTree, htiItemToDown, TreeView_GetParent(hTree, htiItemToDown), htiNextItem);
198     TreeView_DeleteItem(hTree, htiItemToDown); // Delete the item and ALL its children.
199     TreeView_SelectItem(hTree, htiNewItem);
200 
201     return;
202 }
203 
204 void TreeView_UpItem(HWND hTree, HTREEITEM htiItemToUp)
205 {
206     HTREEITEM htiPrevItem, htiPrevPrevItem, htiNewItem;
207 
208     if (!hTree || !htiItemToUp)
209         return;
210 
211     htiPrevItem     = TreeView_GetPrevSibling(hTree, htiItemToUp);
212     htiPrevPrevItem = TreeView_GetPrevSibling(hTree, htiPrevItem);
213     if (!htiPrevPrevItem)
214         htiPrevPrevItem = TVI_FIRST;
215     // if htiPrevItem == NULL , htiPrevPrevItem == NULL.
216 
217     htiNewItem = Tree_Item_Copy(hTree, htiItemToUp, TreeView_GetParent(hTree, htiItemToUp), htiPrevPrevItem);
218     TreeView_DeleteItem(hTree, htiItemToUp); // Delete the item and ALL its children.
219     TreeView_SelectItem(hTree, htiNewItem);
220 
221     return;
222 }
223 
224 HTREEITEM TreeView_GetFirst(HWND hTree)
225 {
226     return TreeView_GetRoot(hTree);
227 }
228 
229 HTREEITEM TreeView_GetLastFromItem(HWND hTree, HTREEITEM hItem)
230 {
231     HTREEITEM htiRet = NULL;
232     HTREEITEM htiIterator;
233 
234     for (htiIterator = hItem ; htiIterator ; htiIterator = TreeView_GetNextSibling(hTree, htiIterator))
235         htiRet = htiIterator;
236 
237     return htiRet;
238 }
239 
240 HTREEITEM TreeView_GetLast(HWND hTree)
241 {
242     return TreeView_GetLastFromItem(hTree, TreeView_GetRoot(hTree));
243 }
244 
245 HTREEITEM TreeView_GetPrev(HWND hTree, HTREEITEM hItem)
246 {
247     HTREEITEM hPrev, hTmp;
248 
249     if (!hTree)
250         return NULL;
251 
252     hPrev = TreeView_GetPrevSibling(hTree, hItem);
253     if (!hPrev)
254         return TreeView_GetParent(hTree, hItem);
255 
256     hTmp = TreeView_GetChild(hTree, hPrev);
257     if (hTmp)
258         return TreeView_GetLastFromItem(hTree, hTmp);
259     else
260         return hPrev;
261 }
262 
263 HTREEITEM TreeView_GetNext(HWND hTree, HTREEITEM hItem)
264 {
265     HTREEITEM hNext;
266 
267     if (!hTree)
268         return NULL;
269 
270     hNext = TreeView_GetChild(hTree, hItem);
271     if (hNext)
272         return hNext;
273 
274     hNext = TreeView_GetNextSibling(hTree, hItem);
275     if (hNext)
276         return hNext;
277     else
278         return TreeView_GetNextSibling(hTree, TreeView_GetParent(hTree, hItem));
279 }
280 
281 /* EOF */
282