WebCore:
authorkocienda <kocienda@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 27 Oct 2004 22:41:14 +0000 (22:41 +0000)
committerkocienda <kocienda@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 27 Oct 2004 22:41:14 +0000 (22:41 +0000)
        Reviewed by Chris

        * khtml/editing/htmlediting.cpp:
        (khtml::CompositeEditCommand::deleteSelection): Added new mergeBlocksAfterDelete flag to control
        whether content not in the block containing the start of the selection is moved to that block
        after the selection is deleted.
        (khtml::DeleteSelectionCommand::DeleteSelectionCommand): Ditto.
        (khtml::DeleteSelectionCommand::doApply): Ditto.
        (khtml::InputNewlineInQuotedContentCommand::InputNewlineInQuotedContentCommand): New command
        to handle the case of inserting a newline when in quoted content in Mail.
        (khtml::InputNewlineInQuotedContentCommand::~InputNewlineInQuotedContentCommand): Ditto.
        (khtml::InputNewlineInQuotedContentCommand::isMailBlockquote): Ditto.
        (khtml::InputNewlineInQuotedContentCommand::isLastVisiblePositionInBlockquote): Ditto.
        (khtml::InputNewlineInQuotedContentCommand::doApply): Ditto.
        (khtml::TypingCommand::insertNewlineInQuotedContent): Support for new newline command.
        (khtml::TypingCommand::doApply): Ditto.
        (khtml::TypingCommand::preservesTypingStyle): Ditto.
        * khtml/editing/htmlediting.h: Add new delclarations.
        (khtml::TypingCommand::): Ditto.
        * kwq/WebCoreBridge.h: Added new bridge method called from WebKit.
        * kwq/WebCoreBridge.mm:
        (-[WebCoreBridge insertNewlineInQuotedContent]): Ditto.

WebKit:

        Reviewed by Chris

        Added new SPI for Mail so it can get the behavior it needs when the user hits
        the return key with the selection in quoted content.

        * WebView.subproj/WebView.m
        * WebView.subproj/WebViewPrivate.h

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@7907 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebCore/ChangeLog-2005-08-23
WebCore/khtml/editing/htmlediting.cpp
WebCore/khtml/editing/htmlediting.h
WebCore/kwq/WebCoreBridge.h
WebCore/kwq/WebCoreBridge.mm
WebKit/ChangeLog
WebKit/WebView.subproj/WebView.m
WebKit/WebView.subproj/WebViewPrivate.h

index ae8b88eddd59db47abb4d579818a50da5d51d782..115f04415d51f30abcdbbaaeaa980cb6ce566f6f 100644 (file)
@@ -1,3 +1,28 @@
+2004-10-27  Ken Kocienda  <kocienda@apple.com>
+
+        Reviewed by Chris
+
+        * khtml/editing/htmlediting.cpp:
+        (khtml::CompositeEditCommand::deleteSelection): Added new mergeBlocksAfterDelete flag to control
+        whether content not in the block containing the start of the selection is moved to that block
+        after the selection is deleted.
+        (khtml::DeleteSelectionCommand::DeleteSelectionCommand): Ditto.
+        (khtml::DeleteSelectionCommand::doApply): Ditto.
+        (khtml::InputNewlineInQuotedContentCommand::InputNewlineInQuotedContentCommand): New command
+        to handle the case of inserting a newline when in quoted content in Mail.
+        (khtml::InputNewlineInQuotedContentCommand::~InputNewlineInQuotedContentCommand): Ditto.
+        (khtml::InputNewlineInQuotedContentCommand::isMailBlockquote): Ditto.
+        (khtml::InputNewlineInQuotedContentCommand::isLastVisiblePositionInBlockquote): Ditto.
+        (khtml::InputNewlineInQuotedContentCommand::doApply): Ditto.
+        (khtml::TypingCommand::insertNewlineInQuotedContent): Support for new newline command.
+        (khtml::TypingCommand::doApply): Ditto.
+        (khtml::TypingCommand::preservesTypingStyle): Ditto.
+        * khtml/editing/htmlediting.h: Add new delclarations.
+        (khtml::TypingCommand::): Ditto.
+        * kwq/WebCoreBridge.h: Added new bridge method called from WebKit.
+        * kwq/WebCoreBridge.mm:
+        (-[WebCoreBridge insertNewlineInQuotedContent]): Ditto.
+
 2004-10-26  Chris Blumenberg  <cblu@apple.com>
 
        Fixed: <rdar://problem/3774243> page up/down, arrow up/down, etc in Safari RSS should scroll main content
