diff options
Diffstat (limited to 'editor/composer/nsComposerCommandsUpdater.cpp')
-rw-r--r-- | editor/composer/nsComposerCommandsUpdater.cpp | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/editor/composer/nsComposerCommandsUpdater.cpp b/editor/composer/nsComposerCommandsUpdater.cpp new file mode 100644 index 000000000..4999f7b92 --- /dev/null +++ b/editor/composer/nsComposerCommandsUpdater.cpp @@ -0,0 +1,381 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/mozalloc.h" // for operator new +#include "nsAString.h" +#include "nsComponentManagerUtils.h" // for do_CreateInstance +#include "nsComposerCommandsUpdater.h" +#include "nsDebug.h" // for NS_ENSURE_TRUE, etc +#include "nsError.h" // for NS_OK, NS_ERROR_FAILURE, etc +#include "nsICommandManager.h" // for nsICommandManager +#include "nsID.h" // for NS_GET_IID, etc +#include "nsIDOMWindow.h" // for nsIDOMWindow +#include "nsIDocShell.h" // for nsIDocShell +#include "nsIInterfaceRequestorUtils.h" // for do_GetInterface +#include "nsISelection.h" // for nsISelection +#include "nsITransactionManager.h" // for nsITransactionManager +#include "nsLiteralString.h" // for NS_LITERAL_STRING +#include "nsPICommandUpdater.h" // for nsPICommandUpdater +#include "nsPIDOMWindow.h" // for nsPIDOMWindow + +class nsIDOMDocument; +class nsITransaction; + +nsComposerCommandsUpdater::nsComposerCommandsUpdater() +: mDirtyState(eStateUninitialized) +, mSelectionCollapsed(eStateUninitialized) +, mFirstDoOfFirstUndo(true) +{ +} + +nsComposerCommandsUpdater::~nsComposerCommandsUpdater() +{ + // cancel any outstanding update timer + if (mUpdateTimer) { + mUpdateTimer->Cancel(); + } +} + +NS_IMPL_ISUPPORTS(nsComposerCommandsUpdater, nsISelectionListener, + nsIDocumentStateListener, nsITransactionListener, nsITimerCallback) + +#if 0 +#pragma mark - +#endif + +NS_IMETHODIMP +nsComposerCommandsUpdater::NotifyDocumentCreated() +{ + // Trigger an nsIObserve notification that the document has been created + UpdateOneCommand("obs_documentCreated"); + return NS_OK; +} + +NS_IMETHODIMP +nsComposerCommandsUpdater::NotifyDocumentWillBeDestroyed() +{ + // cancel any outstanding update timer + if (mUpdateTimer) { + mUpdateTimer->Cancel(); + mUpdateTimer = nullptr; + } + + // We can't call this right now; it is too late in some cases and the window + // is already partially destructed (e.g. JS objects may be gone). +#if 0 + // Trigger an nsIObserve notification that the document will be destroyed + UpdateOneCommand("obs_documentWillBeDestroyed"); +#endif + return NS_OK; +} + + +NS_IMETHODIMP +nsComposerCommandsUpdater::NotifyDocumentStateChanged(bool aNowDirty) +{ + // update document modified. We should have some other notifications for this too. + return UpdateDirtyState(aNowDirty); +} + +NS_IMETHODIMP +nsComposerCommandsUpdater::NotifySelectionChanged(nsIDOMDocument *, + nsISelection *, int16_t) +{ + return PrimeUpdateTimer(); +} + +#if 0 +#pragma mark - +#endif + +NS_IMETHODIMP +nsComposerCommandsUpdater::WillDo(nsITransactionManager *aManager, + nsITransaction *aTransaction, bool *aInterrupt) +{ + *aInterrupt = false; + return NS_OK; +} + +NS_IMETHODIMP +nsComposerCommandsUpdater::DidDo(nsITransactionManager *aManager, + nsITransaction *aTransaction, nsresult aDoResult) +{ + // only need to update if the status of the Undo menu item changes. + int32_t undoCount; + aManager->GetNumberOfUndoItems(&undoCount); + if (undoCount == 1) { + if (mFirstDoOfFirstUndo) { + UpdateCommandGroup(NS_LITERAL_STRING("undo")); + } + mFirstDoOfFirstUndo = false; + } + + return NS_OK; +} + +NS_IMETHODIMP +nsComposerCommandsUpdater::WillUndo(nsITransactionManager *aManager, + nsITransaction *aTransaction, + bool *aInterrupt) +{ + *aInterrupt = false; + return NS_OK; +} + +NS_IMETHODIMP +nsComposerCommandsUpdater::DidUndo(nsITransactionManager *aManager, + nsITransaction *aTransaction, + nsresult aUndoResult) +{ + int32_t undoCount; + aManager->GetNumberOfUndoItems(&undoCount); + if (undoCount == 0) + mFirstDoOfFirstUndo = true; // reset the state for the next do + + UpdateCommandGroup(NS_LITERAL_STRING("undo")); + return NS_OK; +} + +NS_IMETHODIMP +nsComposerCommandsUpdater::WillRedo(nsITransactionManager *aManager, + nsITransaction *aTransaction, + bool *aInterrupt) +{ + *aInterrupt = false; + return NS_OK; +} + +NS_IMETHODIMP +nsComposerCommandsUpdater::DidRedo(nsITransactionManager *aManager, + nsITransaction *aTransaction, + nsresult aRedoResult) +{ + UpdateCommandGroup(NS_LITERAL_STRING("undo")); + return NS_OK; +} + +NS_IMETHODIMP +nsComposerCommandsUpdater::WillBeginBatch(nsITransactionManager *aManager, + bool *aInterrupt) +{ + *aInterrupt = false; + return NS_OK; +} + +NS_IMETHODIMP +nsComposerCommandsUpdater::DidBeginBatch(nsITransactionManager *aManager, + nsresult aResult) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsComposerCommandsUpdater::WillEndBatch(nsITransactionManager *aManager, + bool *aInterrupt) +{ + *aInterrupt = false; + return NS_OK; +} + +NS_IMETHODIMP +nsComposerCommandsUpdater::DidEndBatch(nsITransactionManager *aManager, + nsresult aResult) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsComposerCommandsUpdater::WillMerge(nsITransactionManager *aManager, + nsITransaction *aTopTransaction, + nsITransaction *aTransactionToMerge, + bool *aInterrupt) +{ + *aInterrupt = false; + return NS_OK; +} + +NS_IMETHODIMP +nsComposerCommandsUpdater::DidMerge(nsITransactionManager *aManager, + nsITransaction *aTopTransaction, + nsITransaction *aTransactionToMerge, + bool aDidMerge, nsresult aMergeResult) +{ + return NS_OK; +} + +#if 0 +#pragma mark - +#endif + +nsresult +nsComposerCommandsUpdater::Init(nsPIDOMWindowOuter* aDOMWindow) +{ + NS_ENSURE_ARG(aDOMWindow); + mDOMWindow = do_GetWeakReference(aDOMWindow); + mDocShell = do_GetWeakReference(aDOMWindow->GetDocShell()); + return NS_OK; +} + +nsresult +nsComposerCommandsUpdater::PrimeUpdateTimer() +{ + if (!mUpdateTimer) { + nsresult rv = NS_OK; + mUpdateTimer = do_CreateInstance("@mozilla.org/timer;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + } + + const uint32_t kUpdateTimerDelay = 150; + return mUpdateTimer->InitWithCallback(static_cast<nsITimerCallback*>(this), + kUpdateTimerDelay, + nsITimer::TYPE_ONE_SHOT); +} + + +void nsComposerCommandsUpdater::TimerCallback() +{ + // if the selection state has changed, update stuff + bool isCollapsed = SelectionIsCollapsed(); + if (static_cast<int8_t>(isCollapsed) != mSelectionCollapsed) { + UpdateCommandGroup(NS_LITERAL_STRING("select")); + mSelectionCollapsed = isCollapsed; + } + + // isn't this redundant with the UpdateCommandGroup above? + // can we just nuke the above call? or create a meta command group? + UpdateCommandGroup(NS_LITERAL_STRING("style")); +} + +nsresult +nsComposerCommandsUpdater::UpdateDirtyState(bool aNowDirty) +{ + if (mDirtyState != static_cast<int8_t>(aNowDirty)) { + UpdateCommandGroup(NS_LITERAL_STRING("save")); + UpdateCommandGroup(NS_LITERAL_STRING("undo")); + mDirtyState = aNowDirty; + } + + return NS_OK; +} + +nsresult +nsComposerCommandsUpdater::UpdateCommandGroup(const nsAString& aCommandGroup) +{ + nsCOMPtr<nsPICommandUpdater> commandUpdater = GetCommandUpdater(); + NS_ENSURE_TRUE(commandUpdater, NS_ERROR_FAILURE); + + + // This hardcoded list of commands is temporary. + // This code should use nsIControllerCommandGroup. + if (aCommandGroup.EqualsLiteral("undo")) { + commandUpdater->CommandStatusChanged("cmd_undo"); + commandUpdater->CommandStatusChanged("cmd_redo"); + return NS_OK; + } + + if (aCommandGroup.EqualsLiteral("select") || + aCommandGroup.EqualsLiteral("style")) { + commandUpdater->CommandStatusChanged("cmd_bold"); + commandUpdater->CommandStatusChanged("cmd_italic"); + commandUpdater->CommandStatusChanged("cmd_underline"); + commandUpdater->CommandStatusChanged("cmd_tt"); + + commandUpdater->CommandStatusChanged("cmd_strikethrough"); + commandUpdater->CommandStatusChanged("cmd_superscript"); + commandUpdater->CommandStatusChanged("cmd_subscript"); + commandUpdater->CommandStatusChanged("cmd_nobreak"); + + commandUpdater->CommandStatusChanged("cmd_em"); + commandUpdater->CommandStatusChanged("cmd_strong"); + commandUpdater->CommandStatusChanged("cmd_cite"); + commandUpdater->CommandStatusChanged("cmd_abbr"); + commandUpdater->CommandStatusChanged("cmd_acronym"); + commandUpdater->CommandStatusChanged("cmd_code"); + commandUpdater->CommandStatusChanged("cmd_samp"); + commandUpdater->CommandStatusChanged("cmd_var"); + + commandUpdater->CommandStatusChanged("cmd_increaseFont"); + commandUpdater->CommandStatusChanged("cmd_decreaseFont"); + + commandUpdater->CommandStatusChanged("cmd_paragraphState"); + commandUpdater->CommandStatusChanged("cmd_fontFace"); + commandUpdater->CommandStatusChanged("cmd_fontColor"); + commandUpdater->CommandStatusChanged("cmd_backgroundColor"); + commandUpdater->CommandStatusChanged("cmd_highlight"); + return NS_OK; + } + + if (aCommandGroup.EqualsLiteral("save")) { + // save commands (most are not in C++) + commandUpdater->CommandStatusChanged("cmd_setDocumentModified"); + commandUpdater->CommandStatusChanged("cmd_save"); + return NS_OK; + } + + return NS_OK; +} + +nsresult +nsComposerCommandsUpdater::UpdateOneCommand(const char *aCommand) +{ + nsCOMPtr<nsPICommandUpdater> commandUpdater = GetCommandUpdater(); + NS_ENSURE_TRUE(commandUpdater, NS_ERROR_FAILURE); + + commandUpdater->CommandStatusChanged(aCommand); + + return NS_OK; +} + +bool +nsComposerCommandsUpdater::SelectionIsCollapsed() +{ + nsCOMPtr<nsPIDOMWindowOuter> domWindow = do_QueryReferent(mDOMWindow); + NS_ENSURE_TRUE(domWindow, true); + + nsCOMPtr<nsISelection> domSelection = domWindow->GetSelection(); + if (NS_WARN_IF(!domSelection)) { + return false; + } + + bool selectionCollapsed = false; + domSelection->GetIsCollapsed(&selectionCollapsed); + return selectionCollapsed; +} + +already_AddRefed<nsPICommandUpdater> +nsComposerCommandsUpdater::GetCommandUpdater() +{ + nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mDocShell); + NS_ENSURE_TRUE(docShell, nullptr); + nsCOMPtr<nsICommandManager> manager = docShell->GetCommandManager(); + nsCOMPtr<nsPICommandUpdater> updater = do_QueryInterface(manager); + return updater.forget(); +} + +#if 0 +#pragma mark - +#endif + +nsresult +nsComposerCommandsUpdater::Notify(nsITimer *timer) +{ + NS_ASSERTION(timer == mUpdateTimer.get(), "Hey, this ain't my timer!"); + TimerCallback(); + return NS_OK; +} + +#if 0 +#pragma mark - +#endif + + +nsresult +NS_NewComposerCommandsUpdater(nsISelectionListener** aInstancePtrResult) +{ + RefPtr<nsComposerCommandsUpdater> newThang = new nsComposerCommandsUpdater; + newThang.forget(aInstancePtrResult); + return NS_OK; +} |