shell32: Store the autocompletion object pointer in a window property rather than...
[wine] / dlls / shell32 / tests / brsfolder.c
1 /*
2  * Unit test of the SHBrowseForFolder function.
3  *
4  * Copyright 2009-2010 Michael Mc Donnell
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 #include <windows.h>
21 #include <shlobj.h>
22 #include <shobjidl.h>
23 #include <string.h>
24
25 #include "wine/test.h"
26 #define IDD_MAKENEWFOLDER 0x3746 /* From "../shresdef.h" */
27 #define TIMER_WAIT_MS 50 /* Should be long enough for slow systems */
28
29 static const char new_folder_name[] = "foo";
30
31 /*
32  * Returns the number of folders in a folder.
33  */
34 static int get_number_of_folders(LPCSTR path)
35 {
36     int number_of_folders = 0;
37     char path_search_string[MAX_PATH];
38     WIN32_FIND_DATA find_data;
39     HANDLE find_handle;
40
41     strncpy(path_search_string, path, MAX_PATH);
42     strncat(path_search_string, "*", 1);
43
44     find_handle = FindFirstFile(path_search_string, &find_data);
45     if (find_handle == INVALID_HANDLE_VALUE)
46         return -1;
47
48     do
49     {
50         if ((find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
51             strcmp(find_data.cFileName, ".") != 0 &&
52             strcmp(find_data.cFileName, "..") != 0)
53         {
54             number_of_folders++;
55         }
56     }
57     while (FindNextFile(find_handle, &find_data) != 0);
58
59     FindClose(find_handle);
60     return number_of_folders;
61 }
62
63 static BOOL does_folder_or_file_exist(LPCSTR folder_path)
64 {
65     DWORD file_attributes = GetFileAttributesA(folder_path);
66     return !(file_attributes == INVALID_FILE_ATTRIBUTES);
67 }
68
69 /*
70  * Timer callback used by test_click_make_new_folder_button. It simulates a user
71  * making a new folder and calling it "foo".
72  */
73 static void CALLBACK make_new_folder_timer_callback(HWND hwnd, UINT uMsg,
74                                                     UINT_PTR idEvent, DWORD dwTime)
75 {
76     static int step = 0;
77
78     switch (step++)
79     {
80     case 0:
81         /* Click "Make New Folder" button */
82         PostMessage(hwnd, WM_COMMAND, IDD_MAKENEWFOLDER, 0);
83         break;
84     case 1:
85         /* Set the new folder name to foo by replacing text in edit control */
86         SendMessage(GetFocus(), EM_REPLACESEL, 0, (LPARAM) new_folder_name);
87         SetFocus(hwnd);
88         break;
89     case 2:
90         /*
91          * The test does not trigger the correct state on Windows. This results
92          * in the new folder pidl not being returned. The result is as
93          * expected if the same steps are done manually.
94          * Sending the down key selects the new folder again which sets the
95          * correct state. This ensures that the correct pidl is returned.
96          */
97         keybd_event(VK_DOWN, 0, 0, 0);
98         break;
99     case 3:
100         keybd_event(VK_DOWN, 0, KEYEVENTF_KEYUP, 0);
101         break;
102     case 4:
103         KillTimer(hwnd, idEvent);
104         /* Close dialog box */
105         SendMessage(hwnd, WM_COMMAND, IDOK, 0);
106         break;
107     default:
108         break;
109     }
110 }
111
112 /*
113  * Callback used by test_click_make_new_folder_button. It sets up a timer to
114  * simulate user input.
115  */
116 static int CALLBACK create_new_folder_callback(HWND hwnd, UINT uMsg,
117                                                LPARAM lParam, LPARAM lpData)
118 {
119     switch (uMsg)
120     {
121     case BFFM_INITIALIZED:
122         /* User input is simulated in timer callback */
123         SetTimer(hwnd, 0, TIMER_WAIT_MS, make_new_folder_timer_callback);
124         return TRUE;
125     default:
126         return FALSE;
127     }
128 }
129
130 /*
131  * Tests if clicking the "Make New Folder" button in a SHBrowseForFolder
132  * dialog box creates a new folder. (Bug 17986).
133  *
134  * Here follows a description of what happens on W2K,Vista, W2K8, W7:
135  * When the "Make New Folder" button is clicked a new folder is created and
136  * inserted into the tree. The folder is given a default name that depends on
137  * the locale (e.g. "New Folder"). The folder name is selected and the dialog
138  * waits for the user to type in a new name. The folder is renamed when the user
139  * types in a name and presses enter.
140  *
141  * Note that XP and W2K3 do not select the folder name or wait for the user
142  * to type in a new folder name. This behavior is considered broken as most
143  * users would like to give the folder a name after creating it. The fact that
144  * it originally waited for the user to type in a new folder name(W2K), and then
145  * again was changed back wait for the new folder name(Vista, W2K8, W7),
146  * indicates that MS also believes that it was broken in XP and W2K3.
147  */
148 static void test_click_make_new_folder_button(void)
149 {
150     HRESULT resCoInit;
151     BROWSEINFO bi;
152     LPITEMIDLIST pidl = NULL;
153     LPITEMIDLIST test_folder_pidl;
154     IShellFolder *test_folder_object;
155     char test_folder_path[MAX_PATH];
156     WCHAR test_folder_pathW[MAX_PATH];
157     CHAR new_folder_path[MAX_PATH];
158     CHAR new_folder_pidl_path[MAX_PATH];
159     char selected_folder[MAX_PATH];
160     const CHAR title[] = "test_click_make_new_folder_button";
161     int number_of_folders = -1;
162     SHFILEOPSTRUCT shfileop;
163
164     if (does_folder_or_file_exist(title))
165     {
166         skip("The test folder already exists.\n");
167         return;
168     }
169
170     /* Must initialize COM if using the NEWDIAlOGSTYLE according to MSDN. */
171     resCoInit = CoInitialize(NULL);
172     if(!(resCoInit == S_OK || resCoInit == S_FALSE))
173     {
174         skip("COM could not be initialized %u\n", GetLastError());
175         return;
176     }
177
178     /* Leave room for concatenating title, two backslashes, and an extra NULL. */
179     if (!GetCurrentDirectoryA(MAX_PATH-strlen(title)-3, test_folder_path))
180     {
181         skip("GetCurrentDirectoryA failed %u\n", GetLastError());
182     }
183     strncat(test_folder_path, "\\", 1);
184     strncat(test_folder_path, title, MAX_PATH-1);
185     strncat(test_folder_path, "\\", 1);
186
187     /* Avoid conflicts by creating a test folder. */
188     if (!CreateDirectoryA(title, NULL))
189     {
190         skip("CreateDirectoryA failed %u\n", GetLastError());
191         return;
192     }
193
194     /* Initialize browse info struct for SHBrowseForFolder */
195     bi.hwndOwner = NULL;
196     bi.pszDisplayName = (LPTSTR) &selected_folder;
197     bi.lpszTitle = (LPTSTR) title;
198     bi.ulFlags = BIF_NEWDIALOGSTYLE;
199     bi.lpfn = create_new_folder_callback;
200     /* Use test folder as the root folder for dialog box */
201     MultiByteToWideChar(CP_UTF8, 0, test_folder_path, MAX_PATH,
202         test_folder_pathW, MAX_PATH);
203     SHGetDesktopFolder(&test_folder_object);
204     test_folder_object->lpVtbl->ParseDisplayName(test_folder_object, NULL, NULL,
205         test_folder_pathW, 0UL, &test_folder_pidl, 0UL);
206     bi.pidlRoot = test_folder_pidl;
207
208     /* Display dialog box and let callback click the buttons */
209     pidl = SHBrowseForFolder(&bi);
210
211     number_of_folders = get_number_of_folders(test_folder_path);
212     todo_wine ok(number_of_folders == 1 || broken(number_of_folders == 0) /* W95, W98 */,
213         "Clicking \"Make New Folder\" button did not result in a new folder.\n");
214
215     /* There should be a new folder foo inside the test folder */
216     strcpy(new_folder_path, test_folder_path);
217     strcat(new_folder_path, new_folder_name);
218     todo_wine ok(does_folder_or_file_exist(new_folder_path)
219         || broken(!does_folder_or_file_exist(new_folder_path)) /* W95, W98, XP, W2K3 */,
220         "The new folder did not get the name %s\n", new_folder_name);
221
222     /* Dialog should return a pidl pointing to the new folder */
223     ok(SHGetPathFromIDListA(pidl, new_folder_pidl_path),
224         "SHGetPathFromIDList failed for new folder.\n");
225     todo_wine ok(strcmp(new_folder_path, new_folder_pidl_path) == 0
226         || broken(strcmp(new_folder_path, new_folder_pidl_path) != 0) /* earlier than Vista */,
227         "SHBrowseForFolder did not return the pidl for the new folder. "
228         "Expected '%s' got '%s'\n", new_folder_path, new_folder_pidl_path);
229
230     /* Remove test folder and any subfolders created in this test */
231     shfileop.hwnd = NULL;
232     shfileop.wFunc = FO_DELETE;
233     /* Path must be double NULL terminated */
234     test_folder_path[strlen(test_folder_path)+1] = '\0';
235     shfileop.pFrom = test_folder_path;
236     shfileop.pTo = NULL;
237     shfileop.fFlags = FOF_NOCONFIRMATION|FOF_NOERRORUI|FOF_SILENT;
238     SHFileOperation(&shfileop);
239
240     if (pidl)
241         CoTaskMemFree(pidl);
242     if (test_folder_pidl)
243         CoTaskMemFree(test_folder_pidl);
244     if (test_folder_object)
245         test_folder_object->lpVtbl->Release(test_folder_object);
246
247     CoUninitialize();
248 }
249
250 START_TEST(brsfolder)
251 {
252     test_click_make_new_folder_button();
253 }