3c63fc3ebedd7f7e6556032c61f0d5faf1fa1d22
[WebKit-https.git] / WebCore / editing / DeleteButtonController.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 "DeleteButtonController.h"
28
29 #include "CachedImage.h"
30 #include "CSSMutableStyleDeclaration.h"
31 #include "CSSPrimitiveValue.h"
32 #include "CSSPropertyNames.h"
33 #include "CSSValueKeywords.h"
34 #include "DeleteButton.h"
35 #include "Document.h"
36 #include "Editor.h"
37 #include "Frame.h"
38 #include "htmlediting.h"
39 #include "HTMLDivElement.h"
40 #include "HTMLNames.h"
41 #include "Image.h"
42 #include "Node.h"
43 #include "Range.h"
44 #include "RemoveNodeCommand.h"
45 #include "RenderObject.h"
46 #include "SelectionController.h"
47
48 namespace WebCore {
49
50 using namespace HTMLNames;
51
52 const char* const DeleteButtonController::containerElementIdentifier = "WebKit-Editing-Delete-Container";
53 const char* const DeleteButtonController::buttonElementIdentifier = "WebKit-Editing-Delete-Button";
54 const char* const DeleteButtonController::outlineElementIdentifier = "WebKit-Editing-Delete-Outline";
55
56 DeleteButtonController::DeleteButtonController(Frame* frame)
57     : m_frame(frame)
58     , m_wasStaticPositioned(false)
59     , m_wasAutoZIndex(false)
60 {
61 }
62
63 static bool isDeletableElement(Node* node)
64 {
65     if (!node || !node->isHTMLElement() || !node->isContentEditable())
66         return false;
67
68     RenderObject* renderer = node->renderer();
69     if (!renderer || renderer->width() < 25 || renderer->height() < 25)
70         return false;
71
72     if (node->hasTagName(tableTag) || node->hasTagName(ulTag) || node->hasTagName(olTag))
73         return true;
74
75     if (renderer->isRenderBlock()) {
76         RenderStyle* style = renderer->style();
77         if (!style)
78             return false;
79         if (style->position() == AbsolutePosition || style->position() == FixedPosition)
80             return true;
81         if (style->border().hasBorder())
82             return true;
83     }
84
85     return false;
86 }
87
88 static HTMLElement* enclosingDeletableElement(const Selection& selection)
89 {
90     if (!selection.isContentEditable())
91         return 0;
92
93     RefPtr<Range> range = selection.toRange();
94     if (!range)
95         return 0;
96
97     ExceptionCode ec = 0;
98     Node* container = range->commonAncestorContainer(ec);
99     ASSERT(container);
100     ASSERT(ec == 0);
101
102     // The enclosingNodeOfType function only works on nodes that are editable
103     // (which is strange, given its name).
104     if (!container->isContentEditable())
105         return 0;
106
107     Node* element = enclosingNodeOfType(container, &isDeletableElement);
108     if (!element)
109         return 0;
110
111     ASSERT(element->isHTMLElement());
112     return static_cast<HTMLElement*>(element);
113 }
114
115 void DeleteButtonController::respondToChangedSelection(const Selection& oldSelection)
116 {
117     HTMLElement* oldElement = enclosingDeletableElement(oldSelection);
118     HTMLElement* newElement = enclosingDeletableElement(m_frame->selectionController()->selection());
119     if (oldElement == newElement)
120         return;
121
122     // If the base is inside a deletable element, give the element a delete widget.
123     if (newElement)
124         show(newElement);
125     else
126         hide();
127 }
128
129 void DeleteButtonController::respondToChangedContents()
130 {
131     updateOutlineStyle();
132 }
133
134 void DeleteButtonController::updateOutlineStyle()
135 {
136     if (!m_element || !m_element->renderer() || !m_outlineElement)
137         return;
138
139     CSSMutableStyleDeclaration* style = m_outlineElement->getInlineStyleDecl();
140     style->setProperty(CSS_PROP_WIDTH, String::number(m_element->renderer()->overflowWidth()) + "px");
141     style->setProperty(CSS_PROP_HEIGHT, String::number(m_element->renderer()->overflowHeight()) + "px");
142 }
143
144 void DeleteButtonController::show(HTMLElement* element)
145 {
146     hide();
147
148     if (!element->renderer() || !element->renderer()->isRenderBlock())
149         return;
150
151     if (!m_frame->editor()->shouldShowDeleteInterface(static_cast<HTMLElement*>(element)))
152         return;
153
154     m_element = element;
155
156     m_containerElement = new HTMLDivElement(m_element->document());
157     m_containerElement->setId(containerElementIdentifier);
158
159     CSSMutableStyleDeclaration* style = m_containerElement->getInlineStyleDecl();
160     style->setProperty(CSS_PROP_POSITION, CSS_VAL_ABSOLUTE);
161     style->setProperty(CSS_PROP_TOP, "0px");
162     style->setProperty(CSS_PROP_LEFT, "0px");
163     style->setProperty(CSS_PROP__WEBKIT_USER_DRAG, CSS_VAL_NONE);
164     style->setProperty(CSS_PROP__WEBKIT_USER_SELECT, CSS_VAL_NONE);
165     style->setProperty(CSS_PROP__WEBKIT_USER_MODIFY, CSS_VAL_NONE);
166
167     m_outlineElement = new HTMLDivElement(m_element->document());
168     m_outlineElement->setId(outlineElementIdentifier);
169
170     const int borderWidth = 4;
171     const int borderRadius = 6;
172
173     style = m_outlineElement->getInlineStyleDecl();
174     style->setProperty(CSS_PROP_POSITION, CSS_VAL_ABSOLUTE);
175     style->setProperty(CSS_PROP_CURSOR, CSS_VAL_DEFAULT);
176     style->setProperty(CSS_PROP__WEBKIT_USER_DRAG, CSS_VAL_NONE);
177     style->setProperty(CSS_PROP__WEBKIT_USER_SELECT, CSS_VAL_NONE);
178     style->setProperty(CSS_PROP__WEBKIT_USER_MODIFY, CSS_VAL_NONE);
179     style->setProperty(CSS_PROP_Z_INDEX, String::number(-1));
180     style->setProperty(CSS_PROP_TOP, String::number(-borderWidth) + "px");
181     style->setProperty(CSS_PROP_LEFT, String::number(-borderWidth) + "px");
182     style->setProperty(CSS_PROP_BORDER, String::number(borderWidth) + "px solid rgba(0, 0, 0, 0.6)");
183     style->setProperty(CSS_PROP__WEBKIT_BORDER_RADIUS, String::number(borderRadius) + "px");
184
185     updateOutlineStyle();
186
187     ExceptionCode ec = 0;
188     m_containerElement->appendChild(m_outlineElement.get(), ec);
189     ASSERT(ec == 0);
190     if (ec) {
191         hide();
192         return;
193     }
194
195     m_buttonElement = new DeleteButton(m_element->document());
196     m_buttonElement->setId(buttonElementIdentifier);
197
198     const int buttonWidth = 30;
199     const int buttonHeight = 30;
200
201     style = m_buttonElement->getInlineStyleDecl();
202     style->setProperty(CSS_PROP_POSITION, CSS_VAL_ABSOLUTE);
203     style->setProperty(CSS_PROP_CURSOR, CSS_VAL_DEFAULT);
204     style->setProperty(CSS_PROP__WEBKIT_USER_DRAG, CSS_VAL_NONE);
205     style->setProperty(CSS_PROP__WEBKIT_USER_SELECT, CSS_VAL_NONE);
206     style->setProperty(CSS_PROP__WEBKIT_USER_MODIFY, CSS_VAL_NONE);
207     style->setProperty(CSS_PROP_LEFT, String::number(-buttonWidth / 2) + "px");
208     style->setProperty(CSS_PROP_TOP, String::number(-buttonHeight / 2) + "px");
209     style->setProperty(CSS_PROP_WIDTH, String::number(buttonWidth) + "px");
210     style->setProperty(CSS_PROP_HEIGHT, String::number(buttonHeight) + "px");
211
212     m_buttonElement->setCachedImage(new CachedImage(Image::loadPlatformResource("deleteButton")));
213
214     m_containerElement->appendChild(m_buttonElement.get(), ec);
215     ASSERT(ec == 0);
216     if (ec) {
217         hide();
218         return;
219     }
220
221     m_element->appendChild(m_containerElement.get(), ec);
222     ASSERT(ec == 0);
223     if (ec) {
224         hide();
225         return;
226     }
227
228     if (m_element->renderer()->style()->position() == StaticPosition) {
229         m_element->getInlineStyleDecl()->setProperty(CSS_PROP_POSITION, CSS_VAL_RELATIVE);
230         m_wasStaticPositioned = true;
231     }
232
233     if (m_element->renderer()->style()->hasAutoZIndex()) {
234         m_element->getInlineStyleDecl()->setProperty(CSS_PROP_Z_INDEX, "0");
235         m_wasAutoZIndex = true;
236     }
237
238     m_element->renderer()->repaint(true);
239 }
240
241 void DeleteButtonController::hide()
242 {
243     ExceptionCode ec = 0;
244     if (m_containerElement)
245         m_containerElement->parentNode()->removeChild(m_containerElement.get(), ec);
246
247     if (m_element && m_wasStaticPositioned)
248         m_element->getInlineStyleDecl()->setProperty(CSS_PROP_POSITION, CSS_VAL_STATIC);
249
250     if (m_element && m_wasAutoZIndex)
251         m_element->getInlineStyleDecl()->setProperty(CSS_PROP_Z_INDEX, CSS_VAL_AUTO);
252
253     m_wasStaticPositioned = false;
254     m_wasAutoZIndex = false;
255     m_element = 0;
256     m_outlineElement = 0;
257     m_buttonElement = 0;
258     m_containerElement = 0;
259 }
260
261 void DeleteButtonController::deleteTarget()
262 {
263     if (!m_element)
264         return;
265
266     RefPtr<Node> element = m_element;
267     hide();
268
269     RefPtr<RemoveNodeCommand> command = new RemoveNodeCommand(element.get());
270     command->apply();
271 }
272
273 } // namespace WebCore