oledlg: Send the OLEUI_MSG_ENDDIALOG messsage when closing the dialog.
[wine] / dlls / oledlg / pastespl.c
1 /*
2  * OleUIPasteSpecial implementation
3  *
4  * Copyright 2006 Huw Davies
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 #define COM_NO_WINDOWS_H
22 #define COBJMACROS
23
24 #include <stdarg.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winerror.h"
29 #include "wingdi.h"
30 #include "winuser.h"
31 #include "winnls.h"
32 #include "oledlg.h"
33
34 #include "oledlg_private.h"
35
36 #include "wine/debug.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(ole);
39
40 typedef struct
41 {
42     OLEUIPASTESPECIALW *ps;
43     DWORD flags;
44 } ps_struct_t;
45
46 static const struct ps_flag
47 {
48     DWORD flag;
49     const char *name;
50 } ps_flags[] = {
51 #define PS_FLAG_ENTRY(p) {p, #p}
52     PS_FLAG_ENTRY(PSF_SHOWHELP),
53     PS_FLAG_ENTRY(PSF_SELECTPASTE),
54     PS_FLAG_ENTRY(PSF_SELECTPASTELINK),
55     PS_FLAG_ENTRY(PSF_CHECKDISPLAYASICON),
56     PS_FLAG_ENTRY(PSF_DISABLEDISPLAYASICON),
57     PS_FLAG_ENTRY(PSF_HIDECHANGEICON),
58     PS_FLAG_ENTRY(PSF_STAYONCLIPBOARDCHANGE),
59     PS_FLAG_ENTRY(PSF_NOREFRESHDATAOBJECT),
60     {-1, NULL}
61 #undef PS_FLAG_ENTRY
62 };
63
64 static void dump_ps_flags(DWORD flags)
65 {
66     char flagstr[1000] = "";
67
68     const struct ps_flag *flag = ps_flags;
69     for( ; flag->name; flag++) {
70         if(flags & flag->flag) {
71             strcat(flagstr, flag->name);
72             strcat(flagstr, "|");
73         }
74     }
75     TRACE("flags %08x %s\n", flags, flagstr);
76 }
77
78 static void dump_pastespecial(LPOLEUIPASTESPECIALW ps)
79 {
80     UINT i;
81     dump_ps_flags(ps->dwFlags);
82     TRACE("hwnd %p caption %s hook %p custdata %lx\n",
83           ps->hWndOwner, debugstr_w(ps->lpszCaption), ps->lpfnHook, ps->lCustData);
84     if(IS_INTRESOURCE(ps->lpszTemplate))
85         TRACE("hinst %p template %04x hresource %p\n", ps->hInstance, (WORD)(ULONG_PTR)ps->lpszTemplate, ps->hResource);
86     else
87         TRACE("hinst %p template %s hresource %p\n", ps->hInstance, debugstr_w(ps->lpszTemplate), ps->hResource);
88     TRACE("dataobj %p arrpasteent %p cpasteent %d arrlinktype %p clinktype %d\n",
89           ps->lpSrcDataObj, ps->arrPasteEntries, ps->cPasteEntries,
90           ps->arrLinkTypes, ps->cLinkTypes);
91     TRACE("cclsidex %d lpclsidex %p nselect %d flink %d hmetapict %p size(%d,%d)\n",
92           ps->cClsidExclude, ps->lpClsidExclude, ps->nSelectedIndex, ps->fLink,
93           ps->hMetaPict, ps->sizel.cx, ps->sizel.cy);
94     for(i = 0; i < ps->cPasteEntries; i++)
95     {
96         TRACE("arrPasteEntries[%d]: cFormat %08x pTargetDevice %p dwAspect %d lindex %d tymed %d\n",
97               i, ps->arrPasteEntries[i].fmtetc.cfFormat, ps->arrPasteEntries[i].fmtetc.ptd,
98               ps->arrPasteEntries[i].fmtetc.dwAspect, ps->arrPasteEntries[i].fmtetc.lindex,
99               ps->arrPasteEntries[i].fmtetc.tymed);
100         TRACE("\tformat name %s result text %s flags %04x\n", debugstr_w(ps->arrPasteEntries[i].lpstrFormatName),
101               debugstr_w(ps->arrPasteEntries[i].lpstrResultText), ps->arrPasteEntries[i].dwFlags);
102     }
103     for(i = 0; i < ps->cLinkTypes; i++)
104         TRACE("arrLinkTypes[%d] %08x\n", i, ps->arrLinkTypes[i]);
105     for(i = 0; i < ps->cClsidExclude; i++)
106         TRACE("lpClsidExclude[%d] %s\n", i, debugstr_guid(&ps->lpClsidExclude[i]));
107
108 }
109
110 static inline WCHAR *strdupAtoW(const char *str)
111 {
112     DWORD len;
113     WCHAR *ret;
114     if(!str) return NULL;
115     len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
116     ret = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
117     MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
118     return ret;
119 }
120
121 static BOOL add_entry_to_lb(HWND hdlg, UINT id, OLEUIPASTEENTRYW *pe)
122 {
123     HWND hwnd = GetDlgItem(hdlg, id);
124     BOOL ret = FALSE;
125
126     /* FIXME %s handling */
127
128     /* Note that this suffers from the same bug as native, in that if a new string
129        is a substring of an already added string, then the FINDSTRING will succeed
130        this is probably not what we want */
131     if(SendMessageW(hwnd, LB_FINDSTRING, 0, (LPARAM)pe->lpstrFormatName) == -1)
132     {
133         LRESULT pos = SendMessageW(hwnd, LB_ADDSTRING, 0, (LPARAM)pe->lpstrFormatName);
134         SendMessageW(hwnd, LB_SETITEMDATA, pos, (LPARAM)pe);
135         ret = TRUE;
136     }
137     return ret;
138 }
139
140 static DWORD init_pastelist(HWND hdlg, OLEUIPASTESPECIALW *ps)
141 {
142     IEnumFORMATETC *penum;
143     HRESULT hr;
144     FORMATETC fmts[20];
145     DWORD fetched, items_added = 0;
146
147     hr = IDataObject_EnumFormatEtc(ps->lpSrcDataObj, DATADIR_GET, &penum);
148     if(FAILED(hr))
149     {
150         WARN("Unable to create IEnumFORMATETC\n");
151         return 0;
152     }
153
154     /* The native version grabs only the first 20 fmts and we do the same */
155     hr = IEnumFORMATETC_Next(penum, sizeof(fmts)/sizeof(fmts[0]), fmts, &fetched);
156     TRACE("got %d formats hr %08x\n", fetched, hr);
157
158     if(SUCCEEDED(hr))
159     {
160         DWORD src_fmt, req_fmt;
161         for(req_fmt = 0; req_fmt < ps->cPasteEntries; req_fmt++)
162         {
163             /* This is used by update_struct() to set nSelectedIndex on exit */
164             ps->arrPasteEntries[req_fmt].dwScratchSpace = req_fmt;
165             TRACE("req_fmt %x\n", ps->arrPasteEntries[req_fmt].fmtetc.cfFormat);
166             for(src_fmt = 0; src_fmt < fetched; src_fmt++)
167             {
168                 TRACE("\tenum'ed fmt %x\n", fmts[src_fmt].cfFormat);
169                 if(ps->arrPasteEntries[req_fmt].fmtetc.cfFormat == fmts[src_fmt].cfFormat)
170                 {
171                     add_entry_to_lb(hdlg, IDC_PS_PASTELIST, ps->arrPasteEntries + req_fmt);
172                     items_added++;
173                     break;
174                 }
175             }
176         }
177     }
178
179     IEnumFORMATETC_Release(penum);
180     EnableWindow(GetDlgItem(hdlg, IDC_PS_PASTE), items_added ? TRUE : FALSE);
181     return items_added;
182 }
183
184 static DWORD init_linklist(HWND hdlg, OLEUIPASTESPECIALW *ps)
185 {
186     HRESULT hr;
187     DWORD supported_mask = 0;
188     DWORD items_added = 0;
189     int link, req_fmt;
190     FORMATETC fmt = {0, NULL, DVASPECT_CONTENT, -1, -1};
191
192     for(link = 0; link < ps->cLinkTypes && link < PS_MAXLINKTYPES; link++)
193     {
194         fmt.cfFormat = ps->arrLinkTypes[link];
195         hr = IDataObject_QueryGetData(ps->lpSrcDataObj, &fmt);
196         if(hr == S_OK)
197             supported_mask |= 1 << link;
198     }
199     TRACE("supported_mask %02x\n", supported_mask);
200     for(req_fmt = 0; req_fmt < ps->cPasteEntries; req_fmt++)
201     {
202         DWORD linktypes;
203         if(ps->arrPasteEntries[req_fmt].dwFlags & OLEUIPASTE_LINKANYTYPE)
204             linktypes = 0xff;
205         else
206             linktypes = ps->arrPasteEntries[req_fmt].dwFlags & 0xff;
207
208         if(linktypes & supported_mask)
209         {
210             add_entry_to_lb(hdlg, IDC_PS_PASTELINKLIST, ps->arrPasteEntries + req_fmt);
211             items_added++;
212         }
213     }
214
215     EnableWindow(GetDlgItem(hdlg, IDC_PS_PASTELINK), items_added ? TRUE : FALSE);
216     return items_added;
217 }
218
219 /* copies src_list_id into the display list */
220 static void update_display_list(HWND hdlg, UINT src_list_id)
221 {
222     LONG count, i, old_pos;
223     WCHAR txt[256];
224     LONG item_data;
225     HWND display_list = GetDlgItem(hdlg, IDC_PS_DISPLAYLIST);
226     HWND list = GetDlgItem(hdlg, src_list_id);
227
228     old_pos = SendMessageW(display_list, LB_GETCURSEL, 0, 0);
229     if(old_pos == -1) old_pos = 0;
230
231     SendMessageW(display_list, WM_SETREDRAW, 0, 0);
232     SendMessageW(display_list, LB_RESETCONTENT, 0, 0);
233     count = SendMessageW(list, LB_GETCOUNT, 0, 0);
234     for(i = 0; i < count; i++)
235     {
236         SendMessageW(list, LB_GETTEXT, i, (LPARAM)txt);
237         item_data = SendMessageW(list, LB_GETITEMDATA, i, 0);
238         SendMessageW(display_list, LB_INSERTSTRING, i, (LPARAM)txt);
239         SendMessageW(display_list, LB_SETITEMDATA, i, item_data);
240     }
241     old_pos = max(old_pos, count);
242     SendMessageW(display_list, LB_SETCURSEL, 0, 0);
243     SendMessageW(display_list, WM_SETREDRAW, 1, 0);
244     if(GetForegroundWindow() == hdlg)
245         SetFocus(display_list);
246 }
247
248 static void init_lists(HWND hdlg, ps_struct_t *ps_struct)
249 {
250     DWORD pastes_added = init_pastelist(hdlg, ps_struct->ps);
251     DWORD links_added = init_linklist(hdlg, ps_struct->ps);
252     UINT check_id, list_id;
253
254     if((ps_struct->flags & (PSF_SELECTPASTE | PSF_SELECTPASTELINK)) == 0)
255         ps_struct->flags |= PSF_SELECTPASTE;
256
257     if(!pastes_added && !links_added)
258         ps_struct->flags &= ~(PSF_SELECTPASTE | PSF_SELECTPASTELINK);
259     else if(!pastes_added && (ps_struct->flags & PSF_SELECTPASTE))
260     {
261         ps_struct->flags &= ~PSF_SELECTPASTE;
262         ps_struct->flags |= PSF_SELECTPASTELINK;
263     }
264     else if(!links_added && (ps_struct->flags & PSF_SELECTPASTELINK))
265     {
266         ps_struct->flags &= ~PSF_SELECTPASTELINK;
267         ps_struct->flags |= PSF_SELECTPASTE;
268     }
269
270     check_id = 0;
271     list_id = 0;
272     if(ps_struct->flags & PSF_SELECTPASTE)
273     {
274         check_id = IDC_PS_PASTE;
275         list_id = IDC_PS_PASTELIST;
276     }
277     else if(ps_struct->flags & PSF_SELECTPASTELINK)
278     {
279         check_id = IDC_PS_PASTELINK;
280         list_id = IDC_PS_PASTELINKLIST;
281     }
282
283     CheckRadioButton(hdlg, IDC_PS_PASTE, IDC_PS_PASTELINK, check_id);
284
285     if(list_id)
286         update_display_list(hdlg, list_id);
287     else
288         EnableWindow(GetDlgItem(hdlg, IDOK), 0);
289 }
290
291 static void send_end_dialog_msg(HWND hdlg, ps_struct_t *ps_struct, UINT id)
292 {
293     SendMessageW(hdlg, oleui_msg_enddialog, id, 0);
294 }
295
296 static void update_structure(HWND hdlg, ps_struct_t *ps_struct)
297 {
298     ps_struct->ps->dwFlags = ps_struct->flags;
299     ps_struct->ps->fLink = (ps_struct->flags & PSF_SELECTPASTELINK) ? TRUE : FALSE;
300 }
301
302 static void free_structure(ps_struct_t *ps_struct)
303 {
304     HeapFree(GetProcessHeap(), 0, ps_struct);
305 }
306
307 static INT_PTR CALLBACK ps_dlg_proc(HWND hdlg, UINT msg, WPARAM wp, LPARAM lp)
308 {
309     /* native uses prop name "Structure", but we're not compatible
310        with that so we'll prepend "Wine_". */
311     static const WCHAR prop_name[] = {'W','i','n','e','_','S','t','r','u','c','t','u','r','e',0};
312     ps_struct_t *ps_struct;
313
314     TRACE("(%p, %04x, %08x, %08lx)\n", hdlg, msg, wp, lp);
315
316     ps_struct = GetPropW(hdlg, prop_name);
317
318     if(msg != WM_INITDIALOG)
319     {
320         if(!ps_struct)
321             return 0;
322     }
323
324     switch(msg)
325     {
326     case WM_INITDIALOG:
327     {
328         ps_struct = HeapAlloc(GetProcessHeap(), 0, sizeof(*ps_struct));
329         ps_struct->ps = (OLEUIPASTESPECIALW*)lp;
330         ps_struct->flags = ps_struct->ps->dwFlags;
331
332         SetPropW(hdlg, prop_name, ps_struct);
333
334         if(ps_struct->ps->lpszCaption)
335             SetWindowTextW(hdlg, ps_struct->ps->lpszCaption);
336
337         init_lists(hdlg, ps_struct);
338
339         return TRUE; /* use default focus */
340     }
341     case WM_COMMAND:
342         switch(LOWORD(wp))
343         {
344         case IDOK:
345         case IDCANCEL:
346             send_end_dialog_msg(hdlg, ps_struct, LOWORD(wp));
347             return FALSE;
348         }
349         return FALSE;
350     default:
351         if(msg == oleui_msg_enddialog)
352         {
353             if(wp == IDOK)
354                 update_structure(hdlg, ps_struct);
355             EndDialog(hdlg, wp);
356             free_structure(ps_struct);
357             return TRUE;
358         }
359         return FALSE;
360     }
361
362 }
363
364 /***********************************************************************
365  *           OleUIPasteSpecialA (OLEDLG.4)
366  */
367 UINT WINAPI OleUIPasteSpecialA(LPOLEUIPASTESPECIALA psA)
368 {
369     OLEUIPASTESPECIALW ps;
370     UINT ret;
371     TRACE("(%p)\n", psA);
372
373     memcpy(&ps, psA, psA->cbStruct);
374
375     ps.lpszCaption = strdupAtoW(psA->lpszCaption);
376     if(!IS_INTRESOURCE(ps.lpszTemplate))
377         ps.lpszTemplate = strdupAtoW(psA->lpszTemplate);
378
379     if(psA->cPasteEntries > 0)
380     {
381         DWORD size = psA->cPasteEntries * sizeof(ps.arrPasteEntries[0]);
382         UINT i;
383
384         ps.arrPasteEntries = HeapAlloc(GetProcessHeap(), 0, size);
385         memcpy(ps.arrPasteEntries, psA->arrPasteEntries, size);
386         for(i = 0; i < psA->cPasteEntries; i++)
387         {
388             ps.arrPasteEntries[i].lpstrFormatName =
389                 strdupAtoW(psA->arrPasteEntries[i].lpstrFormatName);
390             ps.arrPasteEntries[i].lpstrResultText =
391                 strdupAtoW(psA->arrPasteEntries[i].lpstrResultText);
392         }
393     }
394
395     ret = OleUIPasteSpecialW(&ps);
396
397     if(psA->cPasteEntries > 0)
398     {
399         UINT i;
400         for(i = 0; i < psA->cPasteEntries; i++)
401         {
402             HeapFree(GetProcessHeap(), 0, (WCHAR*)ps.arrPasteEntries[i].lpstrFormatName);
403             HeapFree(GetProcessHeap(), 0, (WCHAR*)ps.arrPasteEntries[i].lpstrResultText);
404         }
405         HeapFree(GetProcessHeap(), 0, ps.arrPasteEntries);
406     }
407     if(!IS_INTRESOURCE(ps.lpszTemplate))
408         HeapFree(GetProcessHeap(), 0, (WCHAR*)ps.lpszTemplate);
409     HeapFree(GetProcessHeap(), 0, (WCHAR*)ps.lpszCaption);
410
411     /* Copy back the output fields */
412     psA->dwFlags = ps.dwFlags;
413     psA->lpSrcDataObj = ps.lpSrcDataObj;
414     psA->nSelectedIndex = ps.nSelectedIndex;
415     psA->fLink = ps.fLink;
416     psA->hMetaPict = ps.hMetaPict;
417     psA->sizel = ps.sizel;
418
419     return ret;
420 }
421
422 /***********************************************************************
423  *           OleUIPasteSpecialW (OLEDLG.22)
424  */
425 UINT WINAPI OleUIPasteSpecialW(LPOLEUIPASTESPECIALW ps)
426 {
427     LPCDLGTEMPLATEW dlg_templ = (LPCDLGTEMPLATEW)ps->hResource;
428
429     TRACE("(%p)\n", ps);
430
431     if(TRACE_ON(ole)) dump_pastespecial(ps);
432
433     if(!ps->lpSrcDataObj)
434         OleGetClipboard(&ps->lpSrcDataObj);
435
436     if(ps->hInstance || !ps->hResource)
437     {
438         HINSTANCE hInst = ps->hInstance ? ps->hInstance : OLEDLG_hInstance;
439         const WCHAR *name = ps->hInstance ? ps->lpszTemplate : MAKEINTRESOURCEW(IDD_PASTESPECIAL4);
440         HRSRC hrsrc;
441
442         if(name == NULL) return OLEUI_ERR_LPSZTEMPLATEINVALID;
443         hrsrc = FindResourceW(hInst, name, MAKEINTRESOURCEW(RT_DIALOG));
444         if(!hrsrc) return OLEUI_ERR_FINDTEMPLATEFAILURE;
445         dlg_templ = LoadResource(hInst, hrsrc);
446         if(!dlg_templ) return OLEUI_ERR_LOADTEMPLATEFAILURE;
447     }
448
449     DialogBoxIndirectParamW(OLEDLG_hInstance, dlg_templ, ps->hWndOwner, ps_dlg_proc, (LPARAM)ps);
450
451     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
452     return OLEUI_FALSE;
453 }