1 /*
2  * Copyright (C) 2004 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "jsediting.h"
27 #include "editing/htmlediting_impl.h"
28 #include "editor.h"
29 
30 #include "css/cssproperties.h"
31 #include "css/cssvalues.h"
32 #include "css/css_valueimpl.h"
33 #include "xml/dom_selection.h"
34 #include "xml/dom_docimpl.h"
35 #include "dom/dom_string.h"
36 
37 #include "misc/khtml_partaccessor.h"
38 
39 #include <QHash>
40 #include <QString>
41 
42 using khtml::TypingCommandImpl;
43 using khtml::InsertListCommandImpl;
44 //
45 #define KPAC khtml::KHTMLPartAccessor
46 
47 #define DEBUG_COMMANDS
48 
49 namespace DOM
50 {
51 
52 class DocumentImpl;
53 
54 struct CommandImp {
55     bool (*execFn)(KHTMLPart *part, bool userInterface, const DOMString &value);
56     bool (*enabledFn)(KHTMLPart *part);
57     Editor::TriState(*stateFn)(KHTMLPart *part);
58     DOMString(*valueFn)(KHTMLPart *part);
59 };
60 
61 typedef QHash<QString, const CommandImp *> CommandDict;
62 static CommandDict createCommandDictionary();
63 
execCommand(const CommandImp * cmd,bool userInterface,const DOMString & value)64 bool JSEditor::execCommand(const CommandImp *cmd, bool userInterface, const DOMString &value)
65 {
66     if (!cmd || !cmd->enabledFn) {
67         return false;
68     }
69     KHTMLPart *part = m_doc->part();
70     if (!part) {
71         return false;
72     }
73     m_doc->updateLayout();
74     return cmd->enabledFn(part) && cmd->execFn(part, userInterface, value);
75 }
76 
queryCommandEnabled(const CommandImp * cmd)77 bool JSEditor::queryCommandEnabled(const CommandImp *cmd)
78 {
79     if (!cmd || !cmd->enabledFn) {
80         return false;
81     }
82     KHTMLPart *part = m_doc->part();
83     if (!part) {
84         return false;
85     }
86     m_doc->updateLayout();
87     return cmd->enabledFn(part);
88 }
89 
queryCommandIndeterm(const CommandImp * cmd)90 bool JSEditor::queryCommandIndeterm(const CommandImp *cmd)
91 {
92     if (!cmd || !cmd->enabledFn) {
93         return false;
94     }
95     KHTMLPart *part = m_doc->part();
96     if (!part) {
97         return false;
98     }
99     m_doc->updateLayout();
100     return cmd->stateFn(part) == Editor::MixedTriState;
101 }
102 
queryCommandState(const CommandImp * cmd)103 bool JSEditor::queryCommandState(const CommandImp *cmd)
104 {
105     if (!cmd || !cmd->enabledFn) {
106         return false;
107     }
108     KHTMLPart *part = m_doc->part();
109     if (!part) {
110         return false;
111     }
112     m_doc->updateLayout();
113     return cmd->stateFn(part) != Editor::FalseTriState;
114 }
115 
queryCommandSupported(const CommandImp * cmd)116 bool JSEditor::queryCommandSupported(const CommandImp *cmd)
117 {
118     return cmd != nullptr;
119 }
120 
queryCommandValue(const CommandImp * cmd)121 DOMString JSEditor::queryCommandValue(const CommandImp *cmd)
122 {
123     if (!cmd || !cmd->enabledFn) {
124         return DOMString();
125     }
126     KHTMLPart *part = m_doc->part();
127     if (!part) {
128         return DOMString();
129     }
130     m_doc->updateLayout();
131     return cmd->valueFn(part);
132 }
133 
134 // =============================================================================================
135 
136 // Private stuff
137 
execStyleChange(KHTMLPart * part,int propertyID,const DOMString & propertyValue)138 static bool execStyleChange(KHTMLPart *part, int propertyID, const DOMString &propertyValue)
139 {
140     CSSStyleDeclarationImpl *style = new CSSStyleDeclarationImpl(nullptr);
141     style->setProperty(propertyID, propertyValue);
142     style->ref();
143     part->editor()->applyStyle(style);
144     style->deref();
145     return true;
146 }
147 
execStyleChange(KHTMLPart * part,int propertyID,int propertyEnum)148 static bool execStyleChange(KHTMLPart *part, int propertyID, int propertyEnum)
149 {
150     CSSStyleDeclarationImpl *style = new CSSStyleDeclarationImpl(nullptr);
151     style->setProperty(propertyID, propertyEnum);
152     style->ref();
153     part->editor()->applyStyle(style);
154     style->deref();
155     return true;
156 }
157 
execStyleChange(KHTMLPart * part,int propertyID,const char * propertyValue)158 static bool execStyleChange(KHTMLPart *part, int propertyID, const char *propertyValue)
159 {
160     return execStyleChange(part, propertyID, DOMString(propertyValue));
161 }
162 
stateStyle(KHTMLPart * part,int propertyID,const char * desiredValue)163 static Editor::TriState stateStyle(KHTMLPart *part, int propertyID, const char *desiredValue)
164 {
165     CSSStyleDeclarationImpl *style = new CSSStyleDeclarationImpl(nullptr);
166     style->setProperty(propertyID, desiredValue);
167     style->ref();
168     Editor::TriState state = part->editor()->selectionHasStyle(style);
169     style->deref();
170     return state;
171 }
172 
selectionStartHasStyle(KHTMLPart * part,int propertyID,const char * desiredValue)173 static bool selectionStartHasStyle(KHTMLPart *part, int propertyID, const char *desiredValue)
174 {
175     CSSStyleDeclarationImpl *style = new CSSStyleDeclarationImpl(nullptr);
176     style->setProperty(propertyID, desiredValue);
177     style->ref();
178     bool hasStyle = part->editor()->selectionStartHasStyle(style);
179     style->deref();
180     return hasStyle;
181 }
182 
valueStyle(KHTMLPart * part,int propertyID)183 static DOMString valueStyle(KHTMLPart *part, int propertyID)
184 {
185     return part->editor()->selectionStartStylePropertyValue(propertyID);
186 }
187 
188 // =============================================================================================
189 //
190 // execCommand implementations
191 //
192 
execBackColor(KHTMLPart * part,bool,const DOMString & value)193 static bool execBackColor(KHTMLPart *part, bool /*userInterface*/, const DOMString &value)
194 {
195     return execStyleChange(part, CSS_PROP_BACKGROUND_COLOR, value);
196 }
197 
execBold(KHTMLPart * part,bool,const DOMString &)198 static bool execBold(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
199 {
200     bool isBold = selectionStartHasStyle(part, CSS_PROP_FONT_WEIGHT, "bold");
201     return execStyleChange(part, CSS_PROP_FONT_WEIGHT, isBold ? "normal" : "bold");
202 }
203 
execCopy(KHTMLPart * part,bool,const DOMString &)204 static bool execCopy(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
205 {
206     part->editor()->copy();
207     return true;
208 }
209 
execCut(KHTMLPart * part,bool,const DOMString &)210 static bool execCut(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
211 {
212     part->editor()->cut();
213     return true;
214 }
215 
execDelete(KHTMLPart * part,bool,const DOMString &)216 static bool execDelete(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
217 {
218     TypingCommandImpl::deleteKeyPressed0(KPAC::xmlDocImpl(part));
219     return true;
220 }
221 
execFontName(KHTMLPart * part,bool,const DOMString & value)222 static bool execFontName(KHTMLPart *part, bool /*userInterface*/, const DOMString &value)
223 {
224     return execStyleChange(part, CSS_PROP_FONT_FAMILY, value);
225 }
226 
execFontSize(KHTMLPart * part,bool,const DOMString & value)227 static bool execFontSize(KHTMLPart *part, bool /*userInterface*/, const DOMString &value)
228 {
229     // This should handle sizes 1-7 like <font> does. Who the heck designed this interface? (Rhetorical question)
230     bool ok;
231     int val = value.string().toInt(&ok);
232     if (ok && val >= 1 && val <= 7) {
233         int size;
234         switch (val) {
235         case 1: size = CSS_VAL_XX_SMALL; break;
236         case 2: size = CSS_VAL_SMALL; break;
237         case 3: size = CSS_VAL_MEDIUM; break;
238         case 4: size = CSS_VAL_LARGE; break;
239         case 5: size = CSS_VAL_X_LARGE;  break;
240         case 6: size = CSS_VAL_XX_LARGE; break;
241         default: size = CSS_VAL__KHTML_XXX_LARGE;
242         }
243         return execStyleChange(part, CSS_PROP_FONT_SIZE, size);
244     }
245 
246     return execStyleChange(part, CSS_PROP_FONT_SIZE, value);
247 }
248 
execForeColor(KHTMLPart * part,bool,const DOMString & value)249 static bool execForeColor(KHTMLPart *part, bool /*userInterface*/, const DOMString &value)
250 {
251     return execStyleChange(part, CSS_PROP_COLOR, value);
252 }
253 
execIndent(KHTMLPart * part,bool,const DOMString &)254 static bool execIndent(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
255 {
256     part->editor()->indent();
257     return true;
258 }
259 
execInsertNewline(KHTMLPart * part,bool,const DOMString &)260 static bool execInsertNewline(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
261 {
262     TypingCommandImpl::insertNewline0(KPAC::xmlDocImpl(part));
263     return true;
264 }
265 
execInsertParagraph(KHTMLPart *,bool,const DOMString &)266 static bool execInsertParagraph(KHTMLPart * /*part*/, bool /*userInterface*/, const DOMString &/*value*/)
267 {
268     // FIXME: Implement.
269     return false;
270 }
271 
execInsertText(KHTMLPart * part,bool,const DOMString & value)272 static bool execInsertText(KHTMLPart *part, bool /*userInterface*/, const DOMString &value)
273 {
274     TypingCommandImpl::insertText0(KPAC::xmlDocImpl(part), value);
275     return true;
276 }
277 
execInsertOrderedList(KHTMLPart * part,bool,const DOMString &)278 static bool execInsertOrderedList(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
279 {
280     InsertListCommandImpl::insertList(KPAC::xmlDocImpl(part), InsertListCommandImpl::OrderedList);
281     return true;
282 }
283 
execInsertUnorderedList(KHTMLPart * part,bool,const DOMString &)284 static bool execInsertUnorderedList(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
285 {
286     InsertListCommandImpl::insertList(KPAC::xmlDocImpl(part), InsertListCommandImpl::UnorderedList);
287     return true;
288 }
289 
execItalic(KHTMLPart * part,bool,const DOMString &)290 static bool execItalic(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
291 {
292     bool isItalic = selectionStartHasStyle(part, CSS_PROP_FONT_STYLE, "italic");
293     return execStyleChange(part, CSS_PROP_FONT_STYLE, isItalic ? "normal" : "italic");
294 }
295 
execJustifyCenter(KHTMLPart * part,bool,const DOMString &)296 static bool execJustifyCenter(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
297 {
298     return execStyleChange(part, CSS_PROP_TEXT_ALIGN, "center");
299 }
300 
execJustifyFull(KHTMLPart * part,bool,const DOMString &)301 static bool execJustifyFull(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
302 {
303     return execStyleChange(part, CSS_PROP_TEXT_ALIGN, "justify");
304 }
305 
execJustifyLeft(KHTMLPart * part,bool,const DOMString &)306 static bool execJustifyLeft(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
307 {
308     return execStyleChange(part, CSS_PROP_TEXT_ALIGN, "left");
309 }
310 
execJustifyRight(KHTMLPart * part,bool,const DOMString &)311 static bool execJustifyRight(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
312 {
313     return execStyleChange(part, CSS_PROP_TEXT_ALIGN, "right");
314 }
315 
execOutdent(KHTMLPart * part,bool,const DOMString &)316 static bool execOutdent(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
317 {
318     part->editor()->outdent();
319     return true;
320 }
321 
322 #ifndef NO_SUPPORT_PASTE
323 
execPaste(KHTMLPart * part,bool,const DOMString &)324 static bool execPaste(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
325 {
326     part->editor()->paste();
327     return true;
328 }
329 
330 #endif
331 
execPrint(KHTMLPart * part,bool,const DOMString &)332 static bool execPrint(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
333 {
334     part->editor()->print();
335     return true;
336 }
337 
execRedo(KHTMLPart * part,bool,const DOMString &)338 static bool execRedo(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
339 {
340     part->editor()->redo();
341     return true;
342 }
343 
execSelectAll(KHTMLPart * part,bool,const DOMString &)344 static bool execSelectAll(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
345 {
346     part->selectAll();
347     return true;
348 }
349 
execStrikeThrough(KHTMLPart * part,bool,const DOMString &)350 static bool execStrikeThrough(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
351 {
352     bool isStriked = selectionStartHasStyle(part, CSS_PROP_TEXT_DECORATION, "line-through");
353     return execStyleChange(part, CSS_PROP_TEXT_DECORATION, isStriked ? "none" : "line-through");
354 }
355 
execSubscript(KHTMLPart * part,bool,const DOMString &)356 static bool execSubscript(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
357 {
358     return execStyleChange(part, CSS_PROP_VERTICAL_ALIGN, "sub");
359 }
360 
execSuperscript(KHTMLPart * part,bool,const DOMString &)361 static bool execSuperscript(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
362 {
363     return execStyleChange(part, CSS_PROP_VERTICAL_ALIGN, "super");
364 }
365 
execUndo(KHTMLPart * part,bool,const DOMString &)366 static bool execUndo(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
367 {
368     part->editor()->undo();
369     return true;
370 }
371 
execUnderline(KHTMLPart * part,bool,const DOMString &)372 static bool execUnderline(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
373 {
374     bool isUnderline = selectionStartHasStyle(part, CSS_PROP_TEXT_DECORATION, "underline");
375     return execStyleChange(part, CSS_PROP_TEXT_DECORATION, isUnderline ? "none" : "underline");
376 }
377 
execUnselect(KHTMLPart * part,bool,const DOMString &)378 static bool execUnselect(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
379 {
380     KPAC::clearSelection(part);
381     return true;
382 }
383 
384 // =============================================================================================
385 //
386 // queryCommandEnabled implementations
387 //
388 // It's a bit difficult to get a clear notion of the difference between
389 // "supported" and "enabled" from reading the Microsoft documentation, but
390 // what little I could glean from that seems to make some sense.
391 //     Supported = The command is supported by this object.
392 //     Enabled =   The command is available and enabled.
393 
enabled(KHTMLPart *)394 static bool enabled(KHTMLPart * /*part*/)
395 {
396     return true;
397 }
398 
enabledAnySelection(KHTMLPart * part)399 static bool enabledAnySelection(KHTMLPart *part)
400 {
401     return KPAC::caret(part).notEmpty();
402 }
403 
404 #ifndef NO_SUPPORT_PASTE
405 
enabledPaste(KHTMLPart * part)406 static bool enabledPaste(KHTMLPart *part)
407 {
408     return part->editor()->canPaste();
409 }
410 
411 #endif
412 
enabledRangeSelection(KHTMLPart * part)413 static bool enabledRangeSelection(KHTMLPart *part)
414 {
415     return KPAC::caret(part).state() == Selection::RANGE;
416 }
417 
enabledRedo(KHTMLPart * part)418 static bool enabledRedo(KHTMLPart *part)
419 {
420     return part->editor()->canRedo();
421 }
422 
enabledUndo(KHTMLPart * part)423 static bool enabledUndo(KHTMLPart *part)
424 {
425     return part->editor()->canUndo();
426 }
427 
428 // =============================================================================================
429 //
430 // queryCommandIndeterm/State implementations
431 //
432 // It's a bit difficult to get a clear notion of what these methods are supposed
433 // to do from reading the Microsoft documentation, but my current guess is this:
434 //
435 //     queryCommandState and queryCommandIndeterm work in concert to return
436 //     the two bits of information that are needed to tell, for instance,
437 //     if the text of a selection is bold. The answer can be "yes", "no", or
438 //     "partially".
439 //
440 // If this is so, then queryCommandState should return "yes" in the case where
441 // all the text is bold and "no" for non-bold or partially-bold text.
442 // Then, queryCommandIndeterm should return "no" in the case where
443 // all the text is either all bold or not-bold and and "yes" for partially-bold text.
444 
stateNone(KHTMLPart *)445 static Editor::TriState stateNone(KHTMLPart * /*part*/)
446 {
447     return Editor::FalseTriState;
448 }
449 
stateBold(KHTMLPart * part)450 static Editor::TriState stateBold(KHTMLPart *part)
451 {
452     return stateStyle(part, CSS_PROP_FONT_WEIGHT, "bold");
453 }
454 
stateItalic(KHTMLPart * part)455 static Editor::TriState stateItalic(KHTMLPart *part)
456 {
457     return stateStyle(part, CSS_PROP_FONT_STYLE, "italic");
458 }
459 
stateStrike(KHTMLPart * part)460 static Editor::TriState stateStrike(KHTMLPart *part)
461 {
462     return stateStyle(part, CSS_PROP_TEXT_DECORATION, "line-through");
463 }
464 
stateSubscript(KHTMLPart * part)465 static Editor::TriState stateSubscript(KHTMLPart *part)
466 {
467     return stateStyle(part, CSS_PROP_VERTICAL_ALIGN, "sub");
468 }
469 
stateSuperscript(KHTMLPart * part)470 static Editor::TriState stateSuperscript(KHTMLPart *part)
471 {
472     return stateStyle(part, CSS_PROP_VERTICAL_ALIGN, "super");
473 }
474 
stateUnderline(KHTMLPart * part)475 static Editor::TriState stateUnderline(KHTMLPart *part)
476 {
477     return stateStyle(part, CSS_PROP_TEXT_DECORATION, "underline");
478 }
479 
480 // =============================================================================================
481 //
482 // queryCommandValue implementations
483 //
484 
valueNull(KHTMLPart *)485 static DOMString valueNull(KHTMLPart * /*part*/)
486 {
487     return DOMString();
488 }
489 
valueBackColor(KHTMLPart * part)490 static DOMString valueBackColor(KHTMLPart *part)
491 {
492     return valueStyle(part, CSS_PROP_BACKGROUND_COLOR);
493 }
494 
valueFontName(KHTMLPart * part)495 static DOMString valueFontName(KHTMLPart *part)
496 {
497     return valueStyle(part, CSS_PROP_FONT_FAMILY);
498 }
499 
valueFontSize(KHTMLPart * part)500 static DOMString valueFontSize(KHTMLPart *part)
501 {
502     return valueStyle(part, CSS_PROP_FONT_SIZE);
503 }
504 
valueForeColor(KHTMLPart * part)505 static DOMString valueForeColor(KHTMLPart *part)
506 {
507     return valueStyle(part, CSS_PROP_COLOR);
508 }
509 
510 // =============================================================================================
511 
512 struct EditorCommandInfo {
513     const char *name;
514     CommandImp imp;
515 };
516 
517 // NOTE: strictly keep in sync with EditorCommand in editor_command.h
518 static const EditorCommandInfo commands[] = {
519 
520     { "backColor", { execBackColor, enabled, stateNone, valueBackColor } },
521     { "bold", { execBold, enabledAnySelection, stateBold, valueNull } },
522     { "copy", { execCopy, enabledRangeSelection, stateNone, valueNull } },
523     { "cut", { execCut, enabledRangeSelection, stateNone, valueNull } },
524     { "delete", { execDelete, enabledAnySelection, stateNone, valueNull } },
525     { "fontName", { execFontName, enabledAnySelection, stateNone, valueFontName } },
526     { "fontSize", { execFontSize, enabledAnySelection, stateNone, valueFontSize } },
527     { "foreColor", { execForeColor, enabledAnySelection, stateNone, valueForeColor } },
528     { "indent", { execIndent, enabledAnySelection, stateNone, valueNull } },
529     { "insertNewline", { execInsertNewline, enabledAnySelection, stateNone, valueNull } },
530     { "insertOrderedList", { execInsertOrderedList, enabledAnySelection, stateNone, valueNull } },
531     { "insertParagraph", { execInsertParagraph, enabledAnySelection, stateNone, valueNull } },
532     { "insertText", { execInsertText, enabledAnySelection, stateNone, valueNull } },
533     { "insertUnorderedList", { execInsertUnorderedList, enabledAnySelection, stateNone, valueNull } },
534     { "italic", { execItalic, enabledAnySelection, stateItalic, valueNull } },
535     { "justifyCenter", { execJustifyCenter, enabledAnySelection, stateNone, valueNull } },
536     { "justifyFull", { execJustifyFull, enabledAnySelection, stateNone, valueNull } },
537     { "justifyLeft", { execJustifyLeft, enabledAnySelection, stateNone, valueNull } },
538     { "justifyNone", { execJustifyLeft, enabledAnySelection, stateNone, valueNull } },
539     { "justifyRight", { execJustifyRight, enabledAnySelection, stateNone, valueNull } },
540     { "outdent", { execOutdent, enabledAnySelection, stateNone, valueNull } },
541 #ifndef NO_SUPPORT_PASTE
542     { "paste", { execPaste, enabledPaste, stateNone, valueNull } },
543 #else
544     { 0, { 0, 0, 0, 0 } },
545 #endif
546     { "print", { execPrint, enabled, stateNone, valueNull } },
547     { "redo", { execRedo, enabledRedo, stateNone, valueNull } },
548     { "selectAll", { execSelectAll, enabled, stateNone, valueNull } },
549     { "StrikeThrough", {execStrikeThrough, enabled, stateStrike, valueNull } },
550     { "subscript", { execSubscript, enabledAnySelection, stateSubscript, valueNull } },
551     { "superscript", { execSuperscript, enabledAnySelection, stateSuperscript, valueNull } },
552     { "underline", { execUnderline, enabledAnySelection, stateUnderline, valueNull } },
553     { "undo", { execUndo, enabledUndo, stateNone, valueNull } },
554     { "unselect", { execUnselect, enabledAnySelection, stateNone, valueNull } }
555 
556     //
557     // The "unsupported" commands are listed here since they appear in the Microsoft
558     // documentation used as the basis for the list.
559     //
560 
561     // 2d-position (not supported)
562     // absolutePosition (not supported)
563     // blockDirLTR (not supported)
564     // blockDirRTL (not supported)
565     // browseMode (not supported)
566     // clearAuthenticationCache (not supported)
567     // createBookmark (not supported)
568     // createLink (not supported)
569     // dirLTR (not supported)
570     // dirRTL (not supported)
571     // editMode (not supported)
572     // formatBlock (not supported)
573     // inlineDirLTR (not supported)
574     // inlineDirRTL (not supported)
575     // insertButton (not supported)
576     // insertFieldSet (not supported)
577     // insertHorizontalRule (not supported)
578     // insertIFrame (not supported)
579     // insertImage (not supported)
580     // insertInputButton (not supported)
581     // insertInputCheckbox (not supported)
582     // insertInputFileUpload (not supported)
583     // insertInputHidden (not supported)
584     // insertInputImage (not supported)
585     // insertInputPassword (not supported)
586     // insertInputRadio (not supported)
587     // insertInputReset (not supported)
588     // insertInputSubmit (not supported)
589     // insertInputText (not supported)
590     // insertMarquee (not supported)
591     // insertOrderedList (not supported)
592     // insertSelectDropDown (not supported)
593     // insertSelectListBox (not supported)
594     // insertTextArea (not supported)
595     // insertUnorderedList (not supported)
596     // liveResize (not supported)
597     // multipleSelection (not supported)
598     // open (not supported)
599     // overwrite (not supported)
600     // playImage (not supported)
601     // refresh (not supported)
602     // removeFormat (not supported)
603     // removeParaFormat (not supported)
604     // saveAs (not supported)
605     // sizeToControl (not supported)
606     // sizeToControlHeight (not supported)
607     // sizeToControlWidth (not supported)
608     // stop (not supported)
609     // stopimage (not supported)
610     // strikethrough (not supported)
611     // unbookmark (not supported)
612     // underline (not supported)
613     // unlink (not supported)
614 };
615 
createCommandDictionary()616 static CommandDict createCommandDictionary()
617 {
618     const int numCommands = sizeof(commands) / sizeof(commands[0]);
619     CommandDict commandDictionary; // case-insensitive dictionary
620     for (int i = 0; i < numCommands; ++i) {
621         if (commands[i].name) {
622             commandDictionary.insert(QString(commands[i].name).toLower(), &commands[i].imp);
623         }
624     }
625     return commandDictionary;
626 }
627 
commandImp(const DOMString & command)628 const CommandImp *JSEditor::commandImp(const DOMString &command)
629 {
630     static CommandDict commandDictionary = createCommandDictionary();
631     const CommandImp *result = commandDictionary.value(command.string().toLower());
632 #ifdef DEBUG_COMMANDS
633     if (!result) {
634         qCDebug(KHTML_LOG) << "[Command is not supported yet]" << command;
635     }
636 #endif
637     return result;
638 }
639 
commandImp(int command)640 const CommandImp *JSEditor::commandImp(int command)
641 {
642     if (command < 0 || command >= int(sizeof commands / sizeof commands[0])) {
643         return nullptr;
644     }
645     return &commands[command].imp;
646 }
647 
648 } // namespace DOM
649 
650 #undef KPAC
651