WebCore:
[WebKit-https.git] / WebCore / editing / Editor.cpp
1 /*
2  * Copyright (C) 2006 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 "config.h"
27 #include "Editor.h"
28
29 #include "ApplyStyleCommand.h"
30 #include "CSSComputedStyleDeclaration.h"
31 #include "DeleteButtonController.h"
32 #include "DeleteSelectionCommand.h"
33 #include "Document.h"
34 #include "DocumentFragment.h"
35 #include "EditCommand.h"
36 #include "Editor.h"
37 #include "EditorClient.h"
38 #include "HTMLElement.h"
39 #include "HTMLNames.h"
40 #include "HitTestResult.h"
41 #include "Range.h"
42 #include "ReplaceSelectionCommand.h"
43 #include "SelectionController.h"
44 #include "Sound.h"
45 #include "htmlediting.h"
46 #include "markup.h"
47
48 namespace WebCore {
49
50 using namespace HTMLNames;
51
52 // implement as platform-specific
53 static Pasteboard generalPasteboard()
54 {
55     return 0;
56 }
57
58 EditorClient* Editor::client() const
59 {
60     return m_client.get();
61 }
62
63 bool Editor::canCopy()
64 {
65     return false;
66 }
67
68 bool Editor::canCut()
69 {
70     return false;
71 }
72
73 bool Editor::canDelete()
74 {
75     return false;
76 }
77
78 bool Editor::canDeleteRange(Range* range)
79 {
80     ExceptionCode ec = 0;
81     Node* startContainer = range->startContainer(ec);
82     Node* endContainer = range->endContainer(ec);
83     if (!startContainer || !endContainer)
84         return false;
85     
86     if (!startContainer->isContentEditable() || !endContainer->isContentEditable())
87         return false;
88     
89     if (range->collapsed(ec)) {
90         VisiblePosition start(startContainer, range->startOffset(ec), DOWNSTREAM);
91         VisiblePosition previous = start.previous();
92         // FIXME: We sometimes allow deletions at the start of editable roots, like when the caret is in an empty list item.
93         if (previous.isNull() || previous.deepEquivalent().node()->rootEditableElement() != startContainer->rootEditableElement())
94             return false;
95     }
96     return true;
97 }
98
99 bool Editor::canPaste()
100 {
101     return false;
102 }
103
104 bool Editor::canSmartCopyOrDelete()
105 {
106     return false;
107 }
108
109 void Editor::deleteSelection()
110 {
111 }
112
113 void Editor::deleteSelectionWithSmartDelete(bool smartDelete)
114 {
115     if (!m_frame->hasSelection())
116         return;
117     
118     applyCommand(new DeleteSelectionCommand(m_frame->document(), smartDelete));
119 }
120
121 bool Editor::isSelectionRichlyEditable()
122 {
123     return false;
124 }
125
126 void Editor::pasteAsPlainTextWithPasteboard(Pasteboard pasteboard)
127 {
128 }
129
130 void Editor::pasteWithPasteboard(Pasteboard pasteboard, bool allowPlainText)
131 {
132 }
133
134 Range* Editor::selectedRange()
135 {
136     return 0;
137 }
138
139 bool Editor::shouldDeleteRange(Range* range)
140 {
141     ExceptionCode ec;
142     if (!range || range->collapsed(ec))
143         return false;
144     
145     if (!canDeleteRange(range))
146         return false;
147
148     return m_client->shouldDeleteRange(range);
149  }
150
151 bool Editor::tryDHTMLCopy()
152 {
153     bool handled = false;
154     return handled;
155 }
156
157 bool Editor::tryDHTMLCut()
158 {
159     bool handled = false;
160     return handled;
161 }
162
163 bool Editor::tryDHTMLPaste()
164 {
165     bool handled = false;
166     return handled;
167 }
168
169 void Editor::writeSelectionToPasteboard(Pasteboard pasteboard)
170 {
171 }
172
173 bool Editor::shouldShowDeleteInterface(HTMLElement* element)
174 {
175     return m_client->shouldShowDeleteInterface(element);
176 }
177
178 void Editor::respondToChangedSelection(const Selection& oldSelection)
179 {
180     m_deleteButtonController->respondToChangedSelection(oldSelection);
181 }
182
183 void Editor::respondToChangedContents()
184 {
185     m_deleteButtonController->respondToChangedContents();
186 }
187
188 Frame::TriState Editor::selectionUnorderedListState() const
189 {
190     if (m_frame->selectionController()->isCaret()) {
191         Node* selectionNode = m_frame->selectionController()->selection().start().node();
192         if (enclosingNodeWithTag(selectionNode, ulTag))
193             return Frame::trueTriState;
194     } else if (m_frame->selectionController()->isRange()) {
195         Node* startNode = enclosingNodeWithTag(m_frame->selectionController()->selection().start().node(), ulTag);
196         Node* endNode = enclosingNodeWithTag(m_frame->selectionController()->selection().end().node(), ulTag);
197         if (startNode && endNode && startNode == endNode)
198             return Frame::trueTriState;
199     }
200
201     return Frame::falseTriState;
202 }
203
204 Frame::TriState Editor::selectionOrderedListState() const
205 {
206     if (m_frame->selectionController()->isCaret()) {
207         Node* selectionNode = m_frame->selectionController()->selection().start().node();
208         if (enclosingNodeWithTag(selectionNode, olTag))
209             return Frame::trueTriState;
210     } else if (m_frame->selectionController()->isRange()) {
211         Node* startNode = enclosingNodeWithTag(m_frame->selectionController()->selection().start().node(), olTag);
212         Node* endNode = enclosingNodeWithTag(m_frame->selectionController()->selection().end().node(), olTag);
213         if (startNode && endNode && startNode == endNode)
214             return Frame::trueTriState;
215     }
216
217     return Frame::falseTriState;
218 }
219
220 void Editor::removeFormattingAndStyle()
221 {
222     Document* document = frame()->document();
223     
224     // Make a plain text string from the selection to remove formatting like tables and lists.
225     RefPtr<DocumentFragment> text = createFragmentFromText(frame()->selectionController()->toRange().get(), frame()->selectionController()->toString());
226     
227     // Put the fragment made from that string into a style span with the document's
228     // default style to make sure that it is unstyled regardless of where it is inserted.
229     Position pos(document->documentElement(), 0);
230     RefPtr<CSSComputedStyleDeclaration> computedStyle = pos.computedStyle();
231     RefPtr<CSSMutableStyleDeclaration> defaultStyle = computedStyle->copyInheritableProperties();
232     
233     RefPtr<Element> span = createStyleSpanElement(document);
234     span->setAttribute(styleAttr, defaultStyle->cssText());
235     
236     ExceptionCode ec;
237     
238     while (text->lastChild())
239         span->appendChild(text->lastChild(), ec);
240     
241     RefPtr<DocumentFragment> fragment = new DocumentFragment(document);
242     fragment->appendChild(span, ec);
243     
244     applyCommand(new ReplaceSelectionCommand(document, fragment, false, false, false, true, EditActionUnspecified));
245 }
246
247 void Editor::setLastEditCommand(PassRefPtr<EditCommand> lastEditCommand) 
248 {
249     m_lastEditCommand = lastEditCommand;
250 }
251
252 // =============================================================================
253 //
254 // public editing commands
255 //
256 // =============================================================================
257
258 Editor::Editor(Frame* frame, PassRefPtr<EditorClient> client)
259     : m_frame(frame)
260     , m_client(client)
261     , m_deleteButtonController(new DeleteButtonController(frame))
262
263 }
264
265 Editor::~Editor()
266 {
267 }
268
269 void Editor::cut()
270 {
271     if (tryDHTMLCut())
272         return; // DHTML did the whole operation
273     if (!canCut()) {
274         systemBeep();
275         return;
276     }
277     
278     if (shouldDeleteRange(selectedRange())) {
279         writeSelectionToPasteboard(generalPasteboard());
280         deleteSelectionWithSmartDelete(canSmartCopyOrDelete());
281     }
282 }
283
284 void Editor::copy()
285 {
286     if (tryDHTMLCopy())
287         return; // DHTML did the whole operation
288     if (!canCopy()) {
289         systemBeep();
290         return;
291     }
292     writeSelectionToPasteboard(generalPasteboard());
293 }
294
295 void Editor::paste()
296 {
297     if (tryDHTMLPaste())
298         return;     // DHTML did the whole operation
299     if (!canPaste())
300         return;
301     if (isSelectionRichlyEditable())
302         pasteWithPasteboard(generalPasteboard(), true);
303     else
304         pasteAsPlainTextWithPasteboard(generalPasteboard());
305 }
306
307 void Editor::performDelete()
308 {
309     if (!canDelete()) {
310         systemBeep();
311         return;
312     }
313     deleteSelection();
314 }
315
316 } // namespace WebCore