[First patch] design mode (was: [Q] replacing the textarea widget)

Koos Vriezen koos.vriezen at xs4all.nl
Sun Sep 12 20:47:14 BST 2004


On Mon, Sep 06, 2004 at 05:32:32PM +0200, Leo Savernik wrote:
> Am Montag, 6. September 2004 16:13 schrieb Koos Vriezen:
> [...]
> > Ok, I'll see if I can do something in the meantime. If I come up with a
> > patch we can always see what to do with it (it's not the first time a
> > patch is accepted for a particular branch in mind and replaced later, if
> > ever).
> 
> All right, keep me informed.

There seem to be two ways to do this (as usual, ms and moz way). Looks
to me the moz way (http://www.mozilla.org/editor/midas-spec.html), is
far more simplier than the TextObject MS way. What should we support,
both?
Anyway, I've attached some boilerplate code for the midas way. I've already
hooked in to the keyevent code from the caret and some very simply editing
in HTMLDocumentImpl, just to get something working.

Making it work somehow, I've took a look at the quanta KHTMLPart override,
strangely called KafkaWidget, and wondered what would be the best
approach getting this to work. 
I don't think switching transparently to KafkaWidget when entering a
design mode, would be the best thing to do. But re-implement/copy code
from quanta has also its drawbacks, eg it's quite some code (but likely
some shortcuts too, no parallel node tree needed and access to khtml
namespace).

So what to do? Commit this and work further on it? Or no, entering
design mode means switching to kafka unter the hood? Or we only want MS
way of doing this? Or..

Koos
-------------- next part --------------
Index: khtmlview.cpp
===================================================================
RCS file: /home/kde/kdelibs/khtml/khtmlview.cpp,v
retrieving revision 1.664
diff -u -3 -p -r1.664 khtmlview.cpp
--- khtmlview.cpp	28 Aug 2004 13:58:54 -0000	1.664
+++ khtmlview.cpp	12 Sep 2004 19:23:27 -0000
@@ -3295,7 +3295,14 @@ void KHTMLView::caretKeyPressEvent(QKeyE
 	  else
 	    moveCaretToLineEnd();
           break;
-
+        default: {
+          DOM::DocumentImpl *doc = m_part->xmlDocImpl();
+          if (doc && doc->isHTMLDocument() && static_cast<HTMLDocumentImpl*>(doc)->designMode()) {
+            static_cast<HTMLDocumentImpl*>(doc)->designModeKeyEvent(m_part->d->caretNode().handle(), m_part->d->caretOffset(), _ke);
+            if (_ke->isAccepted())
+              return;
+          }
+        }
       }/*end switch*/
 
   if ((m_part->d->caretNode().handle() != oldCaretNode
Index: ecma/kjs_html.h
===================================================================
RCS file: /home/kde/kdelibs/khtml/ecma/kjs_html.h,v
retrieving revision 1.80
diff -u -3 -p -r1.80 kjs_html.h
--- ecma/kjs_html.h	24 Aug 2004 11:00:20 -0000	1.80
+++ ecma/kjs_html.h	12 Sep 2004 19:23:27 -0000
@@ -49,7 +49,8 @@ namespace KJS {
            Images, Applets, Links, Forms, Anchors, Scripts, All, Clear, Open, Close,
            Write, WriteLn, GetElementsByName, GetSelection, CaptureEvents, ReleaseEvents,
            BgColor, FgColor, AlinkColor, LinkColor, VlinkColor, LastModified,
-           Height, Width, Dir, Frames, CompatMode };
+           Height, Width, Dir, Frames, CompatMode,
+           DesignMode, ExecCommand, QueryCommandEnabled, QueryCommandState, QueryCommandValue };
     DOM::Document toDocument() const { return static_cast<DOM::Document>( node ); }
   };
 
