Changeset 8087 in webkit
- Timestamp:
- Nov 29, 2004, 4:07:04 PM (20 years ago)
- Location:
- trunk
- Files:
-
- 20 added
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/WebCore/ChangeLog-2005-08-23
r8085 r8087 1 2004-11-29 Ken Kocienda <kocienda@apple.com> 2 3 Reviewed by Chris 4 5 Rewrite of paste code (specifically the ReplaceSelectionCommand class). Many more cases 6 are handled correctly now, including selections that span multiple blocks, and cases 7 where content on the pasteboard ends in newlines (or what appear to be newlines to a 8 user, really block ends or BRs). I also made one small, but important change in the 9 copy code to annotate the markup written to the pasteboard to support these selections 10 ending in newlines. 11 12 New header that defines a couple of constants used in copying and pasting. 13 14 * ForwardingHeaders/editing/html_interchange.h: Added. 15 * khtml/editing/html_interchange.h: Added. 16 17 Rewrite of the ReplaceSelectionCommand. There are several new helper functions, as well 18 as a new helper class, ReplacementFragment, which encapsulates information and functions 19 pertaining to a document fragment that is being inserted into a document. 20 21 * khtml/editing/htmlediting.cpp: 22 (khtml::ReplacementFragment::ReplacementFragment): 23 (khtml::ReplacementFragment::~ReplacementFragment): 24 (khtml::ReplacementFragment::firstChild): Simple accessor. 25 (khtml::ReplacementFragment::lastChild): Ditto. 26 (khtml::ReplacementFragment::mergeStartNode): Looks at the nodes in a fragment and determines 27 the starting node to use for merging into the block containing the start of the selection. 28 (khtml::ReplacementFragment::mergeEndNode): Same as above, but for the end of the selection. 29 (khtml::ReplacementFragment::pruneEmptyNodes): Simple helper. 30 (khtml::ReplacementFragment::isInterchangeNewlineComment): Determines if a node is the 31 special annotation comment added in by the copy code. 32 (khtml::ReplacementFragment::removeNode): Simple helper. 33 (khtml::isComment): Simple helper. 34 (khtml::isProbablyBlock): Determines if a node is of a type that is usually rendered as a block. 35 I would like to do better than this some day, but this check will hold us until I can do better. 36 (khtml::ReplaceSelectionCommand::ReplaceSelectionCommand): 37 (khtml::ReplaceSelectionCommand::~ReplaceSelectionCommand): 38 (khtml::ReplaceSelectionCommand::doApply): 39 (khtml::ReplaceSelectionCommand::completeHTMLReplacement): Figures out the right ending selection. 40 * khtml/editing/htmlediting.h: Declarations for the new ReplacementFragment class. 41 (khtml::ReplacementFragment::root): 42 (khtml::ReplacementFragment::type): 43 (khtml::ReplacementFragment::isEmpty): 44 (khtml::ReplacementFragment::isSingleTextNode): 45 (khtml::ReplacementFragment::isTreeFragment): 46 (khtml::ReplacementFragment::hasMoreThanOneBlock): 47 (khtml::ReplacementFragment::hasLogicalNewlineAtEnd): 48 49 This smaller set of changes markup generation to add the newline annotation described in the 50 comment at the start of this entry. 51 52 * khtml/xml/dom2_rangeimpl.cpp: 53 (DOM::RangeImpl::addCommentToHTMLMarkup): Simple helper. 54 (DOM::RangeImpl::toHTML): Added new EAnnotateForInterchange default argument to control whether 55 comment annotations are added to the markup generated. 56 * khtml/xml/dom2_rangeimpl.h: Add some new declarations. 57 * kwq/WebCoreBridge.mm: 58 (-[WebCoreBridge markupStringFromRange:nodes:]): Request that markup resulting from call to 59 DOM::RangeImpl::toHTML uses annotations when generating. 60 61 New tests. 62 63 * layout-tests/editing/pasteboard/paste-text-001-expected.txt: Added. 64 * layout-tests/editing/pasteboard/paste-text-001.html: Added. 65 * layout-tests/editing/pasteboard/paste-text-002-expected.txt: Added. 66 * layout-tests/editing/pasteboard/paste-text-002.html: Added. 67 * layout-tests/editing/pasteboard/paste-text-003-expected.txt: Added. 68 * layout-tests/editing/pasteboard/paste-text-003.html: Added. 69 * layout-tests/editing/pasteboard/paste-text-004-expected.txt: Added. 70 * layout-tests/editing/pasteboard/paste-text-004.html: Added. 71 * layout-tests/editing/pasteboard/paste-text-005-expected.txt: Added. 72 * layout-tests/editing/pasteboard/paste-text-005.html: Added. 73 * layout-tests/editing/pasteboard/paste-text-006-expected.txt: Added. 74 * layout-tests/editing/pasteboard/paste-text-006.html: Added. 75 * layout-tests/editing/pasteboard/paste-text-007-expected.txt: Added. 76 * layout-tests/editing/pasteboard/paste-text-007.html: Added. 77 * layout-tests/editing/pasteboard/paste-text-008-expected.txt: Added. 78 * layout-tests/editing/pasteboard/paste-text-008.html: Added. 79 * layout-tests/editing/pasteboard/paste-text-009-expected.txt: Added. 80 * layout-tests/editing/pasteboard/paste-text-009.html: Added. 81 1 82 2004-11-29 Ken Kocienda <kocienda@apple.com> 2 83 -
trunk/WebCore/khtml/editing/htmlediting.cpp
r8081 r8087 42 42 #include "html_elementimpl.h" 43 43 #include "html_imageimpl.h" 44 #include "html_interchange.h" 44 45 #include "htmlattrs.h" 45 46 #include "htmltags.h" … … 66 67 using DOM::DOMString; 67 68 using DOM::DOMStringImpl; 69 using DOM::DoNotStayInBlock; 68 70 using DOM::DoNotUpdateLayout; 69 71 using DOM::EditingTextImpl; 70 72 using DOM::ElementImpl; 73 using DOM::EStayInBlock; 71 74 using DOM::HTMLElementImpl; 72 75 using DOM::HTMLImageElementImpl; … … 2671 2674 // ReplaceSelectionCommand 2672 2675 2673 ReplaceSelectionCommand::ReplaceSelectionCommand(DocumentImpl *document, DocumentFragmentImpl *fragment, bool selectReplacement, bool smartReplace) 2674 : CompositeEditCommand(document), m_fragment(fragment), m_selectReplacement(selectReplacement), m_smartReplace(smartReplace) 2675 { 2676 ASSERT(m_fragment); 2676 ReplacementFragment::ReplacementFragment(DocumentFragmentImpl *fragment) 2677 : m_fragment(fragment), m_hasInterchangeNewlineComment(false), m_hasMoreThanOneBlock(false) 2678 { 2679 if (!m_fragment) { 2680 m_type = EmptyFragment; 2681 return; 2682 } 2683 2677 2684 m_fragment->ref(); 2678 } 2679 2680 ReplaceSelectionCommand::~ReplaceSelectionCommand() 2681 { 2682 ASSERT(m_fragment); 2683 m_fragment->deref(); 2684 } 2685 2686 void ReplaceSelectionCommand::doApply() 2687 { 2685 2688 2686 NodeImpl *firstChild = m_fragment->firstChild(); 2689 2687 NodeImpl *lastChild = m_fragment->lastChild(); 2690 2688 2689 if (!firstChild) { 2690 m_type = EmptyFragment; 2691 return; 2692 } 2693 2694 if (firstChild == lastChild && firstChild->isTextNode()) { 2695 m_type = SingleTextNodeFragment; 2696 return; 2697 } 2698 2699 m_type = TreeFragment; 2700 2701 NodeImpl *node = firstChild; 2702 int blockCount = 0; 2703 NodeImpl *commentToDelete = 0; 2704 while (node) { 2705 NodeImpl *next = node->traverseNextNode(); 2706 if (isInterchangeNewlineComment(node)) { 2707 m_hasInterchangeNewlineComment = true; 2708 commentToDelete = node; 2709 } 2710 else if (isProbablyBlock(node)) 2711 blockCount++; 2712 node = next; 2713 } 2714 2715 if (commentToDelete) 2716 removeNode(commentToDelete); 2717 2718 firstChild = m_fragment->firstChild(); 2719 lastChild = m_fragment->lastChild(); 2720 if (!isProbablyBlock(firstChild)) 2721 blockCount++; 2722 if (!isProbablyBlock(lastChild) && firstChild != lastChild) 2723 blockCount++; 2724 2725 if (blockCount > 1) 2726 m_hasMoreThanOneBlock = true; 2727 } 2728 2729 ReplacementFragment::~ReplacementFragment() 2730 { 2731 if (m_fragment) 2732 m_fragment->deref(); 2733 } 2734 2735 NodeImpl *ReplacementFragment::firstChild() const 2736 { 2737 return m_fragment->firstChild(); 2738 } 2739 2740 NodeImpl *ReplacementFragment::lastChild() const 2741 { 2742 return m_fragment->lastChild(); 2743 } 2744 2745 NodeImpl *ReplacementFragment::mergeStartNode() const 2746 { 2747 NodeImpl *node = m_fragment->firstChild(); 2748 while (node) { 2749 NodeImpl *next = node->traverseNextNode(); 2750 if (!isProbablyBlock(node)) 2751 return node; 2752 node = next; 2753 } 2754 return 0; 2755 } 2756 2757 NodeImpl *ReplacementFragment::mergeEndNode() const 2758 { 2759 NodeImpl *node = m_fragment->lastChild(); 2760 while (node && node->lastChild()) 2761 node = node->lastChild(); 2762 while (node) { 2763 NodeImpl *prev = node->traversePreviousNode(); 2764 if (!isProbablyBlock(node)) { 2765 NodeImpl *previousSibling = node->previousSibling(); 2766 while (1) { 2767 if (!previousSibling || isProbablyBlock(previousSibling)) 2768 return node; 2769 node = previousSibling; 2770 } 2771 } 2772 node = prev; 2773 } 2774 return 0; 2775 } 2776 2777 void ReplacementFragment::pruneEmptyNodes() 2778 { 2779 bool run = true; 2780 while (run) { 2781 run = false; 2782 NodeImpl *node = m_fragment->firstChild(); 2783 while (node) { 2784 if ((node->isTextNode() && static_cast<TextImpl *>(node)->length() == 0) || 2785 (isProbablyBlock(node) && node->childNodeCount() == 0)) { 2786 NodeImpl *next = node->traverseNextSibling(); 2787 removeNode(node); 2788 node = next; 2789 run = true; 2790 } 2791 else { 2792 node = node->traverseNextNode(); 2793 } 2794 } 2795 } 2796 } 2797 2798 bool ReplacementFragment::isInterchangeNewlineComment(const NodeImpl *node) 2799 { 2800 return isComment(node) && node->nodeValue() == KHTMLInterchangeNewline; 2801 } 2802 2803 void ReplacementFragment::removeNode(NodeImpl *node) 2804 { 2805 if (!node) 2806 return; 2807 2808 NodeImpl *parent = node->parentNode(); 2809 if (!parent) 2810 return; 2811 2812 int exceptionCode = 0; 2813 parent->removeChild(node, exceptionCode); 2814 ASSERT(exceptionCode == 0); 2815 } 2816 2817 bool isComment(const NodeImpl *node) 2818 { 2819 return node && node->nodeType() == Node::COMMENT_NODE; 2820 } 2821 2822 bool isProbablyBlock(const NodeImpl *node) 2823 { 2824 if (!node) 2825 return false; 2826 2827 switch (node->id()) { 2828 case ID_BLOCKQUOTE: 2829 case ID_DD: 2830 case ID_DIV: 2831 case ID_DL: 2832 case ID_DT: 2833 case ID_H1: 2834 case ID_H2: 2835 case ID_H3: 2836 case ID_H4: 2837 case ID_H5: 2838 case ID_H6: 2839 case ID_HR: 2840 case ID_LI: 2841 case ID_OL: 2842 case ID_P: 2843 case ID_PRE: 2844 case ID_TD: 2845 case ID_TH: 2846 case ID_TR: 2847 case ID_UL: 2848 return true; 2849 } 2850 2851 return false; 2852 } 2853 2854 ReplaceSelectionCommand::ReplaceSelectionCommand(DocumentImpl *document, DocumentFragmentImpl *fragment, bool selectReplacement, bool smartReplace) 2855 : CompositeEditCommand(document), 2856 m_fragment(fragment), 2857 m_selectReplacement(selectReplacement), 2858 m_smartReplace(smartReplace) 2859 { 2860 } 2861 2862 ReplaceSelectionCommand::~ReplaceSelectionCommand() 2863 { 2864 } 2865 2866 void ReplaceSelectionCommand::doApply() 2867 { 2691 2868 Selection selection = endingSelection(); 2869 VisiblePosition visibleStart(selection.start()); 2870 VisiblePosition visibleEnd(selection.end()); 2871 bool startAtStartOfLine = isFirstVisiblePositionOnLine(visibleStart); 2872 bool startAtStartOfBlock = isFirstVisiblePositionInBlock(visibleStart); 2873 bool startAtEndOfBlock = isLastVisiblePositionInBlock(visibleStart); 2874 bool startAtBlockBoundary = startAtStartOfBlock || startAtEndOfBlock; 2875 NodeImpl *startBlock = selection.start().node()->enclosingBlockFlowElement(); 2876 NodeImpl *endBlock = selection.end().node()->enclosingBlockFlowElement(); 2877 2878 bool mergeStart = !(startAtStartOfLine && (m_fragment.hasInterchangeNewlineComment() || m_fragment.hasMoreThanOneBlock())); 2879 bool mergeEnd = !m_fragment.hasInterchangeNewlineComment() && m_fragment.hasMoreThanOneBlock(); 2880 Position startPos = Position(selection.start().node()->enclosingBlockFlowElement(), 0); 2881 Position endPos; 2882 EStayInBlock upstreamStayInBlock = StayInBlock; 2692 2883 2693 2884 // Delete the current selection, or collapse whitespace, as needed 2694 if (selection.isRange()) 2695 deleteSelection(); 2696 2697 KHTMLPart *part = document()->part(); 2698 ASSERT(part); 2885 if (selection.isRange()) { 2886 deleteSelection(false, !(m_fragment.hasInterchangeNewlineComment() || m_fragment.hasMoreThanOneBlock())); 2887 } 2888 else if (selection.isCaret() && mergeEnd && !startAtBlockBoundary) { 2889 // The start and the end need to wind up in separate blocks. 2890 // Insert a paragraph separator to make that happen. 2891 insertParagraphSeparator(); 2892 upstreamStayInBlock = DoNotStayInBlock; 2893 } 2894 2895 selection = endingSelection(); 2896 if (!startAtBlockBoundary || !startPos.node()->inDocument()) 2897 startPos = selection.start().upstream(upstreamStayInBlock); 2898 endPos = selection.end().downstream(); 2699 2899 2700 2900 // This command does not use any typing style that is set as a residual effect of … … 2702 2902 // FIXME: Improve typing style. 2703 2903 // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement 2904 KHTMLPart *part = document()->part(); 2704 2905 part->clearTypingStyle(); 2705 2906 setTypingStyle(0); 2706 2707 selection = endingSelection();2708 ASSERT(selection.isCaret());2709 2907 2908 if (!m_fragment.firstChild()) 2909 return; 2910 2710 2911 // Now that we are about to add content, check to see if a placeholder element 2711 2912 // can be removed. 2712 Position pos = selection.start(); 2713 NodeImpl *block = pos.node()->enclosingBlockFlowElement(); 2913 NodeImpl *block = startPos.node()->enclosingBlockFlowElement(); 2714 2914 if (removeBlockPlaceholderIfNeeded(block)) { 2715 pos = Position(block, 0);2915 startPos = Position(block, 0); 2716 2916 } 2717 2917 … … 2719 2919 bool addTrailingSpace = false; 2720 2920 if (m_smartReplace) { 2721 addLeadingSpace = pos.leadingWhitespacePosition().isNull();2722 addTrailingSpace = pos.trailingWhitespacePosition().isNull();2921 addLeadingSpace = startPos.leadingWhitespacePosition().isNull(); 2922 addTrailingSpace = endPos.trailingWhitespacePosition().isNull(); 2723 2923 } 2724 2924 2725 2925 #if APPLE_CHANGES 2726 2926 if (addLeadingSpace) { 2727 QChar previousChar = VisiblePosition( pos).previous().character();2927 QChar previousChar = VisiblePosition(startPos).previous().character(); 2728 2928 if (!previousChar.isNull()) { 2729 2929 addLeadingSpace = !KWQ(part)->isCharacterSmartReplaceExempt(previousChar, true); … … 2731 2931 } 2732 2932 if (addTrailingSpace) { 2733 QChar thisChar = VisiblePosition( pos).character();2933 QChar thisChar = VisiblePosition(endPos).character(); 2734 2934 if (!thisChar.isNull()) { 2735 2935 addTrailingSpace = !KWQ(part)->isCharacterSmartReplaceExempt(thisChar, false); … … 2737 2937 } 2738 2938 #endif 2739 2740 if (!firstChild) { 2741 // Pasting something that didn't parse or was empty. 2742 ASSERT(!lastChild); 2743 } else if (firstChild == lastChild && firstChild->isTextNode()) { 2744 // FIXME: HTML fragment case needs to be improved to the point 2745 // where we can remove this separate case. 2746 2747 // Simple text paste. Treat as if the text were typed. 2748 Position upstreamStart(pos.upstream(StayInBlock)); 2749 DOMString text = static_cast<TextImpl *>(firstChild)->data(); 2750 if (addLeadingSpace) { 2751 text = " " + text; 2752 } 2753 if (addTrailingSpace) { 2754 text += " "; 2755 } 2756 inputText(text, m_selectReplacement); 2939 2940 document()->updateLayout(); 2941 2942 NodeImpl *refBlock = startPos.node()->enclosingBlockFlowElement(); 2943 Position insertionPos = startPos; 2944 bool insertBlocksBefore = true; 2945 2946 NodeImpl *firstNodeInserted = 0; 2947 NodeImpl *lastNodeInserted = 0; 2948 bool lastNodeInsertedInMergeEnd = false; 2949 2950 // prune empty nodes from fragment 2951 m_fragment.pruneEmptyNodes(); 2952 2953 // Merge content into the end block, if necessary. 2954 if (mergeEnd) { 2955 NodeImpl *node = m_fragment.mergeEndNode(); 2956 if (node) { 2957 NodeImpl *refNode = node; 2958 NodeImpl *node = refNode ? refNode->nextSibling() : 0; 2959 insertNodeAt(refNode, endPos.node(), endPos.offset()); 2960 firstNodeInserted = refNode; 2961 lastNodeInserted = refNode; 2962 while (node && !isProbablyBlock(node)) { 2963 NodeImpl *next = node->nextSibling(); 2964 insertNodeAfter(node, refNode); 2965 lastNodeInserted = node; 2966 refNode = node; 2967 node = next; 2968 } 2969 lastNodeInsertedInMergeEnd = true; 2970 } 2971 } 2972 2973 // prune empty nodes from fragment 2974 m_fragment.pruneEmptyNodes(); 2975 2976 // Merge content into the start block, if necessary. 2977 if (mergeStart) { 2978 NodeImpl *node = m_fragment.mergeStartNode(); 2979 NodeImpl *insertionNode = 0; 2980 if (node) { 2981 NodeImpl *refNode = node; 2982 NodeImpl *node = refNode ? refNode->nextSibling() : 0; 2983 insertNodeAt(refNode, startPos.node(), startPos.offset()); 2984 firstNodeInserted = refNode; 2985 if (!lastNodeInsertedInMergeEnd) 2986 lastNodeInserted = refNode; 2987 insertionNode = refNode; 2988 while (node && !isProbablyBlock(node)) { 2989 NodeImpl *next = node->nextSibling(); 2990 insertNodeAfter(node, refNode); 2991 if (!lastNodeInsertedInMergeEnd) 2992 lastNodeInserted = node; 2993 insertionNode = node; 2994 refNode = node; 2995 node = next; 2996 } 2997 } 2998 if (insertionNode) 2999 insertionPos = Position(insertionNode, insertionNode->caretMaxOffset()); 3000 insertBlocksBefore = false; 3001 } 3002 3003 // prune empty nodes from fragment 3004 m_fragment.pruneEmptyNodes(); 3005 3006 // Merge everything remaining. 3007 NodeImpl *node = m_fragment.firstChild(); 3008 if (node) { 3009 NodeImpl *refNode = node; 3010 NodeImpl *node = refNode ? refNode->nextSibling() : 0; 3011 if (isProbablyBlock(refNode) && (insertBlocksBefore || startAtStartOfBlock)) { 3012 insertNodeBefore(refNode, refBlock); 3013 } 3014 else if (isProbablyBlock(refNode) && startAtEndOfBlock) { 3015 insertNodeAfter(refNode, refBlock); 3016 } 3017 else { 3018 insertNodeAt(refNode, insertionPos.node(), insertionPos.offset()); 3019 } 3020 if (!firstNodeInserted) 3021 firstNodeInserted = refNode; 3022 if (!lastNodeInsertedInMergeEnd) 3023 lastNodeInserted = refNode; 3024 while (node) { 3025 NodeImpl *next = node->nextSibling(); 3026 insertNodeAfter(node, refNode); 3027 if (!lastNodeInsertedInMergeEnd) 3028 lastNodeInserted = node; 3029 refNode = node; 3030 node = next; 3031 } 3032 insertionPos = Position(lastNodeInserted, lastNodeInserted->caretMaxOffset()); 3033 } 3034 3035 if (m_fragment.hasInterchangeNewlineComment()) { 3036 if (startBlock == endBlock && !isProbablyBlock(lastNodeInserted)) { 3037 setEndingSelection(insertionPos); 3038 insertParagraphSeparator(); 3039 endPos = endingSelection().end().downstream(); 3040 } 3041 completeHTMLReplacement(startPos, endPos); 3042 } 3043 else { 3044 completeHTMLReplacement(firstNodeInserted, lastNodeInserted); 3045 } 3046 } 3047 3048 void ReplaceSelectionCommand::completeHTMLReplacement(const Position &start, const Position &end) 3049 { 3050 if (start.isNull() || !start.node()->inDocument() || end.isNull() || !end.node()->inDocument()) 3051 return; 3052 m_selectReplacement ? setEndingSelection(Selection(start, end)) : setEndingSelection(end); 3053 } 3054 3055 void ReplaceSelectionCommand::completeHTMLReplacement(NodeImpl *firstNodeInserted, NodeImpl *lastNodeInserted) 3056 { 3057 if (!firstNodeInserted || !firstNodeInserted->inDocument() || 3058 !lastNodeInserted || !lastNodeInserted->inDocument()) 3059 return; 3060 3061 // Find the last leaf. 3062 NodeImpl *lastLeaf = lastNodeInserted; 3063 while (1) { 3064 NodeImpl *nextChild = lastLeaf->lastChild(); 3065 if (!nextChild) 3066 break; 3067 lastLeaf = nextChild; 3068 } 3069 3070 // Find the first leaf. 3071 NodeImpl *firstLeaf = firstNodeInserted; 3072 while (1) { 3073 NodeImpl *nextChild = firstLeaf->firstChild(); 3074 if (!nextChild) 3075 break; 3076 firstLeaf = nextChild; 3077 } 3078 3079 Position start(firstLeaf, firstLeaf->caretMinOffset()); 3080 Position end(lastLeaf, lastLeaf->caretMaxOffset()); 3081 Selection replacementSelection(start, end); 3082 if (m_selectReplacement) { 3083 // Select what was inserted. 3084 setEndingSelection(replacementSelection); 2757 3085 } 2758 3086 else { 2759 // HTML fragment paste. 2760 2761 // FIXME: Add leading and trailing spaces to the fragment? 2762 // Or just insert them as we insert it? 2763 2764 NodeImpl *beforeNode = firstChild; 2765 NodeImpl *node = firstChild->nextSibling(); 2766 2767 insertNodeAt(firstChild, pos.node(), pos.offset()); 2768 2769 // Insert the nodes from the fragment 2770 while (node) { 2771 NodeImpl *next = node->nextSibling(); 2772 insertNodeAfter(node, beforeNode); 2773 beforeNode = node; 2774 node = next; 2775 } 2776 ASSERT(beforeNode); 2777 2778 // Find the last leaf. 2779 NodeImpl *lastLeaf = lastChild; 2780 while (1) { 2781 NodeImpl *nextChild = lastLeaf->lastChild(); 2782 if (!nextChild) 2783 break; 2784 lastLeaf = nextChild; 2785 } 2786 2787 // Find the first leaf. 2788 NodeImpl *firstLeaf = firstChild; 2789 while (1) { 2790 NodeImpl *nextChild = firstLeaf->firstChild(); 2791 if (!nextChild) 2792 break; 2793 firstLeaf = nextChild; 2794 } 2795 2796 Selection replacementSelection(Position(firstLeaf, firstLeaf->caretMinOffset()), Position(lastLeaf, lastLeaf->caretMaxOffset())); 2797 if (m_selectReplacement) { 2798 // Select what was inserted. 2799 setEndingSelection(replacementSelection); 2800 } 2801 else { 2802 // Place the cursor after what was inserted, and mark misspellings in the inserted content. 2803 selection = Selection(Position(lastLeaf, lastLeaf->caretMaxOffset())); 2804 setEndingSelection(selection); 2805 } 3087 // Place the cursor after what was inserted, and mark misspellings in the inserted content. 3088 setEndingSelection(end); 2806 3089 } 2807 3090 } -
trunk/WebCore/khtml/editing/htmlediting.h
r8080 r8087 482 482 // ReplaceSelectionCommand 483 483 484 // --- ReplacementFragment helper class 485 486 class ReplacementFragment 487 { 488 public: 489 ReplacementFragment(DOM::DocumentFragmentImpl *fragment); 490 ~ReplacementFragment(); 491 492 enum EFragmentType { EmptyFragment, SingleTextNodeFragment, TreeFragment }; 493 494 DOM::DocumentFragmentImpl *root() const { return m_fragment; } 495 DOM::NodeImpl *firstChild() const; 496 DOM::NodeImpl *lastChild() const; 497 498 DOM::NodeImpl *mergeStartNode() const; 499 DOM::NodeImpl *mergeEndNode() const; 500 501 void pruneEmptyNodes(); 502 503 EFragmentType type() const { return m_type; } 504 bool isEmpty() const { return m_type == EmptyFragment; } 505 bool isSingleTextNode() const { return m_type == SingleTextNodeFragment; } 506 bool isTreeFragment() const { return m_type == TreeFragment; } 507 508 bool hasMoreThanOneBlock() const { return m_hasMoreThanOneBlock; } 509 bool hasInterchangeNewlineComment() const { return m_hasInterchangeNewlineComment; } 510 511 private: 512 // no copy construction or assignment 513 ReplacementFragment(const ReplacementFragment &); 514 ReplacementFragment &operator=(const ReplacementFragment &); 515 516 static bool isInterchangeNewlineComment(const DOM::NodeImpl *); 517 void removeNode(DOM::NodeImpl *); 518 519 EFragmentType m_type; 520 DOM::DocumentFragmentImpl *m_fragment; 521 bool m_hasInterchangeNewlineComment; 522 bool m_hasMoreThanOneBlock; 523 }; 524 525 // free-floating helper functions 526 bool isProbablyBlock(const DOM::NodeImpl *); 527 bool isComment(const DOM::NodeImpl *); 528 484 529 class ReplaceSelectionCommand : public CompositeEditCommand 485 530 { … … 491 536 492 537 private: 493 DOM::DocumentFragmentImpl *m_fragment; 538 void completeHTMLReplacement(const DOM::Position &, const DOM::Position &); 539 void completeHTMLReplacement(DOM::NodeImpl *, DOM::NodeImpl *); 540 541 ReplacementFragment m_fragment; 494 542 bool m_selectReplacement; 495 543 bool m_smartReplace; -
trunk/WebCore/khtml/xml/dom2_rangeimpl.cpp
r7981 r8087 31 31 #include "html/html_elementimpl.h" 32 32 #include "misc/htmltags.h" 33 #include "editing/visible_position.h" 33 34 #include "editing/visible_text.h" 34 35 #include "xml/dom_position.h" … … 827 828 } 828 829 829 DOMString RangeImpl::toHTML(QPtrList<NodeImpl> *nodes) const 830 void RangeImpl::addCommentToHTMLMarkup(const DOMString &comment, QStringList &markups, EAddToMarkup appendOrPrepend) const 831 { 832 if (!m_ownerDocument) 833 return; 834 835 CommentImpl *n = new CommentImpl(m_ownerDocument, comment); 836 n->ref(); 837 switch (appendOrPrepend) { 838 case PrependToMarkup: 839 markups.prepend(n->startMarkup(this)); 840 break; 841 case AppendToMarkup: 842 markups.append(n->startMarkup(this)); 843 break; 844 } 845 n->deref(); 846 } 847 848 DOMString RangeImpl::toHTML(QPtrList<NodeImpl> *nodes, EAnnotateForInterchange annotate) const 830 849 { 831 850 int exceptionCode; … … 848 867 for (NodeImpl *n = startNode(); n != pastEnd; n = next) { 849 868 next = n->traverseNextNode(); 869 if (!n->renderer()) 870 continue; 871 872 if (n->isBlockFlow() && next == pastEnd) { 873 // Don't write out an empty block. 874 continue; 875 } 850 876 851 877 // Add the node to the markup. … … 861 887 862 888 // Check if the node is the last leaf of a tree. 863 if (n->nextSibling() == 0 ) {889 if (n->nextSibling() == 0 || next == pastEnd) { 864 890 if (!ancestorsToClose.isEmpty()) { 865 891 // Close up the ancestors. … … 918 944 } 919 945 946 if (annotate) { 947 Position pos(m_endContainer, m_endOffset); 948 NodeImpl *block = pos.node()->enclosingBlockFlowElement(); 949 NodeImpl *upstreamBlock = pos.upstream().node()->enclosingBlockFlowElement(); 950 if (block != upstreamBlock) { 951 addCommentToHTMLMarkup(KHTMLInterchangeNewline, markups, AppendToMarkup); 952 } 953 } 954 920 955 return markups.join(""); 921 956 } -
trunk/WebCore/khtml/xml/dom2_rangeimpl.h
r7981 r8087 30 30 #include <qptrlist.h> 31 31 #include "dom/dom2_range.h" 32 #include "editing/html_interchange.h" 32 33 #include "misc/shared.h" 34 35 class QStringList; 33 36 34 37 namespace DOM { … … 70 73 void insertNode( NodeImpl *newNode, int &exceptioncode ); 71 74 DOMString toString ( int &exceptioncode ) const; 72 DOMString toHTML(QPtrList<NodeImpl> *nodes=NULL ) const;75 DOMString toHTML(QPtrList<NodeImpl> *nodes=NULL, EAnnotateForInterchange annotate=DoNotAnnotateForInterchange) const; 73 76 DOMString text() const; 74 77 … … 120 123 void checkDeleteExtract(int &exceptioncode); 121 124 bool containedByReadOnly() const; 125 126 enum EAddToMarkup { PrependToMarkup, AppendToMarkup }; 127 void addCommentToHTMLMarkup(const DOMString &, QStringList &, EAddToMarkup) const; 122 128 }; 123 129 -
trunk/WebCore/kwq/WebCoreBridge.mm
r8085 r8087 552 552 nodeList = new QPtrList<NodeImpl>; 553 553 } 554 NSString *markupString = [range _rangeImpl]->toHTML(nodeList ).string().getNSString();554 NSString *markupString = [range _rangeImpl]->toHTML(nodeList, AnnotateForInterchange).string().getNSString(); 555 555 if (nodes) { 556 556 *nodes = [self nodesFromList:nodeList];
Note:
See TracChangeset
for help on using the changeset viewer.