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 +}