index 01a678cb6f0d5aca5f3d91a94024912bb921959f..496e05bbd1eb66cad3284aab4cecc23d567c491a 100644 (file)
@@ -677,18 +677,18 @@ void CompositeEditCommand::replaceText(TextImpl *node, long offset, long count,
     applyCommandToComposite(insertCommand);
 }
 
-void CompositeEditCommand::deleteSelection(bool smartDelete)
+void CompositeEditCommand::deleteSelection(bool smartDelete, bool mergeBlocksAfterDelete)
 {
     if (endingSelection().isRange()) {
-        EditCommandPtr cmd(new DeleteSelectionCommand(document(), smartDelete));
+        EditCommandPtr cmd(new DeleteSelectionCommand(document(), smartDelete, mergeBlocksAfterDelete));
         applyCommandToComposite(cmd);
     }
 }
 
-void CompositeEditCommand::deleteSelection(const Selection &selection, bool smartDelete)
+void CompositeEditCommand::deleteSelection(const Selection &selection, bool smartDelete, bool mergeBlocksAfterDelete)
 {
     if (selection.isRange()) {
-        EditCommandPtr cmd(new DeleteSelectionCommand(document(), selection, smartDelete));
+        EditCommandPtr cmd(new DeleteSelectionCommand(document(), selection, smartDelete, mergeBlocksAfterDelete));
         applyCommandToComposite(cmd);
     }
 }
@@ -1243,13 +1243,13 @@ Position ApplyStyleCommand::positionInsertionPoint(Position pos)
 //------------------------------------------------------------------------------------------
 // DeleteSelectionCommand
 
-DeleteSelectionCommand::DeleteSelectionCommand(DocumentImpl *document, bool smartDelete)
-    : CompositeEditCommand(document), m_hasSelectionToDelete(false), m_smartDelete(smartDelete)
+DeleteSelectionCommand::DeleteSelectionCommand(DocumentImpl *document, bool smartDelete, bool mergeBlocksAfterDelete)
+    : CompositeEditCommand(document), m_hasSelectionToDelete(false), m_smartDelete(smartDelete), m_mergeBlocksAfterDelete(mergeBlocksAfterDelete)
 {
 }
 
-DeleteSelectionCommand::DeleteSelectionCommand(DocumentImpl *document, const Selection &selection, bool smartDelete)
-    : CompositeEditCommand(document), m_selectionToDelete(selection), m_hasSelectionToDelete(true), m_smartDelete(smartDelete)
+DeleteSelectionCommand::DeleteSelectionCommand(DocumentImpl *document, const Selection &selection, bool smartDelete, bool mergeBlocksAfterDelete)
+    : CompositeEditCommand(document), m_selectionToDelete(selection), m_hasSelectionToDelete(true), m_smartDelete(smartDelete), m_mergeBlocksAfterDelete(m_mergeBlocksAfterDelete)
 {
 }
 
@@ -1468,7 +1468,7 @@ void DeleteSelectionCommand::doApply()
     }
     
     // Do block merge if start and end of selection are in different blocks.
