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