xref: /reactos/dll/win32/mshtml/olecmd.c (revision 5100859e)
1 /*
2  * Copyright 2005-2007 Jacek Caban for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #include "mshtml_private.h"
20 
21 #define NSCMD_COPY "cmd_copy"
22 #define NSCMD_SELECTALL           "cmd_selectAll"
23 
24 void do_ns_command(HTMLDocument *This, const char *cmd, nsICommandParams *nsparam)
25 {
26     nsICommandManager *cmdmgr;
27     nsresult nsres;
28 
29     TRACE("(%p)\n", This);
30 
31     if(!This->doc_obj || !This->doc_obj->nscontainer)
32         return;
33 
34     nsres = get_nsinterface((nsISupports*)This->doc_obj->nscontainer->webbrowser, &IID_nsICommandManager, (void**)&cmdmgr);
35     if(NS_FAILED(nsres)) {
36         ERR("Could not get nsICommandManager: %08x\n", nsres);
37         return;
38     }
39 
40     nsres = nsICommandManager_DoCommand(cmdmgr, cmd, nsparam, This->window->nswindow);
41     if(NS_FAILED(nsres))
42         ERR("DoCommand(%s) failed: %08x\n", debugstr_a(cmd), nsres);
43 
44     nsICommandManager_Release(cmdmgr);
45 }
46 
47 static nsIClipboardCommands *get_clipboard_commands(HTMLDocument *doc)
48 {
49     nsIClipboardCommands *clipboard_commands;
50     nsIDocShell *doc_shell;
51     nsresult nsres;
52 
53     nsres = get_nsinterface((nsISupports*)doc->window->nswindow, &IID_nsIDocShell, (void**)&doc_shell);
54     if(NS_FAILED(nsres)) {
55         ERR("Could not get nsIDocShell interface\n");
56         return NULL;
57     }
58 
59     nsres = nsIDocShell_QueryInterface(doc_shell, &IID_nsIClipboardCommands, (void**)&clipboard_commands);
60     nsIDocShell_Release(doc_shell);
61     if(NS_FAILED(nsres)) {
62         ERR("Could not get nsIClipboardCommands interface\n");
63         return NULL;
64     }
65 
66     return clipboard_commands;
67 }
68 
69 /**********************************************************
70  * IOleCommandTarget implementation
71  */
72 
73 static inline HTMLDocument *impl_from_IOleCommandTarget(IOleCommandTarget *iface)
74 {
75     return CONTAINING_RECORD(iface, HTMLDocument, IOleCommandTarget_iface);
76 }
77 
78 static HRESULT exec_open(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
79 {
80     FIXME("(%p)->(%d %p %p)\n", This, nCmdexecopt, pvaIn, pvaOut);
81     return E_NOTIMPL;
82 }
83 
84 static HRESULT exec_new(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
85 {
86     FIXME("(%p)->(%d %p %p)\n", This, nCmdexecopt, pvaIn, pvaOut);
87     return E_NOTIMPL;
88 }
89 
90 static HRESULT exec_save(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
91 {
92     FIXME("(%p)->(%d %p %p)\n", This, nCmdexecopt, pvaIn, pvaOut);
93     return E_NOTIMPL;
94 }
95 
96 static HRESULT exec_save_as(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
97 {
98     FIXME("(%p)->(%d %p %p)\n", This, nCmdexecopt, pvaIn, pvaOut);
99     return E_NOTIMPL;
100 }
101 
102 static HRESULT exec_save_copy_as(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
103 {
104     FIXME("(%p)->(%d %p %p)\n", This, nCmdexecopt, pvaIn, pvaOut);
105     return E_NOTIMPL;
106 }
107 
108 static nsresult set_head_text(nsIPrintSettings *settings, LPCWSTR template, BOOL head, int pos)
109 {
110     if(head) {
111         switch(pos) {
112         case 0:
113             return nsIPrintSettings_SetHeaderStrLeft(settings, template);
114         case 1:
115             return nsIPrintSettings_SetHeaderStrRight(settings, template);
116         case 2:
117             return nsIPrintSettings_SetHeaderStrCenter(settings, template);
118         }
119     }else {
120         switch(pos) {
121         case 0:
122             return nsIPrintSettings_SetFooterStrLeft(settings, template);
123         case 1:
124             return nsIPrintSettings_SetFooterStrRight(settings, template);
125         case 2:
126             return nsIPrintSettings_SetFooterStrCenter(settings, template);
127         }
128     }
129 
130     return NS_OK;
131 }
132 
133 static void set_print_template(nsIPrintSettings *settings, LPCWSTR template, BOOL head)
134 {
135     PRUnichar nstemplate[200]; /* FIXME: Use dynamic allocation */
136     PRUnichar *p = nstemplate;
137     LPCWSTR ptr=template;
138     int pos=0;
139 
140     while(*ptr) {
141         if(*ptr != '&') {
142             *p++ = *ptr++;
143             continue;
144         }
145 
146         switch(*++ptr) {
147         case '&':
148             *p++ = '&';
149             *p++ = '&';
150             ptr++;
151             break;
152         case 'b': /* change align */
153             ptr++;
154             *p = 0;
155             set_head_text(settings, nstemplate, head, pos);
156             p = nstemplate;
157             pos++;
158             break;
159         case 'd': { /* short date */
160             SYSTEMTIME systime;
161             GetLocalTime(&systime);
162             GetDateFormatW(LOCALE_SYSTEM_DEFAULT, 0, &systime, NULL, p,
163                     sizeof(nstemplate)-(p-nstemplate)*sizeof(WCHAR));
164             p += strlenW(p);
165             ptr++;
166             break;
167         }
168         case 'p': /* page number */
169             *p++ = '&';
170             *p++ = 'P';
171             ptr++;
172             break;
173         case 'P': /* page count */
174             *p++ = '?'; /* FIXME */
175             ptr++;
176             break;
177         case 'u':
178             *p++ = '&';
179             *p++ = 'U';
180             ptr++;
181             break;
182         case 'w':
183             /* FIXME: set window title */
184             ptr++;
185             break;
186         default:
187             *p++ = '&';
188             *p++ = *ptr++;
189         }
190     }
191 
192     *p = 0;
193     set_head_text(settings, nstemplate, head, pos);
194 
195     while(++pos < 3)
196         set_head_text(settings, p, head, pos);
197 }
198 
199 static void set_default_templates(nsIPrintSettings *settings)
200 {
201     WCHAR buf[64];
202 
203     static const PRUnichar empty[] = {0};
204 
205     nsIPrintSettings_SetHeaderStrLeft(settings, empty);
206     nsIPrintSettings_SetHeaderStrRight(settings, empty);
207     nsIPrintSettings_SetHeaderStrCenter(settings, empty);
208     nsIPrintSettings_SetFooterStrLeft(settings, empty);
209     nsIPrintSettings_SetFooterStrRight(settings, empty);
210     nsIPrintSettings_SetFooterStrCenter(settings, empty);
211 
212     if(LoadStringW(get_shdoclc(), IDS_PRINT_HEADER_TEMPLATE, buf,
213                    sizeof(buf)/sizeof(WCHAR)))
214         set_print_template(settings, buf, TRUE);
215 
216 
217     if(LoadStringW(get_shdoclc(), IDS_PRINT_FOOTER_TEMPLATE, buf,
218                    sizeof(buf)/sizeof(WCHAR)))
219         set_print_template(settings, buf, FALSE);
220 
221 }
222 
223 static HRESULT exec_print(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
224 {
225     nsIWebBrowserPrint *nsprint;
226     nsIPrintSettings *settings;
227     nsresult nsres;
228 
229     TRACE("(%p)->(%d %s %p)\n", This, nCmdexecopt, debugstr_variant(pvaIn), pvaOut);
230 
231     if(pvaOut)
232         FIXME("unsupported pvaOut\n");
233 
234     if(!This->doc_obj->nscontainer)
235         return S_OK;
236 
237     nsres = get_nsinterface((nsISupports*)This->doc_obj->nscontainer->webbrowser, &IID_nsIWebBrowserPrint,
238             (void**)&nsprint);
239     if(NS_FAILED(nsres)) {
240         ERR("Could not get nsIWebBrowserPrint: %08x\n", nsres);
241         return S_OK;
242     }
243 
244     nsres = nsIWebBrowserPrint_GetGlobalPrintSettings(nsprint, &settings);
245     if(NS_FAILED(nsres))
246         ERR("GetCurrentPrintSettings failed: %08x\n", nsres);
247 
248     set_default_templates(settings);
249 
250     if(pvaIn) {
251         switch(V_VT(pvaIn)) {
252         case VT_BYREF|VT_ARRAY: {
253             VARIANT *opts;
254             DWORD opts_cnt;
255 
256             if(V_ARRAY(pvaIn)->cDims != 1)
257                 WARN("cDims = %d\n", V_ARRAY(pvaIn)->cDims);
258 
259             SafeArrayAccessData(V_ARRAY(pvaIn), (void**)&opts);
260             opts_cnt = V_ARRAY(pvaIn)->rgsabound[0].cElements;
261 
262             if(opts_cnt >= 1) {
263                 switch(V_VT(opts)) {
264                 case VT_BSTR:
265                     TRACE("setting footer %s\n", debugstr_w(V_BSTR(opts)));
266                     set_print_template(settings, V_BSTR(opts), TRUE);
267                     break;
268                 case VT_NULL:
269                     break;
270                 default:
271                     WARN("opts = %s\n", debugstr_variant(opts));
272                 }
273             }
274 
275             if(opts_cnt >= 2) {
276                 switch(V_VT(opts+1)) {
277                 case VT_BSTR:
278                     TRACE("setting footer %s\n", debugstr_w(V_BSTR(opts+1)));
279                     set_print_template(settings, V_BSTR(opts+1), FALSE);
280                     break;
281                 case VT_NULL:
282                     break;
283                 default:
284                     WARN("opts[1] = %s\n", debugstr_variant(opts+1));
285                 }
286             }
287 
288             if(opts_cnt >= 3)
289                 FIXME("Unsupported opts_cnt %d\n", opts_cnt);
290 
291             SafeArrayUnaccessData(V_ARRAY(pvaIn));
292             break;
293         }
294         default:
295             FIXME("unsupported arg %s\n", debugstr_variant(pvaIn));
296         }
297     }
298 
299     nsres = nsIWebBrowserPrint_Print(nsprint, settings, NULL);
300     if(NS_FAILED(nsres))
301         ERR("Print failed: %08x\n", nsres);
302 
303     nsIWebBrowserPrint_Release(nsprint);
304 
305     return S_OK;
306 }
307 
308 static HRESULT exec_print_preview(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
309 {
310     FIXME("(%p)->(%d %p %p)\n", This, nCmdexecopt, pvaIn, pvaOut);
311     return E_NOTIMPL;
312 }
313 
314 static HRESULT exec_page_setup(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
315 {
316     FIXME("(%p)->(%d %p %p)\n", This, nCmdexecopt, pvaIn, pvaOut);
317     return E_NOTIMPL;
318 }
319 
320 static HRESULT exec_spell(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
321 {
322     FIXME("(%p)->(%d %p %p)\n", This, nCmdexecopt, pvaIn, pvaOut);
323     return E_NOTIMPL;
324 }
325 
326 static HRESULT exec_properties(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
327 {
328     FIXME("(%p)->(%d %p %p)\n", This, nCmdexecopt, pvaIn, pvaOut);
329     return E_NOTIMPL;
330 }
331 
332 static HRESULT exec_cut(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
333 {
334     FIXME("(%p)->(%d %p %p)\n", This, nCmdexecopt, pvaIn, pvaOut);
335     return E_NOTIMPL;
336 }
337 
338 static HRESULT exec_copy(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
339 {
340     TRACE("(%p)->(%d %s %p)\n", This, nCmdexecopt, debugstr_variant(pvaIn), pvaOut);
341 
342     do_ns_command(This, NSCMD_COPY, NULL);
343     return S_OK;
344 }
345 
346 static HRESULT exec_paste(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
347 {
348     FIXME("(%p)->(%d %p %p)\n", This, nCmdexecopt, pvaIn, pvaOut);
349     return E_NOTIMPL;
350 }
351 
352 static HRESULT exec_paste_special(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
353 {
354     FIXME("(%p)->(%d %p %p)\n", This, nCmdexecopt, pvaIn, pvaOut);
355     return E_NOTIMPL;
356 }
357 
358 static HRESULT exec_undo(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
359 {
360     FIXME("(%p)->(%d %p %p)\n", This, nCmdexecopt, pvaIn, pvaOut);
361     return E_NOTIMPL;
362 }
363 
364 static HRESULT exec_rendo(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
365 {
366     FIXME("(%p)->(%d %p %p)\n", This, nCmdexecopt, pvaIn, pvaOut);
367     return E_NOTIMPL;
368 }
369 
370 static HRESULT exec_select_all(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *in, VARIANT *out)
371 {
372     TRACE("(%p)\n", This);
373 
374     if(in || out)
375         FIXME("unsupported args\n");
376 
377     if(This->doc_obj->nscontainer)
378         do_ns_command(This, NSCMD_SELECTALL, NULL);
379 
380     update_doc(This, UPDATE_UI);
381     return S_OK;
382 }
383 
384 static HRESULT exec_clear_selection(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
385 {
386     FIXME("(%p)->(%d %p %p)\n", This, nCmdexecopt, pvaIn, pvaOut);
387     return E_NOTIMPL;
388 }
389 
390 static HRESULT exec_zoom(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
391 {
392     FIXME("(%p)->(%d %p %p)\n", This, nCmdexecopt, pvaIn, pvaOut);
393     return E_NOTIMPL;
394 }
395 
396 static HRESULT exec_get_zoom_range(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
397 {
398     FIXME("(%p)->(%d %p %p)\n", This, nCmdexecopt, pvaIn, pvaOut);
399     return E_NOTIMPL;
400 }
401 
402 typedef struct {
403     task_t header;
404     HTMLOuterWindow *window;
405 }refresh_task_t;
406 
407 static void refresh_proc(task_t *_task)
408 {
409     refresh_task_t *task = (refresh_task_t*)_task;
410     HTMLOuterWindow *window = task->window;
411 
412     TRACE("%p\n", window);
413 
414     window->readystate = READYSTATE_UNINITIALIZED;
415 
416     if(window->doc_obj && window->doc_obj->client_cmdtrg) {
417         VARIANT var;
418 
419         V_VT(&var) = VT_I4;
420         V_I4(&var) = 0;
421         IOleCommandTarget_Exec(window->doc_obj->client_cmdtrg, &CGID_ShellDocView, 37, 0, &var, NULL);
422     }
423 
424     load_uri(task->window, task->window->uri, BINDING_REFRESH|BINDING_NOFRAG);
425 }
426 
427 static void refresh_destr(task_t *_task)
428 {
429     refresh_task_t *task = (refresh_task_t*)_task;
430 
431     IHTMLWindow2_Release(&task->window->base.IHTMLWindow2_iface);
432     heap_free(task);
433 }
434 
435 static HRESULT exec_refresh(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
436 {
437     refresh_task_t *task;
438     HRESULT hres;
439 
440     TRACE("(%p)->(%d %s %p)\n", This, nCmdexecopt, debugstr_variant(pvaIn), pvaOut);
441 
442     if(This->doc_obj->client) {
443         IOleCommandTarget *olecmd;
444 
445         hres = IOleClientSite_QueryInterface(This->doc_obj->client, &IID_IOleCommandTarget, (void**)&olecmd);
446         if(SUCCEEDED(hres)) {
447             hres = IOleCommandTarget_Exec(olecmd, &CGID_DocHostCommandHandler, 2300, nCmdexecopt, pvaIn, pvaOut);
448             IOleCommandTarget_Release(olecmd);
449             if(SUCCEEDED(hres))
450                 return S_OK;
451         }
452     }
453 
454     if(!This->window)
455         return E_UNEXPECTED;
456 
457     task = heap_alloc(sizeof(*task));
458     if(!task)
459         return E_OUTOFMEMORY;
460 
461     IHTMLWindow2_AddRef(&This->window->base.IHTMLWindow2_iface);
462     task->window = This->window;
463 
464     return push_task(&task->header, refresh_proc, refresh_destr, This->window->task_magic);
465 }
466 
467 static HRESULT exec_stop(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
468 {
469     FIXME("(%p)->(%d %p %p)\n", This, nCmdexecopt, pvaIn, pvaOut);
470     return E_NOTIMPL;
471 }
472 
473 static HRESULT exec_stop_download(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
474 {
475     FIXME("(%p)->(%d %p %p)\n", This, nCmdexecopt, pvaIn, pvaOut);
476     return E_NOTIMPL;
477 }
478 
479 static HRESULT exec_find(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
480 {
481     FIXME("(%p)->(%d %p %p)\n", This, nCmdexecopt, pvaIn, pvaOut);
482     return E_NOTIMPL;
483 }
484 
485 static HRESULT exec_delete(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
486 {
487     FIXME("(%p)->(%d %p %p)\n", This, nCmdexecopt, pvaIn, pvaOut);
488     return E_NOTIMPL;
489 }
490 
491 static HRESULT exec_enable_interaction(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
492 {
493     FIXME("(%p)->(%d %p %p)\n", This, nCmdexecopt, pvaIn, pvaOut);
494     return E_NOTIMPL;
495 }
496 
497 static HRESULT exec_on_unload(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
498 {
499     TRACE("(%p)->(%d %p %p)\n", This, nCmdexecopt, pvaIn, pvaOut);
500 
501     /* Tests show that we have nothing more to do here */
502 
503     if(pvaOut) {
504         V_VT(pvaOut) = VT_BOOL;
505         V_BOOL(pvaOut) = VARIANT_TRUE;
506     }
507 
508     return S_OK;
509 }
510 
511 static HRESULT exec_show_page_setup(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
512 {
513     FIXME("(%p)->(%d %p %p)\n", This, nCmdexecopt, pvaIn, pvaOut);
514     return E_NOTIMPL;
515 }
516 
517 static HRESULT exec_show_print(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
518 {
519     FIXME("(%p)->(%d %p %p)\n", This, nCmdexecopt, pvaIn, pvaOut);
520     return E_NOTIMPL;
521 }
522 
523 static HRESULT exec_close(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
524 {
525     FIXME("(%p)->(%d %p %p)\n", This, nCmdexecopt, pvaIn, pvaOut);
526     return E_NOTIMPL;
527 }
528 
529 static HRESULT exec_set_print_template(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
530 {
531     FIXME("(%p)->(%d %p %p)\n", This, nCmdexecopt, pvaIn, pvaOut);
532     return E_NOTIMPL;
533 }
534 
535 static HRESULT exec_get_print_template(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
536 {
537     FIXME("(%p)->(%d %p %p)\n", This, nCmdexecopt, pvaIn, pvaOut);
538     return E_NOTIMPL;
539 }
540 
541 static HRESULT exec_optical_zoom(HTMLDocument *This, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
542 {
543     TRACE("(%p)->(%d %s %p)\n", This, nCmdexecopt, debugstr_variant(pvaIn), pvaOut);
544 
545     if(!pvaIn || V_VT(pvaIn) != VT_I4) {
546         FIXME("Unsupported argument %s\n", debugstr_variant(pvaIn));
547         return E_NOTIMPL;
548     }
549 
550     set_viewer_zoom(This->doc_obj->nscontainer, (float)V_I4(pvaIn)/100);
551     return S_OK;
552 }
553 
554 static HRESULT query_mshtml_copy(HTMLDocument *This, OLECMD *cmd)
555 {
556     FIXME("(%p)\n", This);
557     cmd->cmdf = OLECMDF_SUPPORTED|OLECMDF_ENABLED;
558     return S_OK;
559 }
560 
561 static HRESULT exec_mshtml_copy(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
562 {
563     TRACE("(%p)->(%08x %p %p)\n", This, cmdexecopt, in, out);
564 
565     if(This->doc_obj->usermode == EDITMODE)
566         return editor_exec_copy(This, cmdexecopt, in, out);
567 
568     do_ns_command(This, NSCMD_COPY, NULL);
569     return S_OK;
570 }
571 
572 static HRESULT query_mshtml_cut(HTMLDocument *This, OLECMD *cmd)
573 {
574     FIXME("(%p)\n", This);
575     cmd->cmdf = OLECMDF_SUPPORTED|OLECMDF_ENABLED;
576     return S_OK;
577 }
578 
579 static HRESULT exec_mshtml_cut(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
580 {
581     nsIClipboardCommands *clipboard_commands;
582     nsresult nsres;
583 
584     TRACE("(%p)->(%08x %p %p)\n", This, cmdexecopt, in, out);
585 
586     if(This->doc_obj->usermode == EDITMODE)
587         return editor_exec_cut(This, cmdexecopt, in, out);
588 
589     clipboard_commands = get_clipboard_commands(This);
590     if(!clipboard_commands)
591         return E_UNEXPECTED;
592 
593     nsres = nsIClipboardCommands_CutSelection(clipboard_commands);
594     nsIClipboardCommands_Release(clipboard_commands);
595     if(NS_FAILED(nsres)) {
596         ERR("Paste failed: %08x\n", nsres);
597         return E_FAIL;
598     }
599 
600     return S_OK;
601 }
602 
603 static HRESULT query_mshtml_paste(HTMLDocument *This, OLECMD *cmd)
604 {
605     FIXME("(%p)\n", This);
606     cmd->cmdf = OLECMDF_SUPPORTED|OLECMDF_ENABLED;
607     return S_OK;
608 }
609 
610 static HRESULT exec_mshtml_paste(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
611 {
612     nsIClipboardCommands *clipboard_commands;
613     nsresult nsres;
614 
615     TRACE("(%p)->(%08x %p %p)\n", This, cmdexecopt, in, out);
616 
617     if(This->doc_obj->usermode == EDITMODE)
618         return editor_exec_paste(This, cmdexecopt, in, out);
619 
620     clipboard_commands = get_clipboard_commands(This);
621     if(!clipboard_commands)
622         return E_UNEXPECTED;
623 
624     nsres = nsIClipboardCommands_Paste(clipboard_commands);
625     nsIClipboardCommands_Release(clipboard_commands);
626     if(NS_FAILED(nsres)) {
627         ERR("Paste failed: %08x\n", nsres);
628         return E_FAIL;
629     }
630 
631     return S_OK;
632 }
633 
634 static HRESULT query_selall_status(HTMLDocument *This, OLECMD *cmd)
635 {
636     TRACE("(%p)->(%p)\n", This, cmd);
637 
638     cmd->cmdf = OLECMDF_SUPPORTED|OLECMDF_ENABLED;
639     return S_OK;
640 }
641 
642 static HRESULT exec_browsemode(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
643 {
644     WARN("(%p)->(%08x %p %p)\n", This, cmdexecopt, in, out);
645 
646     if(in || out)
647         FIXME("unsupported args\n");
648 
649     This->doc_obj->usermode = BROWSEMODE;
650 
651     return S_OK;
652 }
653 
654 static HRESULT exec_editmode(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
655 {
656     TRACE("(%p)->(%08x %p %p)\n", This, cmdexecopt, in, out);
657 
658     if(in || out)
659         FIXME("unsupported args\n");
660 
661     return setup_edit_mode(This->doc_obj);
662 }
663 
664 static HRESULT exec_htmleditmode(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
665 {
666     FIXME("(%p)->(%08x %p %p)\n", This, cmdexecopt, in, out);
667     return S_OK;
668 }
669 
670 static HRESULT exec_baselinefont3(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
671 {
672     FIXME("(%p)->(%08x %p %p)\n", This, cmdexecopt, in, out);
673     return S_OK;
674 }
675 
676 static HRESULT exec_respectvisibility_indesign(HTMLDocument *This, DWORD cmdexecopt,
677         VARIANT *in, VARIANT *out)
678 {
679     TRACE("(%p)->(%x %s %p)\n", This, cmdexecopt, debugstr_variant(in), out);
680 
681     /* This is turned on by default in Gecko. */
682     if(!in || V_VT(in) != VT_BOOL || !V_BOOL(in))
683         FIXME("Unsupported argument %s\n", debugstr_variant(in));
684 
685     return S_OK;
686 }
687 
688 static HRESULT query_enabled_stub(HTMLDocument *This, OLECMD *cmd)
689 {
690     switch(cmd->cmdID) {
691     case IDM_PRINT:
692         FIXME("CGID_MSHTML: IDM_PRINT\n");
693         cmd->cmdf = OLECMDF_SUPPORTED|OLECMDF_ENABLED;
694         break;
695     case IDM_BLOCKDIRLTR:
696         FIXME("CGID_MSHTML: IDM_BLOCKDIRLTR\n");
697         cmd->cmdf = OLECMDF_SUPPORTED|OLECMDF_ENABLED;
698         break;
699     case IDM_BLOCKDIRRTL:
700         FIXME("CGID_MSHTML: IDM_BLOCKDIRRTL\n");
701         cmd->cmdf = OLECMDF_SUPPORTED|OLECMDF_ENABLED;
702         break;
703     }
704 
705     return S_OK;
706 }
707 
708 static const struct {
709     OLECMDF cmdf;
710     HRESULT (*func)(HTMLDocument*,DWORD,VARIANT*,VARIANT*);
711 } exec_table[] = {
712     {0},
713     { OLECMDF_SUPPORTED,                  exec_open                 }, /* OLECMDID_OPEN */
714     { OLECMDF_SUPPORTED,                  exec_new                  }, /* OLECMDID_NEW */
715     { OLECMDF_SUPPORTED,                  exec_save                 }, /* OLECMDID_SAVE */
716     { OLECMDF_SUPPORTED|OLECMDF_ENABLED,  exec_save_as              }, /* OLECMDID_SAVEAS */
717     { OLECMDF_SUPPORTED,                  exec_save_copy_as         }, /* OLECMDID_SAVECOPYAS */
718     { OLECMDF_SUPPORTED|OLECMDF_ENABLED,  exec_print                }, /* OLECMDID_PRINT */
719     { OLECMDF_SUPPORTED|OLECMDF_ENABLED,  exec_print_preview        }, /* OLECMDID_PRINTPREVIEW */
720     { OLECMDF_SUPPORTED|OLECMDF_ENABLED,  exec_page_setup           }, /* OLECMDID_PAGESETUP */
721     { OLECMDF_SUPPORTED,                  exec_spell                }, /* OLECMDID_SPELL */
722     { OLECMDF_SUPPORTED|OLECMDF_ENABLED,  exec_properties           }, /* OLECMDID_PROPERTIES */
723     { OLECMDF_SUPPORTED,                  exec_cut                  }, /* OLECMDID_CUT */
724     { OLECMDF_SUPPORTED,                  exec_copy                 }, /* OLECMDID_COPY */
725     { OLECMDF_SUPPORTED,                  exec_paste                }, /* OLECMDID_PASTE */
726     { OLECMDF_SUPPORTED,                  exec_paste_special        }, /* OLECMDID_PASTESPECIAL */
727     { OLECMDF_SUPPORTED,                  exec_undo                 }, /* OLECMDID_UNDO */
728     { OLECMDF_SUPPORTED,                  exec_rendo                }, /* OLECMDID_REDO */
729     { OLECMDF_SUPPORTED|OLECMDF_ENABLED,  exec_select_all           }, /* OLECMDID_SELECTALL */
730     { OLECMDF_SUPPORTED,                  exec_clear_selection      }, /* OLECMDID_CLEARSELECTION */
731     { OLECMDF_SUPPORTED,                  exec_zoom                 }, /* OLECMDID_ZOOM */
732     { OLECMDF_SUPPORTED,                  exec_get_zoom_range       }, /* OLECMDID_GETZOOMRANGE */
733     {0},
734     { OLECMDF_SUPPORTED|OLECMDF_ENABLED,  exec_refresh              }, /* OLECMDID_REFRESH */
735     { OLECMDF_SUPPORTED|OLECMDF_ENABLED,  exec_stop                 }, /* OLECMDID_STOP */
736     {0},{0},{0},{0},{0},{0},
737     { OLECMDF_SUPPORTED,                  exec_stop_download        }, /* OLECMDID_STOPDOWNLOAD */
738     {0},
739     { OLECMDF_SUPPORTED|OLECMDF_ENABLED,  exec_find                 }, /* OLECMDID_FIND */
740     { OLECMDF_SUPPORTED,                  exec_delete               }, /* OLECMDID_DELETE */
741     {0},{0},
742     { OLECMDF_SUPPORTED,                  exec_enable_interaction   }, /* OLECMDID_ENABLE_INTERACTION */
743     { OLECMDF_SUPPORTED,                  exec_on_unload            }, /* OLECMDID_ONUNLOAD */
744     {0},{0},{0},{0},{0},
745     { OLECMDF_SUPPORTED,                  exec_show_page_setup      }, /* OLECMDID_SHOWPAGESETUP */
746     { OLECMDF_SUPPORTED,                  exec_show_print           }, /* OLECMDID_SHOWPRINT */
747     {0},{0},
748     { OLECMDF_SUPPORTED,                  exec_close                }, /* OLECMDID_CLOSE */
749     {0},{0},{0},
750     { OLECMDF_SUPPORTED,                  exec_set_print_template   }, /* OLECMDID_SETPRINTTEMPLATE */
751     { OLECMDF_SUPPORTED,                  exec_get_print_template   }, /* OLECMDID_GETPRINTTEMPLATE */
752     {0},{0},{0},{0},{0},{0},{0},{0},{0},{0},
753     { 0, /* not reported as supported */  exec_optical_zoom         }  /* OLECMDID_OPTICAL_ZOOM */
754 };
755 
756 static const cmdtable_t base_cmds[] = {
757     {IDM_COPY,             query_mshtml_copy,     exec_mshtml_copy},
758     {IDM_PASTE,            query_mshtml_paste,    exec_mshtml_paste},
759     {IDM_CUT,              query_mshtml_cut,      exec_mshtml_cut},
760     {IDM_SELECTALL,        query_selall_status,   exec_select_all},
761     {IDM_BROWSEMODE,       NULL,                  exec_browsemode},
762     {IDM_EDITMODE,         NULL,                  exec_editmode},
763     {IDM_PRINT,            query_enabled_stub,    exec_print},
764     {IDM_HTMLEDITMODE,     NULL,                  exec_htmleditmode},
765     {IDM_BASELINEFONT3,    NULL,                  exec_baselinefont3},
766     {IDM_BLOCKDIRLTR,      query_enabled_stub,    NULL},
767     {IDM_BLOCKDIRRTL,      query_enabled_stub,    NULL},
768     {IDM_RESPECTVISIBILITY_INDESIGN, NULL,        exec_respectvisibility_indesign},
769     {0,NULL,NULL}
770 };
771 
772 static HRESULT WINAPI OleCommandTarget_QueryInterface(IOleCommandTarget *iface, REFIID riid, void **ppv)
773 {
774     HTMLDocument *This = impl_from_IOleCommandTarget(iface);
775     return htmldoc_query_interface(This, riid, ppv);
776 }
777 
778 static ULONG WINAPI OleCommandTarget_AddRef(IOleCommandTarget *iface)
779 {
780     HTMLDocument *This = impl_from_IOleCommandTarget(iface);
781     return htmldoc_addref(This);
782 }
783 
784 static ULONG WINAPI OleCommandTarget_Release(IOleCommandTarget *iface)
785 {
786     HTMLDocument *This = impl_from_IOleCommandTarget(iface);
787     return htmldoc_release(This);
788 }
789 
790 static HRESULT query_from_table(HTMLDocument *This, const cmdtable_t *cmdtable, OLECMD *cmd)
791 {
792     const cmdtable_t *iter = cmdtable;
793 
794     cmd->cmdf = 0;
795 
796     while(iter->id && iter->id != cmd->cmdID)
797         iter++;
798 
799     if(!iter->id || !iter->query)
800         return OLECMDERR_E_NOTSUPPORTED;
801 
802     return iter->query(This, cmd);
803 }
804 
805 static HRESULT WINAPI OleCommandTarget_QueryStatus(IOleCommandTarget *iface, const GUID *pguidCmdGroup,
806         ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT *pCmdText)
807 {
808     HTMLDocument *This = impl_from_IOleCommandTarget(iface);
809     HRESULT hres;
810 
811     TRACE("(%p)->(%s %d %p %p)\n", This, debugstr_guid(pguidCmdGroup), cCmds, prgCmds, pCmdText);
812 
813     if(pCmdText)
814         FIXME("Unsupported pCmdText\n");
815     if(!cCmds)
816         return S_OK;
817 
818     if(!pguidCmdGroup) {
819         ULONG i;
820 
821         for(i=0; i<cCmds; i++) {
822             if(prgCmds[i].cmdID < OLECMDID_OPEN || prgCmds[i].cmdID >= sizeof(exec_table)/sizeof(*exec_table)) {
823                 WARN("Unsupported cmdID = %d\n", prgCmds[i].cmdID);
824                 prgCmds[i].cmdf = 0;
825             }else {
826                 if(prgCmds[i].cmdID == OLECMDID_OPEN || prgCmds[i].cmdID == OLECMDID_NEW) {
827                     IOleCommandTarget *cmdtrg = NULL;
828                     OLECMD olecmd;
829 
830                     prgCmds[i].cmdf = OLECMDF_SUPPORTED;
831                     if(This->doc_obj->client) {
832                         hres = IOleClientSite_QueryInterface(This->doc_obj->client, &IID_IOleCommandTarget,
833                                 (void**)&cmdtrg);
834                         if(SUCCEEDED(hres)) {
835                             olecmd.cmdID = prgCmds[i].cmdID;
836                             olecmd.cmdf = 0;
837 
838                             hres = IOleCommandTarget_QueryStatus(cmdtrg, NULL, 1, &olecmd, NULL);
839                             if(SUCCEEDED(hres) && olecmd.cmdf)
840                                 prgCmds[i].cmdf = olecmd.cmdf;
841                         }
842                     }else {
843                         ERR("This->client == NULL, native would crash\n");
844                     }
845                 }else {
846                     prgCmds[i].cmdf = exec_table[prgCmds[i].cmdID].cmdf;
847                     TRACE("cmdID = %d  returning %x\n", prgCmds[i].cmdID, prgCmds[i].cmdf);
848                 }
849             }
850         }
851 
852         return (prgCmds[cCmds-1].cmdf & OLECMDF_SUPPORTED) ? S_OK : OLECMDERR_E_NOTSUPPORTED;
853     }
854 
855     if(IsEqualGUID(&CGID_MSHTML, pguidCmdGroup)) {
856         ULONG i;
857 
858         for(i=0; i<cCmds; i++) {
859             hres = query_from_table(This, base_cmds, prgCmds+i);
860             if(hres == OLECMDERR_E_NOTSUPPORTED)
861                 hres = query_from_table(This, editmode_cmds, prgCmds+i);
862             if(hres == OLECMDERR_E_NOTSUPPORTED)
863                 FIXME("CGID_MSHTML: unsupported cmdID %d\n", prgCmds[i].cmdID);
864         }
865 
866         return (prgCmds[cCmds-1].cmdf & OLECMDF_SUPPORTED) ? S_OK : OLECMDERR_E_NOTSUPPORTED;
867     }
868 
869     FIXME("Unsupported pguidCmdGroup %s\n", debugstr_guid(pguidCmdGroup));
870     return OLECMDERR_E_UNKNOWNGROUP;
871 }
872 
873 static HRESULT exec_from_table(HTMLDocument *This, const cmdtable_t *cmdtable, DWORD cmdid,
874                                DWORD cmdexecopt, VARIANT *in, VARIANT *out)
875 {
876     const cmdtable_t *iter = cmdtable;
877 
878     while(iter->id && iter->id != cmdid)
879         iter++;
880 
881     if(!iter->id || !iter->exec)
882         return OLECMDERR_E_NOTSUPPORTED;
883 
884     return iter->exec(This, cmdexecopt, in, out);
885 }
886 
887 static HRESULT WINAPI OleCommandTarget_Exec(IOleCommandTarget *iface, const GUID *pguidCmdGroup,
888         DWORD nCmdID, DWORD nCmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
889 {
890     HTMLDocument *This = impl_from_IOleCommandTarget(iface);
891 
892     if(!pguidCmdGroup) {
893         if(nCmdID < OLECMDID_OPEN || nCmdID >= sizeof(exec_table)/sizeof(*exec_table) || !exec_table[nCmdID].func) {
894             WARN("Unsupported cmdID = %d\n", nCmdID);
895             return OLECMDERR_E_NOTSUPPORTED;
896         }
897 
898         return exec_table[nCmdID].func(This, nCmdexecopt, pvaIn, pvaOut);
899     }else if(IsEqualGUID(&CGID_Explorer, pguidCmdGroup)) {
900         FIXME("unsupported nCmdID %d of CGID_Explorer group\n", nCmdID);
901         TRACE("%p %p\n", pvaIn, pvaOut);
902         return OLECMDERR_E_NOTSUPPORTED;
903     }else if(IsEqualGUID(&CGID_ShellDocView, pguidCmdGroup)) {
904         FIXME("unsupported nCmdID %d of CGID_ShellDocView group\n", nCmdID);
905         return OLECMDERR_E_NOTSUPPORTED;
906     }else if(IsEqualGUID(&CGID_MSHTML, pguidCmdGroup)) {
907         HRESULT hres = exec_from_table(This, base_cmds, nCmdID, nCmdexecopt, pvaIn, pvaOut);
908         if(hres == OLECMDERR_E_NOTSUPPORTED)
909             hres = exec_from_table(This, editmode_cmds, nCmdID,
910                                    nCmdexecopt, pvaIn, pvaOut);
911         if(hres == OLECMDERR_E_NOTSUPPORTED)
912             FIXME("unsupported nCmdID %d of CGID_MSHTML group\n", nCmdID);
913 
914         return hres;
915     }
916 
917     FIXME("Unsupported pguidCmdGroup %s\n", debugstr_guid(pguidCmdGroup));
918     return OLECMDERR_E_UNKNOWNGROUP;
919 }
920 
921 static const IOleCommandTargetVtbl OleCommandTargetVtbl = {
922     OleCommandTarget_QueryInterface,
923     OleCommandTarget_AddRef,
924     OleCommandTarget_Release,
925     OleCommandTarget_QueryStatus,
926     OleCommandTarget_Exec
927 };
928 
929 void show_context_menu(HTMLDocumentObj *This, DWORD dwID, POINT *ppt, IDispatch *elem)
930 {
931     HMENU menu_res, menu;
932     DWORD cmdid;
933 
934     if(This->hostui && S_OK == IDocHostUIHandler_ShowContextMenu(This->hostui,
935             dwID, ppt, (IUnknown*)&This->basedoc.IOleCommandTarget_iface, elem))
936         return;
937 
938     menu_res = LoadMenuW(get_shdoclc(), MAKEINTRESOURCEW(IDR_BROWSE_CONTEXT_MENU));
939     menu = GetSubMenu(menu_res, dwID);
940 
941     cmdid = TrackPopupMenu(menu, TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD,
942             ppt->x, ppt->y, 0, This->hwnd, NULL);
943     DestroyMenu(menu_res);
944 
945     if(cmdid)
946         IOleCommandTarget_Exec(&This->basedoc.IOleCommandTarget_iface, &CGID_MSHTML, cmdid, 0,
947                 NULL, NULL);
948 }
949 
950 void HTMLDocument_OleCmd_Init(HTMLDocument *This)
951 {
952     This->IOleCommandTarget_iface.lpVtbl = &OleCommandTargetVtbl;
953 }
954