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