1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : * Charles Manske (cmanske@netscape.com)
24 : * Daniel Glazman (glazman@netscape.com)
25 : * Masayuki Nakano <masayuki@d-toybox.com>
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 : #include "nsHTMLEditorEventListener.h"
41 : #include "nsHTMLEditor.h"
42 : #include "nsString.h"
43 :
44 : #include "nsIDOMEvent.h"
45 : #include "nsIDOMNSEvent.h"
46 : #include "nsIDOMElement.h"
47 : #include "nsIDOMMouseEvent.h"
48 : #include "nsISelection.h"
49 : #include "nsIDOMRange.h"
50 : #include "nsIDOMEventTarget.h"
51 : #include "nsIDOMHTMLTableElement.h"
52 : #include "nsIDOMHTMLTableCellElement.h"
53 : #include "nsIContent.h"
54 :
55 : #include "nsIHTMLObjectResizer.h"
56 : #include "nsEditProperty.h"
57 : #include "nsTextEditUtils.h"
58 : #include "nsHTMLEditUtils.h"
59 :
60 : /*
61 : * nsHTMLEditorEventListener implementation
62 : *
63 : */
64 :
65 : #ifdef DEBUG
66 : nsresult
67 0 : nsHTMLEditorEventListener::Connect(nsEditor* aEditor)
68 : {
69 0 : nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryObject(aEditor);
70 0 : nsCOMPtr<nsIHTMLInlineTableEditor> htmlInlineTableEditor = do_QueryObject(aEditor);
71 0 : NS_PRECONDITION(htmlEditor && htmlInlineTableEditor,
72 : "Set nsHTMLEditor or its sub class");
73 0 : return nsEditorEventListener::Connect(aEditor);
74 : }
75 : #endif
76 :
77 : nsHTMLEditor*
78 0 : nsHTMLEditorEventListener::GetHTMLEditor()
79 : {
80 : // mEditor must be nsHTMLEditor or its subclass.
81 0 : return static_cast<nsHTMLEditor*>(mEditor);
82 : }
83 :
84 : NS_IMETHODIMP
85 0 : nsHTMLEditorEventListener::MouseUp(nsIDOMEvent* aMouseEvent)
86 : {
87 0 : NS_ENSURE_TRUE(mEditor, NS_ERROR_NOT_AVAILABLE);
88 :
89 0 : nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(aMouseEvent) );
90 0 : if (!mouseEvent) {
91 : //non-ui event passed in. bad things.
92 0 : return NS_OK;
93 : }
94 :
95 0 : nsHTMLEditor* htmlEditor = GetHTMLEditor();
96 :
97 0 : nsCOMPtr<nsIDOMEventTarget> target;
98 0 : nsresult res = aMouseEvent->GetTarget(getter_AddRefs(target));
99 0 : NS_ENSURE_SUCCESS(res, res);
100 0 : NS_ENSURE_TRUE(target, NS_ERROR_NULL_POINTER);
101 0 : nsCOMPtr<nsIDOMElement> element = do_QueryInterface(target);
102 :
103 : PRInt32 clientX, clientY;
104 0 : mouseEvent->GetClientX(&clientX);
105 0 : mouseEvent->GetClientY(&clientY);
106 0 : htmlEditor->MouseUp(clientX, clientY, element);
107 :
108 0 : return nsEditorEventListener::MouseUp(aMouseEvent);
109 : }
110 :
111 : NS_IMETHODIMP
112 0 : nsHTMLEditorEventListener::MouseDown(nsIDOMEvent* aMouseEvent)
113 : {
114 0 : NS_ENSURE_TRUE(mEditor, NS_ERROR_NOT_AVAILABLE);
115 :
116 0 : nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(aMouseEvent) );
117 0 : if (!mouseEvent) {
118 : //non-ui event passed in. bad things.
119 0 : return NS_OK;
120 : }
121 :
122 0 : nsHTMLEditor* htmlEditor = GetHTMLEditor();
123 :
124 : // Detect only "context menu" click
125 : //XXX This should be easier to do!
126 : // But eDOMEvents_contextmenu and NS_CONTEXTMENU is not exposed in any event interface :-(
127 : PRUint16 buttonNumber;
128 0 : nsresult res = mouseEvent->GetButton(&buttonNumber);
129 0 : NS_ENSURE_SUCCESS(res, res);
130 :
131 0 : bool isContextClick = buttonNumber == 2;
132 :
133 : PRInt32 clickCount;
134 0 : res = mouseEvent->GetDetail(&clickCount);
135 0 : NS_ENSURE_SUCCESS(res, res);
136 :
137 0 : nsCOMPtr<nsIDOMEventTarget> target;
138 0 : nsCOMPtr<nsIDOMNSEvent> internalEvent = do_QueryInterface(aMouseEvent);
139 0 : res = internalEvent->GetExplicitOriginalTarget(getter_AddRefs(target));
140 0 : NS_ENSURE_SUCCESS(res, res);
141 0 : NS_ENSURE_TRUE(target, NS_ERROR_NULL_POINTER);
142 0 : nsCOMPtr<nsIDOMElement> element = do_QueryInterface(target);
143 :
144 0 : if (isContextClick || (buttonNumber == 0 && clickCount == 2))
145 : {
146 0 : nsCOMPtr<nsISelection> selection;
147 0 : mEditor->GetSelection(getter_AddRefs(selection));
148 0 : NS_ENSURE_TRUE(selection, NS_OK);
149 :
150 : // Get location of mouse within target node
151 0 : nsCOMPtr<nsIDOMNode> parent;
152 0 : res = mouseEvent->GetRangeParent(getter_AddRefs(parent));
153 0 : NS_ENSURE_SUCCESS(res, res);
154 0 : NS_ENSURE_TRUE(parent, NS_ERROR_FAILURE);
155 :
156 0 : PRInt32 offset = 0;
157 0 : res = mouseEvent->GetRangeOffset(&offset);
158 0 : NS_ENSURE_SUCCESS(res, res);
159 :
160 : // Detect if mouse point is within current selection for context click
161 0 : bool nodeIsInSelection = false;
162 0 : if (isContextClick)
163 : {
164 : bool isCollapsed;
165 0 : selection->GetIsCollapsed(&isCollapsed);
166 0 : if (!isCollapsed)
167 : {
168 : PRInt32 rangeCount;
169 0 : res = selection->GetRangeCount(&rangeCount);
170 0 : NS_ENSURE_SUCCESS(res, res);
171 :
172 0 : for (PRInt32 i = 0; i < rangeCount; i++)
173 : {
174 0 : nsCOMPtr<nsIDOMRange> range;
175 :
176 0 : res = selection->GetRangeAt(i, getter_AddRefs(range));
177 0 : if (NS_FAILED(res) || !range)
178 0 : continue;//don't bail yet, iterate through them all
179 :
180 0 : res = range->IsPointInRange(parent, offset, &nodeIsInSelection);
181 :
182 : // Done when we find a range that we are in
183 0 : if (nodeIsInSelection)
184 : break;
185 : }
186 : }
187 : }
188 0 : nsCOMPtr<nsIDOMNode> node = do_QueryInterface(target);
189 0 : if (node && !nodeIsInSelection)
190 : {
191 0 : if (!element)
192 : {
193 0 : if (isContextClick)
194 : {
195 : // Set the selection to the point under the mouse cursor:
196 0 : selection->Collapse(parent, offset);
197 : }
198 : else
199 : {
200 : // Get enclosing link if in text so we can select the link
201 0 : nsCOMPtr<nsIDOMElement> linkElement;
202 0 : res = htmlEditor->GetElementOrParentByTagName(NS_LITERAL_STRING("href"), node, getter_AddRefs(linkElement));
203 0 : NS_ENSURE_SUCCESS(res, res);
204 0 : if (linkElement)
205 0 : element = linkElement;
206 : }
207 : }
208 : // Select entire element clicked on if NOT within an existing selection
209 : // and not the entire body, or table-related elements
210 0 : if (element)
211 : {
212 : nsCOMPtr<nsIDOMNode> selectAllNode =
213 0 : htmlEditor->FindUserSelectAllNode(element);
214 :
215 0 : if (selectAllNode)
216 : {
217 0 : nsCOMPtr<nsIDOMElement> newElement = do_QueryInterface(selectAllNode);
218 0 : if (newElement)
219 : {
220 0 : node = selectAllNode;
221 0 : element = newElement;
222 : }
223 : }
224 :
225 0 : if (isContextClick && !nsHTMLEditUtils::IsImage(node))
226 : {
227 0 : selection->Collapse(parent, offset);
228 : }
229 : else
230 : {
231 0 : htmlEditor->SelectElement(element);
232 : }
233 : }
234 : }
235 : // HACK !!! Context click places the caret but the context menu consumes
236 : // the event; so we need to check resizing state ourselves
237 0 : htmlEditor->CheckSelectionStateForAnonymousButtons(selection);
238 :
239 : // Prevent bubbling if we changed selection or
240 : // for all context clicks
241 0 : if (element || isContextClick)
242 : {
243 : #ifndef XP_OS2
244 0 : mouseEvent->PreventDefault();
245 : #endif
246 0 : return NS_OK;
247 0 : }
248 : }
249 0 : else if (!isContextClick && buttonNumber == 0 && clickCount == 1)
250 : {
251 : // if the target element is an image, we have to display resizers
252 : PRInt32 clientX, clientY;
253 0 : mouseEvent->GetClientX(&clientX);
254 0 : mouseEvent->GetClientY(&clientY);
255 0 : htmlEditor->MouseDown(clientX, clientY, element, aMouseEvent);
256 : }
257 :
258 0 : return nsEditorEventListener::MouseDown(aMouseEvent);
259 : }
260 :
261 : NS_IMETHODIMP
262 0 : nsHTMLEditorEventListener::MouseClick(nsIDOMEvent* aMouseEvent)
263 : {
264 0 : NS_ENSURE_TRUE(mEditor, NS_ERROR_NOT_AVAILABLE);
265 :
266 0 : nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(aMouseEvent) );
267 0 : if (!mouseEvent) {
268 : //non-ui event passed in. bad things.
269 0 : return NS_OK;
270 : }
271 :
272 0 : nsCOMPtr<nsIDOMEventTarget> target;
273 0 : nsresult res = aMouseEvent->GetTarget(getter_AddRefs(target));
274 0 : NS_ENSURE_SUCCESS(res, res);
275 0 : NS_ENSURE_TRUE(target, NS_ERROR_NULL_POINTER);
276 0 : nsCOMPtr<nsIDOMElement> element = do_QueryInterface(target);
277 :
278 0 : GetHTMLEditor()->DoInlineTableEditingAction(element);
279 :
280 0 : return nsEditorEventListener::MouseClick(aMouseEvent);
281 : }
|