-    if (endBlock != startBlock && downstreamEnd.node()->inDocument()) {
+    if (m_mergeBlocksAfterDelete && endBlock != startBlock && downstreamEnd.node()->inDocument()) {
         LOG(Editing,  "merging content from end block");
         moveNodesAfterNode(downstreamEnd.node(), upstreamStart.node());
     }
@@ -1724,6 +1724,151 @@ void InputNewlineCommand::doApply()
     }
 }
 
+//------------------------------------------------------------------------------------------
+// InputNewlineInQuotedContentCommand
+
+InputNewlineInQuotedContentCommand::InputNewlineInQuotedContentCommand(DocumentImpl *document)
+    : CompositeEditCommand(document)
+{
+    ancestors.setAutoDelete(true);
+    clonedNodes.setAutoDelete(true);
+}
+
+InputNewlineInQuotedContentCommand::~InputNewlineInQuotedContentCommand()
+{
+    if (m_breakNode)
+        m_breakNode->deref();
+}
+
+bool InputNewlineInQuotedContentCommand::isMailBlockquote(const NodeImpl *node) const
+{
+    if (!node || !node->renderer() || !node->isElementNode() && node->id() != ID_BLOCKQUOTE)
+        return false;
+        
+    return static_cast<const ElementImpl *>(node)->getAttribute("type") == "cite";
+}
+
+bool InputNewlineInQuotedContentCommand::isLastVisiblePositionInBlockquote(const VisiblePosition &pos, const NodeImpl *blockquote) const
+{
+    if (pos.isNull())
+        return false;
+        
+    VisiblePosition next = pos.next();
+    return next.isNull() || !next.deepEquivalent().node()->isAncestor(blockquote);
+}
+
+void InputNewlineInQuotedContentCommand::doApply()
+{
+    Selection selection = endingSelection();
+    if (selection.isNone())
+        return;
+    
+    // Delete the current selection.
+    Position pos = selection.start();
+    if (selection.isRange()) {
+        deleteSelection(false, false);
+        pos = endingSelection().start().upstream();
+    }
+    
+    // Find the top-most blockquote from the start.
+    NodeImpl *startNode = pos.node();
+    NodeImpl *topBlockquote = 0;
+    for (NodeImpl *n = startNode->parentNode(); n; n = n->parentNode()) {
+        if (isMailBlockquote(n))
+            topBlockquote = n;
+    }
+    if (!topBlockquote || !topBlockquote->parentNode())
+        return;
+
+    // Build up list of ancestors in between the start node and the top blockquote.
+    for (NodeImpl *n = startNode->parentNode(); n && n != topBlockquote; n = n->parentNode())
+        ancestors.prepend(n);
+
+    // Insert a break after the top blockquote.
+    int exceptionCode = 0;
+    m_breakNode = document()->createHTMLElement("BR", exceptionCode);
+    m_breakNode->ref();
+    ASSERT(exceptionCode == 0);
+    insertNodeAfter(m_breakNode, topBlockquote);
+
+    if (!isLastVisiblePositionInBlockquote(VisiblePosition(pos), topBlockquote)) {
+        // Split at pos if in the middle of a text node.
+        if (startNode->isTextNode()) {
+            TextImpl *textNode = static_cast<TextImpl *>(startNode);
+            bool atEnd = (unsigned long)pos.offset() >= textNode->length();
+            if (pos.offset() > 0 && !atEnd) {
+                SplitTextNodeCommand *splitCommand = new SplitTextNodeCommand(document(), textNode, pos.offset());
+                EditCommandPtr cmd(splitCommand);
+                applyCommandToComposite(cmd);
+                startNode = splitCommand->node();
+                pos = Position(startNode, 0);
+            }
+            else if (atEnd) {
+                startNode = startNode->traverseNextNode();
+                ASSERT(startNode);
+            }
+        }
+        else if (pos.offset() > 0) {
+            startNode = startNode->traverseNextNode();
+            ASSERT(startNode);
+        }
+
+        // Insert a clone of the top blockquote after the break.
+        NodeImpl *clonedBlockquote = topBlockquote->cloneNode(false);
+        clonedNodes.append(clonedBlockquote);
+        insertNodeAfter(clonedBlockquote, m_breakNode);
+        
+        // Make clones of ancestors in between the start node and the top blockquote.
+        NodeImpl *parent = clonedBlockquote;
+        for (QPtrListIterator<NodeImpl> it(ancestors); it.current(); ++it) {
+            NodeImpl *child = it.current()->cloneNode(false); // shallow clone
+            clonedNodes.append(child);
+            appendNode(child, parent);
+            parent = child;
+        }
+
+        // Move the start node and the siblings of the start node.
+        NodeImpl *n = startNode;
+        bool startIsBR = n->id() == ID_BR;
+        if (startIsBR)
+            n = n->nextSibling();
+        while (n) {
+            NodeImpl *next = n->nextSibling();
+            removeNode(n);
+            appendNode(n, parent);
+            n = next;
+        }
+        
+        // Move everything after the start node.
+        NodeImpl *leftParent = ancestors.last();
+
+        if (!startIsBR) {
+            if (!leftParent)
+                leftParent = topBlockquote;
+            ElementImpl *b = document()->createHTMLElement("BR", exceptionCode);
+            clonedNodes.append(b);
+            ASSERT(exceptionCode == 0);
+            appendNode(b, leftParent);
+        }
+        
+        leftParent = ancestors.last();
+        while (leftParent && leftParent != topBlockquote) {
+            parent = parent->parentNode();
+            NodeImpl *n = leftParent->nextSibling();
+            while (n) {
+                NodeImpl *next = n->nextSibling();
+                removeNode(n);
+                appendNode(n, parent);
+                n = next;
+            }
+            leftParent = leftParent->parentNode();
+        }
+    }
+    
+    // Put the selection right before the break.
+    setEndingSelection(Position(m_breakNode, 0));
+}
+
 //------------------------------------------------------------------------------------------
 // InputTextCommand
 
