/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Netscape Communications Corporation. * Portions created by the Initial Developer are Copyright (C) 1998 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Simon Fraser * Michael Judge * Charles Manske * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "nsPIDOMWindow.h" #include "nsComposerCommandsUpdater.h" #include "nsComponentManagerUtils.h" #include "nsIDOMDocument.h" #include "nsISelection.h" #include "nsIInterfaceRequestorUtils.h" #include "nsString.h" #include "nsICommandManager.h" #include "nsIDocShell.h" #include "nsITransactionManager.h" nsComposerCommandsUpdater::nsComposerCommandsUpdater() : mDOMWindow(nsnull) , mDirtyState(eStateUninitialized) , mSelectionCollapsed(eStateUninitialized) , mFirstDoOfFirstUndo(PR_TRUE) { } nsComposerCommandsUpdater::~nsComposerCommandsUpdater() { } NS_IMPL_ISUPPORTS4(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 = nsnull; } // 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(PRBool aNowDirty) { // update document modified. We should have some other notifications for this too. return UpdateDirtyState(aNowDirty); } NS_IMETHODIMP nsComposerCommandsUpdater::NotifySelectionChanged(nsIDOMDocument *, nsISelection *, PRInt16) { return PrimeUpdateTimer(); } #if 0 #pragma mark - #endif NS_IMETHODIMP nsComposerCommandsUpdater::WillDo(nsITransactionManager *aManager, nsITransaction *aTransaction, PRBool *aInterrupt) { *aInterrupt = PR_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. PRInt32 undoCount; aManager->GetNumberOfUndoItems(&undoCount); if (undoCount == 1) { if (mFirstDoOfFirstUndo) UpdateCommandGroup(NS_LITERAL_STRING("undo")); mFirstDoOfFirstUndo = PR_FALSE; } return NS_OK; } NS_IMETHODIMP nsComposerCommandsUpdater::WillUndo(nsITransactionManager *aManager, nsITransaction *aTransaction, PRBool *aInterrupt) { *aInterrupt = PR_FALSE; return NS_OK; } NS_IMETHODIMP nsComposerCommandsUpdater::DidUndo(nsITransactionManager *aManager, nsITransaction *aTransaction, nsresult aUndoResult) { PRInt32 undoCount; aManager->GetNumberOfUndoItems(&undoCount); if (undoCount == 0) mFirstDoOfFirstUndo = PR_TRUE; // reset the state for the next do UpdateCommandGroup(NS_LITERAL_STRING("undo")); return NS_OK; } NS_IMETHODIMP nsComposerCommandsUpdater::WillRedo(nsITransactionManager *aManager, nsITransaction *aTransaction, PRBool *aInterrupt) { *aInterrupt = PR_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, PRBool *aInterrupt) { *aInterrupt = PR_FALSE; return NS_OK; } NS_IMETHODIMP nsComposerCommandsUpdater::DidBeginBatch(nsITransactionManager *aManager, nsresult aResult) { return NS_OK; } NS_IMETHODIMP nsComposerCommandsUpdater::WillEndBatch(nsITransactionManager *aManager, PRBool *aInterrupt) { *aInterrupt = PR_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, PRBool *aInterrupt) { *aInterrupt = PR_FALSE; return NS_OK; } NS_IMETHODIMP nsComposerCommandsUpdater::DidMerge(nsITransactionManager *aManager, nsITransaction *aTopTransaction, nsITransaction *aTransactionToMerge, PRBool aDidMerge, nsresult aMergeResult) { return NS_OK; } #if 0 #pragma mark - #endif nsresult nsComposerCommandsUpdater::Init(nsIDOMWindow* aDOMWindow) { NS_ENSURE_ARG(aDOMWindow); mDOMWindow = aDOMWindow; nsCOMPtr window(do_QueryInterface(aDOMWindow)); if (window) { mDocShell = do_GetWeakReference(window->GetDocShell()); } return NS_OK; } nsresult nsComposerCommandsUpdater::PrimeUpdateTimer() { if (!mUpdateTimer) { nsresult rv = NS_OK; mUpdateTimer = do_CreateInstance("@mozilla.org/timer;1", &rv); if (NS_FAILED(rv)) return rv; } const PRUint32 kUpdateTimerDelay = 150; return mUpdateTimer->InitWithCallback(static_cast(this), kUpdateTimerDelay, nsITimer::TYPE_ONE_SHOT); } void nsComposerCommandsUpdater::TimerCallback() { // if the selection state has changed, update stuff PRBool isCollapsed = SelectionIsCollapsed(); if (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(PRBool aNowDirty) { if (mDirtyState != aNowDirty) { UpdateCommandGroup(NS_LITERAL_STRING("save")); UpdateCommandGroup(NS_LITERAL_STRING("undo")); mDirtyState = aNowDirty; } return NS_OK; } nsresult nsComposerCommandsUpdater::UpdateCommandGroup(const nsAString& aCommandGroup) { nsCOMPtr commandUpdater = GetCommandUpdater(); if (!commandUpdater) return 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"); } else 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"); } else if (aCommandGroup.EqualsLiteral("save")) { // save commands (most are not in C++) commandUpdater->CommandStatusChanged("cmd_setDocumentModified"); commandUpdater->CommandStatusChanged("cmd_save"); } return NS_OK; } nsresult nsComposerCommandsUpdater::UpdateOneCommand(const char *aCommand) { nsCOMPtr commandUpdater = GetCommandUpdater(); if (!commandUpdater) return NS_ERROR_FAILURE; commandUpdater->CommandStatusChanged(aCommand); return NS_OK; } PRBool nsComposerCommandsUpdater::SelectionIsCollapsed() { if (!mDOMWindow) return PR_TRUE; nsCOMPtr domSelection; if (NS_SUCCEEDED(mDOMWindow->GetSelection(getter_AddRefs(domSelection))) && domSelection) { PRBool selectionCollapsed = PR_FALSE; domSelection->GetIsCollapsed(&selectionCollapsed); return selectionCollapsed; } NS_WARNING("nsComposerCommandsUpdater::SelectionIsCollapsed - no domSelection"); return PR_FALSE; } already_AddRefed nsComposerCommandsUpdater::GetCommandUpdater() { nsCOMPtr docShell = do_QueryReferent(mDocShell); NS_ENSURE_TRUE(docShell, nsnull); nsCOMPtr manager = do_GetInterface(docShell); nsCOMPtr updater = do_QueryInterface(manager); nsPICommandUpdater* retVal = nsnull; updater.swap(retVal); return retVal; } #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) { nsComposerCommandsUpdater* newThang = new nsComposerCommandsUpdater; if (!newThang) return NS_ERROR_OUT_OF_MEMORY; return newThang->QueryInterface(NS_GET_IID(nsISelectionListener), (void **)aInstancePtrResult); }