1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
7 #include "mozilla/mozalloc.h" // for operator new
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
27 nsComposerCommandsUpdater::nsComposerCommandsUpdater()
28 : mDirtyState(eStateUninitialized
)
29 , mSelectionCollapsed(eStateUninitialized
)
30 , mFirstDoOfFirstUndo(true)
34 nsComposerCommandsUpdater::~nsComposerCommandsUpdater()
36 // cancel any outstanding update timer
39 mUpdateTimer
->Cancel();
43 NS_IMPL_ISUPPORTS(nsComposerCommandsUpdater
, nsISelectionListener
,
44 nsIDocumentStateListener
, nsITransactionListener
, nsITimerCallback
)
51 nsComposerCommandsUpdater::NotifyDocumentCreated()
53 // Trigger an nsIObserve notification that the document has been created
54 UpdateOneCommand("obs_documentCreated");
59 nsComposerCommandsUpdater::NotifyDocumentWillBeDestroyed()
61 // cancel any outstanding update timer
64 mUpdateTimer
->Cancel();
65 mUpdateTimer
= nullptr;
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).
71 // Trigger an nsIObserve notification that the document will be destroyed
72 UpdateOneCommand("obs_documentWillBeDestroyed");
79 nsComposerCommandsUpdater::NotifyDocumentStateChanged(bool aNowDirty
)
81 // update document modified. We should have some other notifications for this too.
82 return UpdateDirtyState(aNowDirty
);
86 nsComposerCommandsUpdater::NotifySelectionChanged(nsIDOMDocument
*,
87 nsISelection
*, int16_t)
89 return PrimeUpdateTimer();
97 nsComposerCommandsUpdater::WillDo(nsITransactionManager
*aManager
,
98 nsITransaction
*aTransaction
, bool *aInterrupt
)
105 nsComposerCommandsUpdater::DidDo(nsITransactionManager
*aManager
,
106 nsITransaction
*aTransaction
, nsresult aDoResult
)
108 // only need to update if the status of the Undo menu item changes.
110 aManager
->GetNumberOfUndoItems(&undoCount
);
113 if (mFirstDoOfFirstUndo
)
114 UpdateCommandGroup(NS_LITERAL_STRING("undo"));
115 mFirstDoOfFirstUndo
= false;
122 nsComposerCommandsUpdater::WillUndo(nsITransactionManager
*aManager
,
123 nsITransaction
*aTransaction
,
131 nsComposerCommandsUpdater::DidUndo(nsITransactionManager
*aManager
,
132 nsITransaction
*aTransaction
,
133 nsresult aUndoResult
)
136 aManager
->GetNumberOfUndoItems(&undoCount
);
138 mFirstDoOfFirstUndo
= true; // reset the state for the next do
140 UpdateCommandGroup(NS_LITERAL_STRING("undo"));
145 nsComposerCommandsUpdater::WillRedo(nsITransactionManager
*aManager
,
146 nsITransaction
*aTransaction
,
154 nsComposerCommandsUpdater::DidRedo(nsITransactionManager
*aManager
,
155 nsITransaction
*aTransaction
,
156 nsresult aRedoResult
)
158 UpdateCommandGroup(NS_LITERAL_STRING("undo"));
163 nsComposerCommandsUpdater::WillBeginBatch(nsITransactionManager
*aManager
,
171 nsComposerCommandsUpdater::DidBeginBatch(nsITransactionManager
*aManager
,
178 nsComposerCommandsUpdater::WillEndBatch(nsITransactionManager
*aManager
,
186 nsComposerCommandsUpdater::DidEndBatch(nsITransactionManager
*aManager
,
193 nsComposerCommandsUpdater::WillMerge(nsITransactionManager
*aManager
,
194 nsITransaction
*aTopTransaction
,
195 nsITransaction
*aTransactionToMerge
,
203 nsComposerCommandsUpdater::DidMerge(nsITransactionManager
*aManager
,
204 nsITransaction
*aTopTransaction
,
205 nsITransaction
*aTransactionToMerge
,
206 bool aDidMerge
, nsresult aMergeResult
)
216 nsComposerCommandsUpdater::Init(nsIDOMWindow
* aDOMWindow
)
218 NS_ENSURE_ARG(aDOMWindow
);
219 mDOMWindow
= do_GetWeakReference(aDOMWindow
);
221 nsCOMPtr
<nsPIDOMWindow
> window(do_QueryInterface(aDOMWindow
));
224 mDocShell
= do_GetWeakReference(window
->GetDocShell());
230 nsComposerCommandsUpdater::PrimeUpdateTimer()
235 mUpdateTimer
= do_CreateInstance("@mozilla.org/timer;1", &rv
);
236 NS_ENSURE_SUCCESS(rv
, rv
);
239 const uint32_t kUpdateTimerDelay
= 150;
240 return mUpdateTimer
->InitWithCallback(static_cast<nsITimerCallback
*>(this),
242 nsITimer::TYPE_ONE_SHOT
);
246 void nsComposerCommandsUpdater::TimerCallback()
248 // if the selection state has changed, update stuff
249 bool isCollapsed
= SelectionIsCollapsed();
250 if (static_cast<int8_t>(isCollapsed
) != mSelectionCollapsed
)
252 UpdateCommandGroup(NS_LITERAL_STRING("select"));
253 mSelectionCollapsed
= isCollapsed
;
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"));
262 nsComposerCommandsUpdater::UpdateDirtyState(bool aNowDirty
)
264 if (mDirtyState
!= static_cast<int8_t>(aNowDirty
))
266 UpdateCommandGroup(NS_LITERAL_STRING("save"));
267 UpdateCommandGroup(NS_LITERAL_STRING("undo"));
268 mDirtyState
= aNowDirty
;
275 nsComposerCommandsUpdater::UpdateCommandGroup(const nsAString
& aCommandGroup
)
277 nsCOMPtr
<nsPICommandUpdater
> commandUpdater
= GetCommandUpdater();
278 NS_ENSURE_TRUE(commandUpdater
, NS_ERROR_FAILURE
);
281 // This hardcoded list of commands is temporary.
282 // This code should use nsIControllerCommandGroup.
283 if (aCommandGroup
.EqualsLiteral("undo"))
285 commandUpdater
->CommandStatusChanged("cmd_undo");
286 commandUpdater
->CommandStatusChanged("cmd_redo");
288 else if (aCommandGroup
.EqualsLiteral("select") ||
289 aCommandGroup
.EqualsLiteral("style"))
291 commandUpdater
->CommandStatusChanged("cmd_bold");
292 commandUpdater
->CommandStatusChanged("cmd_italic");
293 commandUpdater
->CommandStatusChanged("cmd_underline");
294 commandUpdater
->CommandStatusChanged("cmd_tt");
296 commandUpdater
->CommandStatusChanged("cmd_strikethrough");
297 commandUpdater
->CommandStatusChanged("cmd_superscript");
298 commandUpdater
->CommandStatusChanged("cmd_subscript");
299 commandUpdater
->CommandStatusChanged("cmd_nobreak");
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");
310 commandUpdater
->CommandStatusChanged("cmd_increaseFont");
311 commandUpdater
->CommandStatusChanged("cmd_decreaseFont");
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");
319 else if (aCommandGroup
.EqualsLiteral("save"))
321 // save commands (most are not in C++)
322 commandUpdater
->CommandStatusChanged("cmd_setDocumentModified");
323 commandUpdater
->CommandStatusChanged("cmd_save");
329 nsComposerCommandsUpdater::UpdateOneCommand(const char *aCommand
)
331 nsCOMPtr
<nsPICommandUpdater
> commandUpdater
= GetCommandUpdater();
332 NS_ENSURE_TRUE(commandUpdater
, NS_ERROR_FAILURE
);
334 commandUpdater
->CommandStatusChanged(aCommand
);
340 nsComposerCommandsUpdater::SelectionIsCollapsed()
342 nsCOMPtr
<nsIDOMWindow
> domWindow
= do_QueryReferent(mDOMWindow
);
343 NS_ENSURE_TRUE(domWindow
, true);
345 nsCOMPtr
<nsISelection
> domSelection
;
346 if (NS_SUCCEEDED(domWindow
->GetSelection(getter_AddRefs(domSelection
))) && domSelection
)
348 bool selectionCollapsed
= false;
349 domSelection
->GetIsCollapsed(&selectionCollapsed
);
350 return selectionCollapsed
;
353 NS_WARNING("nsComposerCommandsUpdater::SelectionIsCollapsed - no domSelection");
358 already_AddRefed
<nsPICommandUpdater
>
359 nsComposerCommandsUpdater::GetCommandUpdater()
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();
373 nsComposerCommandsUpdater::Notify(nsITimer
*timer
)
375 NS_ASSERTION(timer
== mUpdateTimer
.get(), "Hey, this ain't my timer!");
386 NS_NewComposerCommandsUpdater(nsISelectionListener
** aInstancePtrResult
)
388 nsComposerCommandsUpdater
* newThang
= new nsComposerCommandsUpdater
;
389 NS_ENSURE_TRUE(newThang
, NS_ERROR_OUT_OF_MEMORY
);
391 return newThang
->QueryInterface(NS_GET_IID(nsISelectionListener
),
392 (void **)aInstancePtrResult
);