@@ -2563,6 +2708,23 @@ void TypingCommand::insertNewline(DocumentImpl *document)
     }
 }
 
+void TypingCommand::insertNewlineInQuotedContent(DocumentImpl *document)
+{
+    ASSERT(document);
+    
+    KHTMLPart *part = document->part();
+    ASSERT(part);
+    
+    EditCommandPtr lastEditCommand = part->lastEditCommand();
+    if (isOpenForMoreTypingCommand(lastEditCommand)) {
+        static_cast<TypingCommand *>(lastEditCommand.get())->insertNewlineInQuotedContent();
+    }
+    else {
+        EditCommandPtr cmd(new TypingCommand(document, InsertNewlineInQuotedContent));
+        cmd.apply();
+    }
+}
+
 bool TypingCommand::isOpenForMoreTypingCommand(const EditCommandPtr &cmd)
 {
     return cmd.isTypingCommand() &&
@@ -2590,6 +2752,9 @@ void TypingCommand::doApply()
         case InsertNewline:
             insertNewline();
             return;
+        case InsertNewlineInQuotedContent:
+            insertNewlineInQuotedContent();
+            return;
     }
 
     ASSERT_NOT_REACHED();
@@ -2658,6 +2823,13 @@ void TypingCommand::insertNewline()
     typingAddedToOpenCommand();
 }
 
+void TypingCommand::insertNewlineInQuotedContent()
+{
+    EditCommandPtr cmd(new InputNewlineInQuotedContentCommand(document()));
+    applyCommandToComposite(cmd);
+    typingAddedToOpenCommand();
+}
+
 void TypingCommand::issueCommandForDeleteKey()
 {
     Selection selectionToDelete;
@@ -2744,6 +2916,7 @@ bool TypingCommand::preservesTypingStyle() const
             return true;
         case InsertText:
         case InsertNewline:
+        case InsertNewlineInQuotedContent:
             return false;
     }
     ASSERT_NOT_REACHED();
