|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 #include "mozilla/dom/Element.h" |
|
6 #include "nsAString.h" |
|
7 #include "nsCOMPtr.h" |
|
8 #include "nsDebug.h" |
|
9 #include "nsError.h" |
|
10 #include "nsHTMLEditUtils.h" |
|
11 #include "nsHTMLEditor.h" |
|
12 #include "nsIContent.h" |
|
13 #include "nsIDOMElement.h" |
|
14 #include "nsIDOMEventTarget.h" |
|
15 #include "nsIDOMHTMLElement.h" |
|
16 #include "nsIDOMNode.h" |
|
17 #include "nsIHTMLEditor.h" |
|
18 #include "nsIHTMLObjectResizer.h" |
|
19 #include "nsIPresShell.h" |
|
20 #include "nsLiteralString.h" |
|
21 #include "nsReadableUtils.h" |
|
22 #include "nsString.h" |
|
23 #include "nscore.h" |
|
24 |
|
25 // Uncomment the following line if you want to disable |
|
26 // table deletion when the only column/row is removed |
|
27 // #define DISABLE_TABLE_DELETION 1 |
|
28 |
|
29 NS_IMETHODIMP |
|
30 nsHTMLEditor::SetInlineTableEditingEnabled(bool aIsEnabled) |
|
31 { |
|
32 mIsInlineTableEditingEnabled = aIsEnabled; |
|
33 return NS_OK; |
|
34 } |
|
35 |
|
36 NS_IMETHODIMP |
|
37 nsHTMLEditor::GetInlineTableEditingEnabled(bool * aIsEnabled) |
|
38 { |
|
39 *aIsEnabled = mIsInlineTableEditingEnabled; |
|
40 return NS_OK; |
|
41 } |
|
42 |
|
43 NS_IMETHODIMP |
|
44 nsHTMLEditor::ShowInlineTableEditingUI(nsIDOMElement * aCell) |
|
45 { |
|
46 NS_ENSURE_ARG_POINTER(aCell); |
|
47 |
|
48 // do nothing if aCell is not a table cell... |
|
49 if (!nsHTMLEditUtils::IsTableCell(aCell)) |
|
50 return NS_OK; |
|
51 |
|
52 if (mInlineEditedCell) { |
|
53 NS_ERROR("call HideInlineTableEditingUI first"); |
|
54 return NS_ERROR_UNEXPECTED; |
|
55 } |
|
56 |
|
57 // the resizers and the shadow will be anonymous children of the body |
|
58 nsCOMPtr<nsIDOMElement> bodyElement = do_QueryInterface(GetRoot()); |
|
59 NS_ENSURE_TRUE(bodyElement, NS_ERROR_NULL_POINTER); |
|
60 |
|
61 CreateAnonymousElement(NS_LITERAL_STRING("a"), bodyElement, |
|
62 NS_LITERAL_STRING("mozTableAddColumnBefore"), |
|
63 false, getter_AddRefs(mAddColumnBeforeButton)); |
|
64 CreateAnonymousElement(NS_LITERAL_STRING("a"), bodyElement, |
|
65 NS_LITERAL_STRING("mozTableRemoveColumn"), |
|
66 false, getter_AddRefs(mRemoveColumnButton)); |
|
67 CreateAnonymousElement(NS_LITERAL_STRING("a"), bodyElement, |
|
68 NS_LITERAL_STRING("mozTableAddColumnAfter"), |
|
69 false, getter_AddRefs(mAddColumnAfterButton)); |
|
70 |
|
71 CreateAnonymousElement(NS_LITERAL_STRING("a"), bodyElement, |
|
72 NS_LITERAL_STRING("mozTableAddRowBefore"), |
|
73 false, getter_AddRefs(mAddRowBeforeButton)); |
|
74 CreateAnonymousElement(NS_LITERAL_STRING("a"), bodyElement, |
|
75 NS_LITERAL_STRING("mozTableRemoveRow"), |
|
76 false, getter_AddRefs(mRemoveRowButton)); |
|
77 CreateAnonymousElement(NS_LITERAL_STRING("a"), bodyElement, |
|
78 NS_LITERAL_STRING("mozTableAddRowAfter"), |
|
79 false, getter_AddRefs(mAddRowAfterButton)); |
|
80 |
|
81 AddMouseClickListener(mAddColumnBeforeButton); |
|
82 AddMouseClickListener(mRemoveColumnButton); |
|
83 AddMouseClickListener(mAddColumnAfterButton); |
|
84 AddMouseClickListener(mAddRowBeforeButton); |
|
85 AddMouseClickListener(mRemoveRowButton); |
|
86 AddMouseClickListener(mAddRowAfterButton); |
|
87 |
|
88 mInlineEditedCell = aCell; |
|
89 return RefreshInlineTableEditingUI(); |
|
90 } |
|
91 |
|
92 NS_IMETHODIMP |
|
93 nsHTMLEditor::HideInlineTableEditingUI() |
|
94 { |
|
95 mInlineEditedCell = nullptr; |
|
96 |
|
97 RemoveMouseClickListener(mAddColumnBeforeButton); |
|
98 RemoveMouseClickListener(mRemoveColumnButton); |
|
99 RemoveMouseClickListener(mAddColumnAfterButton); |
|
100 RemoveMouseClickListener(mAddRowBeforeButton); |
|
101 RemoveMouseClickListener(mRemoveRowButton); |
|
102 RemoveMouseClickListener(mAddRowAfterButton); |
|
103 |
|
104 // get the presshell's document observer interface. |
|
105 nsCOMPtr<nsIPresShell> ps = GetPresShell(); |
|
106 // We allow the pres shell to be null; when it is, we presume there |
|
107 // are no document observers to notify, but we still want to |
|
108 // UnbindFromTree. |
|
109 |
|
110 // get the root content node. |
|
111 nsCOMPtr<nsIContent> bodyContent = GetRoot(); |
|
112 NS_ENSURE_TRUE(bodyContent, NS_ERROR_FAILURE); |
|
113 |
|
114 DeleteRefToAnonymousNode(mAddColumnBeforeButton, bodyContent, ps); |
|
115 mAddColumnBeforeButton = nullptr; |
|
116 DeleteRefToAnonymousNode(mRemoveColumnButton, bodyContent, ps); |
|
117 mRemoveColumnButton = nullptr; |
|
118 DeleteRefToAnonymousNode(mAddColumnAfterButton, bodyContent, ps); |
|
119 mAddColumnAfterButton = nullptr; |
|
120 DeleteRefToAnonymousNode(mAddRowBeforeButton, bodyContent, ps); |
|
121 mAddRowBeforeButton = nullptr; |
|
122 DeleteRefToAnonymousNode(mRemoveRowButton, bodyContent, ps); |
|
123 mRemoveRowButton = nullptr; |
|
124 DeleteRefToAnonymousNode(mAddRowAfterButton, bodyContent, ps); |
|
125 mAddRowAfterButton = nullptr; |
|
126 |
|
127 return NS_OK; |
|
128 } |
|
129 |
|
130 NS_IMETHODIMP |
|
131 nsHTMLEditor::DoInlineTableEditingAction(nsIDOMElement * aElement) |
|
132 { |
|
133 NS_ENSURE_ARG_POINTER(aElement); |
|
134 bool anonElement = false; |
|
135 if (aElement && |
|
136 NS_SUCCEEDED(aElement->HasAttribute(NS_LITERAL_STRING("_moz_anonclass"), &anonElement)) && |
|
137 anonElement) { |
|
138 nsAutoString anonclass; |
|
139 nsresult res = aElement->GetAttribute(NS_LITERAL_STRING("_moz_anonclass"), anonclass); |
|
140 NS_ENSURE_SUCCESS(res, res); |
|
141 |
|
142 if (!StringBeginsWith(anonclass, NS_LITERAL_STRING("mozTable"))) |
|
143 return NS_OK; |
|
144 |
|
145 nsCOMPtr<nsIDOMNode> tableNode = GetEnclosingTable(mInlineEditedCell); |
|
146 nsCOMPtr<nsIDOMElement> tableElement = do_QueryInterface(tableNode); |
|
147 int32_t rowCount, colCount; |
|
148 res = GetTableSize(tableElement, &rowCount, &colCount); |
|
149 NS_ENSURE_SUCCESS(res, res); |
|
150 |
|
151 bool hideUI = false; |
|
152 bool hideResizersWithInlineTableUI = (mResizedObject == tableElement); |
|
153 |
|
154 if (anonclass.EqualsLiteral("mozTableAddColumnBefore")) |
|
155 InsertTableColumn(1, false); |
|
156 else if (anonclass.EqualsLiteral("mozTableAddColumnAfter")) |
|
157 InsertTableColumn(1, true); |
|
158 else if (anonclass.EqualsLiteral("mozTableAddRowBefore")) |
|
159 InsertTableRow(1, false); |
|
160 else if (anonclass.EqualsLiteral("mozTableAddRowAfter")) |
|
161 InsertTableRow(1, true); |
|
162 else if (anonclass.EqualsLiteral("mozTableRemoveColumn")) { |
|
163 DeleteTableColumn(1); |
|
164 #ifndef DISABLE_TABLE_DELETION |
|
165 hideUI = (colCount == 1); |
|
166 #endif |
|
167 } |
|
168 else if (anonclass.EqualsLiteral("mozTableRemoveRow")) { |
|
169 DeleteTableRow(1); |
|
170 #ifndef DISABLE_TABLE_DELETION |
|
171 hideUI = (rowCount == 1); |
|
172 #endif |
|
173 } |
|
174 else |
|
175 return NS_OK; |
|
176 |
|
177 if (hideUI) { |
|
178 HideInlineTableEditingUI(); |
|
179 if (hideResizersWithInlineTableUI) |
|
180 HideResizers(); |
|
181 } |
|
182 } |
|
183 |
|
184 return NS_OK; |
|
185 } |
|
186 |
|
187 void |
|
188 nsHTMLEditor::AddMouseClickListener(nsIDOMElement * aElement) |
|
189 { |
|
190 nsCOMPtr<nsIDOMEventTarget> evtTarget(do_QueryInterface(aElement)); |
|
191 if (evtTarget) { |
|
192 evtTarget->AddEventListener(NS_LITERAL_STRING("click"), |
|
193 mEventListener, true); |
|
194 } |
|
195 } |
|
196 |
|
197 void |
|
198 nsHTMLEditor::RemoveMouseClickListener(nsIDOMElement * aElement) |
|
199 { |
|
200 nsCOMPtr<nsIDOMEventTarget> evtTarget(do_QueryInterface(aElement)); |
|
201 if (evtTarget) { |
|
202 evtTarget->RemoveEventListener(NS_LITERAL_STRING("click"), |
|
203 mEventListener, true); |
|
204 } |
|
205 } |
|
206 |
|
207 NS_IMETHODIMP |
|
208 nsHTMLEditor::RefreshInlineTableEditingUI() |
|
209 { |
|
210 nsCOMPtr<nsIDOMHTMLElement> htmlElement = do_QueryInterface(mInlineEditedCell); |
|
211 if (!htmlElement) { |
|
212 return NS_ERROR_NULL_POINTER; |
|
213 } |
|
214 |
|
215 int32_t xCell, yCell, wCell, hCell; |
|
216 GetElementOrigin(mInlineEditedCell, xCell, yCell); |
|
217 |
|
218 nsresult res = htmlElement->GetOffsetWidth(&wCell); |
|
219 NS_ENSURE_SUCCESS(res, res); |
|
220 res = htmlElement->GetOffsetHeight(&hCell); |
|
221 NS_ENSURE_SUCCESS(res, res); |
|
222 |
|
223 int32_t xHoriz = xCell + wCell/2; |
|
224 int32_t yVert = yCell + hCell/2; |
|
225 |
|
226 nsCOMPtr<nsIDOMNode> tableNode = GetEnclosingTable(mInlineEditedCell); |
|
227 nsCOMPtr<nsIDOMElement> tableElement = do_QueryInterface(tableNode); |
|
228 int32_t rowCount, colCount; |
|
229 res = GetTableSize(tableElement, &rowCount, &colCount); |
|
230 NS_ENSURE_SUCCESS(res, res); |
|
231 |
|
232 SetAnonymousElementPosition(xHoriz-10, yCell-7, mAddColumnBeforeButton); |
|
233 #ifdef DISABLE_TABLE_DELETION |
|
234 NS_NAMED_LITERAL_STRING(classStr, "class"); |
|
235 |
|
236 if (colCount== 1) { |
|
237 mRemoveColumnButton->SetAttribute(classStr, |
|
238 NS_LITERAL_STRING("hidden")); |
|
239 } |
|
240 else { |
|
241 bool hasClass = false; |
|
242 res = mRemoveColumnButton->HasAttribute(classStr, &hasClass); |
|
243 if (NS_SUCCEEDED(res) && hasClass) |
|
244 mRemoveColumnButton->RemoveAttribute(classStr); |
|
245 #endif |
|
246 SetAnonymousElementPosition(xHoriz-4, yCell-7, mRemoveColumnButton); |
|
247 #ifdef DISABLE_TABLE_DELETION |
|
248 } |
|
249 #endif |
|
250 SetAnonymousElementPosition(xHoriz+6, yCell-7, mAddColumnAfterButton); |
|
251 |
|
252 SetAnonymousElementPosition(xCell-7, yVert-10, mAddRowBeforeButton); |
|
253 #ifdef DISABLE_TABLE_DELETION |
|
254 if (rowCount== 1) { |
|
255 mRemoveRowButton->SetAttribute(classStr, |
|
256 NS_LITERAL_STRING("hidden")); |
|
257 } |
|
258 else { |
|
259 bool hasClass = false; |
|
260 res = mRemoveRowButton->HasAttribute(classStr, &hasClass); |
|
261 if (NS_SUCCEEDED(res) && hasClass) |
|
262 mRemoveRowButton->RemoveAttribute(classStr); |
|
263 #endif |
|
264 SetAnonymousElementPosition(xCell-7, yVert-4, mRemoveRowButton); |
|
265 #ifdef DISABLE_TABLE_DELETION |
|
266 } |
|
267 #endif |
|
268 SetAnonymousElementPosition(xCell-7, yVert+6, mAddRowAfterButton); |
|
269 |
|
270 return NS_OK; |
|
271 } |
|
272 |