editor/composer/src/nsComposerCommandsUpdater.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/editor/composer/src/nsComposerCommandsUpdater.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,393 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
     1.5 + *
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "mozilla/mozalloc.h"           // for operator new
    1.11 +#include "nsAString.h"
    1.12 +#include "nsComponentManagerUtils.h"    // for do_CreateInstance
    1.13 +#include "nsComposerCommandsUpdater.h"
    1.14 +#include "nsDebug.h"                    // for NS_ENSURE_TRUE, etc
    1.15 +#include "nsError.h"                    // for NS_OK, NS_ERROR_FAILURE, etc
    1.16 +#include "nsICommandManager.h"          // for nsICommandManager
    1.17 +#include "nsID.h"                       // for NS_GET_IID, etc
    1.18 +#include "nsIDOMWindow.h"               // for nsIDOMWindow
    1.19 +#include "nsIDocShell.h"                // for nsIDocShell
    1.20 +#include "nsIInterfaceRequestorUtils.h"  // for do_GetInterface
    1.21 +#include "nsISelection.h"               // for nsISelection
    1.22 +#include "nsITransactionManager.h"      // for nsITransactionManager
    1.23 +#include "nsLiteralString.h"            // for NS_LITERAL_STRING
    1.24 +#include "nsPICommandUpdater.h"         // for nsPICommandUpdater
    1.25 +#include "nsPIDOMWindow.h"              // for nsPIDOMWindow
    1.26 +
    1.27 +class nsIDOMDocument;
    1.28 +class nsITransaction;
    1.29 +
    1.30 +nsComposerCommandsUpdater::nsComposerCommandsUpdater()
    1.31 +:  mDirtyState(eStateUninitialized)
    1.32 +,  mSelectionCollapsed(eStateUninitialized)
    1.33 +,  mFirstDoOfFirstUndo(true)
    1.34 +{
    1.35 +}
    1.36 +
    1.37 +nsComposerCommandsUpdater::~nsComposerCommandsUpdater()
    1.38 +{
    1.39 +  // cancel any outstanding update timer
    1.40 +  if (mUpdateTimer)
    1.41 +  {
    1.42 +    mUpdateTimer->Cancel();
    1.43 +  }
    1.44 +}
    1.45 +
    1.46 +NS_IMPL_ISUPPORTS(nsComposerCommandsUpdater, nsISelectionListener,
    1.47 +                  nsIDocumentStateListener, nsITransactionListener, nsITimerCallback)
    1.48 +
    1.49 +#if 0
    1.50 +#pragma mark -
    1.51 +#endif
    1.52 +
    1.53 +NS_IMETHODIMP
    1.54 +nsComposerCommandsUpdater::NotifyDocumentCreated()
    1.55 +{
    1.56 +  // Trigger an nsIObserve notification that the document has been created
    1.57 +  UpdateOneCommand("obs_documentCreated");
    1.58 +  return NS_OK;
    1.59 +}
    1.60 +
    1.61 +NS_IMETHODIMP
    1.62 +nsComposerCommandsUpdater::NotifyDocumentWillBeDestroyed()
    1.63 +{
    1.64 +  // cancel any outstanding update timer
    1.65 +  if (mUpdateTimer)
    1.66 +  {
    1.67 +    mUpdateTimer->Cancel();
    1.68 +    mUpdateTimer = nullptr;
    1.69 +  }
    1.70 +  
    1.71 +  // We can't call this right now; it is too late in some cases and the window
    1.72 +  // is already partially destructed (e.g. JS objects may be gone).
    1.73 +#if 0
    1.74 +  // Trigger an nsIObserve notification that the document will be destroyed
    1.75 +  UpdateOneCommand("obs_documentWillBeDestroyed");
    1.76 +#endif
    1.77 +  return NS_OK;
    1.78 +}
    1.79 +
    1.80 +
    1.81 +NS_IMETHODIMP
    1.82 +nsComposerCommandsUpdater::NotifyDocumentStateChanged(bool aNowDirty)
    1.83 +{
    1.84 +  // update document modified. We should have some other notifications for this too.
    1.85 +  return UpdateDirtyState(aNowDirty);
    1.86 +}
    1.87 +
    1.88 +NS_IMETHODIMP
    1.89 +nsComposerCommandsUpdater::NotifySelectionChanged(nsIDOMDocument *,
    1.90 +                                                  nsISelection *, int16_t)
    1.91 +{
    1.92 +  return PrimeUpdateTimer();
    1.93 +}
    1.94 +
    1.95 +#if 0
    1.96 +#pragma mark -
    1.97 +#endif
    1.98 +
    1.99 +NS_IMETHODIMP
   1.100 +nsComposerCommandsUpdater::WillDo(nsITransactionManager *aManager,
   1.101 +                                  nsITransaction *aTransaction, bool *aInterrupt)
   1.102 +{
   1.103 +  *aInterrupt = false;
   1.104 +  return NS_OK;
   1.105 +}
   1.106 +
   1.107 +NS_IMETHODIMP
   1.108 +nsComposerCommandsUpdater::DidDo(nsITransactionManager *aManager,
   1.109 +  nsITransaction *aTransaction, nsresult aDoResult)
   1.110 +{
   1.111 +  // only need to update if the status of the Undo menu item changes.
   1.112 +  int32_t undoCount;
   1.113 +  aManager->GetNumberOfUndoItems(&undoCount);
   1.114 +  if (undoCount == 1)
   1.115 +  {
   1.116 +    if (mFirstDoOfFirstUndo)
   1.117 +      UpdateCommandGroup(NS_LITERAL_STRING("undo"));
   1.118 +    mFirstDoOfFirstUndo = false;
   1.119 +  }
   1.120 +	
   1.121 +  return NS_OK;
   1.122 +}
   1.123 +
   1.124 +NS_IMETHODIMP 
   1.125 +nsComposerCommandsUpdater::WillUndo(nsITransactionManager *aManager,
   1.126 +                                    nsITransaction *aTransaction,
   1.127 +                                    bool *aInterrupt)
   1.128 +{
   1.129 +  *aInterrupt = false;
   1.130 +  return NS_OK;
   1.131 +}
   1.132 +
   1.133 +NS_IMETHODIMP
   1.134 +nsComposerCommandsUpdater::DidUndo(nsITransactionManager *aManager,
   1.135 +                                   nsITransaction *aTransaction,
   1.136 +                                   nsresult aUndoResult)
   1.137 +{
   1.138 +  int32_t undoCount;
   1.139 +  aManager->GetNumberOfUndoItems(&undoCount);
   1.140 +  if (undoCount == 0)
   1.141 +    mFirstDoOfFirstUndo = true;    // reset the state for the next do
   1.142 +
   1.143 +  UpdateCommandGroup(NS_LITERAL_STRING("undo"));
   1.144 +  return NS_OK;
   1.145 +}
   1.146 +
   1.147 +NS_IMETHODIMP
   1.148 +nsComposerCommandsUpdater::WillRedo(nsITransactionManager *aManager,
   1.149 +                                    nsITransaction *aTransaction,
   1.150 +                                    bool *aInterrupt)
   1.151 +{
   1.152 +  *aInterrupt = false;
   1.153 +  return NS_OK;
   1.154 +}
   1.155 +
   1.156 +NS_IMETHODIMP
   1.157 +nsComposerCommandsUpdater::DidRedo(nsITransactionManager *aManager,  
   1.158 +                                   nsITransaction *aTransaction,
   1.159 +                                   nsresult aRedoResult)
   1.160 +{
   1.161 +  UpdateCommandGroup(NS_LITERAL_STRING("undo"));
   1.162 +  return NS_OK;
   1.163 +}
   1.164 +
   1.165 +NS_IMETHODIMP
   1.166 +nsComposerCommandsUpdater::WillBeginBatch(nsITransactionManager *aManager,
   1.167 +                                          bool *aInterrupt)
   1.168 +{
   1.169 +  *aInterrupt = false;
   1.170 +  return NS_OK;
   1.171 +}
   1.172 +
   1.173 +NS_IMETHODIMP
   1.174 +nsComposerCommandsUpdater::DidBeginBatch(nsITransactionManager *aManager,
   1.175 +                                         nsresult aResult)
   1.176 +{
   1.177 +  return NS_OK;
   1.178 +}
   1.179 +
   1.180 +NS_IMETHODIMP
   1.181 +nsComposerCommandsUpdater::WillEndBatch(nsITransactionManager *aManager,
   1.182 +                                        bool *aInterrupt)
   1.183 +{
   1.184 +  *aInterrupt = false;
   1.185 +  return NS_OK;
   1.186 +}
   1.187 +
   1.188 +NS_IMETHODIMP
   1.189 +nsComposerCommandsUpdater::DidEndBatch(nsITransactionManager *aManager, 
   1.190 +                                       nsresult aResult)
   1.191 +{
   1.192 +  return NS_OK;
   1.193 +}
   1.194 +
   1.195 +NS_IMETHODIMP
   1.196 +nsComposerCommandsUpdater::WillMerge(nsITransactionManager *aManager,
   1.197 +                                     nsITransaction *aTopTransaction,
   1.198 +                                     nsITransaction *aTransactionToMerge,
   1.199 +                                     bool *aInterrupt)
   1.200 +{
   1.201 +  *aInterrupt = false;
   1.202 +  return NS_OK;
   1.203 +}
   1.204 +
   1.205 +NS_IMETHODIMP
   1.206 +nsComposerCommandsUpdater::DidMerge(nsITransactionManager *aManager,
   1.207 +                                    nsITransaction *aTopTransaction,
   1.208 +                                    nsITransaction *aTransactionToMerge,
   1.209 +                                    bool aDidMerge, nsresult aMergeResult)
   1.210 +{
   1.211 +  return NS_OK;
   1.212 +}
   1.213 +
   1.214 +#if 0
   1.215 +#pragma mark -
   1.216 +#endif
   1.217 +
   1.218 +nsresult
   1.219 +nsComposerCommandsUpdater::Init(nsIDOMWindow* aDOMWindow)
   1.220 +{
   1.221 +  NS_ENSURE_ARG(aDOMWindow);
   1.222 +  mDOMWindow = do_GetWeakReference(aDOMWindow);
   1.223 +
   1.224 +  nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aDOMWindow));
   1.225 +  if (window)
   1.226 +  {
   1.227 +    mDocShell = do_GetWeakReference(window->GetDocShell());
   1.228 +  }
   1.229 +  return NS_OK;
   1.230 +}
   1.231 +
   1.232 +nsresult
   1.233 +nsComposerCommandsUpdater::PrimeUpdateTimer()
   1.234 +{
   1.235 +  if (!mUpdateTimer)
   1.236 +  {
   1.237 +    nsresult rv = NS_OK;
   1.238 +    mUpdateTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
   1.239 +    NS_ENSURE_SUCCESS(rv, rv);
   1.240 +  }
   1.241 +
   1.242 +  const uint32_t kUpdateTimerDelay = 150;
   1.243 +  return mUpdateTimer->InitWithCallback(static_cast<nsITimerCallback*>(this),
   1.244 +                                        kUpdateTimerDelay,
   1.245 +                                        nsITimer::TYPE_ONE_SHOT);
   1.246 +}
   1.247 +
   1.248 +
   1.249 +void nsComposerCommandsUpdater::TimerCallback()
   1.250 +{
   1.251 +  // if the selection state has changed, update stuff
   1.252 +  bool isCollapsed = SelectionIsCollapsed();
   1.253 +  if (static_cast<int8_t>(isCollapsed) != mSelectionCollapsed)
   1.254 +  {
   1.255 +    UpdateCommandGroup(NS_LITERAL_STRING("select"));
   1.256 +    mSelectionCollapsed = isCollapsed;
   1.257 +  }
   1.258 +
   1.259 +  // isn't this redundant with the UpdateCommandGroup above?
   1.260 +  // can we just nuke the above call? or create a meta command group?
   1.261 +  UpdateCommandGroup(NS_LITERAL_STRING("style"));
   1.262 +}
   1.263 +
   1.264 +nsresult
   1.265 +nsComposerCommandsUpdater::UpdateDirtyState(bool aNowDirty)
   1.266 +{
   1.267 +  if (mDirtyState != static_cast<int8_t>(aNowDirty))
   1.268 +  {
   1.269 +    UpdateCommandGroup(NS_LITERAL_STRING("save"));
   1.270 +    UpdateCommandGroup(NS_LITERAL_STRING("undo"));
   1.271 +    mDirtyState = aNowDirty;
   1.272 +  }
   1.273 +
   1.274 +  return NS_OK;
   1.275 +}
   1.276 +
   1.277 +nsresult
   1.278 +nsComposerCommandsUpdater::UpdateCommandGroup(const nsAString& aCommandGroup)
   1.279 +{
   1.280 +  nsCOMPtr<nsPICommandUpdater> commandUpdater = GetCommandUpdater();
   1.281 +  NS_ENSURE_TRUE(commandUpdater, NS_ERROR_FAILURE);
   1.282 +
   1.283 +  
   1.284 +  // This hardcoded list of commands is temporary.
   1.285 +  // This code should use nsIControllerCommandGroup.
   1.286 +  if (aCommandGroup.EqualsLiteral("undo"))
   1.287 +  {
   1.288 +    commandUpdater->CommandStatusChanged("cmd_undo");
   1.289 +    commandUpdater->CommandStatusChanged("cmd_redo");
   1.290 +  }
   1.291 +  else if (aCommandGroup.EqualsLiteral("select") ||
   1.292 +           aCommandGroup.EqualsLiteral("style"))
   1.293 +  {
   1.294 +    commandUpdater->CommandStatusChanged("cmd_bold");
   1.295 +    commandUpdater->CommandStatusChanged("cmd_italic");
   1.296 +    commandUpdater->CommandStatusChanged("cmd_underline");
   1.297 +    commandUpdater->CommandStatusChanged("cmd_tt");
   1.298 +
   1.299 +    commandUpdater->CommandStatusChanged("cmd_strikethrough");
   1.300 +    commandUpdater->CommandStatusChanged("cmd_superscript");
   1.301 +    commandUpdater->CommandStatusChanged("cmd_subscript");
   1.302 +    commandUpdater->CommandStatusChanged("cmd_nobreak");
   1.303 +
   1.304 +    commandUpdater->CommandStatusChanged("cmd_em");
   1.305 +    commandUpdater->CommandStatusChanged("cmd_strong");
   1.306 +    commandUpdater->CommandStatusChanged("cmd_cite");
   1.307 +    commandUpdater->CommandStatusChanged("cmd_abbr");
   1.308 +    commandUpdater->CommandStatusChanged("cmd_acronym");
   1.309 +    commandUpdater->CommandStatusChanged("cmd_code");
   1.310 +    commandUpdater->CommandStatusChanged("cmd_samp");
   1.311 +    commandUpdater->CommandStatusChanged("cmd_var");
   1.312 +   
   1.313 +    commandUpdater->CommandStatusChanged("cmd_increaseFont");
   1.314 +    commandUpdater->CommandStatusChanged("cmd_decreaseFont");
   1.315 +
   1.316 +    commandUpdater->CommandStatusChanged("cmd_paragraphState");
   1.317 +    commandUpdater->CommandStatusChanged("cmd_fontFace");
   1.318 +    commandUpdater->CommandStatusChanged("cmd_fontColor");
   1.319 +    commandUpdater->CommandStatusChanged("cmd_backgroundColor");
   1.320 +    commandUpdater->CommandStatusChanged("cmd_highlight");
   1.321 +  }  
   1.322 +  else if (aCommandGroup.EqualsLiteral("save"))
   1.323 +  {
   1.324 +    // save commands (most are not in C++)
   1.325 +    commandUpdater->CommandStatusChanged("cmd_setDocumentModified");
   1.326 +    commandUpdater->CommandStatusChanged("cmd_save");
   1.327 +  }
   1.328 +  return NS_OK;  
   1.329 +}
   1.330 +
   1.331 +nsresult
   1.332 +nsComposerCommandsUpdater::UpdateOneCommand(const char *aCommand)
   1.333 +{
   1.334 +  nsCOMPtr<nsPICommandUpdater> commandUpdater = GetCommandUpdater();
   1.335 +  NS_ENSURE_TRUE(commandUpdater, NS_ERROR_FAILURE);
   1.336 +
   1.337 +  commandUpdater->CommandStatusChanged(aCommand);
   1.338 +
   1.339 +  return NS_OK;  
   1.340 +}
   1.341 +
   1.342 +bool
   1.343 +nsComposerCommandsUpdater::SelectionIsCollapsed()
   1.344 +{
   1.345 +  nsCOMPtr<nsIDOMWindow> domWindow = do_QueryReferent(mDOMWindow);
   1.346 +  NS_ENSURE_TRUE(domWindow, true);
   1.347 +
   1.348 +  nsCOMPtr<nsISelection> domSelection;
   1.349 +  if (NS_SUCCEEDED(domWindow->GetSelection(getter_AddRefs(domSelection))) && domSelection)
   1.350 +  {
   1.351 +    bool selectionCollapsed = false;
   1.352 +    domSelection->GetIsCollapsed(&selectionCollapsed);
   1.353 +    return selectionCollapsed;
   1.354 +  }
   1.355 +
   1.356 +  NS_WARNING("nsComposerCommandsUpdater::SelectionIsCollapsed - no domSelection");
   1.357 +
   1.358 +  return false;
   1.359 +}
   1.360 +
   1.361 +already_AddRefed<nsPICommandUpdater>
   1.362 +nsComposerCommandsUpdater::GetCommandUpdater()
   1.363 +{
   1.364 +  nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mDocShell);
   1.365 +  NS_ENSURE_TRUE(docShell, nullptr);
   1.366 +  nsCOMPtr<nsICommandManager> manager = do_GetInterface(docShell);
   1.367 +  nsCOMPtr<nsPICommandUpdater> updater = do_QueryInterface(manager);
   1.368 +  return updater.forget();
   1.369 +}
   1.370 +
   1.371 +#if 0
   1.372 +#pragma mark -
   1.373 +#endif
   1.374 +
   1.375 +nsresult
   1.376 +nsComposerCommandsUpdater::Notify(nsITimer *timer)
   1.377 +{
   1.378 +  NS_ASSERTION(timer == mUpdateTimer.get(), "Hey, this ain't my timer!");
   1.379 +  TimerCallback();
   1.380 +  return NS_OK;
   1.381 +}
   1.382 +
   1.383 +#if 0
   1.384 +#pragma mark -
   1.385 +#endif
   1.386 +
   1.387 +
   1.388 +nsresult
   1.389 +NS_NewComposerCommandsUpdater(nsISelectionListener** aInstancePtrResult)
   1.390 +{
   1.391 +  nsComposerCommandsUpdater* newThang = new nsComposerCommandsUpdater;
   1.392 +  NS_ENSURE_TRUE(newThang, NS_ERROR_OUT_OF_MEMORY);
   1.393 +
   1.394 +  return newThang->QueryInterface(NS_GET_IID(nsISelectionListener),
   1.395 +                                  (void **)aInstancePtrResult);
   1.396 +}

mercurial