1 : /* -*- Mode: C++; tab-width: 2; 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 : * Pierre Phaneuf <pp@ludusdesign.com>
24 : *
25 : * Alternatively, the contents of this file may be used under the terms of
26 : * either of the GNU General Public License Version 2 or later (the "GPL"),
27 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 : * in which case the provisions of the GPL or the LGPL are applicable instead
29 : * of those above. If you wish to allow use of your version of this file only
30 : * under the terms of either the GPL or the LGPL, and not to allow others to
31 : * use your version of this file under the terms of the MPL, indicate your
32 : * decision by deleting the provisions above and replace them with the notice
33 : * and other provisions required by the GPL or the LGPL. If you do not delete
34 : * the provisions above, a recipient may use your version of this file under
35 : * the terms of any one of the MPL, the GPL or the LGPL.
36 : *
37 : * ***** END LICENSE BLOCK ***** */
38 :
39 :
40 : #include "TypeInState.h"
41 : #include "nsEditor.h"
42 :
43 : /********************************************************************
44 : * XPCOM cruft
45 : *******************************************************************/
46 :
47 1464 : NS_IMPL_CYCLE_COLLECTION_1(TypeInState, mLastSelectionContainer)
48 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(TypeInState)
49 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(TypeInState)
50 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TypeInState)
51 0 : NS_INTERFACE_MAP_ENTRY(nsISelectionListener)
52 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
53 0 : NS_INTERFACE_MAP_END
54 :
55 : /********************************************************************
56 : * public methods
57 : *******************************************************************/
58 :
59 0 : TypeInState::TypeInState() :
60 : mSetArray()
61 : ,mClearedArray()
62 : ,mRelativeFontSize(0)
63 0 : ,mLastSelectionOffset(0)
64 : {
65 0 : Reset();
66 0 : }
67 :
68 0 : TypeInState::~TypeInState()
69 : {
70 : // Call Reset() to release any data that may be in
71 : // mClearedArray and mSetArray.
72 :
73 0 : Reset();
74 0 : }
75 :
76 0 : nsresult TypeInState::UpdateSelState(nsISelection *aSelection)
77 : {
78 0 : NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
79 :
80 0 : bool isCollapsed = false;
81 0 : nsresult result = aSelection->GetIsCollapsed(&isCollapsed);
82 :
83 0 : NS_ENSURE_SUCCESS(result, result);
84 :
85 0 : if (isCollapsed)
86 : {
87 0 : result = nsEditor::GetStartNodeAndOffset(aSelection, getter_AddRefs(mLastSelectionContainer), &mLastSelectionOffset);
88 : }
89 0 : return result;
90 : }
91 :
92 :
93 0 : NS_IMETHODIMP TypeInState::NotifySelectionChanged(nsIDOMDocument *, nsISelection *aSelection, PRInt16)
94 : {
95 : // XXX: Selection currently generates bogus selection changed notifications
96 : // XXX: (bug 140303). It can notify us when the selection hasn't actually
97 : // XXX: changed, and it notifies us more than once for the same change.
98 : // XXX:
99 : // XXX: The following code attempts to work around the bogus notifications,
100 : // XXX: and should probably be removed once bug 140303 is fixed.
101 : // XXX:
102 : // XXX: This code temporarily fixes the problem where clicking the mouse in
103 : // XXX: the same location clears the type-in-state.
104 :
105 0 : if (aSelection)
106 : {
107 0 : bool isCollapsed = false;
108 0 : nsresult result = aSelection->GetIsCollapsed(&isCollapsed);
109 0 : NS_ENSURE_SUCCESS(result, result);
110 :
111 0 : PRInt32 rangeCount = 0;
112 0 : result = aSelection->GetRangeCount(&rangeCount);
113 0 : NS_ENSURE_SUCCESS(result, result);
114 :
115 0 : if (isCollapsed && rangeCount)
116 : {
117 0 : nsCOMPtr<nsIDOMNode> selNode;
118 0 : PRInt32 selOffset = 0;
119 :
120 0 : result = nsEditor::GetStartNodeAndOffset(aSelection, getter_AddRefs(selNode), &selOffset);
121 :
122 0 : NS_ENSURE_SUCCESS(result, result);
123 :
124 0 : if (selNode && selNode == mLastSelectionContainer && selOffset == mLastSelectionOffset)
125 : {
126 : // We got a bogus selection changed notification!
127 0 : return NS_OK;
128 : }
129 :
130 0 : mLastSelectionContainer = selNode;
131 0 : mLastSelectionOffset = selOffset;
132 : }
133 : else
134 : {
135 0 : mLastSelectionContainer = nsnull;
136 0 : mLastSelectionOffset = 0;
137 : }
138 : }
139 :
140 0 : Reset();
141 0 : return NS_OK;
142 : }
143 :
144 0 : void TypeInState::Reset()
145 : {
146 0 : for(PRUint32 i = 0, n = mClearedArray.Length(); i < n; i++) {
147 0 : delete mClearedArray[i];
148 : }
149 0 : mClearedArray.Clear();
150 0 : for(PRUint32 i = 0, n = mSetArray.Length(); i < n; i++) {
151 0 : delete mSetArray[i];
152 : }
153 0 : mSetArray.Clear();
154 0 : }
155 :
156 :
157 0 : nsresult TypeInState::SetProp(nsIAtom *aProp, const nsString &aAttr, const nsString &aValue)
158 : {
159 : // special case for big/small, these nest
160 0 : if (nsEditProperty::big == aProp)
161 : {
162 0 : mRelativeFontSize++;
163 0 : return NS_OK;
164 : }
165 0 : if (nsEditProperty::small == aProp)
166 : {
167 0 : mRelativeFontSize--;
168 0 : return NS_OK;
169 : }
170 :
171 : PRInt32 index;
172 : PropItem *item;
173 :
174 0 : if (IsPropSet(aProp,aAttr,nsnull,index))
175 : {
176 : // if it's already set, update the value
177 0 : item = mSetArray[index];
178 0 : item->value = aValue;
179 : }
180 : else
181 : {
182 : // make a new propitem
183 0 : item = new PropItem(aProp,aAttr,aValue);
184 0 : NS_ENSURE_TRUE(item, NS_ERROR_OUT_OF_MEMORY);
185 :
186 : // add it to the list of set properties
187 0 : mSetArray.AppendElement(item);
188 :
189 : // remove it from the list of cleared properties, if we have a match
190 0 : RemovePropFromClearedList(aProp,aAttr);
191 : }
192 :
193 0 : return NS_OK;
194 : }
195 :
196 :
197 0 : nsresult TypeInState::ClearAllProps()
198 : {
199 : // null prop means "all" props
200 0 : return ClearProp(nsnull,EmptyString());
201 : }
202 :
203 0 : nsresult TypeInState::ClearProp(nsIAtom *aProp, const nsString &aAttr)
204 : {
205 : // if it's already cleared we are done
206 0 : if (IsPropCleared(aProp,aAttr)) return NS_OK;
207 :
208 : // make a new propitem
209 0 : PropItem *item = new PropItem(aProp,aAttr,EmptyString());
210 0 : NS_ENSURE_TRUE(item, NS_ERROR_OUT_OF_MEMORY);
211 :
212 : // remove it from the list of set properties, if we have a match
213 0 : RemovePropFromSetList(aProp,aAttr);
214 :
215 : // add it to the list of cleared properties
216 0 : mClearedArray.AppendElement(item);
217 :
218 0 : return NS_OK;
219 : }
220 :
221 :
222 : /***************************************************************************
223 : * TakeClearProperty: hands back next property item on the clear list.
224 : * caller assumes ownership of PropItem and must delete it.
225 : */
226 0 : nsresult TypeInState::TakeClearProperty(PropItem **outPropItem)
227 : {
228 0 : NS_ENSURE_TRUE(outPropItem, NS_ERROR_NULL_POINTER);
229 0 : *outPropItem = nsnull;
230 0 : PRUint32 count = mClearedArray.Length();
231 0 : if (count)
232 : {
233 0 : count--; // indizes are zero based
234 0 : *outPropItem = mClearedArray[count];
235 0 : mClearedArray.RemoveElementAt(count);
236 : }
237 0 : return NS_OK;
238 : }
239 :
240 : /***************************************************************************
241 : * TakeSetProperty: hands back next poroperty item on the set list.
242 : * caller assumes ownership of PropItem and must delete it.
243 : */
244 0 : nsresult TypeInState::TakeSetProperty(PropItem **outPropItem)
245 : {
246 0 : NS_ENSURE_TRUE(outPropItem, NS_ERROR_NULL_POINTER);
247 0 : *outPropItem = nsnull;
248 0 : PRUint32 count = mSetArray.Length();
249 0 : if (count)
250 : {
251 0 : count--; // indizes are zero based
252 0 : *outPropItem = mSetArray[count];
253 0 : mSetArray.RemoveElementAt(count);
254 : }
255 0 : return NS_OK;
256 : }
257 :
258 : //**************************************************************************
259 : // TakeRelativeFontSize: hands back relative font value, which is then
260 : // cleared out.
261 0 : nsresult TypeInState::TakeRelativeFontSize(PRInt32 *outRelSize)
262 : {
263 0 : NS_ENSURE_TRUE(outRelSize, NS_ERROR_NULL_POINTER);
264 0 : *outRelSize = mRelativeFontSize;
265 0 : mRelativeFontSize = 0;
266 0 : return NS_OK;
267 : }
268 :
269 0 : nsresult TypeInState::GetTypingState(bool &isSet, bool &theSetting, nsIAtom *aProp)
270 : {
271 0 : return GetTypingState(isSet, theSetting, aProp, EmptyString(), nsnull);
272 : }
273 :
274 0 : nsresult TypeInState::GetTypingState(bool &isSet,
275 : bool &theSetting,
276 : nsIAtom *aProp,
277 : const nsString &aAttr,
278 : nsString *aValue)
279 : {
280 0 : if (IsPropSet(aProp, aAttr, aValue))
281 : {
282 0 : isSet = true;
283 0 : theSetting = true;
284 : }
285 0 : else if (IsPropCleared(aProp, aAttr))
286 : {
287 0 : isSet = true;
288 0 : theSetting = false;
289 : }
290 : else
291 : {
292 0 : isSet = false;
293 : }
294 0 : return NS_OK;
295 : }
296 :
297 :
298 :
299 : /********************************************************************
300 : * protected methods
301 : *******************************************************************/
302 :
303 0 : nsresult TypeInState::RemovePropFromSetList(nsIAtom *aProp,
304 : const nsString &aAttr)
305 : {
306 : PRInt32 index;
307 0 : if (!aProp)
308 : {
309 : // clear _all_ props
310 0 : for(PRUint32 i = 0, n = mSetArray.Length(); i < n; i++) {
311 0 : delete mSetArray[i];
312 : }
313 0 : mSetArray.Clear();
314 0 : mRelativeFontSize=0;
315 : }
316 0 : else if (FindPropInList(aProp, aAttr, nsnull, mSetArray, index))
317 : {
318 0 : delete mSetArray[index];
319 0 : mSetArray.RemoveElementAt(index);
320 : }
321 0 : return NS_OK;
322 : }
323 :
324 :
325 0 : nsresult TypeInState::RemovePropFromClearedList(nsIAtom *aProp,
326 : const nsString &aAttr)
327 : {
328 : PRInt32 index;
329 0 : if (FindPropInList(aProp, aAttr, nsnull, mClearedArray, index))
330 : {
331 0 : delete mClearedArray[index];
332 0 : mClearedArray.RemoveElementAt(index);
333 : }
334 0 : return NS_OK;
335 : }
336 :
337 :
338 0 : bool TypeInState::IsPropSet(nsIAtom *aProp,
339 : const nsString &aAttr,
340 : nsString* outValue)
341 : {
342 : PRInt32 i;
343 0 : return IsPropSet(aProp, aAttr, outValue, i);
344 : }
345 :
346 :
347 0 : bool TypeInState::IsPropSet(nsIAtom *aProp,
348 : const nsString &aAttr,
349 : nsString *outValue,
350 : PRInt32 &outIndex)
351 : {
352 : // linear search. list should be short.
353 0 : PRUint32 i, count = mSetArray.Length();
354 0 : for (i=0; i<count; i++)
355 : {
356 0 : PropItem *item = mSetArray[i];
357 0 : if ( (item->tag == aProp) &&
358 0 : (item->attr == aAttr) )
359 : {
360 0 : if (outValue) *outValue = item->value;
361 0 : outIndex = i;
362 0 : return true;
363 : }
364 : }
365 0 : return false;
366 : }
367 :
368 :
369 0 : bool TypeInState::IsPropCleared(nsIAtom *aProp,
370 : const nsString &aAttr)
371 : {
372 : PRInt32 i;
373 0 : return IsPropCleared(aProp, aAttr, i);
374 : }
375 :
376 :
377 0 : bool TypeInState::IsPropCleared(nsIAtom *aProp,
378 : const nsString &aAttr,
379 : PRInt32 &outIndex)
380 : {
381 0 : if (FindPropInList(aProp, aAttr, nsnull, mClearedArray, outIndex))
382 0 : return true;
383 0 : if (FindPropInList(0, EmptyString(), nsnull, mClearedArray, outIndex))
384 : {
385 : // special case for all props cleared
386 0 : outIndex = -1;
387 0 : return true;
388 : }
389 0 : return false;
390 : }
391 :
392 0 : bool TypeInState::FindPropInList(nsIAtom *aProp,
393 : const nsAString &aAttr,
394 : nsAString *outValue,
395 : nsTArray<PropItem*> &aList,
396 : PRInt32 &outIndex)
397 : {
398 : // linear search. list should be short.
399 0 : PRUint32 i, count = aList.Length();
400 0 : for (i=0; i<count; i++)
401 : {
402 0 : PropItem *item = aList[i];
403 0 : if ( (item->tag == aProp) &&
404 0 : (item->attr == aAttr) )
405 : {
406 0 : if (outValue) *outValue = item->value;
407 0 : outIndex = i;
408 0 : return true;
409 : }
410 : }
411 0 : return false;
412 : }
413 :
414 :
415 :
416 : /********************************************************************
417 : * PropItem: helper struct for TypeInState
418 : *******************************************************************/
419 :
420 0 : PropItem::PropItem() :
421 : tag(nsnull)
422 : ,attr()
423 0 : ,value()
424 : {
425 0 : MOZ_COUNT_CTOR(PropItem);
426 0 : }
427 :
428 0 : PropItem::PropItem(nsIAtom *aTag, const nsAString &aAttr, const nsAString &aValue) :
429 : tag(aTag)
430 : ,attr(aAttr)
431 0 : ,value(aValue)
432 : {
433 0 : MOZ_COUNT_CTOR(PropItem);
434 0 : }
435 :
436 0 : PropItem::~PropItem()
437 : {
438 0 : MOZ_COUNT_DTOR(PropItem);
439 4392 : }
|