shell32/shellview: Implemented IFolderView::GetFocusedItem.
[wine] / dlls / shell32 / tests / shlview.c
1 /*
2  * Unit test of the IShellView
3  *
4  * Copyright 2010 Nikolay Sivov for CodeWeavers
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 <stdarg.h>
22 #include <stdio.h>
23
24 #define COBJMACROS
25 #define CONST_VTABLE
26
27 #include "windef.h"
28 #include "winbase.h"
29 #include "wtypes.h"
30 #include "shellapi.h"
31
32 #include "shlguid.h"
33 #include "shlobj.h"
34 #include "shobjidl.h"
35 #include "shlwapi.h"
36 #include "ocidl.h"
37 #include "oleauto.h"
38
39 #include "wine/test.h"
40
41 #include "msg.h"
42
43 #define LISTVIEW_SEQ_INDEX  0
44 #define NUM_MSG_SEQUENCES   1
45
46 static struct msg_sequence *sequences[NUM_MSG_SEQUENCES];
47
48 static LRESULT WINAPI listview_subclass_proc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
49 {
50     WNDPROC oldproc = (WNDPROC)GetWindowLongPtrA(hwnd, GWLP_USERDATA);
51     static LONG defwndproc_counter = 0;
52     LRESULT ret;
53     struct message msg;
54
55     trace("listview: %p, %04x, %08lx, %08lx\n", hwnd, message, wParam, lParam);
56
57     msg.message = message;
58     msg.flags = sent|wparam|lparam;
59     if (defwndproc_counter) msg.flags |= defwinproc;
60     msg.wParam = wParam;
61     msg.lParam = lParam;
62     add_message(sequences, LISTVIEW_SEQ_INDEX, &msg);
63
64     defwndproc_counter++;
65     ret = CallWindowProcA(oldproc, hwnd, message, wParam, lParam);
66     defwndproc_counter--;
67     return ret;
68 }
69
70 static HWND subclass_listview(HWND hwnd)
71 {
72     WNDPROC oldproc;
73     HWND listview;
74
75     /* listview is a first child */
76     listview = FindWindowExA(hwnd, NULL, WC_LISTVIEWA, NULL);
77
78     oldproc = (WNDPROC)SetWindowLongPtrA(listview, GWLP_WNDPROC,
79                                         (LONG_PTR)listview_subclass_proc);
80     SetWindowLongPtrA(listview, GWLP_USERDATA, (LONG_PTR)oldproc);
81
82     return listview;
83 }
84
85 typedef struct {
86
87     const IShellBrowserVtbl *lpVtbl;
88     LONG ref;
89 } IShellBrowserImpl;
90
91 /* dummy IShellBrowser implementation */
92 static const IShellBrowserVtbl IShellBrowserImpl_Vtbl;
93
94 IShellBrowser* IShellBrowserImpl_Construct(void)
95 {
96     IShellBrowserImpl *browser;
97
98     browser = HeapAlloc(GetProcessHeap(), 0, sizeof(*browser));
99     browser->lpVtbl = &IShellBrowserImpl_Vtbl;
100     browser->ref = 1;
101
102     return (IShellBrowser*)browser;
103 }
104
105 static HRESULT WINAPI IShellBrowserImpl_QueryInterface(IShellBrowser *iface,
106                                             REFIID riid,
107                                             LPVOID *ppvObj)
108 {
109     IShellBrowserImpl *This = (IShellBrowserImpl *)iface;
110
111     *ppvObj = NULL;
112
113     if(IsEqualIID(riid, &IID_IUnknown))
114     {
115         *ppvObj = This;
116     }
117     else if(IsEqualIID(riid, &IID_IOleWindow))
118     {
119         *ppvObj = This;
120     }
121     else if(IsEqualIID(riid, &IID_IShellBrowser))
122     {
123         *ppvObj = This;
124     }
125
126     if(*ppvObj)
127     {
128         IUnknown_AddRef( (IShellBrowser*) *ppvObj);
129         return S_OK;
130     }
131
132     return E_NOINTERFACE;
133 }
134
135 static ULONG WINAPI IShellBrowserImpl_AddRef(IShellBrowser * iface)
136 {
137     IShellBrowserImpl *This = (IShellBrowserImpl *)iface;
138     return InterlockedIncrement(&This->ref);
139 }
140
141 static ULONG WINAPI IShellBrowserImpl_Release(IShellBrowser * iface)
142 {
143     IShellBrowserImpl *This = (IShellBrowserImpl *)iface;
144     ULONG ref = InterlockedDecrement(&This->ref);
145
146     if (!ref)
147     {
148         HeapFree(GetProcessHeap(), 0, This);
149         return 0;
150     }
151     return ref;
152 }
153
154 static HRESULT WINAPI IShellBrowserImpl_GetWindow(IShellBrowser *iface,
155                                            HWND *phwnd)
156 {
157     if (phwnd) *phwnd = GetDesktopWindow();
158     return S_OK;
159 }
160
161 static HRESULT WINAPI IShellBrowserImpl_ContextSensitiveHelp(IShellBrowser *iface,
162                                                       BOOL fEnterMode)
163 {
164     return E_NOTIMPL;
165 }
166
167 static HRESULT WINAPI IShellBrowserImpl_BrowseObject(IShellBrowser *iface,
168                                               LPCITEMIDLIST pidl,
169                                               UINT wFlags)
170 {
171     return E_NOTIMPL;
172 }
173
174 static HRESULT WINAPI IShellBrowserImpl_EnableModelessSB(IShellBrowser *iface,
175                                               BOOL fEnable)
176
177 {
178     return E_NOTIMPL;
179 }
180
181 static HRESULT WINAPI IShellBrowserImpl_GetControlWindow(IShellBrowser *iface,
182                                               UINT id,
183                                               HWND *lphwnd)
184
185 {
186     return E_NOTIMPL;
187 }
188
189 static HRESULT WINAPI IShellBrowserImpl_GetViewStateStream(IShellBrowser *iface,
190                                                 DWORD mode,
191                                                 LPSTREAM *pStrm)
192
193 {
194     return E_NOTIMPL;
195 }
196
197 static HRESULT WINAPI IShellBrowserImpl_InsertMenusSB(IShellBrowser *iface,
198                                            HMENU hmenuShared,
199                                            LPOLEMENUGROUPWIDTHS lpMenuWidths)
200
201 {
202     return E_NOTIMPL;
203 }
204
205 static HRESULT WINAPI IShellBrowserImpl_OnViewWindowActive(IShellBrowser *iface,
206                                                 IShellView *ppshv)
207
208 {
209     return E_NOTIMPL;
210 }
211
212 static HRESULT WINAPI IShellBrowserImpl_QueryActiveShellView(IShellBrowser *iface,
213                                                   IShellView **ppshv)
214
215 {
216     return E_NOTIMPL;
217 }
218
219 static HRESULT WINAPI IShellBrowserImpl_RemoveMenusSB(IShellBrowser *iface,
220                                            HMENU hmenuShared)
221
222 {
223     return E_NOTIMPL;
224 }
225
226 static HRESULT WINAPI IShellBrowserImpl_SendControlMsg(IShellBrowser *iface,
227                                             UINT id,
228                                             UINT uMsg,
229                                             WPARAM wParam,
230                                             LPARAM lParam,
231                                             LRESULT *pret)
232
233 {
234     return E_NOTIMPL;
235 }
236
237 static HRESULT WINAPI IShellBrowserImpl_SetMenuSB(IShellBrowser *iface,
238                                        HMENU hmenuShared,
239                                        HOLEMENU holemenuReserved,
240                                        HWND hwndActiveObject)
241
242 {
243     return E_NOTIMPL;
244 }
245
246 static HRESULT WINAPI IShellBrowserImpl_SetStatusTextSB(IShellBrowser *iface,
247                                              LPCOLESTR lpszStatusText)
248
249 {
250     return E_NOTIMPL;
251 }
252
253 static HRESULT WINAPI IShellBrowserImpl_SetToolbarItems(IShellBrowser *iface,
254                                              LPTBBUTTON lpButtons,
255                                              UINT nButtons,
256                                              UINT uFlags)
257
258 {
259     return E_NOTIMPL;
260 }
261
262 static HRESULT WINAPI IShellBrowserImpl_TranslateAcceleratorSB(IShellBrowser *iface,
263                                                     LPMSG lpmsg,
264                                                     WORD wID)
265
266 {
267     return E_NOTIMPL;
268 }
269
270 static const IShellBrowserVtbl IShellBrowserImpl_Vtbl =
271 {
272     IShellBrowserImpl_QueryInterface,
273     IShellBrowserImpl_AddRef,
274     IShellBrowserImpl_Release,
275     IShellBrowserImpl_GetWindow,
276     IShellBrowserImpl_ContextSensitiveHelp,
277     IShellBrowserImpl_InsertMenusSB,
278     IShellBrowserImpl_SetMenuSB,
279     IShellBrowserImpl_RemoveMenusSB,
280     IShellBrowserImpl_SetStatusTextSB,
281     IShellBrowserImpl_EnableModelessSB,
282     IShellBrowserImpl_TranslateAcceleratorSB,
283     IShellBrowserImpl_BrowseObject,
284     IShellBrowserImpl_GetViewStateStream,
285     IShellBrowserImpl_GetControlWindow,
286     IShellBrowserImpl_SendControlMsg,
287     IShellBrowserImpl_QueryActiveShellView,
288     IShellBrowserImpl_OnViewWindowActive,
289     IShellBrowserImpl_SetToolbarItems
290 };
291
292 static const struct message empty_seq[] = {
293     { 0 }
294 };
295
296 static const struct message folderview_getspacing_seq[] = {
297     { LVM_GETITEMSPACING, wparam|sent, FALSE },
298     { 0 }
299 };
300
301 static const struct message folderview_getselectionmarked_seq[] = {
302     { LVM_GETSELECTIONMARK, sent },
303     { 0 }
304 };
305
306 static const struct message folderview_getfocused_seq[] = {
307     { LVM_GETNEXTITEM, sent|wparam|lparam, -1, LVNI_FOCUSED },
308     { 0 }
309 };
310
311 static void test_IShellView_CreateViewWindow(void)
312 {
313     IShellFolder *desktop;
314     FOLDERSETTINGS settings;
315     IShellView *view;
316     HWND hwnd_view;
317     HRESULT hr;
318     RECT r = {0};
319
320     hr = SHGetDesktopFolder(&desktop);
321     ok(hr == S_OK, "got (0x%08x)\n", hr);
322
323     hr = IShellFolder_CreateViewObject(desktop, NULL, &IID_IShellView, (void**)&view);
324     ok(hr == S_OK, "got (0x%08x)\n", hr);
325
326 if (0)
327 {
328     /* crashes on native */
329     hr = IShellView_CreateViewWindow(view, NULL, &settings, NULL, NULL, NULL);
330 }
331
332     settings.ViewMode = FVM_ICON;
333     settings.fFlags = 0;
334     hwnd_view = (HWND)0xdeadbeef;
335     hr = IShellView_CreateViewWindow(view, NULL, &settings, NULL, NULL, &hwnd_view);
336     ok(hr == E_UNEXPECTED, "got (0x%08x)\n", hr);
337     ok(hwnd_view == 0, "got %p\n", hwnd_view);
338
339     hwnd_view = (HWND)0xdeadbeef;
340     hr = IShellView_CreateViewWindow(view, NULL, &settings, NULL, &r, &hwnd_view);
341     ok(hr == E_UNEXPECTED, "got (0x%08x)\n", hr);
342     ok(hwnd_view == 0, "got %p\n", hwnd_view);
343
344     IShellView_Release(view);
345     IShellFolder_Release(desktop);
346 }
347
348 static void test_IFolderView(void)
349 {
350     IShellFolder *desktop;
351     FOLDERSETTINGS settings;
352     IShellView *view;
353     IShellBrowser *browser;
354     IFolderView *fv;
355     HWND hwnd_view, hwnd_list;
356     HRESULT hr;
357     INT ret;
358     POINT pt;
359     RECT r;
360
361     hr = SHGetDesktopFolder(&desktop);
362     ok(hr == S_OK, "got (0x%08x)\n", hr);
363
364     hr = IShellFolder_CreateViewObject(desktop, NULL, &IID_IShellView, (void**)&view);
365     ok(hr == S_OK, "got (0x%08x)\n", hr);
366
367     hr = IShellView_QueryInterface(view, &IID_IFolderView, (void**)&fv);
368     if (hr != S_OK)
369     {
370         win_skip("IFolderView not supported by desktop folder\n");
371         IShellView_Release(view);
372         IShellFolder_Release(desktop);
373         return;
374     }
375
376     /* call methods before window creation */
377     hr = IFolderView_GetSpacing(fv, NULL);
378     ok(hr == S_FALSE || broken(hr == S_OK) /* win7 */, "got (0x%08x)\n", hr);
379
380 if (0)
381 {
382     /* crashes on Vista and Win2k8 - List not created yet case */
383     hr = IFolderView_GetSpacing(fv, &pt);
384
385     /* crashes on XP */
386     hr = IFolderView_GetSelectionMarkedItem(fv, NULL);
387     hr = IFolderView_GetFocusedItem(fv, NULL);
388 }
389
390     browser = IShellBrowserImpl_Construct();
391
392     settings.ViewMode = FVM_ICON;
393     settings.fFlags = 0;
394     hwnd_view = (HWND)0xdeadbeef;
395     r.left = r.top = 0;
396     r.right = r.bottom = 100;
397     hr = IShellView_CreateViewWindow(view, NULL, &settings, browser, &r, &hwnd_view);
398     ok(hr == S_OK, "got (0x%08x)\n", hr);
399     ok(IsWindow(hwnd_view), "got %p\n", hwnd_view);
400
401     hwnd_list = subclass_listview(hwnd_view);
402     if (!hwnd_list)
403     {
404         win_skip("Failed to subclass ListView control\n");
405         IShellBrowser_Release(browser);
406         IFolderView_Release(fv);
407         IShellView_Release(view);
408         IShellFolder_Release(desktop);
409         return;
410     }
411
412     /* IFolderView::GetSpacing */
413     flush_sequences(sequences, NUM_MSG_SEQUENCES);
414     hr = IFolderView_GetSpacing(fv, NULL);
415     ok(hr == S_OK, "got (0x%08x)\n", hr);
416     ok_sequence(sequences, LISTVIEW_SEQ_INDEX, empty_seq, "IFolderView::GetSpacing, empty", FALSE);
417
418     flush_sequences(sequences, NUM_MSG_SEQUENCES);
419     hr = IFolderView_GetSpacing(fv, &pt);
420     ok(hr == S_OK, "got (0x%08x)\n", hr);
421     /* fails with empty sequence on win7 for unknown reason */
422     if (sequences[LISTVIEW_SEQ_INDEX]->count)
423     {
424         ok_sequence(sequences, LISTVIEW_SEQ_INDEX, folderview_getspacing_seq, "IFolderView::GetSpacing", FALSE);
425         ok(pt.x > 0, "got %d\n", pt.x);
426         ok(pt.y > 0, "got %d\n", pt.y);
427         ret = SendMessageA(hwnd_list, LVM_GETITEMSPACING, 0, 0);
428         ok(pt.x == LOWORD(ret) && pt.y == HIWORD(ret), "got (%d, %d)\n", LOWORD(ret), HIWORD(ret));
429     }
430
431     /* IFolderView::GetSelectionMarkedItem */
432 if (0)
433 {
434     /* crashes on XP */
435     hr = IFolderView_GetSelectionMarkedItem(fv, NULL);
436 }
437
438     flush_sequences(sequences, NUM_MSG_SEQUENCES);
439     hr = IFolderView_GetSelectionMarkedItem(fv, &ret);
440     ok(hr == S_OK, "got (0x%08x)\n", hr);
441     ok_sequence(sequences, LISTVIEW_SEQ_INDEX, folderview_getselectionmarked_seq,
442                                   "IFolderView::GetSelectionMarkedItem", FALSE);
443
444     /* IFolderView::GetFocusedItem */
445     flush_sequences(sequences, NUM_MSG_SEQUENCES);
446     hr = IFolderView_GetFocusedItem(fv, &ret);
447     ok(hr == S_OK, "got (0x%08x)\n", hr);
448     ok_sequence(sequences, LISTVIEW_SEQ_INDEX, folderview_getfocused_seq,
449                                   "IFolderView::GetFocusedItem", FALSE);
450
451     IShellBrowser_Release(browser);
452     IFolderView_Release(fv);
453     IShellView_Release(view);
454     IShellFolder_Release(desktop);
455 }
456
457 START_TEST(shlview)
458 {
459     OleInitialize(NULL);
460
461     init_msg_sequences(sequences, NUM_MSG_SEQUENCES);
462
463     test_IShellView_CreateViewWindow();
464     test_IFolderView();
465
466     OleUninitialize();
467 }