Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : #include "TypeInState.h"
7 :
8 : #include <stddef.h>
9 :
10 : #include "nsError.h"
11 : #include "mozilla/EditorBase.h"
12 : #include "mozilla/mozalloc.h"
13 : #include "mozilla/dom/Selection.h"
14 : #include "nsAString.h"
15 : #include "nsDebug.h"
16 : #include "nsGkAtoms.h"
17 : #include "nsIDOMNode.h"
18 : #include "nsISupportsBase.h"
19 : #include "nsISupportsImpl.h"
20 : #include "nsReadableUtils.h"
21 : #include "nsStringFwd.h"
22 :
23 : class nsIAtom;
24 : class nsIDOMDocument;
25 :
26 : namespace mozilla {
27 :
28 : using namespace dom;
29 :
30 : /********************************************************************
31 : * mozilla::TypeInState
32 : *******************************************************************/
33 :
34 0 : NS_IMPL_CYCLE_COLLECTION(TypeInState, mLastSelectionContainer)
35 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(TypeInState)
36 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(TypeInState)
37 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TypeInState)
38 0 : NS_INTERFACE_MAP_ENTRY(nsISelectionListener)
39 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
40 0 : NS_INTERFACE_MAP_END
41 :
42 0 : TypeInState::TypeInState()
43 : : mRelativeFontSize(0)
44 0 : , mLastSelectionOffset(0)
45 : {
46 0 : Reset();
47 0 : }
48 :
49 0 : TypeInState::~TypeInState()
50 : {
51 : // Call Reset() to release any data that may be in
52 : // mClearedArray and mSetArray.
53 :
54 0 : Reset();
55 0 : }
56 :
57 : nsresult
58 0 : TypeInState::UpdateSelState(Selection* aSelection)
59 : {
60 0 : NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
61 :
62 0 : if (!aSelection->Collapsed()) {
63 0 : return NS_OK;
64 : }
65 :
66 0 : return EditorBase::GetStartNodeAndOffset(
67 0 : aSelection, getter_AddRefs(mLastSelectionContainer),
68 0 : &mLastSelectionOffset);
69 : }
70 :
71 :
72 : NS_IMETHODIMP
73 0 : TypeInState::NotifySelectionChanged(nsIDOMDocument* aDOMDocument,
74 : nsISelection* aSelection,
75 : int16_t aReason)
76 : {
77 : // XXX: Selection currently generates bogus selection changed notifications
78 : // XXX: (bug 140303). It can notify us when the selection hasn't actually
79 : // XXX: changed, and it notifies us more than once for the same change.
80 : // XXX:
81 : // XXX: The following code attempts to work around the bogus notifications,
82 : // XXX: and should probably be removed once bug 140303 is fixed.
83 : // XXX:
84 : // XXX: This code temporarily fixes the problem where clicking the mouse in
85 : // XXX: the same location clears the type-in-state.
86 : RefPtr<Selection> selection =
87 0 : aSelection ? aSelection->AsSelection() : nullptr;
88 :
89 0 : if (aSelection) {
90 0 : int32_t rangeCount = selection->RangeCount();
91 :
92 0 : if (selection->Collapsed() && rangeCount) {
93 0 : nsCOMPtr<nsIDOMNode> selNode;
94 0 : int32_t selOffset = 0;
95 :
96 : nsresult rv =
97 0 : EditorBase::GetStartNodeAndOffset(selection, getter_AddRefs(selNode),
98 0 : &selOffset);
99 :
100 0 : NS_ENSURE_SUCCESS(rv, rv);
101 :
102 0 : if (selNode &&
103 0 : selNode == mLastSelectionContainer &&
104 0 : selOffset == mLastSelectionOffset) {
105 : // We got a bogus selection changed notification!
106 0 : return NS_OK;
107 : }
108 :
109 0 : mLastSelectionContainer = selNode;
110 0 : mLastSelectionOffset = selOffset;
111 : } else {
112 0 : mLastSelectionContainer = nullptr;
113 0 : mLastSelectionOffset = 0;
114 : }
115 : }
116 :
117 0 : Reset();
118 0 : return NS_OK;
119 : }
120 :
121 : void
122 0 : TypeInState::Reset()
123 : {
124 0 : for (size_t i = 0, n = mClearedArray.Length(); i < n; i++) {
125 0 : delete mClearedArray[i];
126 : }
127 0 : mClearedArray.Clear();
128 0 : for (size_t i = 0, n = mSetArray.Length(); i < n; i++) {
129 0 : delete mSetArray[i];
130 : }
131 0 : mSetArray.Clear();
132 0 : }
133 :
134 :
135 : void
136 0 : TypeInState::SetProp(nsIAtom* aProp,
137 : const nsAString& aAttr,
138 : const nsAString& aValue)
139 : {
140 : // special case for big/small, these nest
141 0 : if (nsGkAtoms::big == aProp) {
142 0 : mRelativeFontSize++;
143 0 : return;
144 : }
145 0 : if (nsGkAtoms::small == aProp) {
146 0 : mRelativeFontSize--;
147 0 : return;
148 : }
149 :
150 : int32_t index;
151 0 : if (IsPropSet(aProp, aAttr, nullptr, index)) {
152 : // if it's already set, update the value
153 0 : mSetArray[index]->value = aValue;
154 0 : return;
155 : }
156 :
157 : // Make a new propitem and add it to the list of set properties.
158 0 : mSetArray.AppendElement(new PropItem(aProp, aAttr, aValue));
159 :
160 : // remove it from the list of cleared properties, if we have a match
161 0 : RemovePropFromClearedList(aProp, aAttr);
162 : }
163 :
164 :
165 : void
166 0 : TypeInState::ClearAllProps()
167 : {
168 : // null prop means "all" props
169 0 : ClearProp(nullptr, EmptyString());
170 0 : }
171 :
172 : void
173 0 : TypeInState::ClearProp(nsIAtom* aProp,
174 : const nsAString& aAttr)
175 : {
176 : // if it's already cleared we are done
177 0 : if (IsPropCleared(aProp, aAttr)) {
178 0 : return;
179 : }
180 :
181 : // make a new propitem
182 0 : PropItem* item = new PropItem(aProp, aAttr, EmptyString());
183 :
184 : // remove it from the list of set properties, if we have a match
185 0 : RemovePropFromSetList(aProp, aAttr);
186 :
187 : // add it to the list of cleared properties
188 0 : mClearedArray.AppendElement(item);
189 : }
190 :
191 :
192 : /**
193 : * TakeClearProperty() hands back next property item on the clear list.
194 : * Caller assumes ownership of PropItem and must delete it.
195 : */
196 : UniquePtr<PropItem>
197 0 : TypeInState::TakeClearProperty()
198 : {
199 0 : size_t count = mClearedArray.Length();
200 0 : if (!count) {
201 0 : return nullptr;
202 : }
203 :
204 0 : --count; // indices are zero based
205 0 : PropItem* propItem = mClearedArray[count];
206 0 : mClearedArray.RemoveElementAt(count);
207 0 : return UniquePtr<PropItem>(propItem);
208 : }
209 :
210 : /**
211 : * TakeSetProperty() hands back next poroperty item on the set list.
212 : * Caller assumes ownership of PropItem and must delete it.
213 : */
214 : UniquePtr<PropItem>
215 0 : TypeInState::TakeSetProperty()
216 : {
217 0 : size_t count = mSetArray.Length();
218 0 : if (!count) {
219 0 : return nullptr;
220 : }
221 0 : count--; // indices are zero based
222 0 : PropItem* propItem = mSetArray[count];
223 0 : mSetArray.RemoveElementAt(count);
224 0 : return UniquePtr<PropItem>(propItem);
225 : }
226 :
227 : /**
228 : * TakeRelativeFontSize() hands back relative font value, which is then
229 : * cleared out.
230 : */
231 : int32_t
232 0 : TypeInState::TakeRelativeFontSize()
233 : {
234 0 : int32_t relSize = mRelativeFontSize;
235 0 : mRelativeFontSize = 0;
236 0 : return relSize;
237 : }
238 :
239 : void
240 0 : TypeInState::GetTypingState(bool& isSet,
241 : bool& theSetting,
242 : nsIAtom* aProp)
243 : {
244 0 : GetTypingState(isSet, theSetting, aProp, EmptyString(), nullptr);
245 0 : }
246 :
247 : void
248 0 : TypeInState::GetTypingState(bool& isSet,
249 : bool& theSetting,
250 : nsIAtom* aProp,
251 : const nsString& aAttr,
252 : nsString* aValue)
253 : {
254 0 : if (IsPropSet(aProp, aAttr, aValue)) {
255 0 : isSet = true;
256 0 : theSetting = true;
257 0 : } else if (IsPropCleared(aProp, aAttr)) {
258 0 : isSet = true;
259 0 : theSetting = false;
260 : } else {
261 0 : isSet = false;
262 : }
263 0 : }
264 :
265 : void
266 0 : TypeInState::RemovePropFromSetList(nsIAtom* aProp,
267 : const nsAString& aAttr)
268 : {
269 : int32_t index;
270 0 : if (!aProp) {
271 : // clear _all_ props
272 0 : for (size_t i = 0, n = mSetArray.Length(); i < n; i++) {
273 0 : delete mSetArray[i];
274 : }
275 0 : mSetArray.Clear();
276 0 : mRelativeFontSize=0;
277 0 : } else if (FindPropInList(aProp, aAttr, nullptr, mSetArray, index)) {
278 0 : delete mSetArray[index];
279 0 : mSetArray.RemoveElementAt(index);
280 : }
281 0 : }
282 :
283 : void
284 0 : TypeInState::RemovePropFromClearedList(nsIAtom* aProp,
285 : const nsAString& aAttr)
286 : {
287 : int32_t index;
288 0 : if (FindPropInList(aProp, aAttr, nullptr, mClearedArray, index)) {
289 0 : delete mClearedArray[index];
290 0 : mClearedArray.RemoveElementAt(index);
291 : }
292 0 : }
293 :
294 : bool
295 0 : TypeInState::IsPropSet(nsIAtom* aProp,
296 : const nsAString& aAttr,
297 : nsAString* outValue)
298 : {
299 : int32_t i;
300 0 : return IsPropSet(aProp, aAttr, outValue, i);
301 : }
302 :
303 : bool
304 0 : TypeInState::IsPropSet(nsIAtom* aProp,
305 : const nsAString& aAttr,
306 : nsAString* outValue,
307 : int32_t& outIndex)
308 : {
309 : // linear search. list should be short.
310 0 : size_t count = mSetArray.Length();
311 0 : for (size_t i = 0; i < count; i++) {
312 0 : PropItem *item = mSetArray[i];
313 0 : if (item->tag == aProp && item->attr == aAttr) {
314 0 : if (outValue) {
315 0 : *outValue = item->value;
316 : }
317 0 : outIndex = i;
318 0 : return true;
319 : }
320 : }
321 0 : return false;
322 : }
323 :
324 :
325 : bool
326 0 : TypeInState::IsPropCleared(nsIAtom* aProp,
327 : const nsAString& aAttr)
328 : {
329 : int32_t i;
330 0 : return IsPropCleared(aProp, aAttr, i);
331 : }
332 :
333 :
334 : bool
335 0 : TypeInState::IsPropCleared(nsIAtom* aProp,
336 : const nsAString& aAttr,
337 : int32_t& outIndex)
338 : {
339 0 : if (FindPropInList(aProp, aAttr, nullptr, mClearedArray, outIndex)) {
340 0 : return true;
341 : }
342 0 : if (FindPropInList(0, EmptyString(), nullptr, mClearedArray, outIndex)) {
343 : // special case for all props cleared
344 0 : outIndex = -1;
345 0 : return true;
346 : }
347 0 : return false;
348 : }
349 :
350 : bool
351 0 : TypeInState::FindPropInList(nsIAtom* aProp,
352 : const nsAString& aAttr,
353 : nsAString* outValue,
354 : nsTArray<PropItem*>& aList,
355 : int32_t& outIndex)
356 : {
357 : // linear search. list should be short.
358 0 : size_t count = aList.Length();
359 0 : for (size_t i = 0; i < count; i++) {
360 0 : PropItem *item = aList[i];
361 0 : if (item->tag == aProp && item->attr == aAttr) {
362 0 : if (outValue) {
363 0 : *outValue = item->value;
364 : }
365 0 : outIndex = i;
366 0 : return true;
367 : }
368 : }
369 0 : return false;
370 : }
371 :
372 : /********************************************************************
373 : * mozilla::PropItem: helper struct for mozilla::TypeInState
374 : *******************************************************************/
375 :
376 0 : PropItem::PropItem()
377 0 : : tag(nullptr)
378 : {
379 0 : MOZ_COUNT_CTOR(PropItem);
380 0 : }
381 :
382 0 : PropItem::PropItem(nsIAtom* aTag,
383 : const nsAString& aAttr,
384 0 : const nsAString &aValue)
385 : : tag(aTag)
386 : , attr(aAttr)
387 0 : , value(aValue)
388 : {
389 0 : MOZ_COUNT_CTOR(PropItem);
390 0 : }
391 :
392 0 : PropItem::~PropItem()
393 : {
394 0 : MOZ_COUNT_DTOR(PropItem);
395 0 : }
396 :
397 : } // namespace mozilla
|