gecko-dev/editor/composer/src/nsComposerCommandsUpdater.cpp

418 строки
12 KiB
C++

/* -*- 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 <sfraser@netscape.com>
* Michael Judge <mjudge@netscape.com>
* Charles Manske <cmanske@netscape.com>
*
* 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 "nsIDOMWindow.h"
#include "nsComposerCommandsUpdater.h"
#include "nsComponentManagerUtils.h"
#include "nsIDOMDocument.h"
#include "nsISelection.h"
#include "nsIScriptGlobalObject.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsString.h"
#include "nsICommandManager.h"
#include "nsPICommandUpdater.h"
#include "nsIDocShell.h"
#include "nsITransactionManager.h"
nsComposerCommandsUpdater::nsComposerCommandsUpdater()
: mDOMWindow(nsnull)
, mDocShell(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 udpate 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 *, short)
{
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<nsIScriptGlobalObject> scriptObject(do_QueryInterface(aDOMWindow));
if (scriptObject)
{
mDocShell = scriptObject->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(NS_STATIC_CAST(nsITimerCallback*, 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)
{
if (!mDocShell) return NS_ERROR_FAILURE;
nsCOMPtr<nsICommandManager> commandManager = do_GetInterface(mDocShell);
nsCOMPtr<nsPICommandUpdater> commandUpdater = do_QueryInterface(commandManager);
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)
{
if (!mDocShell) return NS_ERROR_FAILURE;
nsCOMPtr<nsICommandManager> commandManager = do_GetInterface(mDocShell);
nsCOMPtr<nsPICommandUpdater> commandUpdater = do_QueryInterface(commandManager);
if (!commandUpdater) return NS_ERROR_FAILURE;
commandUpdater->CommandStatusChanged(aCommand);
return NS_OK;
}
PRBool
nsComposerCommandsUpdater::SelectionIsCollapsed()
{
if (!mDOMWindow) return PR_TRUE;
nsCOMPtr<nsISelection> 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;
}
#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);
}