|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- |
|
2 * |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "mozilla/mozalloc.h" // for operator new |
|
8 #include "nsAString.h" |
|
9 #include "nsComponentManagerUtils.h" // for do_CreateInstance |
|
10 #include "nsComposerCommandsUpdater.h" |
|
11 #include "nsDebug.h" // for NS_ENSURE_TRUE, etc |
|
12 #include "nsError.h" // for NS_OK, NS_ERROR_FAILURE, etc |
|
13 #include "nsICommandManager.h" // for nsICommandManager |
|
14 #include "nsID.h" // for NS_GET_IID, etc |
|
15 #include "nsIDOMWindow.h" // for nsIDOMWindow |
|
16 #include "nsIDocShell.h" // for nsIDocShell |
|
17 #include "nsIInterfaceRequestorUtils.h" // for do_GetInterface |
|
18 #include "nsISelection.h" // for nsISelection |
|
19 #include "nsITransactionManager.h" // for nsITransactionManager |
|
20 #include "nsLiteralString.h" // for NS_LITERAL_STRING |
|
21 #include "nsPICommandUpdater.h" // for nsPICommandUpdater |
|
22 #include "nsPIDOMWindow.h" // for nsPIDOMWindow |
|
23 |
|
24 class nsIDOMDocument; |
|
25 class nsITransaction; |
|
26 |
|
27 nsComposerCommandsUpdater::nsComposerCommandsUpdater() |
|
28 : mDirtyState(eStateUninitialized) |
|
29 , mSelectionCollapsed(eStateUninitialized) |
|
30 , mFirstDoOfFirstUndo(true) |
|
31 { |
|
32 } |
|
33 |
|
34 nsComposerCommandsUpdater::~nsComposerCommandsUpdater() |
|
35 { |
|
36 // cancel any outstanding update timer |
|
37 if (mUpdateTimer) |
|
38 { |
|
39 mUpdateTimer->Cancel(); |
|
40 } |
|
41 } |
|
42 |
|
43 NS_IMPL_ISUPPORTS(nsComposerCommandsUpdater, nsISelectionListener, |
|
44 nsIDocumentStateListener, nsITransactionListener, nsITimerCallback) |
|
45 |
|
46 #if 0 |
|
47 #pragma mark - |
|
48 #endif |
|
49 |
|
50 NS_IMETHODIMP |
|
51 nsComposerCommandsUpdater::NotifyDocumentCreated() |
|
52 { |
|
53 // Trigger an nsIObserve notification that the document has been created |
|
54 UpdateOneCommand("obs_documentCreated"); |
|
55 return NS_OK; |
|
56 } |
|
57 |
|
58 NS_IMETHODIMP |
|
59 nsComposerCommandsUpdater::NotifyDocumentWillBeDestroyed() |
|
60 { |
|
61 // cancel any outstanding update timer |
|
62 if (mUpdateTimer) |
|
63 { |
|
64 mUpdateTimer->Cancel(); |
|
65 mUpdateTimer = nullptr; |
|
66 } |
|
67 |
|
68 // We can't call this right now; it is too late in some cases and the window |
|
69 // is already partially destructed (e.g. JS objects may be gone). |
|
70 #if 0 |
|
71 // Trigger an nsIObserve notification that the document will be destroyed |
|
72 UpdateOneCommand("obs_documentWillBeDestroyed"); |
|
73 #endif |
|
74 return NS_OK; |
|
75 } |
|
76 |
|
77 |
|
78 NS_IMETHODIMP |
|
79 nsComposerCommandsUpdater::NotifyDocumentStateChanged(bool aNowDirty) |
|
80 { |
|
81 // update document modified. We should have some other notifications for this too. |
|
82 return UpdateDirtyState(aNowDirty); |
|
83 } |
|
84 |
|
85 NS_IMETHODIMP |
|
86 nsComposerCommandsUpdater::NotifySelectionChanged(nsIDOMDocument *, |
|
87 nsISelection *, int16_t) |
|
88 { |
|
89 return PrimeUpdateTimer(); |
|
90 } |
|
91 |
|
92 #if 0 |
|
93 #pragma mark - |
|
94 #endif |
|
95 |
|
96 NS_IMETHODIMP |
|
97 nsComposerCommandsUpdater::WillDo(nsITransactionManager *aManager, |
|
98 nsITransaction *aTransaction, bool *aInterrupt) |
|
99 { |
|
100 *aInterrupt = false; |
|
101 return NS_OK; |
|
102 } |
|
103 |
|
104 NS_IMETHODIMP |
|
105 nsComposerCommandsUpdater::DidDo(nsITransactionManager *aManager, |
|
106 nsITransaction *aTransaction, nsresult aDoResult) |
|
107 { |
|
108 // only need to update if the status of the Undo menu item changes. |
|
109 int32_t undoCount; |
|
110 aManager->GetNumberOfUndoItems(&undoCount); |
|
111 if (undoCount == 1) |
|
112 { |
|
113 if (mFirstDoOfFirstUndo) |
|
114 UpdateCommandGroup(NS_LITERAL_STRING("undo")); |
|
115 mFirstDoOfFirstUndo = false; |
|
116 } |
|
117 |
|
118 return NS_OK; |
|
119 } |
|
120 |
|
121 NS_IMETHODIMP |
|
122 nsComposerCommandsUpdater::WillUndo(nsITransactionManager *aManager, |
|
123 nsITransaction *aTransaction, |
|
124 bool *aInterrupt) |
|
125 { |
|
126 *aInterrupt = false; |
|
127 return NS_OK; |
|
128 } |
|
129 |
|
130 NS_IMETHODIMP |
|
131 nsComposerCommandsUpdater::DidUndo(nsITransactionManager *aManager, |
|
132 nsITransaction *aTransaction, |
|
133 nsresult aUndoResult) |
|
134 { |
|
135 int32_t undoCount; |
|
136 aManager->GetNumberOfUndoItems(&undoCount); |
|
137 if (undoCount == 0) |
|
138 mFirstDoOfFirstUndo = true; // reset the state for the next do |
|
139 |
|
140 UpdateCommandGroup(NS_LITERAL_STRING("undo")); |
|
141 return NS_OK; |
|
142 } |
|
143 |
|
144 NS_IMETHODIMP |
|
145 nsComposerCommandsUpdater::WillRedo(nsITransactionManager *aManager, |
|
146 nsITransaction *aTransaction, |
|
147 bool *aInterrupt) |
|
148 { |
|
149 *aInterrupt = false; |
|
150 return NS_OK; |
|
151 } |
|
152 |
|
153 NS_IMETHODIMP |
|
154 nsComposerCommandsUpdater::DidRedo(nsITransactionManager *aManager, |
|
155 nsITransaction *aTransaction, |
|
156 nsresult aRedoResult) |
|
157 { |
|
158 UpdateCommandGroup(NS_LITERAL_STRING("undo")); |
|
159 return NS_OK; |
|
160 } |
|
161 |
|
162 NS_IMETHODIMP |
|
163 nsComposerCommandsUpdater::WillBeginBatch(nsITransactionManager *aManager, |
|
164 bool *aInterrupt) |
|
165 { |
|
166 *aInterrupt = false; |
|
167 return NS_OK; |
|
168 } |
|
169 |
|
170 NS_IMETHODIMP |
|
171 nsComposerCommandsUpdater::DidBeginBatch(nsITransactionManager *aManager, |
|
172 nsresult aResult) |
|
173 { |
|
174 return NS_OK; |
|
175 } |
|
176 |
|
177 NS_IMETHODIMP |
|
178 nsComposerCommandsUpdater::WillEndBatch(nsITransactionManager *aManager, |
|
179 bool *aInterrupt) |
|
180 { |
|
181 *aInterrupt = false; |
|
182 return NS_OK; |
|
183 } |
|
184 |
|
185 NS_IMETHODIMP |
|
186 nsComposerCommandsUpdater::DidEndBatch(nsITransactionManager *aManager, |
|
187 nsresult aResult) |
|
188 { |
|
189 return NS_OK; |
|
190 } |
|
191 |
|
192 NS_IMETHODIMP |
|
193 nsComposerCommandsUpdater::WillMerge(nsITransactionManager *aManager, |
|
194 nsITransaction *aTopTransaction, |
|
195 nsITransaction *aTransactionToMerge, |
|
196 bool *aInterrupt) |
|
197 { |
|
198 *aInterrupt = false; |
|
199 return NS_OK; |
|
200 } |
|
201 |
|
202 NS_IMETHODIMP |
|
203 nsComposerCommandsUpdater::DidMerge(nsITransactionManager *aManager, |
|
204 nsITransaction *aTopTransaction, |
|
205 nsITransaction *aTransactionToMerge, |
|
206 bool aDidMerge, nsresult aMergeResult) |
|
207 { |
|
208 return NS_OK; |
|
209 } |
|
210 |
|
211 #if 0 |
|
212 #pragma mark - |
|
213 #endif |
|
214 |
|
215 nsresult |
|
216 nsComposerCommandsUpdater::Init(nsIDOMWindow* aDOMWindow) |
|
217 { |
|
218 NS_ENSURE_ARG(aDOMWindow); |
|
219 mDOMWindow = do_GetWeakReference(aDOMWindow); |
|
220 |
|
221 nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aDOMWindow)); |
|
222 if (window) |
|
223 { |
|
224 mDocShell = do_GetWeakReference(window->GetDocShell()); |
|
225 } |
|
226 return NS_OK; |
|
227 } |
|
228 |
|
229 nsresult |
|
230 nsComposerCommandsUpdater::PrimeUpdateTimer() |
|
231 { |
|
232 if (!mUpdateTimer) |
|
233 { |
|
234 nsresult rv = NS_OK; |
|
235 mUpdateTimer = do_CreateInstance("@mozilla.org/timer;1", &rv); |
|
236 NS_ENSURE_SUCCESS(rv, rv); |
|
237 } |
|
238 |
|
239 const uint32_t kUpdateTimerDelay = 150; |
|
240 return mUpdateTimer->InitWithCallback(static_cast<nsITimerCallback*>(this), |
|
241 kUpdateTimerDelay, |
|
242 nsITimer::TYPE_ONE_SHOT); |
|
243 } |
|
244 |
|
245 |
|
246 void nsComposerCommandsUpdater::TimerCallback() |
|
247 { |
|
248 // if the selection state has changed, update stuff |
|
249 bool isCollapsed = SelectionIsCollapsed(); |
|
250 if (static_cast<int8_t>(isCollapsed) != mSelectionCollapsed) |
|
251 { |
|
252 UpdateCommandGroup(NS_LITERAL_STRING("select")); |
|
253 mSelectionCollapsed = isCollapsed; |
|
254 } |
|
255 |
|
256 // isn't this redundant with the UpdateCommandGroup above? |
|
257 // can we just nuke the above call? or create a meta command group? |
|
258 UpdateCommandGroup(NS_LITERAL_STRING("style")); |
|
259 } |
|
260 |
|
261 nsresult |
|
262 nsComposerCommandsUpdater::UpdateDirtyState(bool aNowDirty) |
|
263 { |
|
264 if (mDirtyState != static_cast<int8_t>(aNowDirty)) |
|
265 { |
|
266 UpdateCommandGroup(NS_LITERAL_STRING("save")); |
|
267 UpdateCommandGroup(NS_LITERAL_STRING("undo")); |
|
268 mDirtyState = aNowDirty; |
|
269 } |
|
270 |
|
271 return NS_OK; |
|
272 } |
|
273 |
|
274 nsresult |
|
275 nsComposerCommandsUpdater::UpdateCommandGroup(const nsAString& aCommandGroup) |
|
276 { |
|
277 nsCOMPtr<nsPICommandUpdater> commandUpdater = GetCommandUpdater(); |
|
278 NS_ENSURE_TRUE(commandUpdater, NS_ERROR_FAILURE); |
|
279 |
|
280 |
|
281 // This hardcoded list of commands is temporary. |
|
282 // This code should use nsIControllerCommandGroup. |
|
283 if (aCommandGroup.EqualsLiteral("undo")) |
|
284 { |
|
285 commandUpdater->CommandStatusChanged("cmd_undo"); |
|
286 commandUpdater->CommandStatusChanged("cmd_redo"); |
|
287 } |
|
288 else if (aCommandGroup.EqualsLiteral("select") || |
|
289 aCommandGroup.EqualsLiteral("style")) |
|
290 { |
|
291 commandUpdater->CommandStatusChanged("cmd_bold"); |
|
292 commandUpdater->CommandStatusChanged("cmd_italic"); |
|
293 commandUpdater->CommandStatusChanged("cmd_underline"); |
|
294 commandUpdater->CommandStatusChanged("cmd_tt"); |
|
295 |
|
296 commandUpdater->CommandStatusChanged("cmd_strikethrough"); |
|
297 commandUpdater->CommandStatusChanged("cmd_superscript"); |
|
298 commandUpdater->CommandStatusChanged("cmd_subscript"); |
|
299 commandUpdater->CommandStatusChanged("cmd_nobreak"); |
|
300 |
|
301 commandUpdater->CommandStatusChanged("cmd_em"); |
|
302 commandUpdater->CommandStatusChanged("cmd_strong"); |
|
303 commandUpdater->CommandStatusChanged("cmd_cite"); |
|
304 commandUpdater->CommandStatusChanged("cmd_abbr"); |
|
305 commandUpdater->CommandStatusChanged("cmd_acronym"); |
|
306 commandUpdater->CommandStatusChanged("cmd_code"); |
|
307 commandUpdater->CommandStatusChanged("cmd_samp"); |
|
308 commandUpdater->CommandStatusChanged("cmd_var"); |
|
309 |
|
310 commandUpdater->CommandStatusChanged("cmd_increaseFont"); |
|
311 commandUpdater->CommandStatusChanged("cmd_decreaseFont"); |
|
312 |
|
313 commandUpdater->CommandStatusChanged("cmd_paragraphState"); |
|
314 commandUpdater->CommandStatusChanged("cmd_fontFace"); |
|
315 commandUpdater->CommandStatusChanged("cmd_fontColor"); |
|
316 commandUpdater->CommandStatusChanged("cmd_backgroundColor"); |
|
317 commandUpdater->CommandStatusChanged("cmd_highlight"); |
|
318 } |
|
319 else if (aCommandGroup.EqualsLiteral("save")) |
|
320 { |
|
321 // save commands (most are not in C++) |
|
322 commandUpdater->CommandStatusChanged("cmd_setDocumentModified"); |
|
323 commandUpdater->CommandStatusChanged("cmd_save"); |
|
324 } |
|
325 return NS_OK; |
|
326 } |
|
327 |
|
328 nsresult |
|
329 nsComposerCommandsUpdater::UpdateOneCommand(const char *aCommand) |
|
330 { |
|
331 nsCOMPtr<nsPICommandUpdater> commandUpdater = GetCommandUpdater(); |
|
332 NS_ENSURE_TRUE(commandUpdater, NS_ERROR_FAILURE); |
|
333 |
|
334 commandUpdater->CommandStatusChanged(aCommand); |
|
335 |
|
336 return NS_OK; |
|
337 } |
|
338 |
|
339 bool |
|
340 nsComposerCommandsUpdater::SelectionIsCollapsed() |
|
341 { |
|
342 nsCOMPtr<nsIDOMWindow> domWindow = do_QueryReferent(mDOMWindow); |
|
343 NS_ENSURE_TRUE(domWindow, true); |
|
344 |
|
345 nsCOMPtr<nsISelection> domSelection; |
|
346 if (NS_SUCCEEDED(domWindow->GetSelection(getter_AddRefs(domSelection))) && domSelection) |
|
347 { |
|
348 bool selectionCollapsed = false; |
|
349 domSelection->GetIsCollapsed(&selectionCollapsed); |
|
350 return selectionCollapsed; |
|
351 } |
|
352 |
|
353 NS_WARNING("nsComposerCommandsUpdater::SelectionIsCollapsed - no domSelection"); |
|
354 |
|
355 return false; |
|
356 } |
|
357 |
|
358 already_AddRefed<nsPICommandUpdater> |
|
359 nsComposerCommandsUpdater::GetCommandUpdater() |
|
360 { |
|
361 nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mDocShell); |
|
362 NS_ENSURE_TRUE(docShell, nullptr); |
|
363 nsCOMPtr<nsICommandManager> manager = do_GetInterface(docShell); |
|
364 nsCOMPtr<nsPICommandUpdater> updater = do_QueryInterface(manager); |
|
365 return updater.forget(); |
|
366 } |
|
367 |
|
368 #if 0 |
|
369 #pragma mark - |
|
370 #endif |
|
371 |
|
372 nsresult |
|
373 nsComposerCommandsUpdater::Notify(nsITimer *timer) |
|
374 { |
|
375 NS_ASSERTION(timer == mUpdateTimer.get(), "Hey, this ain't my timer!"); |
|
376 TimerCallback(); |
|
377 return NS_OK; |
|
378 } |
|
379 |
|
380 #if 0 |
|
381 #pragma mark - |
|
382 #endif |
|
383 |
|
384 |
|
385 nsresult |
|
386 NS_NewComposerCommandsUpdater(nsISelectionListener** aInstancePtrResult) |
|
387 { |
|
388 nsComposerCommandsUpdater* newThang = new nsComposerCommandsUpdater; |
|
389 NS_ENSURE_TRUE(newThang, NS_ERROR_OUT_OF_MEMORY); |
|
390 |
|
391 return newThang->QueryInterface(NS_GET_IID(nsISelectionListener), |
|
392 (void **)aInstancePtrResult); |
|
393 } |