don't discard iframe children.
[kdelibs.git] / khtml / editing / jsediting.cpp
blobbb437dba05bc8ccd008c473bb1469f9211a53190
1 /*
2 * Copyright (C) 2004 Apple Computer, Inc. All rights reserved.
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.
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.
26 #include "jsediting.h"
27 #include "htmlediting.h"
28 #include "editor.h"
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"
37 #include "misc/khtml_partaccessor.h"
39 #include <QHash>
40 #include <QString>
42 using khtml::TypingCommand;
43 //
44 #define KPAC khtml::KHTMLPartAccessor
46 namespace DOM {
48 class DocumentImpl;
50 struct CommandImp {
51 bool (*execFn)(KHTMLPart *part, bool userInterface, const DOMString &value);
52 bool (*enabledFn)(KHTMLPart *part);
53 Editor::TriState (*stateFn)(KHTMLPart *part);
54 DOMString (*valueFn)(KHTMLPart *part);
57 typedef QHash<QString,const CommandImp*> CommandDict;
58 static CommandDict createCommandDictionary();
60 bool JSEditor::execCommand(const CommandImp *cmd, bool userInterface, const DOMString &value)
62 if (!cmd || !cmd->enabledFn)
63 return false;
64 KHTMLPart *part = m_doc->part();
65 if (!part)
66 return false;
67 m_doc->updateLayout();
68 return cmd->enabledFn(part) && cmd->execFn(part, userInterface, value);
71 bool JSEditor::queryCommandEnabled(const CommandImp *cmd)
73 if (!cmd || !cmd->enabledFn)
74 return false;
75 KHTMLPart *part = m_doc->part();
76 if (!part)
77 return false;
78 m_doc->updateLayout();
79 return cmd->enabledFn(part);
82 bool JSEditor::queryCommandIndeterm(const CommandImp *cmd)
84 if (!cmd || !cmd->enabledFn)
85 return false;
86 KHTMLPart *part = m_doc->part();
87 if (!part)
88 return false;
89 m_doc->updateLayout();
90 return cmd->stateFn(part) == Editor::MixedTriState;
93 bool JSEditor::queryCommandState(const CommandImp *cmd)
95 if (!cmd || !cmd->enabledFn)
96 return false;
97 KHTMLPart *part = m_doc->part();
98 if (!part)
99 return false;
100 m_doc->updateLayout();
101 return cmd->stateFn(part) != Editor::FalseTriState;
104 bool JSEditor::queryCommandSupported(const CommandImp *cmd)
106 return cmd != 0;
109 DOMString JSEditor::queryCommandValue(const CommandImp *cmd)
111 if (!cmd || !cmd->enabledFn)
112 return DOMString();
113 KHTMLPart *part = m_doc->part();
114 if (!part)
115 return DOMString();
116 m_doc->updateLayout();
117 return cmd->valueFn(part);
120 // =============================================================================================
122 // Private stuff
124 static bool execStyleChange(KHTMLPart *part, int propertyID, const DOMString &propertyValue)
126 CSSStyleDeclarationImpl *style = new CSSStyleDeclarationImpl(0);
127 style->setProperty(propertyID, propertyValue);
128 style->ref();
129 part->editor()->applyStyle(style);
130 style->deref();
131 return true;
134 static bool execStyleChange(KHTMLPart *part, int propertyID, int propertyEnum)
136 CSSStyleDeclarationImpl *style = new CSSStyleDeclarationImpl(0);
137 style->setProperty(propertyID, propertyEnum);
138 style->ref();
139 part->editor()->applyStyle(style);
140 style->deref();
141 return true;
144 static bool execStyleChange(KHTMLPart *part, int propertyID, const char *propertyValue)
146 return execStyleChange(part, propertyID, DOMString(propertyValue));
149 static Editor::TriState stateStyle(KHTMLPart *part, int propertyID, const char *desiredValue)
151 CSSStyleDeclarationImpl *style = new CSSStyleDeclarationImpl(0);
152 style->setProperty(propertyID, desiredValue);
153 style->ref();
154 Editor::TriState state = part->editor()->selectionHasStyle(style);
155 style->deref();
156 return state;
159 static bool selectionStartHasStyle(KHTMLPart *part, int propertyID, const char *desiredValue)
161 CSSStyleDeclarationImpl *style = new CSSStyleDeclarationImpl(0);
162 style->setProperty(propertyID, desiredValue);
163 style->ref();
164 bool hasStyle = part->editor()->selectionStartHasStyle(style);
165 style->deref();
166 return hasStyle;
169 static DOMString valueStyle(KHTMLPart *part, int propertyID)
171 return part->editor()->selectionStartStylePropertyValue(propertyID);
174 // =============================================================================================
176 // execCommand implementations
179 static bool execBackColor(KHTMLPart *part, bool /*userInterface*/, const DOMString &value)
181 return execStyleChange(part, CSS_PROP_BACKGROUND_COLOR, value);
184 static bool execBold(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
186 bool isBold = selectionStartHasStyle(part, CSS_PROP_FONT_WEIGHT, "bold");
187 return execStyleChange(part, CSS_PROP_FONT_WEIGHT, isBold ? "normal" : "bold");
190 static bool execCopy(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
192 part->editor()->copy();
193 return true;
196 static bool execCut(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
198 part->editor()->cut();
199 return true;
202 static bool execDelete(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
204 TypingCommand::deleteKeyPressed(KPAC::xmlDocImpl(part));
205 return true;
208 static bool execFontName(KHTMLPart *part, bool /*userInterface*/, const DOMString &value)
210 return execStyleChange(part, CSS_PROP_FONT_FAMILY, value);
213 static bool execFontSize(KHTMLPart *part, bool /*userInterface*/, const DOMString &value)
215 // This should handle sizes 1-7 like <font> does. Who the heck designed this interface? (Rhetorical question)
216 bool ok;
217 int val = value.string().toInt(&ok);
218 if (ok && val >= 1 && val <= 7) {
219 int size;
220 switch (val) {
221 case 1: size = CSS_VAL_XX_SMALL; break;
222 case 2: size = CSS_VAL_SMALL; break;
223 case 3: size = CSS_VAL_MEDIUM; break;
224 case 4: size = CSS_VAL_LARGE; break;
225 case 5: size = CSS_VAL_X_LARGE; break;
226 case 6: size = CSS_VAL_XX_LARGE; break;
227 default: size = CSS_VAL__KHTML_XXX_LARGE;
229 return execStyleChange(part, CSS_PROP_FONT_SIZE, size);
232 return execStyleChange(part, CSS_PROP_FONT_SIZE, value);
235 static bool execForeColor(KHTMLPart *part, bool /*userInterface*/, const DOMString &value)
237 return execStyleChange(part, CSS_PROP_COLOR, value);
240 static bool execIndent(KHTMLPart * /*part*/, bool /*userInterface*/, const DOMString &/*value*/)
242 // FIXME: Implement.
243 return false;
246 static bool execInsertNewline(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
248 TypingCommand::insertNewline(KPAC::xmlDocImpl(part));
249 return true;
252 static bool execInsertParagraph(KHTMLPart * /*part*/, bool /*userInterface*/, const DOMString &/*value*/)
254 // FIXME: Implement.
255 return false;
258 static bool execInsertText(KHTMLPart *part, bool /*userInterface*/, const DOMString &value)
260 TypingCommand::insertText(KPAC::xmlDocImpl(part), value);
261 return true;
264 static bool execItalic(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
266 bool isItalic = selectionStartHasStyle(part, CSS_PROP_FONT_STYLE, "italic");
267 return execStyleChange(part, CSS_PROP_FONT_STYLE, isItalic ? "normal" : "italic");
270 static bool execJustifyCenter(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
272 return execStyleChange(part, CSS_PROP_TEXT_ALIGN, "center");
275 static bool execJustifyFull(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
277 return execStyleChange(part, CSS_PROP_TEXT_ALIGN, "justify");
280 static bool execJustifyLeft(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
282 return execStyleChange(part, CSS_PROP_TEXT_ALIGN, "left");
285 static bool execJustifyRight(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
287 return execStyleChange(part, CSS_PROP_TEXT_ALIGN, "right");
290 static bool execOutdent(KHTMLPart * /*part*/, bool /*userInterface*/, const DOMString &/*value*/)
292 // FIXME: Implement.
293 return false;
296 #ifndef NO_SUPPORT_PASTE
298 static bool execPaste(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
300 part->editor()->paste();
301 return true;
304 #endif
306 static bool execPrint(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
308 part->editor()->print();
309 return true;
312 static bool execRedo(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
314 part->editor()->redo();
315 return true;
318 static bool execSelectAll(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
320 part->selectAll();
321 return true;
324 static bool execSubscript(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
326 return execStyleChange(part, CSS_PROP_VERTICAL_ALIGN, "sub");
329 static bool execSuperscript(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
331 return execStyleChange(part, CSS_PROP_VERTICAL_ALIGN, "super");
334 static bool execUndo(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
336 part->editor()->undo();
337 return true;
340 static bool execUnselect(KHTMLPart *part, bool /*userInterface*/, const DOMString &/*value*/)
342 KPAC::clearSelection(part);
343 return true;
346 // =============================================================================================
348 // queryCommandEnabled implementations
350 // It's a bit difficult to get a clear notion of the difference between
351 // "supported" and "enabled" from reading the Microsoft documentation, but
352 // what little I could glean from that seems to make some sense.
353 // Supported = The command is supported by this object.
354 // Enabled = The command is available and enabled.
356 static bool enabled(KHTMLPart * /*part*/)
358 return true;
361 static bool enabledAnySelection(KHTMLPart *part)
363 return KPAC::caret(part).notEmpty();
366 #ifndef NO_SUPPORT_PASTE
368 static bool enabledPaste(KHTMLPart *part)
370 return part->editor()->canPaste();
373 #endif
375 static bool enabledRangeSelection(KHTMLPart *part)
377 return KPAC::caret(part).state() == Selection::RANGE;
380 static bool enabledRedo(KHTMLPart *part)
382 return part->editor()->canRedo();
385 static bool enabledUndo(KHTMLPart *part)
387 return part->editor()->canUndo();
390 // =============================================================================================
392 // queryCommandIndeterm/State implementations
394 // It's a bit difficult to get a clear notion of what these methods are supposed
395 // to do from reading the Microsoft documentation, but my current guess is this:
397 // queryCommandState and queryCommandIndeterm work in concert to return
398 // the two bits of information that are needed to tell, for instance,
399 // if the text of a selection is bold. The answer can be "yes", "no", or
400 // "partially".
402 // If this is so, then queryCommandState should return "yes" in the case where
403 // all the text is bold and "no" for non-bold or partially-bold text.
404 // Then, queryCommandIndeterm should return "no" in the case where
405 // all the text is either all bold or not-bold and and "yes" for partially-bold text.
407 static Editor::TriState stateNone(KHTMLPart * /*part*/)
409 return Editor::FalseTriState;
412 static Editor::TriState stateBold(KHTMLPart *part)
414 return stateStyle(part, CSS_PROP_FONT_WEIGHT, "bold");
417 static Editor::TriState stateItalic(KHTMLPart *part)
419 return stateStyle(part, CSS_PROP_FONT_STYLE, "italic");
422 static Editor::TriState stateSubscript(KHTMLPart *part)
424 return stateStyle(part, CSS_PROP_VERTICAL_ALIGN, "sub");
427 static Editor::TriState stateSuperscript(KHTMLPart *part)
429 return stateStyle(part, CSS_PROP_VERTICAL_ALIGN, "super");
432 // =============================================================================================
434 // queryCommandValue implementations
437 static DOMString valueNull(KHTMLPart * /*part*/)
439 return DOMString();
442 static DOMString valueBackColor(KHTMLPart *part)
444 return valueStyle(part, CSS_PROP_BACKGROUND_COLOR);
447 static DOMString valueFontName(KHTMLPart *part)
449 return valueStyle(part, CSS_PROP_FONT_FAMILY);
452 static DOMString valueFontSize(KHTMLPart *part)
454 return valueStyle(part, CSS_PROP_FONT_SIZE);
457 static DOMString valueForeColor(KHTMLPart *part)
459 return valueStyle(part, CSS_PROP_COLOR);
462 // =============================================================================================
464 struct EditorCommandInfo { const char *name; CommandImp imp; };
466 // NOTE: strictly keep in sync with EditorCommand in editor_command.h
467 static const EditorCommandInfo commands[] = {
469 { "backColor", { execBackColor, enabled, stateNone, valueBackColor } },
470 { "bold", { execBold, enabledAnySelection, stateBold, valueNull } },
471 { "copy", { execCopy, enabledRangeSelection, stateNone, valueNull } },
472 { "cut", { execCut, enabledRangeSelection, stateNone, valueNull } },
473 { "delete", { execDelete, enabledAnySelection, stateNone, valueNull } },
474 { "fontName", { execFontName, enabledAnySelection, stateNone, valueFontName } },
475 { "fontSize", { execFontSize, enabledAnySelection, stateNone, valueFontSize } },
476 { "foreColor", { execForeColor, enabledAnySelection, stateNone, valueForeColor } },
477 { "indent", { execIndent, enabledAnySelection, stateNone, valueNull } },
478 { "insertNewline", { execInsertNewline, enabledAnySelection, stateNone, valueNull } },
479 { "insertParagraph", { execInsertParagraph, enabledAnySelection, stateNone, valueNull } },
480 { "insertText", { execInsertText, enabledAnySelection, stateNone, valueNull } },
481 { "italic", { execItalic, enabledAnySelection, stateItalic, valueNull } },
482 { "justifyCenter", { execJustifyCenter, enabledAnySelection, stateNone, valueNull } },
483 { "justifyFull", { execJustifyFull, enabledAnySelection, stateNone, valueNull } },
484 { "justifyLeft", { execJustifyLeft, enabledAnySelection, stateNone, valueNull } },
485 { "justifyNone", { execJustifyLeft, enabledAnySelection, stateNone, valueNull } },
486 { "justifyRight", { execJustifyRight, enabledAnySelection, stateNone, valueNull } },
487 { "outdent", { execOutdent, enabledAnySelection, stateNone, valueNull } },
488 #ifndef NO_SUPPORT_PASTE
489 { "paste", { execPaste, enabledPaste, stateNone, valueNull } },
490 #else
491 { 0, { 0, 0, 0, 0 } },
492 #endif
493 { "print", { execPrint, enabled, stateNone, valueNull } },
494 { "redo", { execRedo, enabledRedo, stateNone, valueNull } },
495 { "selectAll", { execSelectAll, enabled, stateNone, valueNull } },
496 { "subscript", { execSubscript, enabledAnySelection, stateSubscript, valueNull } },
497 { "superscript", { execSuperscript, enabledAnySelection, stateSuperscript, valueNull } },
498 { "undo", { execUndo, enabledUndo, stateNone, valueNull } },
499 { "unselect", { execUnselect, enabledAnySelection, stateNone, valueNull } }
502 // The "unsupported" commands are listed here since they appear in the Microsoft
503 // documentation used as the basis for the list.
506 // 2d-position (not supported)
507 // absolutePosition (not supported)
508 // blockDirLTR (not supported)
509 // blockDirRTL (not supported)
510 // browseMode (not supported)
511 // clearAuthenticationCache (not supported)
512 // createBookmark (not supported)
513 // createLink (not supported)
514 // dirLTR (not supported)
515 // dirRTL (not supported)
516 // editMode (not supported)
517 // formatBlock (not supported)
518 // inlineDirLTR (not supported)
519 // inlineDirRTL (not supported)
520 // insertButton (not supported)
521 // insertFieldSet (not supported)
522 // insertHorizontalRule (not supported)
523 // insertIFrame (not supported)
524 // insertImage (not supported)
525 // insertInputButton (not supported)
526 // insertInputCheckbox (not supported)
527 // insertInputFileUpload (not supported)
528 // insertInputHidden (not supported)
529 // insertInputImage (not supported)
530 // insertInputPassword (not supported)
531 // insertInputRadio (not supported)
532 // insertInputReset (not supported)
533 // insertInputSubmit (not supported)
534 // insertInputText (not supported)
535 // insertMarquee (not supported)
536 // insertOrderedList (not supported)
537 // insertSelectDropDown (not supported)
538 // insertSelectListBox (not supported)
539 // insertTextArea (not supported)
540 // insertUnorderedList (not supported)
541 // liveResize (not supported)
542 // multipleSelection (not supported)
543 // open (not supported)
544 // overwrite (not supported)
545 // playImage (not supported)
546 // refresh (not supported)
547 // removeFormat (not supported)
548 // removeParaFormat (not supported)
549 // saveAs (not supported)
550 // sizeToControl (not supported)
551 // sizeToControlHeight (not supported)
552 // sizeToControlWidth (not supported)
553 // stop (not supported)
554 // stopimage (not supported)
555 // strikethrough (not supported)
556 // unbookmark (not supported)
557 // underline (not supported)
558 // unlink (not supported)
561 static CommandDict createCommandDictionary()
563 const int numCommands = sizeof(commands) / sizeof(commands[0]);
564 CommandDict commandDictionary; // case-insensitive dictionary
565 for (int i = 0; i < numCommands; ++i) {
566 if (commands[i].name)
567 commandDictionary.insert(QString(commands[i].name).toLower(), &commands[i].imp);
569 return commandDictionary;
572 const CommandImp *JSEditor::commandImp(const DOMString &command)
574 static CommandDict commandDictionary = createCommandDictionary();
575 CommandDict::const_iterator it = commandDictionary.constFind(command.string().toLower());
576 return commandDictionary.value( command.string().toLower() );
579 const CommandImp *JSEditor::commandImp(int command)
581 if (command < 0 || command >= int(sizeof commands / sizeof commands[0]) )
582 return 0;
583 return &commands[command].imp;
588 } // namespace DOM
590 #undef KPAC