1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : *
3 : * ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is mozilla.org code.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * Netscape Communications Corporation.
20 : * Portions created by the Initial Developer are Copyright (C) 1998
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Simon Fraser <sfraser@netscape.com>
25 : * Michael Judge <mjudge@netscape.com>
26 : * Charles Manske <cmanske@netscape.com>
27 : *
28 : * Alternatively, the contents of this file may be used under the terms of
29 : * either of the GNU General Public License Version 2 or later (the "GPL"),
30 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 : * in which case the provisions of the GPL or the LGPL are applicable instead
32 : * of those above. If you wish to allow use of your version of this file only
33 : * under the terms of either the GPL or the LGPL, and not to allow others to
34 : * use your version of this file under the terms of the MPL, indicate your
35 : * decision by deleting the provisions above and replace them with the notice
36 : * and other provisions required by the GPL or the LGPL. If you do not delete
37 : * the provisions above, a recipient may use your version of this file under
38 : * the terms of any one of the MPL, the GPL or the LGPL.
39 : *
40 : * ***** END LICENSE BLOCK ***** */
41 :
42 : #include "nsPIDOMWindow.h"
43 : #include "nsComposerCommandsUpdater.h"
44 : #include "nsComponentManagerUtils.h"
45 : #include "nsIDOMDocument.h"
46 : #include "nsISelection.h"
47 :
48 : #include "nsIInterfaceRequestorUtils.h"
49 : #include "nsString.h"
50 :
51 : #include "nsICommandManager.h"
52 :
53 : #include "nsIDocShell.h"
54 : #include "nsITransactionManager.h"
55 :
56 0 : nsComposerCommandsUpdater::nsComposerCommandsUpdater()
57 : : mDirtyState(eStateUninitialized)
58 : , mSelectionCollapsed(eStateUninitialized)
59 0 : , mFirstDoOfFirstUndo(true)
60 : {
61 0 : }
62 :
63 0 : nsComposerCommandsUpdater::~nsComposerCommandsUpdater()
64 : {
65 : // cancel any outstanding update timer
66 0 : if (mUpdateTimer)
67 : {
68 0 : mUpdateTimer->Cancel();
69 : }
70 0 : }
71 :
72 0 : NS_IMPL_ISUPPORTS4(nsComposerCommandsUpdater, nsISelectionListener,
73 : nsIDocumentStateListener, nsITransactionListener, nsITimerCallback)
74 :
75 : #if 0
76 : #pragma mark -
77 : #endif
78 :
79 : NS_IMETHODIMP
80 0 : nsComposerCommandsUpdater::NotifyDocumentCreated()
81 : {
82 : // Trigger an nsIObserve notification that the document has been created
83 0 : UpdateOneCommand("obs_documentCreated");
84 0 : return NS_OK;
85 : }
86 :
87 : NS_IMETHODIMP
88 0 : nsComposerCommandsUpdater::NotifyDocumentWillBeDestroyed()
89 : {
90 : // cancel any outstanding update timer
91 0 : if (mUpdateTimer)
92 : {
93 0 : mUpdateTimer->Cancel();
94 0 : mUpdateTimer = nsnull;
95 : }
96 :
97 : // We can't call this right now; it is too late in some cases and the window
98 : // is already partially destructed (e.g. JS objects may be gone).
99 : #if 0
100 : // Trigger an nsIObserve notification that the document will be destroyed
101 : UpdateOneCommand("obs_documentWillBeDestroyed");
102 : #endif
103 0 : return NS_OK;
104 : }
105 :
106 :
107 : NS_IMETHODIMP
108 0 : nsComposerCommandsUpdater::NotifyDocumentStateChanged(bool aNowDirty)
109 : {
110 : // update document modified. We should have some other notifications for this too.
111 0 : return UpdateDirtyState(aNowDirty);
112 : }
113 :
114 : NS_IMETHODIMP
115 0 : nsComposerCommandsUpdater::NotifySelectionChanged(nsIDOMDocument *,
116 : nsISelection *, PRInt16)
117 : {
118 0 : return PrimeUpdateTimer();
119 : }
120 :
121 : #if 0
122 : #pragma mark -
123 : #endif
124 :
125 : NS_IMETHODIMP
126 0 : nsComposerCommandsUpdater::WillDo(nsITransactionManager *aManager,
127 : nsITransaction *aTransaction, bool *aInterrupt)
128 : {
129 0 : *aInterrupt = false;
130 0 : return NS_OK;
131 : }
132 :
133 : NS_IMETHODIMP
134 0 : nsComposerCommandsUpdater::DidDo(nsITransactionManager *aManager,
135 : nsITransaction *aTransaction, nsresult aDoResult)
136 : {
137 : // only need to update if the status of the Undo menu item changes.
138 : PRInt32 undoCount;
139 0 : aManager->GetNumberOfUndoItems(&undoCount);
140 0 : if (undoCount == 1)
141 : {
142 0 : if (mFirstDoOfFirstUndo)
143 0 : UpdateCommandGroup(NS_LITERAL_STRING("undo"));
144 0 : mFirstDoOfFirstUndo = false;
145 : }
146 :
147 0 : return NS_OK;
148 : }
149 :
150 : NS_IMETHODIMP
151 0 : nsComposerCommandsUpdater::WillUndo(nsITransactionManager *aManager,
152 : nsITransaction *aTransaction,
153 : bool *aInterrupt)
154 : {
155 0 : *aInterrupt = false;
156 0 : return NS_OK;
157 : }
158 :
159 : NS_IMETHODIMP
160 0 : nsComposerCommandsUpdater::DidUndo(nsITransactionManager *aManager,
161 : nsITransaction *aTransaction,
162 : nsresult aUndoResult)
163 : {
164 : PRInt32 undoCount;
165 0 : aManager->GetNumberOfUndoItems(&undoCount);
166 0 : if (undoCount == 0)
167 0 : mFirstDoOfFirstUndo = true; // reset the state for the next do
168 :
169 0 : UpdateCommandGroup(NS_LITERAL_STRING("undo"));
170 0 : return NS_OK;
171 : }
172 :
173 : NS_IMETHODIMP
174 0 : nsComposerCommandsUpdater::WillRedo(nsITransactionManager *aManager,
175 : nsITransaction *aTransaction,
176 : bool *aInterrupt)
177 : {
178 0 : *aInterrupt = false;
179 0 : return NS_OK;
180 : }
181 :
182 : NS_IMETHODIMP
183 0 : nsComposerCommandsUpdater::DidRedo(nsITransactionManager *aManager,
184 : nsITransaction *aTransaction,
185 : nsresult aRedoResult)
186 : {
187 0 : UpdateCommandGroup(NS_LITERAL_STRING("undo"));
188 0 : return NS_OK;
189 : }
190 :
191 : NS_IMETHODIMP
192 0 : nsComposerCommandsUpdater::WillBeginBatch(nsITransactionManager *aManager,
193 : bool *aInterrupt)
194 : {
195 0 : *aInterrupt = false;
196 0 : return NS_OK;
197 : }
198 :
199 : NS_IMETHODIMP
200 0 : nsComposerCommandsUpdater::DidBeginBatch(nsITransactionManager *aManager,
201 : nsresult aResult)
202 : {
203 0 : return NS_OK;
204 : }
205 :
206 : NS_IMETHODIMP
207 0 : nsComposerCommandsUpdater::WillEndBatch(nsITransactionManager *aManager,
208 : bool *aInterrupt)
209 : {
210 0 : *aInterrupt = false;
211 0 : return NS_OK;
212 : }
213 :
214 : NS_IMETHODIMP
215 0 : nsComposerCommandsUpdater::DidEndBatch(nsITransactionManager *aManager,
216 : nsresult aResult)
217 : {
218 0 : return NS_OK;
219 : }
220 :
221 : NS_IMETHODIMP
222 0 : nsComposerCommandsUpdater::WillMerge(nsITransactionManager *aManager,
223 : nsITransaction *aTopTransaction,
224 : nsITransaction *aTransactionToMerge,
225 : bool *aInterrupt)
226 : {
227 0 : *aInterrupt = false;
228 0 : return NS_OK;
229 : }
230 :
231 : NS_IMETHODIMP
232 0 : nsComposerCommandsUpdater::DidMerge(nsITransactionManager *aManager,
233 : nsITransaction *aTopTransaction,
234 : nsITransaction *aTransactionToMerge,
235 : bool aDidMerge, nsresult aMergeResult)
236 : {
237 0 : return NS_OK;
238 : }
239 :
240 : #if 0
241 : #pragma mark -
242 : #endif
243 :
244 : nsresult
245 0 : nsComposerCommandsUpdater::Init(nsIDOMWindow* aDOMWindow)
246 : {
247 0 : NS_ENSURE_ARG(aDOMWindow);
248 0 : mDOMWindow = do_GetWeakReference(aDOMWindow);
249 :
250 0 : nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(aDOMWindow));
251 0 : if (window)
252 : {
253 0 : mDocShell = do_GetWeakReference(window->GetDocShell());
254 : }
255 0 : return NS_OK;
256 : }
257 :
258 : nsresult
259 0 : nsComposerCommandsUpdater::PrimeUpdateTimer()
260 : {
261 0 : if (!mUpdateTimer)
262 : {
263 0 : nsresult rv = NS_OK;
264 0 : mUpdateTimer = do_CreateInstance("@mozilla.org/timer;1", &rv);
265 0 : NS_ENSURE_SUCCESS(rv, rv);
266 : }
267 :
268 0 : const PRUint32 kUpdateTimerDelay = 150;
269 0 : return mUpdateTimer->InitWithCallback(static_cast<nsITimerCallback*>(this),
270 : kUpdateTimerDelay,
271 0 : nsITimer::TYPE_ONE_SHOT);
272 : }
273 :
274 :
275 0 : void nsComposerCommandsUpdater::TimerCallback()
276 : {
277 : // if the selection state has changed, update stuff
278 0 : bool isCollapsed = SelectionIsCollapsed();
279 0 : if (isCollapsed != mSelectionCollapsed)
280 : {
281 0 : UpdateCommandGroup(NS_LITERAL_STRING("select"));
282 0 : mSelectionCollapsed = isCollapsed;
283 : }
284 :
285 : // isn't this redundant with the UpdateCommandGroup above?
286 : // can we just nuke the above call? or create a meta command group?
287 0 : UpdateCommandGroup(NS_LITERAL_STRING("style"));
288 0 : }
289 :
290 : nsresult
291 0 : nsComposerCommandsUpdater::UpdateDirtyState(bool aNowDirty)
292 : {
293 0 : if (mDirtyState != aNowDirty)
294 : {
295 0 : UpdateCommandGroup(NS_LITERAL_STRING("save"));
296 0 : UpdateCommandGroup(NS_LITERAL_STRING("undo"));
297 0 : mDirtyState = aNowDirty;
298 : }
299 :
300 0 : return NS_OK;
301 : }
302 :
303 : nsresult
304 0 : nsComposerCommandsUpdater::UpdateCommandGroup(const nsAString& aCommandGroup)
305 : {
306 0 : nsCOMPtr<nsPICommandUpdater> commandUpdater = GetCommandUpdater();
307 0 : NS_ENSURE_TRUE(commandUpdater, NS_ERROR_FAILURE);
308 :
309 :
310 : // This hardcoded list of commands is temporary.
311 : // This code should use nsIControllerCommandGroup.
312 0 : if (aCommandGroup.EqualsLiteral("undo"))
313 : {
314 0 : commandUpdater->CommandStatusChanged("cmd_undo");
315 0 : commandUpdater->CommandStatusChanged("cmd_redo");
316 : }
317 0 : else if (aCommandGroup.EqualsLiteral("select") ||
318 0 : aCommandGroup.EqualsLiteral("style"))
319 : {
320 0 : commandUpdater->CommandStatusChanged("cmd_bold");
321 0 : commandUpdater->CommandStatusChanged("cmd_italic");
322 0 : commandUpdater->CommandStatusChanged("cmd_underline");
323 0 : commandUpdater->CommandStatusChanged("cmd_tt");
324 :
325 0 : commandUpdater->CommandStatusChanged("cmd_strikethrough");
326 0 : commandUpdater->CommandStatusChanged("cmd_superscript");
327 0 : commandUpdater->CommandStatusChanged("cmd_subscript");
328 0 : commandUpdater->CommandStatusChanged("cmd_nobreak");
329 :
330 0 : commandUpdater->CommandStatusChanged("cmd_em");
331 0 : commandUpdater->CommandStatusChanged("cmd_strong");
332 0 : commandUpdater->CommandStatusChanged("cmd_cite");
333 0 : commandUpdater->CommandStatusChanged("cmd_abbr");
334 0 : commandUpdater->CommandStatusChanged("cmd_acronym");
335 0 : commandUpdater->CommandStatusChanged("cmd_code");
336 0 : commandUpdater->CommandStatusChanged("cmd_samp");
337 0 : commandUpdater->CommandStatusChanged("cmd_var");
338 :
339 0 : commandUpdater->CommandStatusChanged("cmd_increaseFont");
340 0 : commandUpdater->CommandStatusChanged("cmd_decreaseFont");
341 :
342 0 : commandUpdater->CommandStatusChanged("cmd_paragraphState");
343 0 : commandUpdater->CommandStatusChanged("cmd_fontFace");
344 0 : commandUpdater->CommandStatusChanged("cmd_fontColor");
345 0 : commandUpdater->CommandStatusChanged("cmd_backgroundColor");
346 0 : commandUpdater->CommandStatusChanged("cmd_highlight");
347 : }
348 0 : else if (aCommandGroup.EqualsLiteral("save"))
349 : {
350 : // save commands (most are not in C++)
351 0 : commandUpdater->CommandStatusChanged("cmd_setDocumentModified");
352 0 : commandUpdater->CommandStatusChanged("cmd_save");
353 : }
354 0 : return NS_OK;
355 : }
356 :
357 : nsresult
358 0 : nsComposerCommandsUpdater::UpdateOneCommand(const char *aCommand)
359 : {
360 0 : nsCOMPtr<nsPICommandUpdater> commandUpdater = GetCommandUpdater();
361 0 : NS_ENSURE_TRUE(commandUpdater, NS_ERROR_FAILURE);
362 :
363 0 : commandUpdater->CommandStatusChanged(aCommand);
364 :
365 0 : return NS_OK;
366 : }
367 :
368 : bool
369 0 : nsComposerCommandsUpdater::SelectionIsCollapsed()
370 : {
371 0 : nsCOMPtr<nsIDOMWindow> domWindow = do_QueryReferent(mDOMWindow);
372 0 : NS_ENSURE_TRUE(domWindow, true);
373 :
374 0 : nsCOMPtr<nsISelection> domSelection;
375 0 : if (NS_SUCCEEDED(domWindow->GetSelection(getter_AddRefs(domSelection))) && domSelection)
376 : {
377 0 : bool selectionCollapsed = false;
378 0 : domSelection->GetIsCollapsed(&selectionCollapsed);
379 0 : return selectionCollapsed;
380 : }
381 :
382 0 : NS_WARNING("nsComposerCommandsUpdater::SelectionIsCollapsed - no domSelection");
383 :
384 0 : return false;
385 : }
386 :
387 : already_AddRefed<nsPICommandUpdater>
388 0 : nsComposerCommandsUpdater::GetCommandUpdater()
389 : {
390 0 : nsCOMPtr<nsIDocShell> docShell = do_QueryReferent(mDocShell);
391 0 : NS_ENSURE_TRUE(docShell, nsnull);
392 0 : nsCOMPtr<nsICommandManager> manager = do_GetInterface(docShell);
393 0 : nsCOMPtr<nsPICommandUpdater> updater = do_QueryInterface(manager);
394 0 : nsPICommandUpdater* retVal = nsnull;
395 0 : updater.swap(retVal);
396 0 : return retVal;
397 : }
398 :
399 : #if 0
400 : #pragma mark -
401 : #endif
402 :
403 : nsresult
404 0 : nsComposerCommandsUpdater::Notify(nsITimer *timer)
405 : {
406 0 : NS_ASSERTION(timer == mUpdateTimer.get(), "Hey, this ain't my timer!");
407 0 : TimerCallback();
408 0 : return NS_OK;
409 : }
410 :
411 : #if 0
412 : #pragma mark -
413 : #endif
414 :
415 :
416 : nsresult
417 0 : NS_NewComposerCommandsUpdater(nsISelectionListener** aInstancePtrResult)
418 : {
419 0 : nsComposerCommandsUpdater* newThang = new nsComposerCommandsUpdater;
420 0 : NS_ENSURE_TRUE(newThang, NS_ERROR_OUT_OF_MEMORY);
421 :
422 : return newThang->QueryInterface(NS_GET_IID(nsISelectionListener),
423 0 : (void **)aInstancePtrResult);
424 : }
|