comctl32/dpa: Some DPA_Merge tests.
[wine] / dlls / shell32 / changenotify.c
1 /*
2  *      shell change notification
3  *
4  * Copyright 2000 Juergen Schmied
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 <string.h>
23
24 #define NONAMELESSUNION
25 #define NONAMELESSSTRUCT
26 #include "windef.h"
27 #include "winbase.h"
28 #include "wine/debug.h"
29 #include "shell32_main.h"
30
31 WINE_DEFAULT_DEBUG_CHANNEL(shell);
32
33 static CRITICAL_SECTION SHELL32_ChangenotifyCS;
34 static CRITICAL_SECTION_DEBUG critsect_debug =
35 {
36     0, 0, &SHELL32_ChangenotifyCS,
37     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
38       0, 0, { (DWORD_PTR)(__FILE__ ": SHELL32_ChangenotifyCS") }
39 };
40 static CRITICAL_SECTION SHELL32_ChangenotifyCS = { &critsect_debug, -1, 0, 0, 0, 0 };
41
42 typedef SHChangeNotifyEntry *LPNOTIFYREGISTER;
43
44 /* internal list of notification clients (internal) */
45 typedef struct _NOTIFICATIONLIST
46 {
47         struct _NOTIFICATIONLIST *next;
48         struct _NOTIFICATIONLIST *prev;
49         HWND hwnd;              /* window to notify */
50         DWORD uMsg;             /* message to send */
51         LPNOTIFYREGISTER apidl; /* array of entries to watch*/
52         UINT cidl;              /* number of pidls in array */
53         LONG wEventMask;        /* subscribed events */
54         LONG wSignalledEvent;   /* event that occurred */
55         DWORD dwFlags;          /* client flags */
56         LPCITEMIDLIST pidlSignaled; /*pidl of the path that caused the signal*/
57     
58 } NOTIFICATIONLIST, *LPNOTIFICATIONLIST;
59
60 static NOTIFICATIONLIST *head, *tail;
61
62 #define SHCNE_NOITEMEVENTS ( \
63    SHCNE_ASSOCCHANGED )
64
65 #define SHCNE_ONEITEMEVENTS ( \
66    SHCNE_ATTRIBUTES | SHCNE_CREATE | SHCNE_DELETE | SHCNE_DRIVEADD | \
67    SHCNE_DRIVEADDGUI | SHCNE_DRIVEREMOVED | SHCNE_FREESPACE | \
68    SHCNE_MEDIAINSERTED | SHCNE_MEDIAREMOVED | SHCNE_MKDIR | \
69    SHCNE_NETSHARE | SHCNE_NETUNSHARE | SHCNE_RMDIR | \
70    SHCNE_SERVERDISCONNECT | SHCNE_UPDATEDIR | SHCNE_UPDATEIMAGE )
71
72 #define SHCNE_TWOITEMEVENTS ( \
73    SHCNE_RENAMEFOLDER | SHCNE_RENAMEITEM | SHCNE_UPDATEITEM )
74
75 /* for dumping events */
76 static const char * DumpEvent( LONG event )
77 {
78     if( event == SHCNE_ALLEVENTS )
79         return "SHCNE_ALLEVENTS";
80 #define DUMPEV(x)  ,( event & SHCNE_##x )? #x " " : ""
81     return wine_dbg_sprintf( "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s"
82     DUMPEV(RENAMEITEM)
83     DUMPEV(CREATE)
84     DUMPEV(DELETE)
85     DUMPEV(MKDIR)
86     DUMPEV(RMDIR)
87     DUMPEV(MEDIAINSERTED)
88     DUMPEV(MEDIAREMOVED)
89     DUMPEV(DRIVEREMOVED)
90     DUMPEV(DRIVEADD)
91     DUMPEV(NETSHARE)
92     DUMPEV(NETUNSHARE)
93     DUMPEV(ATTRIBUTES)
94     DUMPEV(UPDATEDIR)
95     DUMPEV(UPDATEITEM)
96     DUMPEV(SERVERDISCONNECT)
97     DUMPEV(UPDATEIMAGE)
98     DUMPEV(DRIVEADDGUI)
99     DUMPEV(RENAMEFOLDER)
100     DUMPEV(FREESPACE)
101     DUMPEV(EXTENDED_EVENT)
102     DUMPEV(ASSOCCHANGED)
103     DUMPEV(INTERRUPT)
104     );
105 #undef DUMPEV
106 }
107
108 static BOOL RefreshFileTypeAssociations(void)
109 {
110     static WCHAR szWinemenubuilder[] = {
111         'w','i','n','e','m','e','n','u','b','u','i','l','d','e','r','.','e','x','e',
112         ' ','-','a',0 };
113     STARTUPINFOW si;
114     PROCESS_INFORMATION pi;
115     BOOL ret;
116
117     TRACE("starting %s\n",debugstr_w(szWinemenubuilder));
118
119     memset(&si, 0, sizeof(si));
120     si.cb = sizeof(si);
121
122     ret = CreateProcessW( NULL, szWinemenubuilder, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi );
123
124     if (ret)
125     {
126         CloseHandle( pi.hProcess );
127         CloseHandle( pi.hThread );
128     }
129
130     return ret;
131 }
132
133 static const char * NodeName(const NOTIFICATIONLIST *item)
134 {
135     const char *str;
136     WCHAR path[MAX_PATH];
137
138     if(SHGetPathFromIDListW(item->apidl[0].pidl, path ))
139         str = wine_dbg_sprintf("%s", debugstr_w(path));
140     else
141         str = wine_dbg_sprintf("<not a disk file>" );
142     return str;
143 }
144
145 static void AddNode(LPNOTIFICATIONLIST item)
146 {
147     TRACE("item %p\n", item );
148
149     /* link items */
150     item->prev = tail;
151     item->next = NULL;
152     if( tail )
153         tail->next = item;
154     else
155         head = item;
156     tail = item;
157 }
158
159 static LPNOTIFICATIONLIST FindNode( HANDLE hitem )
160 {
161     LPNOTIFICATIONLIST ptr;
162     for( ptr = head; ptr; ptr = ptr->next )
163         if( ptr == hitem )
164             return ptr;
165     return NULL;
166 }
167
168 static void DeleteNode(LPNOTIFICATIONLIST item)
169 {
170     UINT i;
171
172     TRACE("item=%p prev=%p next=%p\n", item, item->prev, item->next);
173
174     /* remove item from list */
175     if( item->prev )
176         item->prev->next = item->next;
177     else
178         head = item->next;
179     if( item->next )
180         item->next->prev = item->prev;
181     else
182         tail = item->prev;
183
184     /* free the item */
185     for (i=0; i<item->cidl; i++)
186         SHFree((LPITEMIDLIST)item->apidl[i].pidl);
187     SHFree(item->apidl);
188     SHFree(item);
189 }
190
191 void InitChangeNotifications(void)
192 {
193 }
194
195 void FreeChangeNotifications(void)
196 {
197     TRACE("\n");
198
199     EnterCriticalSection(&SHELL32_ChangenotifyCS);
200
201     while( head )
202         DeleteNode( head );
203
204     LeaveCriticalSection(&SHELL32_ChangenotifyCS);
205
206     DeleteCriticalSection(&SHELL32_ChangenotifyCS);
207 }
208
209 /*************************************************************************
210  * SHChangeNotifyRegister                       [SHELL32.2]
211  *
212  */
213 ULONG WINAPI
214 SHChangeNotifyRegister(
215     HWND hwnd,
216     int fSources,
217     LONG wEventMask,
218     UINT uMsg,
219     int cItems,
220     SHChangeNotifyEntry *lpItems)
221 {
222     LPNOTIFICATIONLIST item;
223     int i;
224
225     item = SHAlloc(sizeof(NOTIFICATIONLIST));
226
227     TRACE("(%p,0x%08x,0x%08x,0x%08x,%d,%p) item=%p\n",
228         hwnd, fSources, wEventMask, uMsg, cItems, lpItems, item);
229
230     item->next = NULL;
231     item->prev = NULL;
232     item->cidl = cItems;
233     item->apidl = SHAlloc(sizeof(SHChangeNotifyEntry) * cItems);
234     for(i=0;i<cItems;i++)
235     {
236         item->apidl[i].pidl = ILClone(lpItems[i].pidl);
237         item->apidl[i].fRecursive = lpItems[i].fRecursive;
238     }
239     item->hwnd = hwnd;
240     item->uMsg = uMsg;
241     item->wEventMask = wEventMask;
242     item->wSignalledEvent = 0;
243     item->dwFlags = fSources;
244
245     TRACE("new node: %s\n", NodeName( item ));
246
247     EnterCriticalSection(&SHELL32_ChangenotifyCS);
248
249     AddNode(item);
250
251     LeaveCriticalSection(&SHELL32_ChangenotifyCS);
252
253     return (ULONG)item;
254 }
255
256 /*************************************************************************
257  * SHChangeNotifyDeregister                     [SHELL32.4]
258  */
259 BOOL WINAPI SHChangeNotifyDeregister(ULONG hNotify)
260 {
261     LPNOTIFICATIONLIST node;
262
263     TRACE("(0x%08x)\n", hNotify);
264
265     EnterCriticalSection(&SHELL32_ChangenotifyCS);
266
267     node = FindNode((HANDLE)hNotify);
268     if( node )
269         DeleteNode(node);
270
271     LeaveCriticalSection(&SHELL32_ChangenotifyCS);
272
273     return node?TRUE:FALSE;
274 }
275
276 /*************************************************************************
277  * SHChangeNotifyUpdateEntryList                [SHELL32.5]
278  */
279 BOOL WINAPI SHChangeNotifyUpdateEntryList(DWORD unknown1, DWORD unknown2,
280                               DWORD unknown3, DWORD unknown4)
281 {
282     FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n",
283           unknown1, unknown2, unknown3, unknown4);
284
285     return -1;
286 }
287
288 static BOOL should_notify( LPCITEMIDLIST changed, LPCITEMIDLIST watched, BOOL sub )
289 {
290     TRACE("%p %p %d\n", changed, watched, sub );
291     if ( !watched )
292         return FALSE;
293     if (ILIsEqual( watched, changed ) )
294         return TRUE;
295     if( sub && ILIsParent( watched, changed, TRUE ) )
296         return TRUE;
297     return FALSE;
298 }
299
300 /*************************************************************************
301  * SHChangeNotify                               [SHELL32.@]
302  */
303 void WINAPI SHChangeNotify(LONG wEventId, UINT uFlags, LPCVOID dwItem1, LPCVOID dwItem2)
304 {
305     LPCITEMIDLIST Pidls[2];
306     LPNOTIFICATIONLIST ptr;
307     UINT typeFlag = uFlags & SHCNF_TYPE;
308
309     Pidls[0] = NULL;
310     Pidls[1] = NULL;
311
312     TRACE("(0x%08x,0x%08x,%p,%p):stub.\n", wEventId, uFlags, dwItem1, dwItem2);
313
314     if( ( wEventId & SHCNE_NOITEMEVENTS ) && ( dwItem1 || dwItem2 ) )
315     {
316         TRACE("dwItem1 and dwItem2 are not zero, but should be\n");
317         dwItem1 = 0;
318         dwItem2 = 0;
319         return;
320     }
321     else if( ( wEventId & SHCNE_ONEITEMEVENTS ) && dwItem2 )
322     {
323         TRACE("dwItem2 is not zero, but should be\n");
324         dwItem2 = 0;
325         return;
326     }
327
328     if( ( ( wEventId & SHCNE_NOITEMEVENTS ) && 
329           ( wEventId & ~SHCNE_NOITEMEVENTS ) ) ||
330         ( ( wEventId & SHCNE_ONEITEMEVENTS ) && 
331           ( wEventId & ~SHCNE_ONEITEMEVENTS ) ) ||
332         ( ( wEventId & SHCNE_TWOITEMEVENTS ) && 
333           ( wEventId & ~SHCNE_TWOITEMEVENTS ) ) )
334     {
335         WARN("mutually incompatible events listed\n");
336         return;
337     }
338
339     /* convert paths in IDLists*/
340     switch (typeFlag)
341     {
342     case SHCNF_PATHA:
343         if (dwItem1) Pidls[0] = SHSimpleIDListFromPathA(dwItem1);
344         if (dwItem2) Pidls[1] = SHSimpleIDListFromPathA(dwItem2);
345         break;
346     case SHCNF_PATHW:
347         if (dwItem1) Pidls[0] = SHSimpleIDListFromPathW(dwItem1);
348         if (dwItem2) Pidls[1] = SHSimpleIDListFromPathW(dwItem2);
349         break;
350     case SHCNF_IDLIST:
351         Pidls[0] = dwItem1;
352         Pidls[1] = dwItem2;
353         break;
354     case SHCNF_PRINTERA:
355     case SHCNF_PRINTERW:
356         FIXME("SHChangeNotify with (uFlags & SHCNF_PRINTER)\n");
357         return;
358     case SHCNF_DWORD:
359     default:
360         FIXME("unknown type %08x\n",typeFlag);
361         return;
362     }
363
364     {
365         WCHAR path[MAX_PATH];
366
367         if( Pidls[0] && SHGetPathFromIDListW(Pidls[0], path ))
368             TRACE("notify %08x on item1 = %s\n", wEventId, debugstr_w(path));
369     
370         if( Pidls[1] && SHGetPathFromIDListW(Pidls[1], path ))
371             TRACE("notify %08x on item2 = %s\n", wEventId, debugstr_w(path));
372     }
373
374     EnterCriticalSection(&SHELL32_ChangenotifyCS);
375
376     /* loop through the list */
377     for( ptr = head; ptr; ptr = ptr->next )
378     {
379         BOOL notify;
380         DWORD i;
381
382         notify = FALSE;
383
384         TRACE("trying %p\n", ptr);
385
386         for( i=0; (i<ptr->cidl) && !notify ; i++ )
387         {
388             LPCITEMIDLIST pidl = ptr->apidl[i].pidl;
389             BOOL subtree = ptr->apidl[i].fRecursive;
390
391             if (wEventId & ptr->wEventMask)
392             {
393                 if( !pidl )          /* all ? */
394                     notify = TRUE;
395                 else if( wEventId & SHCNE_NOITEMEVENTS )
396                     notify = TRUE;
397                 else if( wEventId & ( SHCNE_ONEITEMEVENTS | SHCNE_TWOITEMEVENTS ) )
398                     notify = should_notify( Pidls[0], pidl, subtree );
399                 else if( wEventId & SHCNE_TWOITEMEVENTS )
400                     notify = should_notify( Pidls[1], pidl, subtree );
401             }
402         }
403
404         if( !notify )
405             continue;
406
407         ptr->pidlSignaled = ILClone(Pidls[0]);
408
409         TRACE("notifying %s, event %s(%x) before\n", NodeName( ptr ), DumpEvent(
410                wEventId ),wEventId );
411
412         ptr->wSignalledEvent |= wEventId;
413
414         if (ptr->dwFlags  & SHCNRF_NewDelivery)
415             SendMessageA(ptr->hwnd, ptr->uMsg, (WPARAM) ptr, (LPARAM) GetCurrentProcessId());
416         else
417             SendMessageA(ptr->hwnd, ptr->uMsg, (WPARAM)Pidls, wEventId);
418
419         TRACE("notifying %s, event %s(%x) after\n", NodeName( ptr ), DumpEvent(
420                 wEventId ),wEventId );
421
422     }
423     TRACE("notify Done\n");
424     LeaveCriticalSection(&SHELL32_ChangenotifyCS);
425
426     if (wEventId & SHCNE_ASSOCCHANGED)
427     {
428         TRACE("refreshing file type associations\n");
429         RefreshFileTypeAssociations();
430     }
431     
432     /* if we allocated it, free it. The ANSI flag is also set in its Unicode sibling. */
433     if ((typeFlag & SHCNF_PATHA) || (typeFlag & SHCNF_PRINTERA))
434     {
435         SHFree((LPITEMIDLIST)Pidls[0]);
436         SHFree((LPITEMIDLIST)Pidls[1]);
437     }
438 }
439
440 /*************************************************************************
441  * NTSHChangeNotifyRegister                     [SHELL32.640]
442  * NOTES
443  *   Idlist is an array of structures and Count specifies how many items in the array
444  *   (usually just one I think).
445  */
446 DWORD WINAPI NTSHChangeNotifyRegister(
447     HWND hwnd,
448     LONG events1,
449     LONG events2,
450     DWORD msg,
451     int count,
452     SHChangeNotifyEntry *idlist)
453 {
454     FIXME("(%p,0x%08x,0x%08x,0x%08x,0x%08x,%p):semi stub.\n",
455                 hwnd,events1,events2,msg,count,idlist);
456
457     return SHChangeNotifyRegister(hwnd, events1, events2, msg, count, idlist);
458 }
459
460 /*************************************************************************
461  * SHChangeNotification_Lock                    [SHELL32.644]
462  */
463 HANDLE WINAPI SHChangeNotification_Lock(
464         HANDLE hChange,
465         DWORD dwProcessId,
466         LPITEMIDLIST **lppidls,
467         LPLONG lpwEventId)
468 {
469     DWORD i;
470     LPNOTIFICATIONLIST node;
471     LPCITEMIDLIST *idlist;
472
473     TRACE("%p %08x %p %p\n", hChange, dwProcessId, lppidls, lpwEventId);
474
475     /* EnterCriticalSection(&SHELL32_ChangenotifyCS); */
476
477     node = FindNode( hChange );
478     if( node )
479     {
480         idlist = SHAlloc( sizeof(LPCITEMIDLIST *) * node->cidl );
481         for(i=0; i<node->cidl; i++)
482             idlist[i] = node->pidlSignaled;
483         *lpwEventId = node->wSignalledEvent;
484         *lppidls = (LPITEMIDLIST*)idlist;
485         node->wSignalledEvent = 0;
486     }
487     else
488         ERR("Couldn't find %p\n", hChange );
489
490     /* LeaveCriticalSection(&SHELL32_ChangenotifyCS); */
491
492     return node;
493 }
494
495 /*************************************************************************
496  * SHChangeNotification_Unlock                  [SHELL32.645]
497  */
498 BOOL WINAPI SHChangeNotification_Unlock ( HANDLE hLock)
499 {
500     TRACE("\n");
501     return 1;
502 }
503
504 /*************************************************************************
505  * NTSHChangeNotifyDeregister                   [SHELL32.641]
506  */
507 DWORD WINAPI NTSHChangeNotifyDeregister(ULONG x1)
508 {
509     FIXME("(0x%08x):semi stub.\n",x1);
510
511     return SHChangeNotifyDeregister( x1 );
512 }