Back to index

lightning-sunbird  0.9+nobinonly
nsComposerCommandsUpdater.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
00002  *
00003  * ***** BEGIN LICENSE BLOCK *****
00004  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00005  *
00006  * The contents of this file are subject to the Mozilla Public License Version
00007  * 1.1 (the "License"); you may not use this file except in compliance with
00008  * the License. You may obtain a copy of the License at
00009  * http://www.mozilla.org/MPL/
00010  *
00011  * Software distributed under the License is distributed on an "AS IS" basis,
00012  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00013  * for the specific language governing rights and limitations under the
00014  * License.
00015  *
00016  * The Original Code is mozilla.org code.
00017  *
00018  * The Initial Developer of the Original Code is
00019  * Netscape Communications Corporation.
00020  * Portions created by the Initial Developer are Copyright (C) 1998
00021  * the Initial Developer. All Rights Reserved.
00022  *
00023  * Contributor(s):
00024  *   Simon Fraser   <sfraser@netscape.com>
00025  *   Michael Judge  <mjudge@netscape.com>
00026  *   Charles Manske <cmanske@netscape.com>
00027  *
00028  * Alternatively, the contents of this file may be used under the terms of
00029  * either of the GNU General Public License Version 2 or later (the "GPL"),
00030  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00031  * in which case the provisions of the GPL or the LGPL are applicable instead
00032  * of those above. If you wish to allow use of your version of this file only
00033  * under the terms of either the GPL or the LGPL, and not to allow others to
00034  * use your version of this file under the terms of the MPL, indicate your
00035  * decision by deleting the provisions above and replace them with the notice
00036  * and other provisions required by the GPL or the LGPL. If you do not delete
00037  * the provisions above, a recipient may use your version of this file under
00038  * the terms of any one of the MPL, the GPL or the LGPL.
00039  *
00040  * ***** END LICENSE BLOCK ***** */
00041 
00042 #include "nsIDOMWindow.h"
00043 #include "nsComposerCommandsUpdater.h"
00044 #include "nsComponentManagerUtils.h"
00045 #include "nsIDOMDocument.h"
00046 #include "nsISelection.h"
00047 #include "nsIScriptGlobalObject.h"
00048 
00049 #include "nsIInterfaceRequestorUtils.h"
00050 #include "nsString.h"
00051 
00052 #include "nsICommandManager.h"
00053 
00054 #include "nsIDocShell.h"
00055 #include "nsITransactionManager.h"
00056 
00057 nsComposerCommandsUpdater::nsComposerCommandsUpdater()
00058 :  mDOMWindow(nsnull)
00059 ,  mDirtyState(eStateUninitialized)
00060 ,  mSelectionCollapsed(eStateUninitialized)
00061 ,  mFirstDoOfFirstUndo(PR_TRUE)
00062 {
00063 }
00064 
00065 nsComposerCommandsUpdater::~nsComposerCommandsUpdater()
00066 {
00067 }
00068 
00069 NS_IMPL_ISUPPORTS4(nsComposerCommandsUpdater, nsISelectionListener,
00070                    nsIDocumentStateListener, nsITransactionListener, nsITimerCallback)
00071 
00072 #if 0
00073 #pragma mark -
00074 #endif
00075 
00076 NS_IMETHODIMP
00077 nsComposerCommandsUpdater::NotifyDocumentCreated()
00078 {
00079   // Trigger an nsIObserve notification that the document has been created
00080   UpdateOneCommand("obs_documentCreated");
00081   return NS_OK;
00082 }
00083 
00084 NS_IMETHODIMP
00085 nsComposerCommandsUpdater::NotifyDocumentWillBeDestroyed()
00086 {
00087   // cancel any outstanding udpate timer
00088   if (mUpdateTimer)
00089   {
00090     mUpdateTimer->Cancel();
00091     mUpdateTimer = nsnull;
00092   }
00093   
00094   // We can't call this right now; it is too late in some cases and the window
00095   // is already partially destructed (e.g. JS objects may be gone).
00096 #if 0
00097   // Trigger an nsIObserve notification that the document will be destroyed
00098   UpdateOneCommand("obs_documentWillBeDestroyed");
00099 #endif
00100   return NS_OK;
00101 }
00102 
00103 
00104 NS_IMETHODIMP
00105 nsComposerCommandsUpdater::NotifyDocumentStateChanged(PRBool aNowDirty)
00106 {
00107   // update document modified. We should have some other notifications for this too.
00108   return UpdateDirtyState(aNowDirty);
00109 }
00110 
00111 NS_IMETHODIMP
00112 nsComposerCommandsUpdater::NotifySelectionChanged(nsIDOMDocument *,
00113                                                   nsISelection *, PRInt16)
00114 {
00115   return PrimeUpdateTimer();
00116 }
00117 
00118 #if 0
00119 #pragma mark -
00120 #endif
00121 
00122 NS_IMETHODIMP
00123 nsComposerCommandsUpdater::WillDo(nsITransactionManager *aManager,
00124                                   nsITransaction *aTransaction, PRBool *aInterrupt)
00125 {
00126   *aInterrupt = PR_FALSE;
00127   return NS_OK;
00128 }
00129 
00130 NS_IMETHODIMP
00131 nsComposerCommandsUpdater::DidDo(nsITransactionManager *aManager,
00132   nsITransaction *aTransaction, nsresult aDoResult)
00133 {
00134   // only need to update if the status of the Undo menu item changes.
00135   PRInt32 undoCount;
00136   aManager->GetNumberOfUndoItems(&undoCount);
00137   if (undoCount == 1)
00138   {
00139     if (mFirstDoOfFirstUndo)
00140       UpdateCommandGroup(NS_LITERAL_STRING("undo"));
00141     mFirstDoOfFirstUndo = PR_FALSE;
00142   }
00143        
00144   return NS_OK;
00145 }
00146 
00147 NS_IMETHODIMP 
00148 nsComposerCommandsUpdater::WillUndo(nsITransactionManager *aManager,
00149                                     nsITransaction *aTransaction,
00150                                     PRBool *aInterrupt)
00151 {
00152   *aInterrupt = PR_FALSE;
00153   return NS_OK;
00154 }
00155 
00156 NS_IMETHODIMP
00157 nsComposerCommandsUpdater::DidUndo(nsITransactionManager *aManager,
00158                                    nsITransaction *aTransaction,
00159                                    nsresult aUndoResult)
00160 {
00161   PRInt32 undoCount;
00162   aManager->GetNumberOfUndoItems(&undoCount);
00163   if (undoCount == 0)
00164     mFirstDoOfFirstUndo = PR_TRUE;    // reset the state for the next do
00165 
00166   UpdateCommandGroup(NS_LITERAL_STRING("undo"));
00167   return NS_OK;
00168 }
00169 
00170 NS_IMETHODIMP
00171 nsComposerCommandsUpdater::WillRedo(nsITransactionManager *aManager,
00172                                     nsITransaction *aTransaction,
00173                                     PRBool *aInterrupt)
00174 {
00175   *aInterrupt = PR_FALSE;
00176   return NS_OK;
00177 }
00178 
00179 NS_IMETHODIMP
00180 nsComposerCommandsUpdater::DidRedo(nsITransactionManager *aManager,  
00181                                    nsITransaction *aTransaction,
00182                                    nsresult aRedoResult)
00183 {
00184   UpdateCommandGroup(NS_LITERAL_STRING("undo"));
00185   return NS_OK;
00186 }
00187 
00188 NS_IMETHODIMP
00189 nsComposerCommandsUpdater::WillBeginBatch(nsITransactionManager *aManager,
00190                                           PRBool *aInterrupt)
00191 {
00192   *aInterrupt = PR_FALSE;
00193   return NS_OK;
00194 }
00195 
00196 NS_IMETHODIMP
00197 nsComposerCommandsUpdater::DidBeginBatch(nsITransactionManager *aManager,
00198                                          nsresult aResult)
00199 {
00200   return NS_OK;
00201 }
00202 
00203 NS_IMETHODIMP
00204 nsComposerCommandsUpdater::WillEndBatch(nsITransactionManager *aManager,
00205                                         PRBool *aInterrupt)
00206 {
00207   *aInterrupt = PR_FALSE;
00208   return NS_OK;
00209 }
00210 
00211 NS_IMETHODIMP
00212 nsComposerCommandsUpdater::DidEndBatch(nsITransactionManager *aManager, 
00213                                        nsresult aResult)
00214 {
00215   return NS_OK;
00216 }
00217 
00218 NS_IMETHODIMP
00219 nsComposerCommandsUpdater::WillMerge(nsITransactionManager *aManager,
00220                                      nsITransaction *aTopTransaction,
00221                                      nsITransaction *aTransactionToMerge,
00222                                      PRBool *aInterrupt)
00223 {
00224   *aInterrupt = PR_FALSE;
00225   return NS_OK;
00226 }
00227 
00228 NS_IMETHODIMP
00229 nsComposerCommandsUpdater::DidMerge(nsITransactionManager *aManager,
00230                                     nsITransaction *aTopTransaction,
00231                                     nsITransaction *aTransactionToMerge,
00232                                     PRBool aDidMerge, nsresult aMergeResult)
00233 {
00234   return NS_OK;
00235 }
00236 
00237 #if 0
00238 #pragma mark -
00239 #endif
00240 
00241 nsresult
00242 nsComposerCommandsUpdater::Init(nsIDOMWindow* aDOMWindow)
00243 {
00244   NS_ENSURE_ARG(aDOMWindow);
00245   mDOMWindow = aDOMWindow;
00246 
00247   nsCOMPtr<nsIScriptGlobalObject> scriptObject(do_QueryInterface(aDOMWindow));
00248   if (scriptObject)
00249   {
00250     mDocShell = do_GetWeakReference(scriptObject->GetDocShell());
00251   }
00252   return NS_OK;
00253 }
00254 
00255 nsresult
00256 nsComposerCommandsUpdater::PrimeUpdateTimer()
00257 {
00258   if (!mUpdateTimer)
00259   {
00260     nsresult rv = NS_OK;
00261     mUpdateTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
00262     if (NS_FAILED(rv)) return rv;
00263   }
00264 
00265   const PRUint32 kUpdateTimerDelay = 150;
00266   return mUpdateTimer->InitWithCallback(NS_STATIC_CAST(nsITimerCallback*, this),
00267                                         kUpdateTimerDelay,
00268                                         nsITimer::TYPE_ONE_SHOT);
00269 }
00270 
00271 
00272 void nsComposerCommandsUpdater::TimerCallback()
00273 {
00274   // if the selection state has changed, update stuff
00275   PRBool isCollapsed = SelectionIsCollapsed();
00276   if (isCollapsed != mSelectionCollapsed)
00277   {
00278     UpdateCommandGroup(NS_LITERAL_STRING("select"));
00279     mSelectionCollapsed = isCollapsed;
00280   }
00281   
00282   // isn't this redundant with the UpdateCommandGroup above?
00283   // can we just nuke the above call? or create a meta command group?
00284   UpdateCommandGroup(NS_LITERAL_STRING("style"));
00285 }
00286 
00287 nsresult
00288 nsComposerCommandsUpdater::UpdateDirtyState(PRBool aNowDirty)
00289 {
00290   if (mDirtyState != aNowDirty)
00291   {
00292     UpdateCommandGroup(NS_LITERAL_STRING("save"));
00293     UpdateCommandGroup(NS_LITERAL_STRING("undo"));
00294     mDirtyState = aNowDirty;
00295   }
00296   
00297   return NS_OK;  
00298 }
00299 
00300 nsresult
00301 nsComposerCommandsUpdater::UpdateCommandGroup(const nsAString& aCommandGroup)
00302 {
00303   nsCOMPtr<nsPICommandUpdater> commandUpdater = GetCommandUpdater();
00304   if (!commandUpdater) return NS_ERROR_FAILURE;
00305 
00306   
00307   // This hardcoded list of commands is temporary.
00308   // This code should use nsIControllerCommandGroup.
00309   if (aCommandGroup.EqualsLiteral("undo"))
00310   {
00311     commandUpdater->CommandStatusChanged("cmd_undo");
00312     commandUpdater->CommandStatusChanged("cmd_redo");
00313   }
00314   else if (aCommandGroup.EqualsLiteral("select") ||
00315            aCommandGroup.EqualsLiteral("style"))
00316   {
00317     commandUpdater->CommandStatusChanged("cmd_bold");
00318     commandUpdater->CommandStatusChanged("cmd_italic");
00319     commandUpdater->CommandStatusChanged("cmd_underline");
00320     commandUpdater->CommandStatusChanged("cmd_tt");
00321 
00322     commandUpdater->CommandStatusChanged("cmd_strikethrough");
00323     commandUpdater->CommandStatusChanged("cmd_superscript");
00324     commandUpdater->CommandStatusChanged("cmd_subscript");
00325     commandUpdater->CommandStatusChanged("cmd_nobreak");
00326 
00327     commandUpdater->CommandStatusChanged("cmd_em");
00328     commandUpdater->CommandStatusChanged("cmd_strong");
00329     commandUpdater->CommandStatusChanged("cmd_cite");
00330     commandUpdater->CommandStatusChanged("cmd_abbr");
00331     commandUpdater->CommandStatusChanged("cmd_acronym");
00332     commandUpdater->CommandStatusChanged("cmd_code");
00333     commandUpdater->CommandStatusChanged("cmd_samp");
00334     commandUpdater->CommandStatusChanged("cmd_var");
00335    
00336     commandUpdater->CommandStatusChanged("cmd_increaseFont");
00337     commandUpdater->CommandStatusChanged("cmd_decreaseFont");
00338 
00339     commandUpdater->CommandStatusChanged("cmd_paragraphState");
00340     commandUpdater->CommandStatusChanged("cmd_fontFace");
00341     commandUpdater->CommandStatusChanged("cmd_fontColor");
00342     commandUpdater->CommandStatusChanged("cmd_backgroundColor");
00343     commandUpdater->CommandStatusChanged("cmd_highlight");
00344   }  
00345   else if (aCommandGroup.EqualsLiteral("save"))
00346   {
00347     // save commands (most are not in C++)
00348     commandUpdater->CommandStatusChanged("cmd_setDocumentModified");
00349     commandUpdater->CommandStatusChanged("cmd_save");
00350   }
00351   return NS_OK;  
00352 }
00353 
00354 nsresult
00355 nsComposerCommandsUpdater::UpdateOneCommand(const char *aCommand)
00356 {
00357   nsCOMPtr<nsPICommandUpdater> commandUpdater = GetCommandUpdater();
00358   if (!commandUpdater) return NS_ERROR_FAILURE;
00359 
00360   commandUpdater->CommandStatusChanged(aCommand);
00361 
00362   return NS_OK;  
00363 }
00364 
00365 PRBool
00366 nsComposerCommandsUpdater::SelectionIsCollapsed()
00367 {
00368   if (!mDOMWindow) return PR_TRUE;
00369 
00370   nsCOMPtr<nsISelection> domSelection;
00371   if (NS_SUCCEEDED(mDOMWindow->GetSelection(getter_AddRefs(domSelection))) && domSelection)
00372   {
00373     PRBool selectionCollapsed = PR_FALSE;
00374     domSelection->GetIsCollapsed(&selectionCollapsed);
00375     return selectionCollapsed;
00376   }
00377 
00378   NS_WARNING("nsComposerCommandsUpdater::SelectionIsCollapsed - no domSelection");
00379 
00380   return PR_FALSE;
00381 }
00382 
00383 already_AddRefed<nsPICommandUpdater>
00384 nsComposerCommandsUpdater::GetCommandUpdater()
00385 {
00386   nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mDocShell);
00387   NS_ENSURE_TRUE(docShell, nsnull);
00388   nsCOMPtr<nsICommandManager> manager = do_GetInterface(docShell);
00389   nsCOMPtr<nsPICommandUpdater> updater = do_QueryInterface(manager);
00390   nsPICommandUpdater* retVal = nsnull;
00391   updater.swap(retVal);
00392   return retVal;
00393 }
00394 
00395 #if 0
00396 #pragma mark -
00397 #endif
00398 
00399 nsresult
00400 nsComposerCommandsUpdater::Notify(nsITimer *timer)
00401 {
00402   NS_ASSERTION(timer == mUpdateTimer.get(), "Hey, this ain't my timer!");
00403   TimerCallback();
00404   return NS_OK;
00405 }
00406 
00407 #if 0
00408 #pragma mark -
00409 #endif
00410 
00411 
00412 nsresult
00413 NS_NewComposerCommandsUpdater(nsISelectionListener** aInstancePtrResult)
00414 {
00415   nsComposerCommandsUpdater* newThang = new nsComposerCommandsUpdater;
00416   if (!newThang)
00417     return NS_ERROR_OUT_OF_MEMORY;
00418 
00419   return newThang->QueryInterface(NS_GET_IID(nsISelectionListener),
00420                                   (void **)aInstancePtrResult);
00421 }