index 09382594bc8024f77f643942d85df96f35ede8ec..1a2d8ba72c00117e46f39dfe5e92a7bc6cec8c66 100644 (file)
@@ -27,6 +27,7 @@
 #define __htmlediting_h__
 
 #include "dom_nodeimpl.h"
+#include "qptrlist.h"
 #include "qvaluelist.h"
 #include "selection.h"
 #include "shared.h"
@@ -42,6 +43,7 @@ namespace khtml {
 
 class EditCommand;
 class Selection;
+class VisiblePosition;
 
 //------------------------------------------------------------------------------------------
 // EditCommandPtr
@@ -176,8 +178,8 @@ protected:
     void appendNode(DOM::NodeImpl *appendChild, DOM::NodeImpl *parentNode);
     void applyCommandToComposite(EditCommandPtr &);
     void deleteKeyPressed();
-    void deleteSelection(bool smartDelete=false);
-    void deleteSelection(const khtml::Selection &selection, bool smartDelete=false);
+    void deleteSelection(bool smartDelete=false, bool mergeBlocksAfterDelete=true);
+    void deleteSelection(const khtml::Selection &selection, bool smartDelete=false, bool mergeBlocksAfterDelete=true);
     void deleteText(DOM::TextImpl *node, long offset, long count);
     void inputText(const DOM::DOMString &text, bool selectInsertedText = false);
     void insertNodeAfter(DOM::NodeImpl *insertChild, DOM::NodeImpl *refChild);
@@ -265,8 +267,8 @@ private:
 class DeleteSelectionCommand : public CompositeEditCommand
 { 
 public:
-    DeleteSelectionCommand(DOM::DocumentImpl *document, bool smartDelete=false);
-    DeleteSelectionCommand(DOM::DocumentImpl *document, const khtml::Selection &selection, bool smartDelete=false);
+    DeleteSelectionCommand(DOM::DocumentImpl *document, bool smartDelete=false, bool mergeBlocksAfterDelete=true);
+    DeleteSelectionCommand(DOM::DocumentImpl *document, const khtml::Selection &selection, bool smartDelete=false, bool mergeBlocksAfterDelete=true);
        
     virtual void doApply();
     
@@ -280,6 +282,7 @@ private:
     khtml::Selection m_selectionToDelete;
     bool m_hasSelectionToDelete;
     bool m_smartDelete;
+    bool m_mergeBlocksAfterDelete;
 };
 
 //------------------------------------------------------------------------------------------
@@ -320,6 +323,26 @@ private:
     void insertNodeBeforePosition(DOM::NodeImpl *node, const DOM::Position &pos);
 };
 
+//------------------------------------------------------------------------------------------
+// InputNewlineInQuotedContentCommand
+
+class InputNewlineInQuotedContentCommand : public CompositeEditCommand
+{
+public:
+    InputNewlineInQuotedContentCommand(DOM::DocumentImpl *);
+    virtual ~InputNewlineInQuotedContentCommand();
+       
+    virtual void doApply();
+    
+private:
+    bool isMailBlockquote(const DOM::NodeImpl *) const;
+    bool isLastVisiblePositionInBlockquote(const VisiblePosition &pos, const DOM::NodeImpl *) const;
+
+    QPtrList<DOM::NodeImpl> ancestors;
+    QPtrList<DOM::NodeImpl> clonedNodes;
+    DOM::ElementImpl *m_breakNode;
+};
+
 //------------------------------------------------------------------------------------------
 // InputTextCommand
 
