quartz: Use proper alloc/free functions for COM objects.
[wine] / dlls / comctl32 / tests / treeview.c
1 /* Unit tests for treeview.
2  *
3  * Copyright 2005 Krzysztof Foltman
4  * Copyright 2007 Christopher James Peterson
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <assert.h>
22 #include <stdarg.h>
23
24 #include "windef.h"
25 #include "winbase.h"
26 #include "wingdi.h"
27 #include "winuser.h"
28 #include "winnls.h"
29 #include "winreg.h"
30 #include "commctrl.h" 
31
32 #include "wine/test.h"
33
34 static HWND hMainWnd;
35
36 static HWND hTree, hEdit;
37 static HTREEITEM hRoot, hChild;
38
39 static int pos = 0;
40 static char sequence[256];
41
42 static void Clear(void)
43 {
44     pos = 0;
45     sequence[0] = '\0';
46 }
47
48 static void AddItem(char ch)
49 {
50     sequence[pos++] = ch;
51     sequence[pos] = '\0';
52 }
53
54 static void IdentifyItem(HTREEITEM hItem)
55 {
56     if (hItem == hRoot) {
57         AddItem('R');
58         return;
59     }
60     if (hItem == hChild) {
61         AddItem('C');
62         return;
63     }
64     if (hItem == NULL) {
65         AddItem('n');
66         return;
67     }
68     AddItem('?');
69 }
70
71 static void FillRoot(void)
72 {
73     TVINSERTSTRUCTA ins;
74     TVITEM tvi;
75     static CHAR root[]  = "Root",
76                 child[] = "Child";
77
78     Clear();
79     AddItem('A');
80     ins.hParent = TVI_ROOT;
81     ins.hInsertAfter = TVI_ROOT;
82     U(ins).item.mask = TVIF_TEXT;
83     U(ins).item.pszText = root;
84     hRoot = TreeView_InsertItem(hTree, &ins);
85     assert(hRoot);
86
87     /* UMLPad 1.15 depends on this being not -1 (I_IMAGECALLBACK) */
88     tvi.hItem = hRoot;
89     tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE;
90     SendMessage( hTree, TVM_GETITEM, 0, (LPARAM)&tvi );
91     ok(tvi.iImage == 0, "tvi.iImage=%d\n", tvi.iImage);
92     ok(tvi.iSelectedImage == 0, "tvi.iSelectedImage=%d\n", tvi.iSelectedImage);
93
94     AddItem('B');
95     ins.hParent = hRoot;
96     ins.hInsertAfter = TVI_FIRST;
97     U(ins).item.mask = TVIF_TEXT;
98     U(ins).item.pszText = child;
99     hChild = TreeView_InsertItem(hTree, &ins);
100     assert(hChild);
101     AddItem('.');
102
103     ok(!strcmp(sequence, "AB."), "Item creation\n");
104 }
105
106 static void DoTest1(void)
107 {
108     BOOL r;
109     r = TreeView_SelectItem(hTree, NULL);
110     Clear();
111     AddItem('1');
112     r = TreeView_SelectItem(hTree, hRoot);
113     AddItem('2');
114     r = TreeView_SelectItem(hTree, hRoot);
115     AddItem('3');
116     r = TreeView_SelectItem(hTree, NULL);
117     AddItem('4');
118     r = TreeView_SelectItem(hTree, NULL);
119     AddItem('5');
120     r = TreeView_SelectItem(hTree, hRoot);
121     AddItem('.');
122     ok(!strcmp(sequence, "1(nR)nR23(Rn)Rn45(nR)nR."), "root-none select test\n");
123 }
124
125 static void DoTest2(void)
126 {
127     BOOL r;
128     r = TreeView_SelectItem(hTree, NULL);
129     Clear();
130     AddItem('1');
131     r = TreeView_SelectItem(hTree, hRoot);
132     AddItem('2');
133     r = TreeView_SelectItem(hTree, hRoot);
134     AddItem('3');
135     r = TreeView_SelectItem(hTree, hChild);
136     AddItem('4');
137     r = TreeView_SelectItem(hTree, hChild);
138     AddItem('5');
139     r = TreeView_SelectItem(hTree, hRoot);
140     AddItem('.');
141     ok(!strcmp(sequence, "1(nR)nR23(RC)RC45(CR)CR."), "root-child select test\n");
142 }
143
144 static void DoFocusTest(void)
145 {
146     TVINSERTSTRUCTA ins;
147     static CHAR child1[]  = "Edit",
148                 child2[]  = "A really long string";
149     HTREEITEM hChild1, hChild2;
150
151     /* This test verifies that when a label is being edited, scrolling
152      * the treeview does not cause the label to lose focus. To test
153      * this, first some additional entries are added to generate
154      * scrollbars.
155      */
156     ins.hParent = hRoot;
157     ins.hInsertAfter = hChild;
158     U(ins).item.mask = TVIF_TEXT;
159     U(ins).item.pszText = child1;
160     hChild1 = TreeView_InsertItem(hTree, &ins);
161     assert(hChild1);
162     ins.hInsertAfter = hChild1;
163     U(ins).item.mask = TVIF_TEXT;
164     U(ins).item.pszText = child2;
165     hChild2 = TreeView_InsertItem(hTree, &ins);
166     assert(hChild2);
167
168     ShowWindow(hMainWnd,SW_SHOW);
169     SendMessageW(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hChild);
170     hEdit = TreeView_EditLabel(hTree, hChild);
171     ScrollWindowEx(hTree, -10, 0, NULL, NULL, NULL, NULL, SW_SCROLLCHILDREN);
172     ok(GetFocus() == hEdit, "Edit control should have focus\n");
173 }
174
175 static void TestGetSetBkColor(void)
176 {
177     COLORREF crColor = RGB(0,0,0);
178
179     todo_wine{
180         /* If the value is -1, the control is using the system color for the background color. */
181         crColor = (COLORREF)SendMessage( hTree, TVM_GETBKCOLOR, 0, 0 );
182         ok(crColor == -1, "Default background color reported as 0x%.8x\n", crColor);
183     }
184
185     /* Test for black background */
186     SendMessage( hTree, TVM_SETBKCOLOR, 0, (LPARAM)RGB(0,0,0) );
187     crColor = (COLORREF)SendMessage( hTree, TVM_GETBKCOLOR, 0, 0 );
188     ok(crColor == RGB(0,0,0), "Black background color reported as 0x%.8x\n", crColor);
189
190     /* Test for white background */
191     SendMessage( hTree, TVM_SETBKCOLOR, 0, (LPARAM)RGB(255,255,255) );
192     crColor = (COLORREF)SendMessage( hTree, TVM_GETBKCOLOR, 0, 0 );
193     ok(crColor == RGB(255,255,255), "White background color reported as 0x%.8x\n", crColor);
194
195     /* Reset the default background */
196     SendMessage( hTree, TVM_SETBKCOLOR, 0, -1 );
197 }
198
199 static void TestGetSetImageList(void)
200 {
201     HIMAGELIST hImageList = NULL;
202
203     /* Test a NULL HIMAGELIST */
204     SendMessage( hTree, TVM_SETIMAGELIST, TVSIL_NORMAL, (LPARAM)hImageList );
205     hImageList = (HIMAGELIST)SendMessage( hTree, TVM_GETIMAGELIST, TVSIL_NORMAL, 0 );
206     ok(hImageList == NULL, "NULL image list, reported as 0x%p, expected 0.\n", hImageList);
207
208     /* TODO: Test an actual image list */
209 }
210
211 static void TestGetSetIndent(void)
212 {
213     int ulIndent = -1;
214     int ulMinIndent = -1;
215     int ulMoreThanTwiceMin = -1;
216
217     /* Finding the minimum indent */
218     SendMessage( hTree, TVM_SETINDENT, 0, 0 );
219     ulMinIndent = (int)SendMessage( hTree, TVM_GETINDENT, 0, 0 );
220
221     /* Checking an indent that is more than twice the default indent */
222     ulMoreThanTwiceMin = 2*ulMinIndent+1;
223     SendMessage( hTree, TVM_SETINDENT, ulMoreThanTwiceMin, 0 );
224     ulIndent = (DWORD)SendMessage( hTree, TVM_GETINDENT, 0, 0 );
225     ok(ulIndent == ulMoreThanTwiceMin, "Indent reported as %d, expected %d\n", ulIndent, ulMoreThanTwiceMin);
226 }
227
228 static void TestGetSetInsertMarkColor(void)
229 {
230     COLORREF crColor = RGB(0,0,0);
231     SendMessage( hTree, TVM_SETBKCOLOR, 0, crColor );
232     crColor = (COLORREF)SendMessage( hTree, TVM_GETBKCOLOR, 0, 0 );
233     ok(crColor == RGB(0,0,0), "Insert mark color reported as 0x%.8x, expected 0x00000000\n", crColor);
234 }
235
236 static void TestGetSetItem(void)
237 {
238     TVITEM tviRoot = {0};
239     int nBufferSize = 80;
240     char szBuffer[80] = {0};
241
242     /* Test the root item */
243     tviRoot.hItem = hRoot;
244     tviRoot.mask = TVIF_TEXT;
245     tviRoot.cchTextMax = nBufferSize;
246     tviRoot.pszText = szBuffer;
247     SendMessage( hTree, TVM_GETITEM, 0, (LPARAM)&tviRoot );
248     ok(!strcmp("Root", szBuffer), "GetItem: szBuffer=\"%s\", expected \"Root\"\n", szBuffer);
249
250     /* Change the root text */
251     strncpy(szBuffer, "Testing123", nBufferSize);
252     SendMessage( hTree, TVM_SETITEM, 0, (LPARAM)&tviRoot );
253     memset(szBuffer, 0, nBufferSize);
254     SendMessage( hTree, TVM_GETITEM, 0, (LPARAM)&tviRoot );
255     ok(!strcmp("Testing123", szBuffer), "GetItem: szBuffer=\"%s\", expected \"Testing123\"\n", szBuffer);
256
257     /* Reset the root text */
258     memset(szBuffer, 0, nBufferSize);
259     strncpy(szBuffer, "Root", nBufferSize);
260     SendMessage( hTree, TVM_SETITEM, 0, (LPARAM)&tviRoot );
261 }
262
263 static void TestGetSetItemHeight(void)
264 {
265     int ulOldHeight = 0;
266     int ulNewHeight = 0;
267
268     /* Assuming default height to begin with */
269     ulOldHeight = (int) SendMessage( hTree, TVM_GETITEMHEIGHT, 0, 0 );
270
271     /* Explicitly setting and getting the default height */
272     SendMessage( hTree, TVM_SETITEMHEIGHT, -1, 0 );
273     ulNewHeight = (int) SendMessage( hTree, TVM_GETITEMHEIGHT, 0, 0 );
274     ok(ulNewHeight == ulOldHeight, "Default height not set properly, reported %d, expected %d\n", ulNewHeight, ulOldHeight);
275
276     /* Explicitly setting and getting the height of twice the normal */
277     SendMessage( hTree, TVM_SETITEMHEIGHT, 2*ulOldHeight, 0 );
278     ulNewHeight = (int) SendMessage( hTree, TVM_GETITEMHEIGHT, 0, 0 );
279     ok(ulNewHeight == 2*ulOldHeight, "New height not set properly, reported %d, expected %d\n", ulNewHeight, 2*ulOldHeight);
280
281     todo_wine {
282         /* Assuming tree doesn't have TVS_NONEVENHEIGHT set, so a set of 9 will round down to 8 */
283         SendMessage( hTree, TVM_SETITEMHEIGHT, 9, 0 );
284         ulNewHeight = (int) SendMessage( hTree, TVM_GETITEMHEIGHT, 0, 0 );
285         ok(ulNewHeight == 8, "Uneven height not set properly, reported %d, expected %d\n", ulNewHeight, 8);
286     }
287 }
288
289 static void TestGetSetScrollTime(void)
290 {
291     int ulExpectedTime = 20;
292     int ulTime = 0;
293     SendMessage( hTree, TVM_SETSCROLLTIME, ulExpectedTime, 0 );
294     ulTime = (int)SendMessage( hTree, TVM_GETSCROLLTIME, 0, 0 );
295     ok(ulTime == ulExpectedTime, "Scroll time reported as %d, expected %d\n", ulTime, ulExpectedTime);
296 }
297
298 static void TestGetSetTextColor(void)
299 {
300     /* If the value is -1, the control is using the system color for the text color. */
301     COLORREF crColor = RGB(0,0,0);
302     crColor = (COLORREF)SendMessage( hTree, TVM_GETTEXTCOLOR, 0, 0 );
303     ok(crColor == -1, "Default text color reported as 0x%.8x\n", crColor);
304
305     /* Test for black text */
306     SendMessage( hTree, TVM_SETTEXTCOLOR, 0, (LPARAM)RGB(0,0,0) );
307     crColor = (COLORREF)SendMessage( hTree, TVM_GETTEXTCOLOR, 0, 0 );
308     ok(crColor == RGB(0,0,0), "Black text color reported as 0x%.8x\n", crColor);
309
310     /* Test for white text */
311     SendMessage( hTree, TVM_SETTEXTCOLOR, 0, (LPARAM)RGB(255,255,255) );
312     crColor = (COLORREF)SendMessage( hTree, TVM_GETTEXTCOLOR, 0, 0 );
313     ok(crColor == RGB(255,255,255), "White text color reported as 0x%.8x\n", crColor);
314
315     /* Reset the default text color */
316     SendMessage( hTree, TVM_SETTEXTCOLOR, 0, -1 );
317 }
318
319 static void TestGetSetToolTips(void)
320 {
321     HWND hwndLastToolTip = NULL;
322
323     /* Testing setting a NULL ToolTip */
324     SendMessage( hTree, TVM_SETTOOLTIPS, 0, 0 );
325     hwndLastToolTip = (HWND)SendMessage( hTree, TVM_GETTOOLTIPS, 0, 0 );
326     ok(hwndLastToolTip == NULL, "NULL tool tip, reported as 0x%p, expected 0.\n", hwndLastToolTip);
327
328     /* TODO: Add a test of an actual tooltip */
329 }
330
331 static void TestGetSetUnicodeFormat(void)
332 {
333     BOOL bPreviousSetting = 0;
334     BOOL bNewSetting = 0;
335
336     /* Set to Unicode */
337     bPreviousSetting = (BOOL)SendMessage( hTree, TVM_SETUNICODEFORMAT, 1, 0 );
338     bNewSetting = (BOOL)SendMessage( hTree, TVM_GETUNICODEFORMAT, 0, 0 );
339     ok(bNewSetting == 1, "Unicode setting did not work.\n");
340
341     /* Set to ANSI */
342     SendMessage( hTree, TVM_SETUNICODEFORMAT, 0, 0 );
343     bNewSetting = (BOOL)SendMessage( hTree, TVM_GETUNICODEFORMAT, 0, 0 );
344     ok(bNewSetting == 0, "ANSI setting did not work.\n");
345
346     /* Revert to original setting */
347     SendMessage( hTree, TVM_SETUNICODEFORMAT, (LPARAM)bPreviousSetting, 0 );
348 }
349
350 static void TestGetSet(void)
351 {
352     TestGetSetBkColor();            /* TVM_GETBKCOLOR and TVM_SETBKCOLOR */
353     TestGetSetImageList();          /* TVM_GETIMAGELIST and TVM_SETIMAGELIST */
354     TestGetSetIndent();             /* TVM_SETINDENT and TVM_GETINDENT */
355     TestGetSetInsertMarkColor();    /* TVM_GETINSERTMARKCOLOR and TVM_GETINSERTMARKCOLOR */
356     TestGetSetItem();               /* TVM_GETITEM and TVM_SETITEM */
357     TestGetSetItemHeight();         /* TVM_GETITEMHEIGHT and TVM_SETITEMHEIGHT*/
358     TestGetSetScrollTime();         /* TVM_GETSCROLLTIME and TVM_SETSCROLLTIME */
359     TestGetSetTextColor();          /* TVM_GETTEXTCOLOR and TVM_SETTEXTCOLOR */
360     TestGetSetToolTips();           /* TVM_GETTOOLTIPS and TVM_SETTOOLTIPS */
361     TestGetSetUnicodeFormat();      /* TVM_GETUNICODEFORMAT and TVM_SETUNICODEFORMAT */
362 }
363
364 static LRESULT CALLBACK MyWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
365 {
366     switch(msg) {
367
368     case WM_CREATE:
369     {
370         hTree = CreateWindowExA(WS_EX_CLIENTEDGE, WC_TREEVIEWA, NULL, WS_CHILD|WS_VISIBLE|
371             TVS_LINESATROOT|TVS_HASLINES|TVS_HASBUTTONS|TVS_EDITLABELS,
372             0, 0, 120, 100, hWnd, (HMENU)100, GetModuleHandleA(0), 0);
373
374         SetFocus(hTree);
375         return 0;
376     }
377     case WM_NOTIFY:
378     {
379         NMHDR *pHdr = (NMHDR *)lParam;
380     
381         if (pHdr->idFrom == 100) {
382             NMTREEVIEWA *pTreeView = (LPNMTREEVIEWA) lParam;
383             switch(pHdr->code) {
384             case TVN_SELCHANGINGA:
385                 AddItem('(');
386                 IdentifyItem(pTreeView->itemOld.hItem);
387                 IdentifyItem(pTreeView->itemNew.hItem);
388                 return 0;
389             case TVN_SELCHANGEDA:
390                 AddItem(')');
391                 IdentifyItem(pTreeView->itemOld.hItem);
392                 IdentifyItem(pTreeView->itemNew.hItem);
393                 return 0;
394             }
395         }
396         return 0;
397     }
398   
399     case WM_SIZE:
400         MoveWindow(hTree, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
401         break;
402       
403     case WM_DESTROY:
404         PostQuitMessage(0);
405         break;
406   
407     default:
408         return DefWindowProcA(hWnd, msg, wParam, lParam);
409     }
410     return 0L;
411 }
412
413 START_TEST(treeview)
414 {
415     WNDCLASSA wc;
416     MSG msg;
417     INITCOMMONCONTROLSEX icex;
418     RECT rc;
419   
420     icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
421     icex.dwICC   = ICC_TREEVIEW_CLASSES;
422     InitCommonControlsEx(&icex);
423   
424     wc.style = CS_HREDRAW | CS_VREDRAW;
425     wc.cbClsExtra = 0;
426     wc.cbWndExtra = 0;
427     wc.hInstance = GetModuleHandleA(NULL);
428     wc.hIcon = NULL;
429     wc.hCursor = LoadCursorA(NULL, MAKEINTRESOURCEA(IDC_IBEAM));
430     wc.hbrBackground = GetSysColorBrush(COLOR_WINDOW);
431     wc.lpszMenuName = NULL;
432     wc.lpszClassName = "MyTestWnd";
433     wc.lpfnWndProc = MyWndProc;
434     RegisterClassA(&wc);
435
436
437     hMainWnd = CreateWindowExA(0, "MyTestWnd", "Blah", WS_OVERLAPPEDWINDOW,
438       CW_USEDEFAULT, CW_USEDEFAULT, 130, 105, NULL, NULL, GetModuleHandleA(NULL), 0);
439     GetClientRect(hMainWnd, &rc);
440
441     FillRoot();
442     DoTest1();
443     DoTest2();
444     DoFocusTest();
445     TestGetSet();
446
447     PostMessageA(hMainWnd, WM_CLOSE, 0, 0);
448     while(GetMessageA(&msg,0,0,0)) {
449         TranslateMessage(&msg);
450         DispatchMessageA(&msg);
451     }
452 }