2 * Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
29 #include "ApplyStyleCommand.h"
30 #include "CSSComputedStyleDeclaration.h"
31 #include "DeleteButtonController.h"
33 #include "DocumentFragment.h"
34 #include "EditorClient.h"
35 #include "EditCommand.h"
36 #include "htmlediting.h"
37 #include "HTMLElement.h"
38 #include "HTMLNames.h"
41 #include "ReplaceSelectionCommand.h"
42 #include "SelectionController.h"
47 using namespace HTMLNames;
49 // implement as platform-specific
50 static Pasteboard generalPasteboard()
55 bool Editor::canCopy()
65 bool Editor::canDelete()
70 bool Editor::canDeleteRange(Range* range)
73 Node* startContainer = range->startContainer(ec);
74 Node* endContainer = range->endContainer(ec);
75 if (!startContainer || !endContainer)
78 if (!startContainer->isContentEditable() || !endContainer->isContentEditable())
81 if (range->collapsed(ec)) {
82 VisiblePosition start(startContainer, range->startOffset(ec), DOWNSTREAM);
83 VisiblePosition previous = start.previous();
84 // FIXME: We sometimes allow deletions at the start of editable roots, like when the caret is in an empty list item.
85 if (previous.isNull() || previous.deepEquivalent().node()->rootEditableElement() != startContainer->rootEditableElement())
91 bool Editor::canPaste()
96 bool Editor::canSmartCopyOrDelete()
101 void Editor::deleteSelection()
105 void Editor::deleteSelectionWithSmartDelete(bool enabled)
109 bool Editor::isSelectionRichlyEditable()
114 void Editor::pasteAsPlainTextWithPasteboard(Pasteboard pasteboard)
118 void Editor::pasteWithPasteboard(Pasteboard pasteboard, bool allowPlainText)
122 Range* Editor::selectedRange()
127 bool Editor::shouldDeleteRange(Range* range)
130 if (!range || range->collapsed(ec))
133 if (!canDeleteRange(range))
136 return m_client->shouldDeleteRange(range);
139 bool Editor::tryDHTMLCopy()
141 bool handled = false;
145 bool Editor::tryDHTMLCut()
147 bool handled = false;
151 bool Editor::tryDHTMLPaste()
153 bool handled = false;
157 void Editor::writeSelectionToPasteboard(Pasteboard pasteboard)
161 bool Editor::shouldShowDeleteInterface(HTMLElement* element)
163 return m_client->shouldShowDeleteInterface(element);
166 void Editor::respondToChangedSelection(const Selection& oldSelection)
168 m_deleteButtonController->respondToChangedSelection(oldSelection);
171 void Editor::respondToChangedContents()
173 m_deleteButtonController->respondToChangedContents();
176 Frame::TriState Editor::selectionUnorderedListState() const
178 if (m_frame->selectionController()->isCaret()) {
179 Node* selectionNode = m_frame->selectionController()->selection().start().node();
180 if (enclosingNodeWithTag(selectionNode, ulTag))
181 return Frame::trueTriState;
182 } else if (m_frame->selectionController()->isRange()) {
183 Node* startNode = enclosingNodeWithTag(m_frame->selectionController()->selection().start().node(), ulTag);
184 Node* endNode = enclosingNodeWithTag(m_frame->selectionController()->selection().end().node(), ulTag);
185 if (startNode && endNode && startNode == endNode)
186 return Frame::trueTriState;
189 return Frame::falseTriState;
192 Frame::TriState Editor::selectionOrderedListState() const
194 if (m_frame->selectionController()->isCaret()) {
195 Node* selectionNode = m_frame->selectionController()->selection().start().node();
196 if (enclosingNodeWithTag(selectionNode, olTag))
197 return Frame::trueTriState;
198 } else if (m_frame->selectionController()->isRange()) {
199 Node* startNode = enclosingNodeWithTag(m_frame->selectionController()->selection().start().node(), olTag);
200 Node* endNode = enclosingNodeWithTag(m_frame->selectionController()->selection().end().node(), olTag);
201 if (startNode && endNode && startNode == endNode)
202 return Frame::trueTriState;
205 return Frame::falseTriState;
208 void Editor::removeFormattingAndStyle()
210 Document* document = frame()->document();
212 // Make a plain text string from the selection to remove formatting like tables and lists.
213 RefPtr<DocumentFragment> text = createFragmentFromText(frame()->selectionController()->toRange().get(), frame()->selectionController()->toString());
215 // Put the fragment made from that string into a style span with the document's
216 // default style to make sure that it is unstyled regardless of where it is inserted.
217 Position pos(document->documentElement(), 0);
218 RefPtr<CSSComputedStyleDeclaration> computedStyle = pos.computedStyle();
219 RefPtr<CSSMutableStyleDeclaration> defaultStyle = computedStyle->copyInheritableProperties();
221 RefPtr<Element> span = createStyleSpanElement(document);
222 span->setAttribute(styleAttr, defaultStyle->cssText());
226 while (text->lastChild())
227 span->appendChild(text->lastChild(), ec);
229 RefPtr<DocumentFragment> fragment = new DocumentFragment(document);
230 fragment->appendChild(span, ec);
232 applyCommand(new ReplaceSelectionCommand(document, fragment, false, false, false, true, EditActionUnspecified));
235 // =============================================================================
237 // public editing commands
239 // =============================================================================
241 Editor::Editor(Frame* frame, PassRefPtr<EditorClient> client)
244 , m_deleteButtonController(new DeleteButtonController(frame))
255 return; // DHTML did the whole operation
261 if (shouldDeleteRange(selectedRange())) {
262 writeSelectionToPasteboard(generalPasteboard());
263 deleteSelectionWithSmartDelete(canSmartCopyOrDelete());
270 return; // DHTML did the whole operation
275 writeSelectionToPasteboard(generalPasteboard());
281 return; // DHTML did the whole operation
284 if (isSelectionRichlyEditable())
285 pasteWithPasteboard(generalPasteboard(), true);
287 pasteAsPlainTextWithPasteboard(generalPasteboard());
290 void Editor::performDelete()
299 } // namespace WebCore