Index: ecma/kjs_html.cpp
===================================================================
RCS file: /home/kde/kdelibs/khtml/ecma/kjs_html.cpp,v
retrieving revision 1.269
diff -u -3 -p -r1.269 kjs_html.cpp
--- ecma/kjs_html.cpp	24 Aug 2004 11:00:20 -0000	1.269
+++ ecma/kjs_html.cpp	12 Sep 2004 19:23:27 -0000
@@ -63,6 +63,77 @@
 
 using namespace KJS;
 
+// -------------------------------------------------------------------------
+
+// execCommand for design mode document
+
+enum ExecCommandType {
+    ExecBackcolor, ExecBold, ExecCopy, ExecCreatelink, ExecCut, ExecDelete,
+    ExecFontname, ExecFontsize, ExecForecolor, ExecFormatblock, ExecHeading,
+    ExecIndent, ExecInserthorizontalrule, ExecInsertimage,
+    ExecInsertorderedlist, ExecInsertunorderedlist, ExecItalic,
+    ExecJustifycenter, ExecJustifyfull, ExecJustifyleft, ExecJustifyright,
+    ExecOutdent, ExecPaste, ExecRedo, ExecRemoveformat, ExecSelectall,
+    ExecStrikethrough, ExecSubscript, ExecSuperscript, ExecUnderline,
+    ExecUndo, ExecUnlink
+};
+
+static struct ExecCommandEntry {
+    const char *command;
+    ExecCommandType type;
+} exec_commands[] = {
+      { "backcolor", ExecBackcolor },
+      { "bold", ExecBold },
+      { "copy", ExecCopy },
+      { "createlink", ExecCreatelink },
+      { "cut", ExecCut },
+      { "delete", ExecDelete },
+      { "fontname", ExecFontname },
+      { "fontsize", ExecFontsize },
+      { "forecolor", ExecForecolor },
+      { "formatblock", ExecFormatblock },
+      { "heading", ExecHeading },
+      { "indent", ExecIndent },
+      { "inserthorizontalrule", ExecInserthorizontalrule },
+      { "insertimage", ExecInsertimage },
+      { "insertorderedlist", ExecInsertorderedlist },
+      { "insertunorderedlist", ExecInsertunorderedlist },
+      { "italic", ExecItalic },
+      { "justifycenter", ExecJustifycenter },
+      { "justifyfull", ExecJustifyfull },
+      { "justifyleft", ExecJustifyleft },
+      { "justifyright", ExecJustifyright },
+      { "outdent", ExecOutdent },
+      { "paste", ExecPaste },
+      { "redo", ExecRedo },
+      { "removeformat", ExecRemoveformat },
+      { "selectall", ExecSelectall },
+      { "strikethrough", ExecStrikethrough },
+      { "subscript", ExecSubscript },
+      { "superscript", ExecSuperscript },
+      { "underline", ExecUnderline },
+      { "undo", ExecUndo },
+      { "unlink", ExecUnlink }
+};
+
+static ExecCommandEntry *execCommandType( const char *cmd, int b=0, int e=sizeof(exec_commands)/sizeof(ExecCommandEntry) )
+{
+    if (e - b < 2) {
+        if (b != e && !strcmp(exec_commands[b].command, cmd))
+            return exec_commands + b;
+        return 0L;
+    }
+    int mid = (b + e) / 2;
+    int cmp = strcmp(exec_commands[mid].command, cmd);
+    if (cmp < 0)
+        return execCommandType(cmd, mid + 1, e);
+    if (cmp > 0)
+        return execCommandType(cmd, b, mid);
+    return exec_commands + mid;
+}
+
+// -------------------------------------------------------------------------
+
 IMPLEMENT_PROTOFUNC_DOM(HTMLDocFunction)
 
 Value KJS::HTMLDocFunction::tryCall(ExecState *exec, Object &thisObj, const List &args)