@@ -571,13 +594,14 @@ private:
 class TypingCommand : public CompositeEditCommand
 {
 public:
-    enum ETypingCommand { DeleteKey, InsertText, InsertNewline };
+    enum ETypingCommand { DeleteKey, InsertText, InsertNewline, InsertNewlineInQuotedContent };
 
     TypingCommand(DOM::DocumentImpl *document, ETypingCommand, const DOM::DOMString &text = "", bool selectInsertedText = false);
 
     static void deleteKeyPressed(DOM::DocumentImpl *document);
     static void insertText(DOM::DocumentImpl *document, const DOM::DOMString &text, bool selectInsertedText = false);
     static void insertNewline(DOM::DocumentImpl *document);
+    static void insertNewlineInQuotedContent(DOM::DocumentImpl *document);
     static bool isOpenForMoreTypingCommand(const EditCommandPtr &);
     static void closeTyping(const EditCommandPtr &);
     
@@ -588,6 +612,7 @@ public:
 
     void insertText(const DOM::DOMString &text, bool selectInsertedText);
     void insertNewline();
+    void insertNewlineInQuotedContent();
     void deleteKeyPressed();
 
 private:
index 61de24f27fb3631725aab6336973495956ea64a0..dca453a6b3d53a333be45b3a92828198263104d8 100644 (file)
@@ -334,6 +334,7 @@ typedef enum {
 
 - (void)insertText:(NSString *)text selectInsertedText:(BOOL)selectInsertedText;
 - (void)insertNewline;
+- (void)insertNewlineInQuotedContent;
 
 - (void)setSelectionToDragCaret;
 - (void)moveSelectionToDragCaret:(DOMDocumentFragment *)selectionFragment smartMove:(BOOL)smartMove;
index 5c7728cc8da8d5e30052ebb5c0e5b8c04aefe0cd..447479d2a1cb823885b42aa5b7212174950b27db 100644 (file)
@@ -1564,6 +1564,19 @@ static HTMLFormElementImpl *formElementFromDOMElement(DOMElement *element)
     [self ensureSelectionVisible];
 }
 
+- (void)insertNewlineInQuotedContent
+{
+    if (!_part || !_part->xmlDocImpl())
+        return;
+    
+    Selection selection(_part->selection());
+    if (selection.isNone())
+        return;
+    
+    TypingCommand::insertNewlineInQuotedContent(_part->xmlDocImpl());
+    [self ensureSelectionVisible];
+}
+
 - (void)insertText:(NSString *)text selectInsertedText:(BOOL)selectInsertedText
 {
     if (!_part || !_part->xmlDocImpl())
index 61aea3e4c9561e973b23976c9e488c4adf476e3a..aeea4fa7092356d520cd2d678fc8100af19eec4d 100644 (file)
@@ -1,3 +1,13 @@
+2004-10-27  Ken Kocienda  <kocienda@apple.com>
+
+        Reviewed by Chris
+
+        Added new SPI for Mail so it can get the behavior it needs when the user hits
+        the return key with the selection in quoted content.
+
+        * WebView.subproj/WebView.m
+        * WebView.subproj/WebViewPrivate.h
+
 2004-10-26  Chris Blumenberg  <cblu@apple.com>
 
        Fixed exception that Darin encountered in Mail.
index ea1c95d3bd2ac1fb996ec3d5081798a7953e06cf..712380cee54e980a65098b9e6ad51361a96a33f8 100644 (file)
@@ -2834,6 +2834,15 @@ FORWARD(yankAndSelect)
 
 @end
 
+@implementation WebView (WebViewEditingInMail)
+
+- (void)_insertNewlineInQuotedContent;
+{
+    [[self _bridgeForCurrentSelection] insertNewlineInQuotedContent];
+}
+
+@end
+
 @implementation WebView (WebFileInternal)
 
 - (void)_preflightSpellCheckerNow:(id)sender
index 8eab69d5e64937505e0c7e88f68e7021b84bf386..6c91c79e112c7abccda3de267409a160693003a7 100644 (file)
@@ -252,6 +252,10 @@ Could be worth adding to the API.
 - (void)_drawHeaderAndFooter;
 @end
 
+@interface WebView (WebViewEditingInMail)
+- (void)_insertNewlineInQuotedContent;
+@end
+
 @interface _WebSafeForwarder : NSObject
 {
     id target; // Non-retained.  Don't retain delegates;