ec17d7aaf41f4e0d8a31ecf9a61ed75345d3009c
[WebKit-https.git] / WebCore / khtml / editing / rebalance_whitespace_command.cpp
1 /*
2  * Copyright (C) 2005 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 "rebalance_whitespace_command.h"
27
28 #include "htmlediting.h"
29 #include "visible_text.h"
30 #include "xml/dom_docimpl.h"
31 #include "xml/dom_textimpl.h"
32
33 #if APPLE_CHANGES
34 #include <kxmlcore/Assertions.h>
35 #else
36 #define ASSERT(assertion) assert(assertion)
37 #endif
38
39 using DOM::DOMString;
40 using DOM::DocumentImpl;
41 using DOM::Position;
42 using DOM::TextImpl;
43
44 namespace khtml {
45
46 RebalanceWhitespaceCommand::RebalanceWhitespaceCommand(DocumentImpl *document, const Position &pos)
47     : EditCommand(document), m_position(pos), m_upstreamOffset(InvalidOffset), m_downstreamOffset(InvalidOffset)
48 {
49 }
50
51 RebalanceWhitespaceCommand::~RebalanceWhitespaceCommand()
52 {
53 }
54
55 static inline bool isNBSP(const QChar &c)
56 {
57     return c.unicode() == 0xa0;
58 }
59
60 void RebalanceWhitespaceCommand::doApply()
61 {
62     static DOMString space(" ");
63
64     if (m_position.isNull() || !m_position.node()->isTextNode())
65         return;
66         
67     TextImpl *textNode = static_cast<TextImpl *>(m_position.node());
68     DOMString text = textNode->data();
69     if (text.length() == 0)
70         return;
71     
72     // find upstream offset
73     int upstream = m_position.offset();
74     while (upstream > 0 && isCollapsibleWhitespace(text[upstream - 1]) || isNBSP(text[upstream - 1])) {
75         upstream--;
76         m_upstreamOffset = upstream;
77     }
78
79     // find downstream offset
80     int downstream = m_position.offset();
81     while ((unsigned)downstream < text.length() && isCollapsibleWhitespace(text[downstream]) || isNBSP(text[downstream])) {
82         downstream++;
83         m_downstreamOffset = downstream;
84     }
85
86     if (m_upstreamOffset == InvalidOffset && m_downstreamOffset == InvalidOffset)
87         return;
88         
89     m_upstreamOffset = upstream;
90     m_downstreamOffset = downstream;
91     int length = m_downstreamOffset - m_upstreamOffset;
92     
93     m_beforeString = text.substring(m_upstreamOffset, length);
94     
95     // The following loop figures out a "rebalanced" whitespace string for any length
96     // string, and takes into account the special cases that need to handled for the
97     // start and end of strings (i.e. first and last character must be an nbsp.
98     int i = m_upstreamOffset;
99     while (i < m_downstreamOffset) {
100         int add = (m_downstreamOffset - i) % 3;
101         switch (add) {
102             case 0:
103                 m_afterString += nonBreakingSpaceString();
104                 m_afterString += space;
105                 m_afterString += nonBreakingSpaceString();
106                 add = 3;
107                 break;
108             case 1:
109                 if (i == 0 || (unsigned)i + 1 == text.length()) // at start or end of string
110                     m_afterString += nonBreakingSpaceString();
111                 else
112                     m_afterString += space;
113                 break;
114             case 2:
115                 if ((unsigned)i + 2 == text.length()) {
116                      // at end of string
117                     m_afterString += nonBreakingSpaceString();
118                     m_afterString += nonBreakingSpaceString();
119                 }
120                 else {
121                     m_afterString += nonBreakingSpaceString();
122                     m_afterString += space;
123                 }
124                 break;
125         }
126         i += add;
127     }
128     
129     text.remove(m_upstreamOffset, length);
130     text.insert(m_afterString, m_upstreamOffset);
131 }
132
133 void RebalanceWhitespaceCommand::doUnapply()
134 {
135     if (m_upstreamOffset == InvalidOffset && m_downstreamOffset == InvalidOffset)
136         return;
137     
138     ASSERT(m_position.node()->isTextNode());
139     TextImpl *textNode = static_cast<TextImpl *>(m_position.node());
140     DOMString text = textNode->data();
141     text.remove(m_upstreamOffset, m_afterString.length());
142     text.insert(m_beforeString, m_upstreamOffset);
143 }
144
145 bool RebalanceWhitespaceCommand::preservesTypingStyle() const
146 {
147     return true;
148 }
149
150 } // namespace khtml
151