@@ -123,6 +194,62 @@ Value KJS::HTMLDocFunction::tryCall(Exec
   case HTMLDocument::ReleaseEvents:
     // Do nothing for now. These are NS-specific legacy calls.
     break;
+  case HTMLDocument::ExecCommand:
+    if (args.size() > 0) {
+      QString cmd = args[0].toString(exec).qstring();
+      bool showUI = false;
+      QString param;
+      if (args.size() > 1) {
+        showUI = args[1].toBoolean(exec);
+        if (args.size() > 2)
+          param = args[2].toString(exec).qstring();
+      }
+      ExecCommandEntry *entry = execCommandType(cmd.ascii());
+      if (entry) {
+        switch (entry->type) {
+          case ExecBackcolor:
+          case ExecBold:
+          case ExecCopy:
+          case ExecCreatelink:
+          case ExecCut:
+          case ExecDelete:
+          case ExecFontname:
+          case ExecFontsize:
+          case ExecForecolor:
+          case ExecFormatblock:
+          case ExecHeading:
+          case ExecIndent:
+          case ExecInserthorizontalrule:
+          case ExecInsertimage:
+          case ExecInsertorderedlist:
+          case ExecInsertunorderedlist:
+          case ExecItalic:
+          case ExecJustifycenter:
+          case ExecJustifyfull:
+          case ExecJustifyleft:
+          case ExecJustifyright:
+          case ExecOutdent:
+          case ExecPaste:
+          case ExecRedo:
+          case ExecRemoveformat:
+          case ExecSelectall:
+          case ExecStrikethrough:
+          case ExecSubscript:
+          case ExecSuperscript:
+          case ExecUnderline:
+          case ExecUndo:
+          case ExecUnlink:
+            break;
+          }
+      }
+    }
+    break;
+  case HTMLDocument::QueryCommandEnabled:
+    break;
+  case HTMLDocument::QueryCommandState:
+    break;
+  case HTMLDocument::QueryCommandValue:
+    break;
   }
 
   return Undefined();
@@ -167,6 +294,12 @@ const ClassInfo KJS::HTMLDocument::info 
   compatMode		HTMLDocument::CompatMode	DontDelete|ReadOnly
 #IE extension
   frames		HTMLDocument::Frames		DontDelete|ReadOnly
+#Midas http://www.mozilla.org/editor/midas-spec.html
+  designMode            HTMLDocument::DesignMode        DontDelete
+  execCommand           HTMLDocument::ExecCommand       DontDelete|Function 1
+  queryCommandEnabled   HTMLDocument::QueryCommandEnabled DontDelete|Function 1
+  queryCommandState     HTMLDocument::QueryCommandState DontDelete|Function 1
+  queryCommandValue     HTMLDocument::QueryCommandValue DontDelete|Function 1
 #potentially obsolete array properties
 # layers
 # plugins
@@ -327,10 +460,16 @@ Value KJS::HTMLDocument::tryGet(ExecStat
     case GetSelection:
     case CaptureEvents:
     case ReleaseEvents:
+    case ExecCommand:
+    case QueryCommandEnabled:
+    case QueryCommandState:
+    case QueryCommandValue:
       return lookupOrCreateFunction<HTMLDocFunction>( exec, propertyName, this, entry->value, entry->params, entry->attr );
     case CompatMode:
       return getString(static_cast<HTMLDocumentImpl *>(doc.handle())->parseMode()
               == DocumentImpl::Compat ? "BackCompat" : "CSS1Compat");
+    case DesignMode:
+      return String(static_cast<HTMLDocumentImpl *>(doc.handle())->designMode() ? "on" : "off");
     }
   }
   // Look for overrides
@@ -449,6 +588,9 @@ void KJS::HTMLDocument::putValueProperty
   case Dir:
     body.setDir(val);
     break;
+  case DesignMode:
+    static_cast<HTMLDocumentImpl *>(doc.handle())->setDesignMode(val=="on");
+    break;
   default:
     kdDebug(6070) << "WARNING: HTMLDocument::putValueProperty unhandled token " << token << endl;
   }
