mshtml: Reimplement editor mode left key using command controller.
[wine] / dlls / mshtml / editor.c
1 /*
2  * Copyright 2006-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 "config.h"
20
21 #include <stdarg.h>
22 #include <stdio.h>
23
24 #define COBJMACROS
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winuser.h"
29 #include "winnls.h"
30 #include "ole2.h"
31 #include "mshtmcid.h"
32
33 #include "wine/debug.h"
34 #include "wine/unicode.h"
35
36 #include "mshtml_private.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
39
40 #define NSCMD_ALIGN        "cmd_align"
41 #define NSCMD_BOLD         "cmd_bold"
42 #define NSCMD_CHARNEXT     "cmd_charNext"
43 #define NSCMD_CHARPREVIOUS "cmd_charPrevious"
44 #define NSCMD_FONTCOLOR    "cmd_fontColor"
45 #define NSCMD_FONTFACE     "cmd_fontFace"
46 #define NSCMD_INDENT       "cmd_indent"
47 #define NSCMD_INSERTHR     "cmd_insertHR"
48 #define NSCMD_ITALIC       "cmd_italic"
49 #define NSCMD_LINENEXT     "cmd_lineNext"
50 #define NSCMD_LINEPREVIOUS "cmd_linePrevious"
51 #define NSCMD_MOVEPAGEDOWN "cmd_movePageDown"
52 #define NSCMD_MOVEPAGEUP   "cmd_movePageUp"
53 #define NSCMD_OL           "cmd_ol"
54 #define NSCMD_OUTDENT      "cmd_outdent"
55 #define NSCMD_SELECTCHARNEXT      "cmd_selectCharNext"
56 #define NSCMD_SELECTCHARPREVIOUS  "cmd_selectCharPrevious"
57 #define NSCMD_SELECTLINENEXT      "cmd_selectLineNext"
58 #define NSCMD_SELECTLINEPREVIOUS  "cmd_selectLinePrevious"
59 #define NSCMD_SELECTPAGEDOWN      "cmd_selectPageDown"
60 #define NSCMD_SELECTPAGEUP        "cmd_selectPageUp"
61 #define NSCMD_SELECTWORDNEXT      "cmd_selectWordNext"
62 #define NSCMD_SELECTWORDPREVIOUS  "cmd_selectWordPrevious"
63 #define NSCMD_UL           "cmd_ul"
64 #define NSCMD_UNDERLINE    "cmd_underline"
65 #define NSCMD_WORDNEXT     "cmd_wordNext"
66 #define NSCMD_WORDPREVIOUS "cmd_wordPrevious"
67
68 #define NSSTATE_ATTRIBUTE "state_attribute"
69 #define NSSTATE_ALL       "state_all"
70
71 #define NSALIGN_CENTER "center"
72 #define NSALIGN_LEFT   "left"
73 #define NSALIGN_RIGHT  "right"
74
75 #define DOM_VK_LEFT  VK_LEFT
76 #define DOM_VK_UP    VK_UP
77 #define DOM_VK_RIGHT VK_RIGHT
78 #define DOM_VK_DOWN  VK_DOWN
79
80 static const WCHAR wszFont[] = {'f','o','n','t',0};
81 static const WCHAR wszSize[] = {'s','i','z','e',0};
82
83 static void do_ns_command(NSContainer *This, const char *cmd, nsICommandParams *nsparam)
84 {
85     nsICommandManager *cmdmgr;
86     nsIInterfaceRequestor *iface_req;
87     nsresult nsres;
88
89     TRACE("(%p)\n", This);
90
91     nsres = nsIWebBrowser_QueryInterface(This->webbrowser,
92             &IID_nsIInterfaceRequestor, (void**)&iface_req);
93     if(NS_FAILED(nsres)) {
94         ERR("Could not get nsIInterfaceRequestor: %08x\n", nsres);
95         return;
96     }
97
98     nsres = nsIInterfaceRequestor_GetInterface(iface_req, &IID_nsICommandManager,
99                                                (void**)&cmdmgr);
100     nsIInterfaceRequestor_Release(iface_req);
101     if(NS_FAILED(nsres)) {
102         ERR("Could not get nsICommandManager: %08x\n", nsres);
103         return;
104     }
105
106     nsres = nsICommandManager_DoCommand(cmdmgr, cmd, nsparam, NULL);
107     if(NS_FAILED(nsres))
108         ERR("DoCommand(%s) failed: %08x\n", debugstr_a(cmd), nsres);
109
110     nsICommandManager_Release(cmdmgr);
111 }
112
113 static void do_ns_editor_command(NSContainer *This, const char *cmd)
114 {
115     nsresult nsres;
116
117     if(!This->editor_controller)
118         return;
119
120     nsres = nsIController_DoCommand(This->editor_controller, cmd);
121     if(NS_FAILED(nsres))
122         ERR("DoCommand(%s) failed: %08x\n", debugstr_a(cmd), nsres);
123 }
124
125 static nsresult get_ns_command_state(NSContainer *This, const char *cmd, nsICommandParams *nsparam)
126 {
127     nsICommandManager *cmdmgr;
128     nsIInterfaceRequestor *iface_req;
129     nsresult nsres;
130
131     nsres = nsIWebBrowser_QueryInterface(This->webbrowser,
132             &IID_nsIInterfaceRequestor, (void**)&iface_req);
133     if(NS_FAILED(nsres)) {
134         ERR("Could not get nsIInterfaceRequestor: %08x\n", nsres);
135         return nsres;
136     }
137
138     nsres = nsIInterfaceRequestor_GetInterface(iface_req, &IID_nsICommandManager,
139                                                (void**)&cmdmgr);
140     nsIInterfaceRequestor_Release(iface_req);
141     if(NS_FAILED(nsres)) {
142         ERR("Could not get nsICommandManager: %08x\n", nsres);
143         return nsres;
144     }
145
146     nsres = nsICommandManager_GetCommandState(cmdmgr, cmd, NULL, nsparam);
147     if(NS_FAILED(nsres))
148         ERR("GetCommandState(%s) failed: %08x\n", debugstr_a(cmd), nsres);
149
150     nsICommandManager_Release(cmdmgr);
151     return nsres;
152 }
153
154 static DWORD query_ns_edit_status(HTMLDocument *This, const char *nscmd)
155 {
156     nsICommandParams *nsparam;
157     PRBool b = FALSE;
158
159     if(This->usermode != EDITMODE || This->readystate < READYSTATE_INTERACTIVE)
160         return OLECMDF_SUPPORTED;
161
162     if(This->nscontainer && nscmd) {
163         nsparam = create_nscommand_params();
164         get_ns_command_state(This->nscontainer, nscmd, nsparam);
165
166         nsICommandParams_GetBooleanValue(nsparam, NSSTATE_ALL, &b);
167
168         nsICommandParams_Release(nsparam);
169     }
170
171     return OLECMDF_SUPPORTED | OLECMDF_ENABLED | (b ? OLECMDF_LATCHED : 0);
172 }
173
174 static void set_ns_align(HTMLDocument *This, const char *align_str)
175 {
176     nsICommandParams *nsparam;
177
178     if(!This->nscontainer)
179         return;
180
181     nsparam = create_nscommand_params();
182     nsICommandParams_SetCStringValue(nsparam, NSSTATE_ATTRIBUTE, align_str);
183
184     do_ns_command(This->nscontainer, NSCMD_ALIGN, nsparam);
185
186     nsICommandParams_Release(nsparam);
187 }
188
189 static DWORD query_align_status(HTMLDocument *This, const char *align_str)
190 {
191     nsICommandParams *nsparam;
192     char *align = NULL;
193
194     if(This->usermode != EDITMODE || This->readystate < READYSTATE_INTERACTIVE)
195         return OLECMDF_SUPPORTED;
196
197     if(This->nscontainer) {
198         nsparam = create_nscommand_params();
199         get_ns_command_state(This->nscontainer, NSCMD_ALIGN, nsparam);
200
201         nsICommandParams_GetCStringValue(nsparam, NSSTATE_ATTRIBUTE, &align);
202
203         nsICommandParams_Release(nsparam);
204     }
205
206     return OLECMDF_SUPPORTED | OLECMDF_ENABLED
207         | (align && !strcmp(align_str, align) ? OLECMDF_LATCHED : 0);
208 }
209
210
211 static nsISelection *get_ns_selection(HTMLDocument *This)
212 {
213     nsIDOMWindow *dom_window;
214     nsISelection *nsselection = NULL;
215     nsresult nsres;
216
217     if(!This->nscontainer)
218         return NULL;
219
220     nsres = nsIWebBrowser_GetContentDOMWindow(This->nscontainer->webbrowser, &dom_window);
221     if(NS_FAILED(nsres))
222         return NULL;
223
224     nsIDOMWindow_GetSelection(dom_window, &nsselection);
225     nsIDOMWindow_Release(dom_window);
226
227     return nsselection;
228
229 }
230
231 static void remove_child_attr(nsIDOMElement *elem, LPCWSTR tag, nsAString *attr_str)
232 {
233     PRBool has_children;
234     PRUint32 child_cnt, i;
235     nsIDOMNode *child_node;
236     nsIDOMNodeList *node_list;
237     PRUint16 node_type;
238
239     nsIDOMElement_HasChildNodes(elem, &has_children);
240     if(!has_children)
241         return;
242
243     nsIDOMElement_GetChildNodes(elem, &node_list);
244     nsIDOMNodeList_GetLength(node_list, &child_cnt);
245
246     for(i=0; i<child_cnt; i++) {
247         nsIDOMNodeList_Item(node_list, i, &child_node);
248
249         nsIDOMNode_GetNodeType(child_node, &node_type);
250         if(node_type == ELEMENT_NODE) {
251             nsIDOMElement *child_elem;
252             nsAString tag_str;
253             const PRUnichar *ctag;
254
255             nsIDOMNode_QueryInterface(child_node, &IID_nsIDOMElement, (void**)&child_elem);
256
257             nsAString_Init(&tag_str, NULL);
258             nsIDOMElement_GetTagName(child_elem, &tag_str);
259             nsAString_GetData(&tag_str, &ctag, NULL);
260
261             if(!strcmpiW(ctag, tag))
262                 /* FIXME: remove node if there are no more attributes */
263                 nsIDOMElement_RemoveAttribute(child_elem, attr_str);
264
265             nsAString_Finish(&tag_str);
266
267             remove_child_attr(child_elem, tag, attr_str);
268
269             nsIDOMNode_Release(child_elem);
270         }
271
272         nsIDOMNode_Release(child_node);
273     }
274
275     nsIDOMNodeList_Release(node_list);
276 }
277
278 static void get_font_size(HTMLDocument *This, WCHAR *ret)
279 {
280     nsISelection *nsselection = get_ns_selection(This);
281     nsIDOMElement *elem = NULL;
282     nsIDOMNode *node = NULL, *tmp_node;
283     nsAString tag_str;
284     LPCWSTR tag;
285     PRUint16 node_type;
286     nsresult nsres;
287
288     *ret = 0;
289
290     if(!nsselection)
291         return;
292
293     nsISelection_GetFocusNode(nsselection, &node);
294     nsISelection_Release(nsselection);
295
296     while(node) {
297         nsres = nsIDOMNode_GetNodeType(node, &node_type);
298         if(NS_FAILED(nsres) || node_type == DOCUMENT_NODE)
299             break;
300
301         if(node_type == ELEMENT_NODE) {
302             nsIDOMNode_QueryInterface(node, &IID_nsIDOMElement, (void**)&elem);
303
304             nsAString_Init(&tag_str, NULL);
305             nsIDOMElement_GetTagName(elem, &tag_str);
306             nsAString_GetData(&tag_str, &tag, NULL);
307
308             if(!strcmpiW(tag, wszFont)) {
309                 nsAString size_str, val_str;
310                 LPCWSTR val;
311
312                 TRACE("found font tag %p\n", elem);
313
314                 nsAString_Init(&size_str, wszSize);
315                 nsAString_Init(&val_str, NULL);
316
317                 nsIDOMElement_GetAttribute(elem, &size_str, &val_str);
318                 nsAString_GetData(&val_str, &val, NULL);
319
320                 if(*val) {
321                     TRACE("found size %s\n", debugstr_w(val));
322                     strcpyW(ret, val);
323                 }
324
325                 nsAString_Finish(&size_str);
326                 nsAString_Finish(&val_str);
327             }
328
329             nsAString_Finish(&tag_str);
330
331             nsIDOMElement_Release(elem);
332         }
333
334         if(*ret)
335             break;
336
337         tmp_node = node;
338         nsIDOMNode_GetParentNode(node, &node);
339         nsIDOMNode_Release(tmp_node);
340     }
341
342     if(node)
343         nsIDOMNode_Release(node);
344 }
345
346 static void set_font_size(HTMLDocument *This, LPCWSTR size)
347 {
348     nsISelection *nsselection;
349     PRBool collapsed;
350     nsIDOMDocument *nsdoc;
351     nsIDOMElement *elem;
352     nsIDOMRange *range;
353     PRInt32 range_cnt = 0;
354     nsAString font_str;
355     nsAString size_str;
356     nsAString val_str;
357     nsresult nsres;
358
359     nsselection = get_ns_selection(This);
360
361     if(!nsselection)
362         return;
363
364     nsres = nsIWebNavigation_GetDocument(This->nscontainer->navigation, &nsdoc);
365     if(NS_FAILED(nsres))
366         return;
367
368     nsAString_Init(&font_str, wszFont);
369     nsAString_Init(&size_str, wszSize);
370     nsAString_Init(&val_str, size);
371
372     nsISelection_GetRangeCount(nsselection, &range_cnt);
373     if(range_cnt != 1)
374         FIXME("range_cnt %d not supprted\n", range_cnt);
375
376     nsIDOMDocument_CreateElement(nsdoc, &font_str, &elem);
377     nsIDOMElement_SetAttribute(elem, &size_str, &val_str);
378
379     nsISelection_GetRangeAt(nsselection, 0, &range);
380     nsISelection_GetIsCollapsed(nsselection, &collapsed);
381     nsISelection_RemoveAllRanges(nsselection);
382
383     nsIDOMRange_SurroundContents(range, (nsIDOMNode*)elem);
384
385     if(collapsed) {
386         nsISelection_Collapse(nsselection, (nsIDOMNode*)elem, 0);
387     }else {
388         /* Remove all size attrbutes from the range */
389         remove_child_attr(elem, wszFont, &size_str);
390         nsISelection_SelectAllChildren(nsselection, (nsIDOMNode*)elem);
391     }
392
393     nsIDOMRange_Release(range);
394     nsIDOMElement_Release(elem);
395
396     nsAString_Finish(&font_str);
397     nsAString_Finish(&size_str);
398     nsAString_Finish(&val_str);
399
400     nsISelection_Release(nsselection);
401     nsIDOMDocument_Release(nsdoc);
402 }
403
404 static BOOL is_visible_text_node(nsIDOMNode *node)
405 {
406     nsIDOMCharacterData *char_data;
407     nsAString data_str;
408     LPCWSTR data, ptr;
409     PRUint32 len;
410
411     nsIDOMNode_QueryInterface(node, &IID_nsIDOMCharacterData, (void**)&char_data);
412
413     nsIDOMCharacterData_GetLength(char_data, &len);
414
415     nsAString_Init(&data_str, NULL);
416     nsIDOMCharacterData_GetData(char_data, &data_str);
417     nsAString_GetData(&data_str, &data, NULL);
418
419     if(*data == '\n') {
420         len--;
421         for(ptr=data+1; ptr && isspaceW(*ptr); ptr++)
422             len--;
423     }
424
425     nsAString_Finish(&data_str);
426
427     nsIDOMCharacterData_Release(char_data);
428
429     return len != 0;
430 }
431
432 static nsIDOMNode *get_child_text_node(nsIDOMNode *node, BOOL first)
433 {
434     nsIDOMNode *iter, *iter2;
435
436     if(first)
437         nsIDOMNode_GetFirstChild(node, &iter);
438     else
439         nsIDOMNode_GetLastChild(node, &iter);
440
441     while(iter) {
442         PRUint16 node_type;
443
444         nsIDOMNode_GetNodeType(iter, &node_type);
445         switch(node_type) {
446         case TEXT_NODE:
447             if(is_visible_text_node(iter))
448                 return iter;
449         case ELEMENT_NODE:
450             iter2 = get_child_text_node(iter, first);
451             if(iter2) {
452                 nsIDOMNode_Release(iter);
453                 return iter2;
454             }
455         }
456
457         if(first)
458             nsIDOMNode_GetNextSibling(iter, &iter2);
459         else
460             nsIDOMNode_GetPreviousSibling(iter, &iter2);
461
462         nsIDOMNode_Release(iter);
463         iter = iter2;
464     }
465
466     return NULL;
467 }
468
469 static void handle_arrow_key(HTMLDocument *This, nsIDOMKeyEvent *event, const char **cmds)
470 {
471     int i=0;
472     PRBool b;
473
474     nsIDOMKeyEvent_GetCtrlKey(event, &b);
475     if(b)
476         i |= 1;
477
478     nsIDOMKeyEvent_GetShiftKey(event, &b);
479     if(b)
480         i |= 2;
481
482     do_ns_editor_command(This->nscontainer, cmds[i]);
483
484     nsIDOMKeyEvent_PreventDefault(event);
485 }
486
487 void handle_edit_event(HTMLDocument *This, nsIDOMEvent *event)
488 {
489     nsIDOMKeyEvent *key_event;
490     PRUint32 code;
491
492     nsIDOMEvent_QueryInterface(event, &IID_nsIDOMKeyEvent, (void**)&key_event);
493
494     nsIDOMKeyEvent_GetKeyCode(key_event, &code);
495
496     switch(code) {
497     case DOM_VK_LEFT: {
498         static const char *cmds[] = {
499             NSCMD_CHARPREVIOUS,
500             NSCMD_WORDPREVIOUS,
501             NSCMD_SELECTCHARPREVIOUS,
502             NSCMD_SELECTWORDPREVIOUS
503         };
504
505         TRACE("left\n");
506         handle_arrow_key(This, key_event, cmds);
507         break;
508     }
509     case DOM_VK_RIGHT: {
510         static const char *cmds[] = {
511             NSCMD_CHARNEXT,
512             NSCMD_WORDNEXT,
513             NSCMD_SELECTCHARNEXT,
514             NSCMD_SELECTWORDNEXT
515         };
516
517         TRACE("right\n");
518         handle_arrow_key(This, key_event, cmds);
519         break;
520     }
521     case DOM_VK_UP: {
522         static const char *cmds[] = {
523             NSCMD_LINEPREVIOUS,
524             NSCMD_MOVEPAGEUP,
525             NSCMD_SELECTLINEPREVIOUS,
526             NSCMD_SELECTPAGEUP
527         };
528
529         TRACE("up\n");
530         handle_arrow_key(This, key_event, cmds);
531         break;
532     }
533     case DOM_VK_DOWN: {
534         static const char *cmds[] = {
535             NSCMD_LINENEXT,
536             NSCMD_MOVEPAGEDOWN,
537             NSCMD_SELECTLINENEXT,
538             NSCMD_SELECTPAGEDOWN
539         };
540
541         TRACE("down\n");
542         handle_arrow_key(This, key_event, cmds);
543         break;
544     }
545     };
546
547     nsIDOMKeyEvent_Release(key_event);
548 }
549
550 static void set_ns_fontname(NSContainer *This, const char *fontname)
551 {
552     nsICommandParams *nsparam = create_nscommand_params();
553
554     nsICommandParams_SetCStringValue(nsparam, NSSTATE_ATTRIBUTE, fontname);
555     do_ns_command(This, NSCMD_FONTFACE, nsparam);
556     nsICommandParams_Release(nsparam);
557 }
558
559 static HRESULT exec_fontname(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
560 {
561     TRACE("(%p)->(%p %p)\n", This, in, out);
562
563     if(!This->nscontainer) {
564         update_doc(This, UPDATE_UI);
565         return E_FAIL;
566     }
567
568     if(in) {
569         char *stra;
570         DWORD len;
571
572         if(V_VT(in) != VT_BSTR) {
573             FIXME("Unsupported vt=%d\n", V_VT(out));
574             return E_INVALIDARG;
575         }
576
577         TRACE("%s\n", debugstr_w(V_BSTR(in)));
578
579         len = WideCharToMultiByte(CP_ACP, 0, V_BSTR(in), -1, NULL, 0, NULL, NULL);
580         stra = mshtml_alloc(len);
581         WideCharToMultiByte(CP_ACP, 0, V_BSTR(in), -1, stra, -1, NULL, NULL);
582
583         set_ns_fontname(This->nscontainer, stra);
584
585         mshtml_free(stra);
586
587         update_doc(This, UPDATE_UI);
588     }
589
590     if(out) {
591         nsICommandParams *nsparam;
592         LPWSTR strw;
593         char *stra;
594         DWORD len;
595         nsresult nsres;
596
597         nsparam = create_nscommand_params();
598
599         nsres = get_ns_command_state(This->nscontainer, NSCMD_FONTFACE, nsparam);
600         if(NS_FAILED(nsres))
601             return S_OK;
602
603         nsICommandParams_GetCStringValue(nsparam, NSSTATE_ATTRIBUTE, &stra);
604         nsICommandParams_Release(nsparam);
605
606         len = MultiByteToWideChar(CP_ACP, 0, stra, -1, NULL, 0);
607         strw = mshtml_alloc(len*sizeof(WCHAR));
608         MultiByteToWideChar(CP_ACP, 0, stra, -1, strw, -1);
609         nsfree(stra);
610
611         V_VT(out) = VT_BSTR;
612         V_BSTR(out) = SysAllocString(strw);
613         mshtml_free(strw);
614     }
615
616     return S_OK;
617 }
618
619 static HRESULT exec_forecolor(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
620 {
621     TRACE("(%p)->(%p %p)\n", This, in, out);
622
623     if(in) {
624         if(V_VT(in) == VT_I4) {
625             nsICommandParams *nsparam = create_nscommand_params();
626             char color_str[10];
627
628             sprintf(color_str, "#%02x%02x%02x",
629                     V_I4(in)&0xff, (V_I4(in)>>8)&0xff, (V_I4(in)>>16)&0xff);
630
631             nsICommandParams_SetCStringValue(nsparam, NSSTATE_ATTRIBUTE, color_str);
632             do_ns_command(This->nscontainer, NSCMD_FONTCOLOR, nsparam);
633
634             nsICommandParams_Release(nsparam);
635         }else {
636             FIXME("unsupported in vt=%d\n", V_VT(in));
637         }
638
639         update_doc(This, UPDATE_UI);
640     }
641
642     if(out) {
643         FIXME("unsupported out\n");
644         return E_NOTIMPL;
645     }
646
647     return S_OK;
648 }
649
650 static HRESULT exec_fontsize(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
651 {
652     TRACE("(%p)->(%p %p)\n", This, in, out);
653
654     if(out) {
655         WCHAR val[10] = {0};
656
657         switch(V_VT(out)) {
658         case VT_I4:
659             get_font_size(This, val);
660             V_I4(out) = strtolW(val, NULL, 10);
661             break;
662         case VT_BSTR:
663             get_font_size(This, val);
664             V_BSTR(out) = SysAllocString(val);
665             break;
666         default:
667             FIXME("unsupported vt %d\n", V_VT(out));
668         }
669     }
670
671     if(in) {
672         switch(V_VT(in)) {
673         case VT_I4: {
674             WCHAR size[10];
675             static const WCHAR format[] = {'%','d',0};
676             wsprintfW(size, format, V_I4(in));
677             set_font_size(This, size);
678             break;
679         }
680         case VT_BSTR:
681             set_font_size(This, V_BSTR(in));
682             break;
683         default:
684             FIXME("unsupported vt %d\n", V_VT(in));
685         }
686
687         update_doc(This, UPDATE_UI);
688     }
689
690     return S_OK;
691 }
692
693 static HRESULT exec_bold(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
694 {
695     TRACE("(%p)\n", This);
696
697     if(in || out)
698         FIXME("unsupported args\n");
699
700     if(This->nscontainer)
701         do_ns_command(This->nscontainer, NSCMD_BOLD, NULL);
702
703     update_doc(This, UPDATE_UI);
704     return S_OK;
705 }
706
707 static HRESULT exec_italic(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
708 {
709     TRACE("(%p)\n", This);
710
711     if(in || out)
712         FIXME("unsupported args\n");
713
714     if(This->nscontainer)
715         do_ns_command(This->nscontainer, NSCMD_ITALIC, NULL);
716
717     update_doc(This, UPDATE_UI);
718     return S_OK;
719 }
720
721 static HRESULT query_justify(HTMLDocument *This, OLECMD *cmd)
722 {
723     switch(cmd->cmdID) {
724     case IDM_JUSTIFYCENTER:
725         TRACE("(%p) IDM_JUSTIFYCENTER\n", This);
726         cmd->cmdf = query_align_status(This, NSALIGN_CENTER);
727         break;
728     case IDM_JUSTIFYLEFT:
729         TRACE("(%p) IDM_JUSTIFYLEFT\n", This);
730         /* FIXME: We should set OLECMDF_LATCHED only if it's set explicitly. */
731         if(This->usermode != EDITMODE || This->readystate < READYSTATE_INTERACTIVE)
732             cmd->cmdf = OLECMDF_SUPPORTED;
733         else
734             cmd->cmdf = OLECMDF_SUPPORTED | OLECMDF_ENABLED;
735         break;
736     case IDM_JUSTIFYRIGHT:
737         TRACE("(%p) IDM_JUSTIFYRIGHT\n", This);
738         cmd->cmdf = query_align_status(This, NSALIGN_RIGHT);
739         break;
740     }
741
742     return S_OK;
743 }
744
745 static HRESULT exec_justifycenter(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
746 {
747     TRACE("(%p)\n", This);
748
749     if(in || out)
750         FIXME("unsupported args\n");
751
752     set_ns_align(This, NSALIGN_CENTER);
753     update_doc(This, UPDATE_UI);
754     return S_OK;
755 }
756
757 static HRESULT exec_justifyleft(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
758 {
759     TRACE("(%p)\n", This);
760
761     if(in || out)
762         FIXME("unsupported args\n");
763
764     set_ns_align(This, NSALIGN_LEFT);
765     update_doc(This, UPDATE_UI);
766     return S_OK;
767 }
768
769 static HRESULT exec_justifyright(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
770 {
771     TRACE("(%p)\n", This);
772
773     if(in || out)
774         FIXME("unsupported args\n");
775
776     set_ns_align(This, NSALIGN_RIGHT);
777     update_doc(This, UPDATE_UI);
778     return S_OK;
779 }
780
781 static HRESULT exec_underline(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
782 {
783     TRACE("(%p)\n", This);
784
785     if(in || out)
786         FIXME("unsupported args\n");
787
788     if(This->nscontainer)
789         do_ns_command(This->nscontainer, NSCMD_UNDERLINE, NULL);
790
791     update_doc(This, UPDATE_UI);
792     return S_OK;
793 }
794
795 static HRESULT exec_horizontalline(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
796 {
797     TRACE("(%p)\n", This);
798
799     if(in || out)
800         FIXME("unsupported args\n");
801
802     if(This->nscontainer)
803         do_ns_command(This->nscontainer, NSCMD_INSERTHR, NULL);
804
805     update_doc(This, UPDATE_UI);
806     return S_OK;
807 }
808
809 static HRESULT exec_orderlist(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
810 {
811     TRACE("(%p)\n", This);
812
813     if(in || out)
814         FIXME("unsupported args\n");
815
816     if(This->nscontainer)
817         do_ns_command(This->nscontainer, NSCMD_OL, NULL);
818
819     update_doc(This, UPDATE_UI);
820     return S_OK;
821 }
822
823 static HRESULT exec_unorderlist(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
824 {
825     TRACE("(%p)\n", This);
826
827     if(in || out)
828         FIXME("unsupported args\n");
829
830     if(This->nscontainer)
831         do_ns_command(This->nscontainer, NSCMD_UL, NULL);
832
833     update_doc(This, UPDATE_UI);
834     return S_OK;
835 }
836
837 static HRESULT exec_indent(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
838 {
839     TRACE("(%p)\n", This);
840
841     if(in || out)
842         FIXME("unsupported args\n");
843
844     if(This->nscontainer)
845         do_ns_command(This->nscontainer, NSCMD_INDENT, NULL);
846
847     update_doc(This, UPDATE_UI);
848     return S_OK;
849 }
850
851 static HRESULT exec_outdent(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
852 {
853     TRACE("(%p)\n", This);
854
855     if(in || out)
856         FIXME("unsupported args\n");
857
858     if(This->nscontainer)
859         do_ns_command(This->nscontainer, NSCMD_OUTDENT, NULL);
860
861     update_doc(This, UPDATE_UI);
862     return S_OK;
863 }
864
865 static HRESULT exec_composesettings(HTMLDocument *This, DWORD cmdexecopt, VARIANT *in, VARIANT *out)
866 {
867     if(out || !in || V_VT(in) != VT_BSTR) {
868         WARN("invalid arg\n");
869         return E_INVALIDARG;
870     }
871
872     FIXME("%s\n", debugstr_w(V_BSTR(in)));
873
874     update_doc(This, UPDATE_UI);
875     return S_OK;
876 }
877
878 static HRESULT query_edit_status(HTMLDocument *This, OLECMD *cmd)
879 {
880     switch(cmd->cmdID) {
881     case IDM_FONTNAME:
882         TRACE("CGID_MSHTML: IDM_FONTNAME\n");
883         cmd->cmdf = query_ns_edit_status(This, NULL);
884         break;
885     case IDM_FONTSIZE:
886         TRACE("CGID_MSHTML: IDM_FONTSIZE\n");
887         cmd->cmdf = query_ns_edit_status(This, NULL);
888         break;
889     case IDM_BOLD:
890         TRACE("CGID_MSHTML: IDM_BOLD\n");
891         cmd->cmdf = query_ns_edit_status(This, NSCMD_BOLD);
892         break;
893     case IDM_FORECOLOR:
894         TRACE("CGID_MSHTML: IDM_FORECOLOR\n");
895         cmd->cmdf = query_ns_edit_status(This, NULL);
896         break;
897     case IDM_ITALIC:
898         TRACE("CGID_MSHTML: IDM_ITALIC\n");
899         cmd->cmdf = query_ns_edit_status(This, NSCMD_ITALIC);
900         break;
901     case IDM_UNDERLINE:
902         TRACE("CGID_MSHTML: IDM_UNDERLINE\n");
903         cmd->cmdf = query_ns_edit_status(This, NSCMD_UNDERLINE);
904         break;
905     case IDM_HORIZONTALLINE:
906         TRACE("CGID_MSHTML: IDM_HORIZONTALLINE\n");
907         cmd->cmdf = query_ns_edit_status(This, NULL);
908         break;
909     case IDM_ORDERLIST:
910         TRACE("CGID_MSHTML: IDM_ORDERLIST\n");
911         cmd->cmdf = query_ns_edit_status(This, NSCMD_OL);
912         break;
913     case IDM_UNORDERLIST:
914         TRACE("CGID_MSHTML: IDM_HORIZONTALLINE\n");
915         cmd->cmdf = query_ns_edit_status(This, NSCMD_UL);
916         break;
917     case IDM_INDENT:
918         TRACE("CGID_MSHTML: IDM_INDENT\n");
919         cmd->cmdf = query_ns_edit_status(This, NULL);
920         break;
921     case IDM_OUTDENT:
922         TRACE("CGID_MSHTML: IDM_OUTDENT\n");
923         cmd->cmdf = query_ns_edit_status(This, NULL);
924         break;
925     }
926
927     return S_OK;
928 }
929
930 const cmdtable_t editmode_cmds[] = {
931     {IDM_FONTNAME,        query_edit_status,    exec_fontname},
932     {IDM_FONTSIZE,        query_edit_status,    exec_fontsize},
933     {IDM_FORECOLOR,       query_edit_status,    exec_forecolor},
934     {IDM_BOLD,            query_edit_status,    exec_bold},
935     {IDM_ITALIC,          query_edit_status,    exec_italic},
936     {IDM_JUSTIFYCENTER,   query_justify,        exec_justifycenter},
937     {IDM_JUSTIFYRIGHT,    query_justify,        exec_justifyright},
938     {IDM_JUSTIFYLEFT,     query_justify,        exec_justifyleft},
939     {IDM_UNDERLINE,       query_edit_status,    exec_underline},
940     {IDM_HORIZONTALLINE,  query_edit_status,    exec_horizontalline},
941     {IDM_ORDERLIST,       query_edit_status,    exec_orderlist},
942     {IDM_UNORDERLIST,     query_edit_status,    exec_unorderlist},
943     {IDM_INDENT,          query_edit_status,    exec_indent},
944     {IDM_OUTDENT,         query_edit_status,    exec_outdent},
945     {IDM_COMPOSESETTINGS, NULL,                 exec_composesettings},
946     {0,NULL,NULL}
947 };
948
949 void init_editor(HTMLDocument *This)
950 {
951     update_doc(This, UPDATE_UI);
952
953     if(!This->nscontainer)
954         return;
955
956     set_ns_fontname(This->nscontainer, "Times New Roman");
957 }