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