Index: html/html_documentimpl.h
===================================================================
RCS file: /home/kde/kdelibs/khtml/html/html_documentimpl.h,v
retrieving revision 1.73
diff -u -3 -p -r1.73 html_documentimpl.h
--- html/html_documentimpl.h	9 Sep 2004 09:08:18 -0000	1.73
+++ html/html_documentimpl.h	12 Sep 2004 19:23:28 -0000
@@ -78,6 +78,9 @@ public:
 
     HTMLCollectionImpl::CollectionInfo *collectionInfo(int type) { return m_collection_info+type; }
 
+    void setDesignMode(bool enable=true);
+    bool designMode() const { return m_designMode; }
+    void designModeKeyEvent(DOM::NodeImpl *node, int pos, QKeyEvent *ke);
 protected:
     HTMLElementImpl *bodyElement;
     HTMLElementImpl *htmlElement;
@@ -94,6 +97,7 @@ protected slots:
 private:
     HTMLCollectionImpl::CollectionInfo m_collection_info[HTMLCollectionImpl::LAST_TYPE];
     mutable DOMString m_domain;
+    bool m_designMode;
 };
 
 } //namespace
Index: html/html_documentimpl.cpp
===================================================================
RCS file: /home/kde/kdelibs/khtml/html/html_documentimpl.cpp,v
retrieving revision 1.162
diff -u -3 -p -r1.162 html_documentimpl.cpp
--- html/html_documentimpl.cpp	9 Sep 2004 09:08:18 -0000	1.162
+++ html/html_documentimpl.cpp	12 Sep 2004 19:23:28 -0000
@@ -64,7 +64,7 @@ using namespace khtml;
 
 
 HTMLDocumentImpl::HTMLDocumentImpl(DOMImplementationImpl *_implementation, KHTMLView *v)
-  : DocumentImpl(_implementation, v)
+  : DocumentImpl(_implementation, v), m_designMode(false)
 {
 //    kdDebug( 6090 ) << "HTMLDocumentImpl constructor this = " << this << endl;
     bodyElement = 0;
@@ -434,4 +434,51 @@ void HTMLDocumentImpl::determineParseMod
         recalcStyleSelector();
 }
 
+void HTMLDocumentImpl::setDesignMode(bool enable) {
+    m_designMode = enable;
+    m_view->part()->setCaretMode(enable);
+}
+
+void HTMLDocumentImpl::designModeKeyEvent(DOM::NodeImpl *node, int pos, QKeyEvent *ke) {
+    if (!node) return;
+    int exceptioncode;
+    switch (ke->key()) {
+        case Qt::Key_Backspace:
+            if (node->nodeType() == DOM::Node::TEXT_NODE && pos > 0) {
+                DOM::DOMString textNode = node->nodeValue();
+                DOM::DOMString endText = textNode.split(pos);
+                textNode.truncate(pos-1);
+                node->setNodeValue(textNode + endText, exceptioncode);
+                ke->accept();
+                m_view->part()->setCaretPosition(node, pos-1);
+            }
+            break;
+        case Qt::Key_Delete:
+            if (node->nodeType() == DOM::Node::TEXT_NODE && pos < node->nodeValue().length()) {
+                DOM::DOMString textNode = node->nodeValue();
+                DOM::DOMString endText = textNode.split(pos);
+                node->setNodeValue(textNode + endText.split(1), exceptioncode);
+                ke->accept();
+            }
+            break;
+        case Qt::Key_Return:
+        case Qt::Key_Enter:
+            break;
+        default:
+            if (ke->text().isNull()) {
+                kdDebug() << "null key" << endl;
+            } else if (node->nodeType() == DOM::Node::TEXT_NODE) {
+                DOM::DOMString textNode = node->nodeValue();
+                DOM::DOMString textSplitted = textNode.split(pos);
+                node->setNodeValue(textNode + ke->text() + textSplitted, exceptioncode);
+                ke->accept();
+                m_view->part()->setCaretPosition(node, pos+1);
+            } else if (node->parentNode()) {
+                node->parentNode()->insertBefore(createTextNode(ke->text()), node, exceptioncode);
+                ke->accept();
+            }
+    }
+
+}
+
 #include "html_documentimpl.moc"


More information about the kfm-devel mailing list