~ubuntu-branches/ubuntu/precise/kompozer/precise

« back to all changes in this revision

Viewing changes to mozilla/content/base/src/nsSelection.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Anthony Yarusso
  • Date: 2007-08-27 01:11:03 UTC
  • Revision ID: james.westby@ubuntu.com-20070827011103-2jgf4s6532gqu2ka
Tags: upstream-0.7.10
ImportĀ upstreamĀ versionĀ 0.7.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 
2
/* ***** BEGIN LICENSE BLOCK *****
 
3
 * Version: NPL 1.1/GPL 2.0/LGPL 2.1
 
4
 *
 
5
 * The contents of this file are subject to the Netscape Public License
 
6
 * Version 1.1 (the "License"); you may not use this file except in
 
7
 * compliance with the License. You may obtain a copy of the License at
 
8
 * http://www.mozilla.org/NPL/
 
9
 *
 
10
 * Software distributed under the License is distributed on an "AS IS" basis,
 
11
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 
12
 * for the specific language governing rights and limitations under the
 
13
 * License.
 
14
 *
 
15
 * The Original Code is mozilla.org code.
 
16
 *
 
17
 * The Initial Developer of the Original Code is 
 
18
 * Netscape Communications Corporation.
 
19
 * Portions created by the Initial Developer are Copyright (C) 1998
 
20
 * the Initial Developer. All Rights Reserved.
 
21
 *
 
22
 * Contributor(s):
 
23
 *
 
24
 *
 
25
 * Alternatively, the contents of this file may be used under the terms of
 
26
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 
27
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 
28
 * in which case the provisions of the GPL or the LGPL are applicable instead
 
29
 * of those above. If you wish to allow use of your version of this file only
 
30
 * under the terms of either the GPL or the LGPL, and not to allow others to
 
31
 * use your version of this file under the terms of the NPL, indicate your
 
32
 * decision by deleting the provisions above and replace them with the notice
 
33
 * and other provisions required by the GPL or the LGPL. If you do not delete
 
34
 * the provisions above, a recipient may use your version of this file under
 
35
 * the terms of any one of the NPL, the GPL or the LGPL.
 
36
 *
 
37
 * ***** END LICENSE BLOCK ***** */
 
38
 
 
39
/*
 
40
 * Implementation of selection: nsISelection,nsISelectionPrivate and nsIFrameSelection
 
41
 */
 
42
 
 
43
#include "nsCOMPtr.h"
 
44
#include "nsWeakReference.h"
 
45
#include "nsIFactory.h"
 
46
#include "nsIEnumerator.h"
 
47
#include "nsString.h"
 
48
#include "nsReadableUtils.h"
 
49
#include "nsIDOMRange.h"
 
50
#include "nsIFrameSelection.h"
 
51
#include "nsISelection.h"
 
52
#include "nsISelectionPrivate.h"
 
53
#include "nsISelectionListener.h"
 
54
#include "nsIFocusTracker.h"
 
55
#include "nsIComponentManager.h"
 
56
#include "nsContentCID.h"
 
57
#include "nsIContent.h"
 
58
#include "nsIDOMElement.h"
 
59
#include "nsIDOMNode.h"
 
60
#include "nsRange.h"
 
61
#include "nsCOMArray.h"
 
62
#include "nsGUIEvent.h"
 
63
#include "nsIDOMKeyEvent.h"
 
64
#include "nsITableLayout.h"
 
65
#include "nsITableCellLayout.h"
 
66
#include "nsIDOMNodeList.h"
 
67
#include "nsITextContent.h"
 
68
 
 
69
#include "nsISelectionListener.h"
 
70
#include "nsIContentIterator.h"
 
71
#include "nsIDocumentEncoder.h"
 
72
#include "nsIPrefBranch.h"
 
73
#include "nsIPrefService.h"
 
74
 
 
75
// for IBMBIDI
 
76
#include "nsFrameTraversal.h"
 
77
#include "nsILineIterator.h"
 
78
#include "nsLayoutAtoms.h"
 
79
#include "nsIFrameTraversal.h"
 
80
#include "nsLayoutCID.h"
 
81
static NS_DEFINE_CID(kFrameTraversalCID, NS_FRAMETRAVERSAL_CID);
 
82
 
 
83
#include "nsIDOMText.h"
 
84
 
 
85
#include "nsContentUtils.h"
 
86
 
 
87
//included for desired x position;
 
88
#include "nsIPresContext.h"
 
89
#include "nsIPresShell.h"
 
90
#include "nsICaret.h"
 
91
 
 
92
 
 
93
// included for view scrolling
 
94
#include "nsIViewManager.h"
 
95
#include "nsIScrollableView.h"
 
96
#include "nsIDeviceContext.h"
 
97
#include "nsITimer.h"
 
98
#include "nsIServiceManager.h"
 
99
#include "nsIAutoCopy.h"
 
100
#include "nsIEventQueue.h"
 
101
#include "nsIEventQueueService.h"
 
102
 
 
103
// notifications
 
104
#include "nsIDOMDocument.h"
 
105
#include "nsIDocument.h"
 
106
 
 
107
#include "nsISelectionController.h"//for the enums
 
108
#include "nsHTMLAtoms.h"
 
109
 
 
110
#define STATUS_CHECK_RETURN_MACRO() {if (!mTracker) return NS_ERROR_FAILURE;}
 
111
 
 
112
 
 
113
 
 
114
//#define DEBUG_TABLE 1
 
115
 
 
116
// Selection's use of generated content iterators has been turned off
 
117
// temporarily since it bogs down selection in large documents. Using
 
118
// generated content iterators is slower because it must resolve the style
 
119
// for the content to find out if it has any before/after style, and it
 
120
// increases the number of calls to GetPrimaryFrame() which is very expensive.
 
121
//
 
122
// We can reduce the number of calls to GetPrimaryFrame() during selection
 
123
// by a good factor (maybe 2-3 times) if we just ignore the generated content
 
124
// and NOT hilite it when we cross it.
 
125
//
 
126
// #1 the output system doesn't handle it right now anyway so selecting
 
127
//    has no REAL benefit to generated content.
 
128
// #2 there is no available way given to me by troy that can give back the
 
129
//    necessary data without a frame to work from.
 
130
#ifdef USE_SELECTION_GENERATED_CONTENT_ITERATOR_CODE
 
131
static NS_DEFINE_IID(kCGenContentIteratorCID, NS_GENERATEDCONTENTITERATOR_CID);
 
132
static NS_DEFINE_IID(kCGenSubtreeIteratorCID, NS_GENERATEDSUBTREEITERATOR_CID);
 
133
#else
 
134
static NS_DEFINE_IID(kCContentIteratorCID, NS_CONTENTITERATOR_CID);
 
135
static NS_DEFINE_IID(kCSubtreeIteratorCID, NS_SUBTREEITERATOR_CID);
 
136
#endif // USE_SELECTION_GENERATED_CONTENT_ITERATOR_CODE
 
137
 
 
138
#undef OLD_SELECTION
 
139
#undef OLD_TABLE_SELECTION
 
140
 
 
141
 
 
142
//PROTOTYPES
 
143
class nsSelectionIterator;
 
144
class nsSelection;
 
145
class nsAutoScrollTimer;
 
146
struct nsScrollSelectionIntoViewEvent;
 
147
 
 
148
PRBool  IsValidSelectionPoint(nsSelection *aFrameSel, nsIContent *aContent);
 
149
PRBool  IsValidSelectionPoint(nsSelection *aFrameSel, nsIDOMNode *aDomNode);
 
150
 
 
151
static nsIAtom *GetTag(nsIDOMNode *aNode);
 
152
static nsresult ParentOffset(nsIDOMNode *aNode, nsIDOMNode **aParent, PRInt32 *aChildOffset);
 
153
static nsIDOMNode *GetCellParent(nsIDOMNode *aDomNode);
 
154
 
 
155
 
 
156
#ifdef PRINT_RANGE
 
157
static void printRange(nsIDOMRange *aDomRange);
 
158
#define DEBUG_OUT_RANGE(x)  printRange(x)
 
159
#else
 
160
#define DEBUG_OUT_RANGE(x)  
 
161
#endif //MOZ_DEBUG
 
162
 
 
163
 
 
164
 
 
165
//#define DEBUG_SELECTION // uncomment for printf describing every collapse and extend.
 
166
//#define DEBUG_NAVIGATION
 
167
 
 
168
 
 
169
//#define DEBUG_TABLE_SELECTION 1
 
170
 
 
171
 
 
172
struct CachedOffsetForFrame {
 
173
  CachedOffsetForFrame()
 
174
  : mCachedFrameOffset(0, 0) // nsPoint ctor
 
175
  , mLastCaretFrame(nsnull)
 
176
  , mLastContentOffset(0)
 
177
  , mCanCacheFrameOffset(PR_FALSE)
 
178
  {}
 
179
 
 
180
  nsPoint      mCachedFrameOffset;      // cached frame offset
 
181
  nsIFrame*    mLastCaretFrame;         // store the frame the caret was last drawn in.
 
182
  PRInt32      mLastContentOffset;      // store last content offset
 
183
  PRPackedBool mCanCacheFrameOffset;    // cached frame offset is valid?
 
184
};
 
185
 
 
186
class nsTypedSelection : public nsISelection,
 
187
                         public nsISelectionPrivate,
 
188
                         public nsSupportsWeakReference
 
189
{
 
190
public:
 
191
  nsTypedSelection();
 
192
  nsTypedSelection(nsSelection *aList);
 
193
  virtual ~nsTypedSelection();
 
194
  
 
195
  NS_DECL_ISUPPORTS
 
196
  NS_DECL_NSISELECTION
 
197
  NS_DECL_NSISELECTIONPRIVATE
 
198
 
 
199
  // utility methods for scrolling the selection into view
 
200
  nsresult      GetPresContext(nsIPresContext **aPresContext);
 
201
  nsresult      GetPresShell(nsIPresShell **aPresShell);
 
202
  nsresult      GetRootScrollableView(nsIScrollableView **aScrollableView);
 
203
  nsresult      GetFrameToScrolledViewOffsets(nsIScrollableView *aScrollableView, nsIFrame *aFrame, nscoord *aXOffset, nscoord *aYOffset);
 
204
  nsresult      GetPointFromOffset(nsIFrame *aFrame, PRInt32 aContentOffset, nsPoint *aPoint);
 
205
  nsresult      GetSelectionRegionRectAndScrollableView(SelectionRegion aRegion, nsRect *aRect, nsIScrollableView **aScrollableView);
 
206
  nsresult      ScrollRectIntoView(nsIScrollableView *aScrollableView, nsRect& aRect, PRIntn  aVPercent, PRIntn  aHPercent, PRBool aScrollParentViews);
 
207
 
 
208
  nsresult      PostScrollSelectionIntoViewEvent(SelectionRegion aRegion);
 
209
  NS_IMETHOD    ScrollIntoView(SelectionRegion aRegion=nsISelectionController::SELECTION_FOCUS_REGION, PRBool aIsSynchronous=PR_TRUE);
 
210
  nsresult      AddItem(nsIDOMRange *aRange);
 
211
  nsresult      RemoveItem(nsIDOMRange *aRange);
 
212
 
 
213
  nsresult      Clear(nsIPresContext* aPresContext);
 
214
  // methods for convenience. Note, these don't addref
 
215
  nsIDOMNode*  FetchAnchorNode();  //where did the selection begin
 
216
  PRInt32      FetchAnchorOffset();
 
217
 
 
218
  nsIDOMNode*  FetchOriginalAnchorNode();  //where did the ORIGINAL selection begin
 
219
  PRInt32      FetchOriginalAnchorOffset();
 
220
 
 
221
  nsIDOMNode*  FetchFocusNode();   //where is the carret
 
222
  PRInt32      FetchFocusOffset();
 
223
 
 
224
  nsIDOMNode*  FetchStartParent(nsIDOMRange *aRange);   //skip all the com stuff and give me the start/end
 
225
  PRInt32      FetchStartOffset(nsIDOMRange *aRange);
 
226
  nsIDOMNode*  FetchEndParent(nsIDOMRange *aRange);     //skip all the com stuff and give me the start/end
 
227
  PRInt32      FetchEndOffset(nsIDOMRange *aRange);
 
228
 
 
229
  nsDirection  GetDirection(){return mDirection;}
 
230
  void         SetDirection(nsDirection aDir){mDirection = aDir;}
 
231
  PRBool       GetTrueDirection() {return mTrueDirection;}
 
232
  void         SetTrueDirection(PRBool aBool){mTrueDirection = aBool;}
 
233
  NS_IMETHOD   CopyRangeToAnchorFocus(nsIDOMRange *aRange);
 
234
  
 
235
 
 
236
//  NS_IMETHOD   GetPrimaryFrameForRangeEndpoint(nsIDOMNode *aNode, PRInt32 aOffset, PRBool aIsEndNode, nsIFrame **aResultFrame);
 
237
  NS_IMETHOD   GetPrimaryFrameForAnchorNode(nsIFrame **aResultFrame);
 
238
  NS_IMETHOD   GetPrimaryFrameForFocusNode(nsIFrame **aResultFrame, PRInt32 *aOffset);
 
239
  NS_IMETHOD   SetOriginalAnchorPoint(nsIDOMNode *aNode, PRInt32 aOffset);
 
240
  NS_IMETHOD   GetOriginalAnchorPoint(nsIDOMNode **aNode, PRInt32 *aOffset);
 
241
  NS_IMETHOD   LookUpSelection(nsIContent *aContent, PRInt32 aContentOffset, PRInt32 aContentLength,
 
242
                             SelectionDetails **aReturnDetails, SelectionType aType, PRBool aSlowCheck);
 
243
  NS_IMETHOD   Repaint(nsIPresContext* aPresContext);
 
244
 
 
245
  nsresult     StartAutoScrollTimer(nsIPresContext *aPresContext, nsIFrame *aFrame, nsPoint& aPoint, PRUint32 aDelay);
 
246
  nsresult     StopAutoScrollTimer();
 
247
  nsresult     DoAutoScroll(nsIPresContext *aPresContext, nsIFrame *aFrame, nsPoint& aPoint);
 
248
  nsresult     DoAutoScrollView(nsIPresContext *aPresContext, nsIView *aView, nsPoint& aPoint, PRBool aScrollParentViews);
 
249
  nsresult     ScrollPointIntoClipView(nsIPresContext *aPresContext, nsIView *aView, nsPoint& aPoint, PRBool *aDidScroll);
 
250
  nsresult     ScrollPointIntoView(nsIPresContext *aPresContext, nsIView *aView, nsPoint& aPoint, PRBool aScrollParentViews, PRBool *aDidScroll);
 
251
  nsresult     GetViewAncestorOffset(nsIView *aView, nsIView *aAncestorView, nscoord *aXOffset, nscoord *aYOffset);
 
252
  nsresult     GetClosestScrollableView(nsIView *aView, nsIScrollableView **aScrollableView);
 
253
 
 
254
  SelectionType GetType(){return mType;}
 
255
  void          SetType(SelectionType aType){mType = aType;}
 
256
 
 
257
  nsresult     NotifySelectionListeners();
 
258
 
 
259
private:
 
260
  friend class nsSelectionIterator;
 
261
  friend struct nsScrollSelectionIntoViewEvent;
 
262
 
 
263
 
 
264
  void         setAnchorFocusRange(PRInt32 aIndex); //pass in index into FrameSelection
 
265
  NS_IMETHOD   selectFrames(nsIPresContext* aPresContext, nsIContentIterator *aInnerIter, nsIContent *aContent, nsIDOMRange *aRange, nsIPresShell *aPresShell, PRBool aFlags);
 
266
  NS_IMETHOD   selectFrames(nsIPresContext* aPresContext, nsIDOMRange *aRange, PRBool aSelect);
 
267
  nsresult     getTableCellLocationFromRange(nsIDOMRange *aRange, PRInt32 *aSelectionType, PRInt32 *aRow, PRInt32 *aCol);
 
268
  nsresult     addTableCellRange(nsIDOMRange *aRange, PRBool *aDidAddRange);
 
269
  
 
270
#ifdef OLD_SELECTION
 
271
  NS_IMETHOD   FixupSelectionPoints(nsIDOMRange *aRange, nsDirection *aDir, PRBool *aFixupState);
 
272
#endif //OLD_SELECTION
 
273
 
 
274
  nsCOMArray<nsIDOMRange> mRangeArray;
 
275
 
 
276
  nsCOMPtr<nsIDOMRange> mAnchorFocusRange;
 
277
  nsCOMPtr<nsIDOMRange> mOriginalAnchorRange; //used as a point with range gravity for security
 
278
  nsDirection mDirection; //FALSE = focus, anchor;  TRUE = anchor, focus
 
279
  PRBool mFixupState; //was there a fixup?
 
280
 
 
281
  nsSelection *mFrameSelection;
 
282
  nsWeakPtr mPresShellWeak; //weak reference to presshell.
 
283
  SelectionType mType;//type of this nsTypedSelection;
 
284
  nsAutoScrollTimer *mAutoScrollTimer; // timer for autoscrolling.
 
285
  nsCOMArray<nsISelectionListener> mSelectionListeners;
 
286
  PRBool mTrueDirection;
 
287
  nsCOMPtr<nsIEventQueue> mEventQueue;
 
288
  PRBool mScrollEventPosted;
 
289
  CachedOffsetForFrame *mCachedOffsetForFrame;
 
290
};
 
291
 
 
292
// Stack-class to turn on/off selection batching for table selection
 
293
class nsSelectionBatcher
 
294
{
 
295
private:
 
296
  nsCOMPtr<nsISelectionPrivate> mSelection;
 
297
public:
 
298
  nsSelectionBatcher(nsISelectionPrivate *aSelection) : mSelection(aSelection)
 
299
  {
 
300
    if (mSelection) mSelection->StartBatchChanges();
 
301
  }
 
302
  virtual ~nsSelectionBatcher() 
 
303
  { 
 
304
    if (mSelection) mSelection->EndBatchChanges();
 
305
  }
 
306
};
 
307
 
 
308
class nsSelection : public nsIFrameSelection
 
309
                    
 
310
{
 
311
public:
 
312
  /*interfaces for addref and release and queryinterface*/
 
313
  
 
314
  NS_DECL_ISUPPORTS
 
315
 
 
316
/*BEGIN nsIFrameSelection interfaces*/
 
317
  NS_IMETHOD Init(nsIFocusTracker *aTracker, nsIContent *aLimiter);
 
318
  NS_IMETHOD SetScrollableView(nsIScrollableView *aScrollView);
 
319
  NS_IMETHOD GetScrollableView(nsIScrollableView **aScrollView) {*aScrollView = mScrollView; return NS_OK;}
 
320
 
 
321
  NS_IMETHOD ShutDown();
 
322
  NS_IMETHOD HandleTextEvent(nsGUIEvent *aGUIEvent);
 
323
  NS_IMETHOD HandleKeyEvent(nsIPresContext* aPresContext, nsGUIEvent *aGuiEvent);
 
324
  NS_IMETHOD HandleClick(nsIContent *aNewFocus, PRUint32 aContentOffset, PRUint32 aContentEndOffset, 
 
325
                       PRBool aContinueSelection, PRBool aMultipleSelection,PRBool aHint);
 
326
  NS_IMETHOD HandleDrag(nsIPresContext *aPresContext, nsIFrame *aFrame, nsPoint& aPoint);
 
327
  NS_IMETHOD HandleTableSelection(nsIContent *aParentContent, PRInt32 aContentOffset, PRInt32 aTarget, nsMouseEvent *aMouseEvent);
 
328
  NS_IMETHOD StartAutoScrollTimer(nsIPresContext *aPresContext, nsIFrame *aFrame, nsPoint& aPoint, PRUint32 aDelay);
 
329
  NS_IMETHOD StopAutoScrollTimer();
 
330
  NS_IMETHOD EnableFrameNotification(PRBool aEnable){mNotifyFrames = aEnable; return NS_OK;}
 
331
  NS_IMETHOD LookUpSelection(nsIContent *aContent, PRInt32 aContentOffset, PRInt32 aContentLength,
 
332
                             SelectionDetails **aReturnDetails, PRBool aSlowCheck);
 
333
  NS_IMETHOD SetMouseDownState(PRBool aState);
 
334
  NS_IMETHOD GetMouseDownState(PRBool *aState);
 
335
 
 
336
  NS_IMETHOD GetTableCellSelection(PRBool *aState){if (aState){*aState = mSelectingTableCellMode != 0; return NS_OK;}return NS_ERROR_NULL_POINTER;}
 
337
  NS_IMETHOD ClearTableCellSelection(){mSelectingTableCellMode = 0; return NS_OK;}
 
338
  
 
339
  NS_IMETHOD GetSelection(SelectionType aType, nsISelection **aDomSelection);
 
340
  NS_IMETHOD ScrollSelectionIntoView(SelectionType aType, SelectionRegion aRegion, PRBool aIsSynchronous);
 
341
  NS_IMETHOD RepaintSelection(nsIPresContext* aPresContext, SelectionType aType);
 
342
  NS_IMETHOD GetFrameForNodeOffset(nsIContent *aNode, PRInt32 aOffset, HINT aHint, nsIFrame **aReturnFrame, PRInt32 *aReturnOffset);
 
343
  NS_IMETHOD CommonPageMove(PRBool aForward, PRBool aExtend, nsIScrollableView *aScrollableView, nsIFrameSelection *aFrameSel);
 
344
 
 
345
  NS_IMETHOD AdjustOffsetsFromStyle(nsIFrame *aFrame, PRBool *changeSelection,
 
346
        nsIContent** outContent, PRInt32* outStartOffset, PRInt32* outEndOffset);
 
347
  
 
348
  NS_IMETHOD SetHint(HINT aHintRight);
 
349
  NS_IMETHOD GetHint(HINT *aHintRight);
 
350
  NS_IMETHOD CharacterMove(PRBool aForward, PRBool aExtend);
 
351
  NS_IMETHOD WordMove(PRBool aForward, PRBool aExtend);
 
352
  NS_IMETHOD LineMove(PRBool aForward, PRBool aExtend);
 
353
  NS_IMETHOD IntraLineMove(PRBool aForward, PRBool aExtend); 
 
354
  NS_IMETHOD SelectAll();
 
355
  NS_IMETHOD SetDisplaySelection(PRInt16 aState);
 
356
  NS_IMETHOD GetDisplaySelection(PRInt16 *aState);
 
357
  NS_IMETHOD SetDelayCaretOverExistingSelection(PRBool aDelay);
 
358
  NS_IMETHOD GetDelayCaretOverExistingSelection(PRBool *aDelay);
 
359
  NS_IMETHOD SetDelayedCaretData(nsMouseEvent *aMouseEvent);
 
360
  NS_IMETHOD GetDelayedCaretData(nsMouseEvent **aMouseEvent);
 
361
  NS_IMETHOD GetLimiter(nsIContent **aLimiterContent);
 
362
  NS_IMETHOD SetMouseDoubleDown(PRBool aDoubleDown);
 
363
  NS_IMETHOD GetMouseDoubleDown(PRBool *aDoubleDown);
 
364
  NS_IMETHOD GetPrevNextBidiLevels(nsIPresContext *aPresContext,
 
365
                                   nsIContent *aNode,
 
366
                                   PRUint32 aContentOffset,
 
367
                                   nsIFrame **aPrevFrame,
 
368
                                   nsIFrame **aNextFrame,
 
369
                                   PRUint8 *aPrevLevel,
 
370
                                   PRUint8 *aNextLevel);
 
371
  NS_IMETHOD GetFrameFromLevel(nsIPresContext *aPresContext,
 
372
                               nsIFrame *aFrameIn,
 
373
                               nsDirection aDirection,
 
374
                               PRUint8 aBidiLevel,
 
375
                               nsIFrame **aFrameOut);
 
376
  NS_IMETHOD MaintainSelection();
 
377
  /*END nsIFrameSelection interfaces */
 
378
 
 
379
 
 
380
 
 
381
  nsSelection();
 
382
  virtual ~nsSelection();
 
383
 
 
384
  NS_IMETHOD    StartBatchChanges();
 
385
  NS_IMETHOD    EndBatchChanges();
 
386
  NS_IMETHOD    DeleteFromDocument();
 
387
 
 
388
  nsIFocusTracker *GetTracker(){return mTracker;}
 
389
 
 
390
private:
 
391
  NS_IMETHOD TakeFocus(nsIContent *aNewFocus, PRUint32 aContentOffset, PRUint32 aContentEndOffset, 
 
392
                       PRBool aContinueSelection, PRBool aMultipleSelection);
 
393
 
 
394
  void BidiLevelFromMove(nsIPresContext* aContext,
 
395
                         nsIPresShell* aPresShell,
 
396
                         nsIContent *aNode,
 
397
                         PRUint32 aContentOffset,
 
398
                         PRUint32 aKeycode);
 
399
  void BidiLevelFromClick(nsIContent *aNewFocus, PRUint32 aContentOffset);
 
400
#ifdef VISUALSELECTION
 
401
  NS_IMETHOD VisualSelectFrames(nsIPresContext* aContext,
 
402
                                nsIFrame* aCurrentFrame,
 
403
                                nsPeekOffsetStruct aPos);
 
404
  NS_IMETHOD VisualSequence(nsIPresContext *aPresContext,
 
405
                            nsIFrame* aSelectFrame,
 
406
                            nsIFrame* aCurrentFrame,
 
407
                            nsPeekOffsetStruct* aPos,
 
408
                            PRBool* aNeedVisualSelection);
 
409
  NS_IMETHOD SelectToEdge(nsIFrame *aFrame,
 
410
                          nsIContent *aContent,
 
411
                          PRInt32 aOffset,
 
412
                          PRInt32 aEdge,
 
413
                          PRBool aMultipleSelection);
 
414
  NS_IMETHOD SelectLines(nsIPresContext *aPresContext,
 
415
                         nsDirection aSelectionDirection,
 
416
                         nsIDOMNode *aAnchorNode,
 
417
                         nsIFrame* aAnchorFrame,
 
418
                         PRInt32 aAnchorOffset,
 
419
                         nsIDOMNode *aCurrentNode,
 
420
                         nsIFrame* aCurrentFrame,
 
421
                         PRInt32 aCurrentOffset,
 
422
                         nsPeekOffsetStruct aPos);
 
423
#endif // VISUALSELECTION
 
424
 
 
425
  PRBool AdjustForMaintainedSelection(nsIContent *aContent, PRInt32 aOffset);
 
426
 
 
427
// post and pop reasons for notifications. we may stack these later
 
428
  void    PostReason(PRInt16 aReason) { mSelectionChangeReason = aReason; }
 
429
  PRInt16 PopReason()
 
430
  {
 
431
    PRInt16 retval = mSelectionChangeReason;
 
432
    mSelectionChangeReason = 0;
 
433
    return retval;
 
434
  }
 
435
 
 
436
  friend class nsTypedSelection; 
 
437
#ifdef DEBUG
 
438
  void printSelection();       // for debugging
 
439
#endif /* DEBUG */
 
440
 
 
441
  void ResizeBuffer(PRUint32 aNewBufSize);
 
442
/*HELPER METHODS*/
 
443
  nsresult     MoveCaret(PRUint32 aKeycode, PRBool aContinue, nsSelectionAmount aAmount);
 
444
 
 
445
  nsresult     FetchDesiredX(nscoord &aDesiredX); //the x position requested by the Key Handling for up down
 
446
  void         InvalidateDesiredX(); //do not listen to mDesiredX you must get another.
 
447
  void         SetDesiredX(nscoord aX); //set the mDesiredX
 
448
 
 
449
  nsresult     GetRootForContentSubtree(nsIContent *aContent, nsIContent **aParent);
 
450
  nsresult     GetGlobalViewOffsetsFromFrame(nsIPresContext *aPresContext, nsIFrame *aFrame, nscoord *offsetX, nscoord *offsetY);
 
451
  nsresult     ConstrainFrameAndPointToAnchorSubtree(nsIPresContext *aPresContext, nsIFrame *aFrame, nsPoint& aPoint, nsIFrame **aRetFrame, nsPoint& aRetPoint);
 
452
 
 
453
  PRUint32     GetBatching(){return mBatching;}
 
454
  PRBool       GetNotifyFrames(){return mNotifyFrames;}
 
455
  void         SetDirty(PRBool aDirty=PR_TRUE){if (mBatching) mChangesDuringBatching = aDirty;}
 
456
 
 
457
  nsresult     NotifySelectionListeners(SelectionType aType);     // add parameters to say collapsed etc?
 
458
 
 
459
  // utility method to lookup frame style
 
460
  nsresult      FrameOrParentHasSpecialSelectionStyle(nsIFrame* aFrame, PRUint8 aSelectionStyle, nsIFrame* *foundFrame);
 
461
  nsresult      FrameOrParentHasSpecialSelectionStyleBelowAncestor(nsIFrame* aFrame, PRUint8 aSelectionStyle,
 
462
                                                                  nsIFrame * aAncestorFrame,
 
463
                                                                  nsIFrame* *foundFrame);
 
464
 
 
465
  nsTypedSelection *mDomSelections[nsISelectionController::NUM_SELECTIONTYPES];
 
466
 
 
467
  // Table selection support.
 
468
  // Interfaces that let us get info based on cellmap locations
 
469
  nsITableLayout* GetTableLayout(nsIContent *aTableContent);
 
470
  nsITableCellLayout* GetCellLayout(nsIContent *aCellContent);
 
471
 
 
472
  nsresult SelectBlockOfCells(nsIContent *aStartNode, nsIContent *aEndNode);
 
473
  nsresult SelectRowOrColumn(nsIContent *aCellContent, PRUint32 aTarget);
 
474
  nsresult GetCellIndexes(nsIContent *aCell, PRInt32 &aRowIndex, PRInt32 &aColIndex);
 
475
 
 
476
  nsresult GetFirstSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRange);
 
477
  nsresult GetNextSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRange);
 
478
  nsresult GetFirstCellNodeInRange(nsIDOMRange *aRange, nsIDOMNode **aCellNode);
 
479
  // aTableNode may be null if table isn't needed to be returned
 
480
  PRBool   IsInSameTable(nsIContent *aContent1, nsIContent *aContent2, nsIContent **aTableNode);
 
481
  nsresult GetParentTable(nsIContent *aCellNode, nsIContent **aTableNode);
 
482
  nsresult SelectCellElement(nsIDOMElement* aCellElement);
 
483
  nsresult CreateAndAddRange(nsIDOMNode *aParentNode, PRInt32 aOffset);
 
484
  nsresult ClearNormalSelection();
 
485
 
 
486
  nsCOMPtr<nsIDOMNode> mCellParent; //used to snap to table selection
 
487
  nsCOMPtr<nsIContent> mStartSelectedCell;
 
488
  nsCOMPtr<nsIContent> mEndSelectedCell;
 
489
  nsCOMPtr<nsIContent> mAppendStartSelectedCell;
 
490
  nsCOMPtr<nsIContent> mUnselectCellOnMouseUp;
 
491
  PRInt32  mSelectingTableCellMode;
 
492
  PRInt32  mSelectedCellIndex;
 
493
 
 
494
  // maintain selection
 
495
  nsCOMPtr<nsIDOMRange> mMaintainRange;
 
496
 
 
497
  //batching
 
498
  PRInt32 mBatching;
 
499
    
 
500
  nsIContent *mLimiter;     //limit selection navigation to a child of this node.
 
501
  nsIFocusTracker *mTracker;
 
502
 
 
503
  PRInt16 mSelectionChangeReason; // reason for notifications of selection changing
 
504
  PRInt16 mDisplaySelection; //for visual display purposes.
 
505
 
 
506
  HINT  mHint;   //hint to tell if the selection is at the end of this line or beginning of next
 
507
 
 
508
  PRInt32 mDesiredX;
 
509
  nsIScrollableView *mScrollView;
 
510
 
 
511
  nsMouseEvent mDelayedMouseEvent;
 
512
 
 
513
  PRPackedBool mDelayCaretOverExistingSelection;
 
514
  PRPackedBool mDelayedMouseEventValid;
 
515
 
 
516
  PRPackedBool mChangesDuringBatching;
 
517
  PRPackedBool mNotifyFrames;
 
518
  PRPackedBool mIsEditor;
 
519
  PRPackedBool mDragSelectingCells;
 
520
  PRPackedBool mMouseDownState;   //for drag purposes
 
521
  PRPackedBool mMouseDoubleDownState; //has the doubleclick down happened
 
522
  PRPackedBool mDesiredXSet;
 
523
};
 
524
 
 
525
class nsSelectionIterator : public nsIBidirectionalEnumerator
 
526
{
 
527
public:
 
528
/*BEGIN nsIEnumerator interfaces
 
529
see the nsIEnumerator for more details*/
 
530
 
 
531
  NS_DECL_ISUPPORTS
 
532
 
 
533
  NS_DECL_NSIENUMERATOR
 
534
 
 
535
  NS_DECL_NSIBIDIRECTIONALENUMERATOR
 
536
 
 
537
/*END nsIEnumerator interfaces*/
 
538
/*BEGIN Helper Methods*/
 
539
  NS_IMETHOD CurrentItem(nsIDOMRange **aRange);
 
540
/*END Helper Methods*/
 
541
private:
 
542
  friend class nsTypedSelection;
 
543
 
 
544
  //lame lame lame if delete from document goes away then get rid of this unless its debug
 
545
  friend class nsSelection; 
 
546
 
 
547
  nsSelectionIterator(nsTypedSelection *);
 
548
  virtual ~nsSelectionIterator();
 
549
  PRInt32     mIndex;
 
550
  nsTypedSelection *mDomSelection;
 
551
  SelectionType mType;
 
552
};
 
553
 
 
554
class nsAutoScrollTimer : public nsITimerCallback
 
555
{
 
556
public:
 
557
 
 
558
  NS_DECL_ISUPPORTS
 
559
 
 
560
  nsAutoScrollTimer()
 
561
      : mSelection(0), mView(0), mPresContext(0), mPoint(0,0), mDelay(30)
 
562
  {
 
563
  }
 
564
 
 
565
  virtual ~nsAutoScrollTimer()
 
566
  {
 
567
   if (mTimer)
 
568
       mTimer->Cancel();
 
569
  }
 
570
 
 
571
  nsresult Start(nsIPresContext *aPresContext, nsIView *aView, nsPoint &aPoint)
 
572
  {
 
573
    mView        = aView;
 
574
    mPresContext = aPresContext;
 
575
    mPoint       = aPoint;
 
576
 
 
577
    if (!mTimer)
 
578
    {
 
579
      nsresult result;
 
580
      mTimer = do_CreateInstance("@mozilla.org/timer;1", &result);
 
581
 
 
582
      if (NS_FAILED(result))
 
583
        return result;
 
584
    }
 
585
 
 
586
    return mTimer->InitWithCallback(this, mDelay, nsITimer::TYPE_ONE_SHOT);
 
587
  }
 
588
 
 
589
  nsresult Stop()
 
590
  {
 
591
    nsresult result = NS_OK;
 
592
 
 
593
    if (mTimer)
 
594
    {
 
595
      mTimer->Cancel();
 
596
      mTimer = 0;
 
597
    }
 
598
 
 
599
    return result;
 
600
  }
 
601
 
 
602
  nsresult Init(nsSelection *aFrameSelection, nsTypedSelection *aSelection)
 
603
  {
 
604
    mFrameSelection = aFrameSelection;
 
605
    mSelection = aSelection;
 
606
    return NS_OK;
 
607
  }
 
608
 
 
609
  nsresult SetDelay(PRUint32 aDelay)
 
610
  {
 
611
    mDelay = aDelay;
 
612
    return NS_OK;
 
613
  }
 
614
 
 
615
  NS_IMETHOD Notify(nsITimer *timer)
 
616
  {
 
617
    if (mSelection && mPresContext && mView)
 
618
    {
 
619
      nsIFrame *frame = NS_STATIC_CAST(nsIFrame*, mView->GetClientData());
 
620
 
 
621
      if (!frame)
 
622
        return NS_OK;
 
623
 
 
624
      //the frame passed in here will be a root frame for the view. there is no need to call the constrain
 
625
      //method here. the root frame has NO content now unfortunately...
 
626
      PRInt32 startPos = 0;
 
627
      PRInt32 contentOffsetEnd = 0;
 
628
      PRBool  beginOfContent;
 
629
      nsCOMPtr<nsIContent> newContent;
 
630
      nsresult result = frame->GetContentAndOffsetsFromPoint(mPresContext, mPoint,
 
631
                                                   getter_AddRefs(newContent), 
 
632
                                                   startPos, contentOffsetEnd,beginOfContent);
 
633
 
 
634
      if (NS_SUCCEEDED(result))
 
635
        result = mFrameSelection->HandleClick(newContent, startPos, contentOffsetEnd , PR_TRUE, PR_FALSE, beginOfContent);
 
636
 
 
637
      //mFrameSelection->HandleDrag(mPresContext, mFrame, mPoint);
 
638
      mSelection->DoAutoScrollView(mPresContext, mView, mPoint, PR_TRUE);
 
639
    }
 
640
    return NS_OK;
 
641
  }
 
642
private:
 
643
  nsSelection    *mFrameSelection;
 
644
  nsTypedSelection *mSelection;
 
645
  nsCOMPtr<nsITimer> mTimer;
 
646
  nsIView        *mView;
 
647
  nsIPresContext *mPresContext;
 
648
  nsPoint         mPoint;
 
649
  PRUint32        mDelay;
 
650
};
 
651
 
 
652
NS_IMPL_ADDREF(nsAutoScrollTimer)
 
653
NS_IMPL_RELEASE(nsAutoScrollTimer)
 
654
NS_IMPL_QUERY_INTERFACE1(nsAutoScrollTimer, nsITimerCallback)
 
655
 
 
656
nsresult NS_NewAutoScrollTimer(nsAutoScrollTimer **aResult);
 
657
 
 
658
nsresult NS_NewAutoScrollTimer(nsAutoScrollTimer **aResult)
 
659
{
 
660
  if (!aResult)
 
661
    return NS_ERROR_NULL_POINTER;
 
662
 
 
663
  *aResult = (nsAutoScrollTimer*) new nsAutoScrollTimer;
 
664
 
 
665
  if (!aResult)
 
666
    return NS_ERROR_OUT_OF_MEMORY;
 
667
 
 
668
  NS_ADDREF(*aResult);
 
669
 
 
670
  return NS_OK;
 
671
}
 
672
 
 
673
nsresult NS_NewSelection(nsIFrameSelection **aFrameSelection);
 
674
 
 
675
nsresult NS_NewSelection(nsIFrameSelection **aFrameSelection)
 
676
{
 
677
  nsSelection *rlist = new nsSelection;
 
678
  if (!rlist)
 
679
    return NS_ERROR_OUT_OF_MEMORY;
 
680
  *aFrameSelection = (nsIFrameSelection *)rlist;
 
681
  rlist->AddRef();
 
682
  return NS_OK;
 
683
}
 
684
 
 
685
nsresult NS_NewDomSelection(nsISelection **aDomSelection);
 
686
 
 
687
nsresult NS_NewDomSelection(nsISelection **aDomSelection)
 
688
{
 
689
  nsTypedSelection *rlist = new nsTypedSelection;
 
690
  if (!rlist)
 
691
    return NS_ERROR_OUT_OF_MEMORY;
 
692
  *aDomSelection = (nsISelection *)rlist;
 
693
  rlist->AddRef();
 
694
  return NS_OK;
 
695
}
 
696
 
 
697
static PRInt8
 
698
GetIndexFromSelectionType(SelectionType aType)
 
699
{
 
700
    switch (aType)
 
701
    {
 
702
    case nsISelectionController::SELECTION_NORMAL: return 0; break;
 
703
    case nsISelectionController::SELECTION_SPELLCHECK: return 1; break;
 
704
    case nsISelectionController::SELECTION_IME_RAWINPUT: return 2; break;
 
705
    case nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT: return 3; break;
 
706
    case nsISelectionController::SELECTION_IME_CONVERTEDTEXT: return 4; break;
 
707
    case nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT: return 5; break;
 
708
    case nsISelectionController::SELECTION_ACCESSIBILITY: return 6; break;
 
709
    default:return -1;break;
 
710
    }
 
711
    /* NOTREACHED */
 
712
    return 0;
 
713
}
 
714
 
 
715
static SelectionType 
 
716
GetSelectionTypeFromIndex(PRInt8 aIndex)
 
717
{
 
718
  switch (aIndex)
 
719
  {
 
720
    case 0: return nsISelectionController::SELECTION_NORMAL;break;
 
721
    case 1: return nsISelectionController::SELECTION_SPELLCHECK;break;
 
722
    case 2: return nsISelectionController::SELECTION_IME_RAWINPUT;break;
 
723
    case 3: return nsISelectionController::SELECTION_IME_SELECTEDRAWTEXT;break;
 
724
    case 4: return nsISelectionController::SELECTION_IME_CONVERTEDTEXT;break;
 
725
    case 5: return nsISelectionController::SELECTION_IME_SELECTEDCONVERTEDTEXT;break;
 
726
    case 6: return nsISelectionController::SELECTION_ACCESSIBILITY;break;
 
727
    default:
 
728
      return nsISelectionController::SELECTION_NORMAL;break;
 
729
  }
 
730
  /* NOTREACHED */
 
731
  return 0;
 
732
}
 
733
 
 
734
//utility methods to check the content vs the limiter that will hold selection to a piece of the dom
 
735
PRBool       
 
736
IsValidSelectionPoint(nsSelection *aFrameSel, nsIDOMNode *aDomNode)
 
737
{
 
738
    nsCOMPtr<nsIContent> passedContent;
 
739
    passedContent = do_QueryInterface(aDomNode);
 
740
    if (!passedContent)
 
741
      return PR_FALSE;
 
742
    return IsValidSelectionPoint(aFrameSel,passedContent);
 
743
}
 
744
 
 
745
/*
 
746
The limiter is used specifically for the text areas and textfields
 
747
In that case it is the DIV tag that is anonymously created for the text
 
748
areas/fields.  Text nodes and BR nodes fall beneath it.  In the case of a 
 
749
BR node the limiter will be the parent and the offset will point before or
 
750
after the BR node.  In the case of the text node the parent content is 
 
751
the text node itself and the offset will be the exact character position.
 
752
The offset is not important to check for validity.  Simply look at the 
 
753
passed in content.  If it equals the limiter then the selection point is valid.
 
754
If its parent it the limiter then the point is also valid.  In the case of 
 
755
NO limiter all points are valid since you are in a topmost iframe. (browser
 
756
or composer)
 
757
*/
 
758
PRBool       
 
759
IsValidSelectionPoint(nsSelection *aFrameSel, nsIContent *aContent)
 
760
{
 
761
  if (!aFrameSel || !aContent)
 
762
    return PR_FALSE;
 
763
  if (aFrameSel)
 
764
  {
 
765
    nsresult result;
 
766
    nsCOMPtr<nsIContent> tLimiter;
 
767
    result = aFrameSel->GetLimiter(getter_AddRefs(tLimiter));
 
768
    if (NS_FAILED(result))
 
769
      return PR_FALSE;
 
770
    if (tLimiter && tLimiter != aContent)
 
771
    {
 
772
      if (tLimiter != aContent->GetParent()) //if newfocus == the limiter. thats ok. but if not there and not parent bad
 
773
        return PR_FALSE; //not in the right content. tLimiter said so
 
774
    }
 
775
  }
 
776
  return PR_TRUE;
 
777
}
 
778
 
 
779
 
 
780
NS_IMPL_ADDREF(nsSelectionIterator)
 
781
NS_IMPL_RELEASE(nsSelectionIterator)
 
782
 
 
783
NS_INTERFACE_MAP_BEGIN(nsSelectionIterator)
 
784
  NS_INTERFACE_MAP_ENTRY(nsIEnumerator)
 
785
  NS_INTERFACE_MAP_ENTRY(nsIBidirectionalEnumerator)
 
786
NS_INTERFACE_MAP_END_AGGREGATED(mDomSelection)
 
787
 
 
788
 
 
789
///////////BEGIN nsSelectionIterator methods
 
790
 
 
791
nsSelectionIterator::nsSelectionIterator(nsTypedSelection *aList)
 
792
:mIndex(0)
 
793
{
 
794
  if (!aList)
 
795
  {
 
796
    NS_NOTREACHED("nsSelection");
 
797
    return;
 
798
  }
 
799
  mDomSelection = aList;
 
800
}
 
801
 
 
802
 
 
803
 
 
804
nsSelectionIterator::~nsSelectionIterator()
 
805
{
 
806
}
 
807
 
 
808
 
 
809
 
 
810
////////////END nsSelectionIterator methods
 
811
 
 
812
////////////BEGIN nsIFrameSelectionIterator methods
 
813
 
 
814
 
 
815
 
 
816
NS_IMETHODIMP
 
817
nsSelectionIterator::Next()
 
818
{
 
819
  mIndex++;
 
820
  PRInt32 cnt = mDomSelection->mRangeArray.Count();
 
821
  if (mIndex < cnt)
 
822
    return NS_OK;
 
823
  return NS_ERROR_FAILURE;
 
824
}
 
825
 
 
826
 
 
827
 
 
828
NS_IMETHODIMP
 
829
nsSelectionIterator::Prev()
 
830
{
 
831
  mIndex--;
 
832
  if (mIndex >= 0 )
 
833
    return NS_OK;
 
834
  return NS_ERROR_FAILURE;
 
835
}
 
836
 
 
837
 
 
838
 
 
839
NS_IMETHODIMP
 
840
nsSelectionIterator::First()
 
841
{
 
842
  if (!mDomSelection)
 
843
    return NS_ERROR_NULL_POINTER;
 
844
  mIndex = 0;
 
845
  return NS_OK;
 
846
}
 
847
 
 
848
 
 
849
 
 
850
NS_IMETHODIMP
 
851
nsSelectionIterator::Last()
 
852
{
 
853
  if (!mDomSelection)
 
854
    return NS_ERROR_NULL_POINTER;
 
855
  mIndex = mDomSelection->mRangeArray.Count() - 1;
 
856
  return NS_OK;
 
857
}
 
858
 
 
859
 
 
860
 
 
861
NS_IMETHODIMP 
 
862
nsSelectionIterator::CurrentItem(nsISupports **aItem)
 
863
{
 
864
  if (!aItem)
 
865
    return NS_ERROR_NULL_POINTER;
 
866
 
 
867
  if (mIndex < 0 || mIndex >= mDomSelection->mRangeArray.Count()) {
 
868
    return NS_ERROR_FAILURE;
 
869
  }
 
870
 
 
871
  return CallQueryInterface(mDomSelection->mRangeArray[mIndex],
 
872
                            aItem);
 
873
}
 
874
 
 
875
 
 
876
NS_IMETHODIMP 
 
877
nsSelectionIterator::CurrentItem(nsIDOMRange **aItem)
 
878
{
 
879
  if (!aItem)
 
880
    return NS_ERROR_NULL_POINTER;
 
881
  if (mIndex < 0 || mIndex >= mDomSelection->mRangeArray.Count()) {
 
882
    return NS_ERROR_FAILURE;
 
883
  }
 
884
 
 
885
  *aItem = mDomSelection->mRangeArray[mIndex];
 
886
  NS_IF_ADDREF(*aItem);
 
887
  return NS_OK;
 
888
}
 
889
 
 
890
 
 
891
 
 
892
NS_IMETHODIMP
 
893
nsSelectionIterator::IsDone()
 
894
{
 
895
  PRInt32 cnt = mDomSelection->mRangeArray.Count();
 
896
  if (mIndex >= 0 && mIndex < cnt) {
 
897
    return NS_ENUMERATOR_FALSE;
 
898
  }
 
899
  return NS_OK;
 
900
}
 
901
 
 
902
 
 
903
////////////END nsIFrameSelectionIterator methods
 
904
 
 
905
#ifdef XP_MAC
 
906
#pragma mark -
 
907
#endif
 
908
 
 
909
////////////BEGIN nsSelection methods
 
910
 
 
911
nsSelection::nsSelection()
 
912
{
 
913
  PRInt32 i;
 
914
  for (i = 0;i<nsISelectionController::NUM_SELECTIONTYPES;i++){
 
915
    mDomSelections[i] = nsnull;
 
916
  }
 
917
  for (i = 0;i<nsISelectionController::NUM_SELECTIONTYPES;i++){
 
918
    mDomSelections[i] = new nsTypedSelection(this);
 
919
    if (!mDomSelections[i])
 
920
      return;
 
921
    mDomSelections[i]->AddRef();
 
922
    mDomSelections[i]->SetType(GetSelectionTypeFromIndex(i));
 
923
  }
 
924
  mBatching = 0;
 
925
  mChangesDuringBatching = PR_FALSE;
 
926
  mNotifyFrames = PR_TRUE;
 
927
  mLimiter = nsnull; //no default limiter.
 
928
  
 
929
  mMouseDoubleDownState = PR_FALSE;
 
930
  
 
931
  mHint = HINTLEFT;
 
932
  mDragSelectingCells = PR_FALSE;
 
933
  mSelectingTableCellMode = 0;
 
934
  mSelectedCellIndex = 0;
 
935
 
 
936
 
 
937
  
 
938
  // Check to see if the autocopy pref is enabled
 
939
  //   and add the autocopy listener if it is
 
940
  nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID));
 
941
  if (prefBranch) {
 
942
    static char pref[] = "clipboard.autocopy";
 
943
    PRBool autoCopy = PR_FALSE;
 
944
    if (NS_SUCCEEDED(prefBranch->GetBoolPref(pref, &autoCopy)) && autoCopy) {
 
945
      nsCOMPtr<nsIAutoCopyService> autoCopyService = 
 
946
        do_GetService("@mozilla.org/autocopy;1");
 
947
 
 
948
      if (autoCopyService) {
 
949
        PRInt8 index =
 
950
          GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
 
951
        if (mDomSelections[index]) {
 
952
          autoCopyService->Listen(mDomSelections[index]);
 
953
        }
 
954
      }
 
955
    }
 
956
  }
 
957
 
 
958
  mDisplaySelection = nsISelectionController::SELECTION_OFF;
 
959
 
 
960
  mDelayCaretOverExistingSelection = PR_TRUE;
 
961
  mDelayedMouseEventValid = PR_FALSE;
 
962
  mSelectionChangeReason = nsISelectionListener::NO_REASON;
 
963
}
 
964
 
 
965
 
 
966
nsSelection::~nsSelection() 
 
967
{
 
968
  PRInt32 i;
 
969
  for (i = 0;i<nsISelectionController::NUM_SELECTIONTYPES;i++){
 
970
    if (mDomSelections[i])
 
971
        NS_IF_RELEASE(mDomSelections[i]);
 
972
  }
 
973
}
 
974
 
 
975
 
 
976
NS_IMPL_ISUPPORTS1(nsSelection, nsIFrameSelection)
 
977
 
 
978
 
 
979
nsresult
 
980
nsSelection::FetchDesiredX(nscoord &aDesiredX) //the x position requested by the Key Handling for up down
 
981
{
 
982
  if (!mTracker)
 
983
  {
 
984
    NS_ASSERTION(0,"fetch desired X failed\n");
 
985
    return NS_ERROR_FAILURE;
 
986
  }
 
987
  if (mDesiredXSet)
 
988
  {
 
989
    aDesiredX = mDesiredX;
 
990
    return NS_OK;
 
991
  }
 
992
 
 
993
  nsCOMPtr<nsIPresContext> context;
 
994
  nsresult result = mTracker->GetPresContext(getter_AddRefs(context));
 
995
  if (NS_FAILED(result))
 
996
    return result;
 
997
  if (!context)
 
998
    return NS_ERROR_NULL_POINTER;
 
999
 
 
1000
  nsIPresShell *shell = context->GetPresShell();
 
1001
  if (!shell)
 
1002
    return NS_ERROR_NULL_POINTER;
 
1003
 
 
1004
  nsCOMPtr<nsICaret> caret;
 
1005
  result = shell->GetCaret(getter_AddRefs(caret));
 
1006
  if (NS_FAILED(result))
 
1007
    return result;
 
1008
  if (!caret)
 
1009
    return NS_ERROR_NULL_POINTER;
 
1010
 
 
1011
  nsRect coord;
 
1012
  PRBool  collapsed;
 
1013
  PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
 
1014
  result = caret->SetCaretDOMSelection(mDomSelections[index]);
 
1015
  if (NS_FAILED(result))
 
1016
    return result;
 
1017
 
 
1018
  result = caret->GetCaretCoordinates(nsICaret::eClosestViewCoordinates, mDomSelections[index], &coord, &collapsed, nsnull);
 
1019
  if (NS_FAILED(result))
 
1020
    return result;
 
1021
   
 
1022
  aDesiredX = coord.x;
 
1023
  return NS_OK;
 
1024
}
 
1025
 
 
1026
 
 
1027
 
 
1028
void
 
1029
nsSelection::InvalidateDesiredX() //do not listen to mDesiredX you must get another.
 
1030
{
 
1031
  mDesiredXSet = PR_FALSE;
 
1032
}
 
1033
 
 
1034
 
 
1035
 
 
1036
void
 
1037
nsSelection::SetDesiredX(nscoord aX) //set the mDesiredX
 
1038
{
 
1039
  mDesiredX = aX;
 
1040
  mDesiredXSet = PR_TRUE;
 
1041
}
 
1042
 
 
1043
nsresult
 
1044
nsSelection::GetRootForContentSubtree(nsIContent *aContent, nsIContent **aParent)
 
1045
{
 
1046
  // This method returns the root of the sub-tree containing aContent.
 
1047
  // We do this by searching up through the parent hierarchy, and stopping
 
1048
  // when there are no more parents, or we hit a situation where the
 
1049
  // parent/child relationship becomes invalid.
 
1050
  //
 
1051
  // An example of an invalid parent/child relationship is anonymous content.
 
1052
  // Anonymous content has a pointer to it's parent, but it is not listed
 
1053
  // as a child of it's parent. In this case, the anonymous content would
 
1054
  // be considered the root of the subtree.
 
1055
 
 
1056
  if (!aContent || !aParent)
 
1057
    return NS_ERROR_NULL_POINTER;
 
1058
 
 
1059
  *aParent = 0;
 
1060
 
 
1061
  nsIContent* child = aContent;
 
1062
 
 
1063
  while (child)
 
1064
  {
 
1065
    nsIContent* parent = child->GetParent();
 
1066
 
 
1067
    if (!parent)
 
1068
      break;
 
1069
 
 
1070
    PRUint32 childCount = parent->GetChildCount();
 
1071
 
 
1072
    if (childCount < 1)
 
1073
      break;
 
1074
 
 
1075
    PRInt32 childIndex = parent->IndexOf(child);
 
1076
 
 
1077
    if (childIndex < 0 || ((PRUint32)childIndex) >= childCount)
 
1078
      break;
 
1079
 
 
1080
    child = parent;
 
1081
  }
 
1082
 
 
1083
  NS_IF_ADDREF(*aParent = child);
 
1084
 
 
1085
  return NS_OK;
 
1086
}
 
1087
 
 
1088
nsresult
 
1089
nsSelection::GetGlobalViewOffsetsFromFrame(nsIPresContext *aPresContext, nsIFrame *aFrame, nscoord *offsetX, nscoord *offsetY)
 
1090
{
 
1091
  //
 
1092
  // The idea here is to figure out what the offset of aFrame's view
 
1093
  // is within the global space. Where I define the global space to
 
1094
  // be the coordinate system that exists above all views.
 
1095
  //
 
1096
  // The offsets are calculated by walking up the view parent hierarchy,
 
1097
  // adding up all the view positions, until there are no more views.
 
1098
  //
 
1099
  // A point in a view's coordinate space can be converted to the global
 
1100
  // coordinate space by simply adding the offsets returned by this method
 
1101
  // to the point itself.
 
1102
  //
 
1103
 
 
1104
  if (!aPresContext || !aFrame || !offsetX || !offsetY)
 
1105
    return NS_ERROR_NULL_POINTER;
 
1106
 
 
1107
  *offsetX = *offsetY = 0;
 
1108
 
 
1109
  nsIFrame *frame = aFrame;
 
1110
  while (frame)
 
1111
  {
 
1112
    frame = frame->GetAncestorWithView();
 
1113
 
 
1114
    if (frame) {
 
1115
      nsIView *view = frame->GetView();
 
1116
 
 
1117
      if (view)
 
1118
      {
 
1119
        nsPoint pt = view->GetPosition();
 
1120
        *offsetX += pt.x;
 
1121
        *offsetY += pt.y;
 
1122
      }
 
1123
    }
 
1124
  }
 
1125
 
 
1126
  return NS_OK;
 
1127
}
 
1128
 
 
1129
nsresult
 
1130
nsSelection::ConstrainFrameAndPointToAnchorSubtree(nsIPresContext *aPresContext, nsIFrame *aFrame, nsPoint& aPoint, nsIFrame **aRetFrame, nsPoint& aRetPoint)
 
1131
{
 
1132
  //
 
1133
  // The whole point of this method is to return a frame and point that
 
1134
  // that lie within the same valid subtree as the anchor node's frame,
 
1135
  // for use with the method GetContentAndOffsetsFromPoint().
 
1136
  //
 
1137
  // A valid subtree is defined to be one where all the content nodes in
 
1138
  // the tree have a valid parent-child relationship.
 
1139
  //
 
1140
  // If the anchor frame and aFrame are in the same subtree, aFrame will
 
1141
  // be returned in aRetFrame. If they are in different subtrees, we
 
1142
  // return the frame for the root of the subtree.
 
1143
  //
 
1144
 
 
1145
  if (!aFrame || !aRetFrame)
 
1146
    return NS_ERROR_NULL_POINTER;
 
1147
 
 
1148
  *aRetFrame = aFrame;
 
1149
  aRetPoint  = aPoint;
 
1150
 
 
1151
  //
 
1152
  // Get the frame and content for the selection's anchor point!
 
1153
  //
 
1154
 
 
1155
  nsresult result;
 
1156
  nsCOMPtr<nsIDOMNode> anchorNode;
 
1157
  PRInt32 anchorOffset = 0;
 
1158
  PRInt32 anchorFrameOffset = 0;
 
1159
 
 
1160
  PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
 
1161
  if (! mDomSelections[index])
 
1162
    return NS_ERROR_NULL_POINTER;
 
1163
 
 
1164
  result = mDomSelections[index]->GetAnchorNode(getter_AddRefs(anchorNode));
 
1165
 
 
1166
  if (NS_FAILED(result))
 
1167
    return result;
 
1168
 
 
1169
  if (!anchorNode)
 
1170
    return NS_OK;
 
1171
 
 
1172
  result = mDomSelections[index]->GetAnchorOffset(&anchorOffset);
 
1173
 
 
1174
  if (NS_FAILED(result))
 
1175
    return result;
 
1176
 
 
1177
  nsIFrame *anchorFrame = 0;
 
1178
  nsCOMPtr<nsIContent> anchorContent = do_QueryInterface(anchorNode);
 
1179
 
 
1180
  if (!anchorContent)
 
1181
    return NS_ERROR_FAILURE;
 
1182
  
 
1183
  result = GetFrameForNodeOffset(anchorContent, anchorOffset, mHint, &anchorFrame, &anchorFrameOffset);
 
1184
 
 
1185
  //
 
1186
  // Now find the root of the subtree containing the anchor's content.
 
1187
  //
 
1188
 
 
1189
  nsCOMPtr<nsIContent> anchorRoot;
 
1190
  result = GetRootForContentSubtree(anchorContent, getter_AddRefs(anchorRoot));
 
1191
 
 
1192
  if (NS_FAILED(result))
 
1193
    return result;
 
1194
 
 
1195
  //
 
1196
  // Now find the root of the subtree containing aFrame's content.
 
1197
  //
 
1198
 
 
1199
  nsIContent* content = aFrame->GetContent();
 
1200
 
 
1201
  if (content)
 
1202
  {
 
1203
    nsCOMPtr<nsIContent> contentRoot;
 
1204
 
 
1205
    result = GetRootForContentSubtree(content, getter_AddRefs(contentRoot));
 
1206
 
 
1207
    if (anchorRoot == contentRoot)
 
1208
    {
 
1209
      //
 
1210
      // The anchor and AFrame's root are the same. There
 
1211
      // is no need to constrain, simply return aFrame.
 
1212
      //
 
1213
      *aRetFrame = aFrame;
 
1214
      return NS_OK;
 
1215
    }
 
1216
  }
 
1217
 
 
1218
  //
 
1219
  // aFrame's root does not match the anchor's root, or there is no
 
1220
  // content associated with aFrame. Just return the primary frame
 
1221
  // for the anchor's root. We'll let GetContentAndOffsetsFromPoint()
 
1222
  // find the closest frame aPoint.
 
1223
  //
 
1224
 
 
1225
  result = mTracker->GetPrimaryFrameFor(anchorRoot, aRetFrame);
 
1226
 
 
1227
  if (NS_FAILED(result))
 
1228
    return result;
 
1229
 
 
1230
  if (! *aRetFrame)
 
1231
    return NS_ERROR_FAILURE;
 
1232
 
 
1233
  //
 
1234
  // Now make sure that aRetPoint is converted to the same coordinate
 
1235
  // system used by aRetFrame.
 
1236
  //
 
1237
 
 
1238
  nsPoint frameOffset;
 
1239
  nsPoint retFrameOffset;
 
1240
 
 
1241
  result = GetGlobalViewOffsetsFromFrame(aPresContext, aFrame, &frameOffset.x, &frameOffset.y);
 
1242
 
 
1243
  if (NS_FAILED(result))
 
1244
    return result;
 
1245
 
 
1246
  result = GetGlobalViewOffsetsFromFrame(aPresContext, *aRetFrame, &retFrameOffset.x, &retFrameOffset.y);
 
1247
 
 
1248
  if (NS_FAILED(result))
 
1249
    return result;
 
1250
 
 
1251
  aRetPoint = aPoint + frameOffset - retFrameOffset;
 
1252
 
 
1253
  return NS_OK;
 
1254
}
 
1255
 
 
1256
#ifdef XP_MAC
 
1257
#pragma mark -
 
1258
#endif
 
1259
 
 
1260
#ifdef PRINT_RANGE
 
1261
void printRange(nsIDOMRange *aDomRange)
 
1262
{
 
1263
  if (!aDomRange)
 
1264
  {
 
1265
    printf("NULL nsIDOMRange\n");
 
1266
  }
 
1267
  nsCOMPtr<nsIDOMNode> startNode;
 
1268
  nsCOMPtr<nsIDOMNode> endNode;
 
1269
  PRInt32 startOffset;
 
1270
  PRInt32 endOffset;
 
1271
  aDomRange->GetStartParent(getter_AddRefs(startNode));
 
1272
  aDomRange->GetStartOffset(&startOffset);
 
1273
  aDomRange->GetEndParent(getter_AddRefs(endNode));
 
1274
  aDomRange->GetEndOffset(&endOffset);
 
1275
  
 
1276
  printf("range: 0x%lx\t start: 0x%lx %ld, \t end: 0x%lx,%ld\n",
 
1277
         (unsigned long)aDomRange,
 
1278
         (unsigned long)(nsIDOMNode*)startNode, (long)startOffset,
 
1279
         (unsigned long)(nsIDOMNode*)endNode, (long)endOffset);
 
1280
         
 
1281
}
 
1282
#endif /* PRINT_RANGE */
 
1283
 
 
1284
static
 
1285
nsIAtom *GetTag(nsIDOMNode *aNode)
 
1286
{
 
1287
  nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
 
1288
  if (!content) 
 
1289
  {
 
1290
    NS_NOTREACHED("bad node passed to GetTag()");
 
1291
    return nsnull;
 
1292
  }
 
1293
  
 
1294
  return content->Tag();
 
1295
}
 
1296
 
 
1297
nsresult
 
1298
ParentOffset(nsIDOMNode *aNode, nsIDOMNode **aParent, PRInt32 *aChildOffset)
 
1299
{
 
1300
  if (!aNode || !aParent || !aChildOffset)
 
1301
    return NS_ERROR_NULL_POINTER;
 
1302
 
 
1303
  nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
 
1304
  if (content)
 
1305
  {
 
1306
    nsIContent* parent = content->GetParent();
 
1307
    if (parent)
 
1308
    {
 
1309
      *aChildOffset = parent->IndexOf(content);
 
1310
 
 
1311
      return CallQueryInterface(parent, aParent);
 
1312
    }
 
1313
  }
 
1314
 
 
1315
  return NS_OK;
 
1316
}
 
1317
 
 
1318
nsIDOMNode *
 
1319
GetCellParent(nsIDOMNode *aDomNode)
 
1320
{
 
1321
    if (!aDomNode)
 
1322
      return 0;
 
1323
    nsCOMPtr<nsIDOMNode> parent(aDomNode);
 
1324
    nsCOMPtr<nsIDOMNode> current(aDomNode);
 
1325
    PRInt32 childOffset;
 
1326
    nsIAtom *tag;
 
1327
    // Start with current node and look for a table cell
 
1328
    while(current)
 
1329
    {
 
1330
      tag = GetTag(current);
 
1331
      if (tag == nsHTMLAtoms::td || tag == nsHTMLAtoms::th)
 
1332
        return current;
 
1333
      if (NS_FAILED(ParentOffset(current,getter_AddRefs(parent),&childOffset)) || !parent)
 
1334
        return 0;
 
1335
      current = parent;
 
1336
    }
 
1337
    return 0;
 
1338
}
 
1339
 
 
1340
 
 
1341
NS_IMETHODIMP
 
1342
nsSelection::Init(nsIFocusTracker *aTracker, nsIContent *aLimiter)
 
1343
{
 
1344
  mTracker = aTracker;
 
1345
  mMouseDownState = PR_FALSE;
 
1346
  mDesiredXSet = PR_FALSE;
 
1347
  mLimiter = aLimiter;
 
1348
  mScrollView = nsnull;
 
1349
  return NS_OK;
 
1350
}
 
1351
 
 
1352
NS_IMETHODIMP
 
1353
nsSelection::SetScrollableView(nsIScrollableView *aScrollView)
 
1354
{
 
1355
  mScrollView = aScrollView;
 
1356
  return NS_OK;
 
1357
}
 
1358
 
 
1359
 
 
1360
NS_IMETHODIMP
 
1361
nsSelection::ShutDown()
 
1362
{
 
1363
  return NS_OK;
 
1364
}
 
1365
 
 
1366
  
 
1367
  
 
1368
NS_IMETHODIMP
 
1369
nsSelection::HandleTextEvent(nsGUIEvent *aGUIEvent)
 
1370
{
 
1371
  if (!aGUIEvent)
 
1372
    return NS_ERROR_NULL_POINTER;
 
1373
 
 
1374
#ifdef DEBUG_TAGUE
 
1375
  printf("nsSelection: HandleTextEvent\n");
 
1376
#endif
 
1377
  nsresult result(NS_OK);
 
1378
  if (NS_TEXT_TEXT == aGUIEvent->message) {
 
1379
    PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
 
1380
    result = mDomSelections[index]->ScrollIntoView();
 
1381
  }
 
1382
  return result;
 
1383
}
 
1384
 
 
1385
 
 
1386
nsresult
 
1387
nsSelection::MoveCaret(PRUint32 aKeycode, PRBool aContinue, nsSelectionAmount aAmount)
 
1388
{
 
1389
  nsCOMPtr<nsIPresContext> context;
 
1390
  nsresult result = mTracker->GetPresContext(getter_AddRefs(context));
 
1391
  if (NS_FAILED(result) || !context)
 
1392
    return result?result:NS_ERROR_FAILURE;
 
1393
 
 
1394
  nsCOMPtr<nsIDOMNode> weakNodeUsed;
 
1395
  PRInt32 offsetused = 0;
 
1396
 
 
1397
  PRBool isCollapsed;
 
1398
  nscoord desiredX = 0; //we must keep this around and revalidate it when its just UP/DOWN
 
1399
 
 
1400
  PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
 
1401
  result = mDomSelections[index]->GetIsCollapsed(&isCollapsed);
 
1402
  if (NS_FAILED(result))
 
1403
    return result;
 
1404
  if (aKeycode == nsIDOMKeyEvent::DOM_VK_UP || aKeycode == nsIDOMKeyEvent::DOM_VK_DOWN)
 
1405
  {
 
1406
    result = FetchDesiredX(desiredX);
 
1407
    if (NS_FAILED(result))
 
1408
      return result;
 
1409
    SetDesiredX(desiredX);
 
1410
  }
 
1411
 
 
1412
  if (!isCollapsed && !aContinue) {
 
1413
    switch (aKeycode){
 
1414
      case nsIDOMKeyEvent::DOM_VK_LEFT  : 
 
1415
      case nsIDOMKeyEvent::DOM_VK_UP    : {
 
1416
          if ((mDomSelections[index]->GetDirection() == eDirPrevious)) { //f,a
 
1417
            offsetused = mDomSelections[index]->FetchFocusOffset();
 
1418
            weakNodeUsed = mDomSelections[index]->FetchFocusNode();
 
1419
          }
 
1420
          else {
 
1421
            offsetused = mDomSelections[index]->FetchAnchorOffset();
 
1422
            weakNodeUsed = mDomSelections[index]->FetchAnchorNode();
 
1423
          }
 
1424
          result = mDomSelections[index]->Collapse(weakNodeUsed,offsetused);
 
1425
          mDomSelections[index]->ScrollIntoView();
 
1426
          mHint = HINTRIGHT;
 
1427
          return NS_OK;
 
1428
         } break;
 
1429
      case nsIDOMKeyEvent::DOM_VK_RIGHT : 
 
1430
      case nsIDOMKeyEvent::DOM_VK_DOWN  : {
 
1431
          if ((mDomSelections[index]->GetDirection() == eDirPrevious)) { //f,a
 
1432
            offsetused = mDomSelections[index]->FetchAnchorOffset();
 
1433
            weakNodeUsed = mDomSelections[index]->FetchAnchorNode();
 
1434
          }
 
1435
          else {
 
1436
            offsetused = mDomSelections[index]->FetchFocusOffset();
 
1437
            weakNodeUsed = mDomSelections[index]->FetchFocusNode();
 
1438
          }
 
1439
          result = mDomSelections[index]->Collapse(weakNodeUsed,offsetused);
 
1440
          mDomSelections[index]->ScrollIntoView();
 
1441
          mHint = HINTLEFT;
 
1442
          return NS_OK;
 
1443
         } break;
 
1444
      
 
1445
    }
 
1446
//      if (keyEvent->keyCode == nsIDOMKeyEvent::DOM_VK_UP || keyEvent->keyCode == nsIDOMKeyEvent::DOM_VK_DOWN)
 
1447
//        SetDesiredX(desiredX);
 
1448
  }
 
1449
 
 
1450
  nsCOMPtr<nsICaret> caret;
 
1451
  nsIPresShell *shell = context->GetPresShell();
 
1452
  if (!shell)
 
1453
    return 0;
 
1454
  result = shell->GetCaret(getter_AddRefs(caret));
 
1455
  if (NS_FAILED(result) || !caret)
 
1456
    return 0;
 
1457
 
 
1458
  offsetused = mDomSelections[index]->FetchFocusOffset();
 
1459
  weakNodeUsed = mDomSelections[index]->FetchFocusNode();
 
1460
 
 
1461
  nsIFrame *frame;
 
1462
  result = mDomSelections[index]->GetPrimaryFrameForFocusNode(&frame, &offsetused);
 
1463
 
 
1464
  if (NS_FAILED(result) || !frame)
 
1465
    return result?result:NS_ERROR_FAILURE;
 
1466
  nsCOMPtr<nsIDOMNode> node = do_QueryInterface(frame->GetContent());
 
1467
  nsCOMPtr<nsIDOMNode> parentNode;
 
1468
  nsPeekOffsetStruct pos;
 
1469
 
 
1470
  //set data using mLimiter to stop on scroll views.  If we have a limiter then we stop peeking
 
1471
  //when we hit scrollable views.  If no limiter then just let it go ahead
 
1472
  pos.SetData(mTracker, desiredX, aAmount, eDirPrevious, offsetused, PR_FALSE,
 
1473
              PR_TRUE, PR_TRUE, mLimiter != nsnull, PR_TRUE);
 
1474
 
 
1475
  HINT tHint(mHint); //temporary variable so we dont set mHint until it is necessary
 
1476
  switch (aKeycode){
 
1477
    case nsIDOMKeyEvent::DOM_VK_RIGHT : 
 
1478
        InvalidateDesiredX();
 
1479
        pos.mDirection = eDirNext;
 
1480
        tHint = HINTLEFT;//stick to this line
 
1481
        PostReason(nsISelectionListener::KEYPRESS_REASON);
 
1482
      break;
 
1483
    case nsIDOMKeyEvent::DOM_VK_LEFT  : //no break
 
1484
        InvalidateDesiredX();
 
1485
        tHint = HINTRIGHT;//stick to opposite of movement
 
1486
        PostReason(nsISelectionListener::KEYPRESS_REASON);
 
1487
      break;
 
1488
    case nsIDOMKeyEvent::DOM_VK_DOWN : 
 
1489
        pos.mAmount = eSelectLine;
 
1490
        pos.mDirection = eDirNext;//no break here
 
1491
        PostReason(nsISelectionListener::KEYPRESS_REASON);
 
1492
      break;
 
1493
    case nsIDOMKeyEvent::DOM_VK_UP : 
 
1494
        pos.mAmount = eSelectLine;
 
1495
        PostReason(nsISelectionListener::KEYPRESS_REASON);
 
1496
      break;
 
1497
    case nsIDOMKeyEvent::DOM_VK_HOME :
 
1498
        InvalidateDesiredX();
 
1499
        pos.mAmount = eSelectBeginLine;
 
1500
        tHint = HINTRIGHT;//stick to opposite of movement
 
1501
        PostReason(nsISelectionListener::KEYPRESS_REASON);
 
1502
      break;
 
1503
    case nsIDOMKeyEvent::DOM_VK_END :
 
1504
        InvalidateDesiredX();
 
1505
        pos.mAmount = eSelectEndLine;
 
1506
        tHint = HINTLEFT;//stick to this line
 
1507
        PostReason(nsISelectionListener::KEYPRESS_REASON);
 
1508
     break;
 
1509
  default :return NS_ERROR_FAILURE;
 
1510
  }
 
1511
  pos.mPreferLeft = tHint;
 
1512
  if (NS_SUCCEEDED(result) && NS_SUCCEEDED(result = frame->PeekOffset(context, &pos)) && pos.mResultContent)
 
1513
  {
 
1514
    tHint = (HINT)pos.mPreferLeft;
 
1515
    PRBool bidiEnabled = PR_FALSE;
 
1516
    context->GetBidiEnabled(&bidiEnabled);
 
1517
    if (bidiEnabled)
 
1518
    {
 
1519
      nsIFrame *theFrame;
 
1520
      PRInt32 currentOffset, frameStart, frameEnd;
 
1521
      PRUint8 level;
 
1522
 
 
1523
      // XXX - I expected to be able to use pos.mResultFrame, but when we move from frame to frame
 
1524
      //       and |PeekOffset| is called recursively, pos.mResultFrame on exit is sometimes set to the original
 
1525
      //       frame, not the frame that we ended up in, so I need this call to |GetFrameForNodeOffset|.
 
1526
      //       I don't know if that could or should be changed or if it would break something else.
 
1527
      GetFrameForNodeOffset(pos.mResultContent, pos.mContentOffset, tHint, &theFrame, &currentOffset);
 
1528
      theFrame->GetOffsets(frameStart, frameEnd);
 
1529
 
 
1530
      // the hint might have been reversed by an RTL frame, so make sure of it
 
1531
      if (nsIDOMKeyEvent::DOM_VK_HOME == aKeycode)
 
1532
        pos.mPreferLeft = PR_TRUE;
 
1533
      else if (nsIDOMKeyEvent::DOM_VK_END == aKeycode)
 
1534
        pos.mPreferLeft = PR_FALSE;
 
1535
      tHint = (HINT)pos.mPreferLeft;
 
1536
      if (frameStart !=0 || frameEnd !=0) // Otherwise the frame is not a text frame, so nothing more to do
 
1537
      {
 
1538
        switch (aKeycode) {
 
1539
          case nsIDOMKeyEvent::DOM_VK_HOME:
 
1540
          case nsIDOMKeyEvent::DOM_VK_END:
 
1541
 
 
1542
            // force the offset to the logical beginning (for HOME) or end (for END) of the frame
 
1543
            // (if it is an RTL frame it will be at the visual beginning or end, which we don't want in this case)
 
1544
            if (nsIDOMKeyEvent::DOM_VK_HOME == aKeycode)
 
1545
              pos.mContentOffset = frameStart;
 
1546
            else
 
1547
              pos.mContentOffset = frameEnd;
 
1548
 
 
1549
            // set the cursor Bidi level to the paragraph embedding level
 
1550
            theFrame->GetBidiProperty(context, nsLayoutAtoms::baseLevel, (void**)&level,
 
1551
                                      sizeof(level) );
 
1552
            shell->SetCaretBidiLevel(level);
 
1553
            break;
 
1554
 
 
1555
          default:
 
1556
            // If the current position is not a frame boundary, it's enough just to take the Bidi level of the current frame
 
1557
            if ((pos.mContentOffset != frameStart && pos.mContentOffset != frameEnd)
 
1558
                || (eSelectDir == aAmount)
 
1559
                || (eSelectLine == aAmount))
 
1560
            {
 
1561
              theFrame->GetBidiProperty(context, nsLayoutAtoms::embeddingLevel, (void**)&level,
 
1562
                                        sizeof(level) );
 
1563
              shell->SetCaretBidiLevel(level);
 
1564
            }
 
1565
            else
 
1566
              BidiLevelFromMove(context, shell, pos.mResultContent, pos.mContentOffset, aKeycode);
 
1567
        }
 
1568
      }
 
1569
#ifdef VISUALSELECTION
 
1570
      // Handle visual selection
 
1571
      if (aContinue)
 
1572
      {
 
1573
        result = VisualSelectFrames(context, theFrame, pos);
 
1574
        if (NS_FAILED(result)) // Back out by collapsing the selection to the current position
 
1575
          result = TakeFocus(pos.mResultContent, pos.mContentOffset, pos.mContentOffset, PR_FALSE, PR_FALSE);
 
1576
      }    
 
1577
      else
 
1578
        result = TakeFocus(pos.mResultContent, pos.mContentOffset, pos.mContentOffset, aContinue, PR_FALSE);
 
1579
    }
 
1580
    else
 
1581
#else
 
1582
    }
 
1583
#endif // VISUALSELECTION
 
1584
    result = TakeFocus(pos.mResultContent, pos.mContentOffset, pos.mContentOffset, aContinue, PR_FALSE);
 
1585
  }
 
1586
  if (NS_SUCCEEDED(result))
 
1587
  {
 
1588
    mHint = tHint; //save the hint parameter now for the next time
 
1589
    result = mDomSelections[index]->ScrollIntoView();
 
1590
  }
 
1591
 
 
1592
  return result;
 
1593
}
 
1594
 
 
1595
 
 
1596
 
 
1597
/** This raises a question, if this method is called and the aFrame does not reflect the current
 
1598
 *  focus  DomNode, it is invalid?  The answer now is yes.
 
1599
 */
 
1600
NS_IMETHODIMP
 
1601
nsSelection::HandleKeyEvent(nsIPresContext* aPresContext, nsGUIEvent *aGuiEvent)
 
1602
{
 
1603
  if (!aGuiEvent)
 
1604
    return NS_ERROR_NULL_POINTER;
 
1605
  STATUS_CHECK_RETURN_MACRO();
 
1606
 
 
1607
  nsresult result = NS_ERROR_FAILURE;
 
1608
  if (NS_KEY_PRESS == aGuiEvent->message) {
 
1609
    nsKeyEvent *keyEvent = (nsKeyEvent *)aGuiEvent; //this is ok. It really is a keyevent
 
1610
    switch (keyEvent->keyCode)
 
1611
    {
 
1612
        case nsIDOMKeyEvent::DOM_VK_LEFT  : 
 
1613
        case nsIDOMKeyEvent::DOM_VK_UP    :
 
1614
        case nsIDOMKeyEvent::DOM_VK_DOWN  : 
 
1615
        case nsIDOMKeyEvent::DOM_VK_RIGHT    :
 
1616
        case nsIDOMKeyEvent::DOM_VK_HOME  : 
 
1617
        case nsIDOMKeyEvent::DOM_VK_END    :
 
1618
          break;
 
1619
        default:
 
1620
           return NS_ERROR_FAILURE;
 
1621
    }
 
1622
 
 
1623
//XXX Need xp way get platfrom specific behavior into key navigation.
 
1624
//XXX This really shouldn't have to use an ifdef
 
1625
#ifdef _WIN32
 
1626
    if (keyEvent->isAlt) {
 
1627
      return NS_ERROR_FAILURE;
 
1628
    }
 
1629
#endif
 
1630
    nsSelectionAmount amount = eSelectCharacter;
 
1631
    if (keyEvent->isControl)
 
1632
      amount = eSelectWord;
 
1633
    return MoveCaret(keyEvent->keyCode, keyEvent->isShift, amount);
 
1634
  }
 
1635
  return result;
 
1636
}
 
1637
 
 
1638
//END nsSelection methods
 
1639
 
 
1640
 
 
1641
//BEGIN nsIFrameSelection methods
 
1642
 
 
1643
NS_IMETHODIMP
 
1644
nsTypedSelection::ToString(PRUnichar **aReturn)
 
1645
{
 
1646
  return ToStringWithFormat("text/plain", 0, 0, aReturn);
 
1647
}
 
1648
 
 
1649
 
 
1650
NS_IMETHODIMP
 
1651
nsTypedSelection::ToStringWithFormat(const char * aFormatType, PRUint32 aFlags, 
 
1652
                                   PRInt32 aWrapCol, PRUnichar **aReturn)
 
1653
{
 
1654
  nsresult rv = NS_OK;
 
1655
  if (!aReturn)
 
1656
    return NS_ERROR_NULL_POINTER;
 
1657
  
 
1658
  nsCAutoString formatType( NS_DOC_ENCODER_CONTRACTID_BASE );
 
1659
  formatType.Append(aFormatType);
 
1660
  nsCOMPtr<nsIDocumentEncoder> encoder =
 
1661
           do_CreateInstance(formatType.get(), &rv);
 
1662
  NS_ENSURE_SUCCESS(rv, rv);
 
1663
 
 
1664
  nsCOMPtr<nsIPresShell> shell;
 
1665
  rv = GetPresShell(getter_AddRefs(shell));
 
1666
  if (NS_FAILED(rv) || !shell) {
 
1667
    return NS_ERROR_FAILURE;
 
1668
  }
 
1669
 
 
1670
  nsCOMPtr<nsIDocument> doc;
 
1671
  rv = shell->GetDocument(getter_AddRefs(doc));
 
1672
  NS_ENSURE_SUCCESS(rv, rv);
 
1673
 
 
1674
  // Flags should always include OutputSelectionOnly if we're coming from here:
 
1675
  aFlags |= nsIDocumentEncoder::OutputSelectionOnly;
 
1676
  nsAutoString readstring;
 
1677
  readstring.AssignWithConversion(aFormatType);
 
1678
  rv = encoder->Init(doc, readstring, aFlags);
 
1679
  NS_ENSURE_SUCCESS(rv, rv);
 
1680
 
 
1681
  encoder->SetSelection(this);
 
1682
  if (aWrapCol != 0)
 
1683
    encoder->SetWrapColumn(aWrapCol);
 
1684
 
 
1685
  nsAutoString tmp;
 
1686
  rv = encoder->EncodeToString(tmp);
 
1687
  *aReturn = ToNewUnicode(tmp);//get the unicode pointer from it. this is temporary
 
1688
  return rv;
 
1689
}
 
1690
 
 
1691
NS_IMETHODIMP
 
1692
nsTypedSelection::SetInterlinePosition(PRBool aHintRight)
 
1693
{
 
1694
  nsIFrameSelection::HINT hint;
 
1695
  if (aHintRight)
 
1696
    hint = nsIFrameSelection::HINTRIGHT;
 
1697
  else
 
1698
    hint = nsIFrameSelection::HINTLEFT;
 
1699
  return mFrameSelection->SetHint(hint);
 
1700
}
 
1701
 
 
1702
NS_IMETHODIMP
 
1703
nsTypedSelection::GetInterlinePosition(PRBool *aHintRight)
 
1704
{
 
1705
  nsIFrameSelection::HINT hint;
 
1706
  nsresult rv = mFrameSelection->GetHint(&hint);
 
1707
  if (hint == nsIFrameSelection::HINTRIGHT)
 
1708
    *aHintRight = PR_TRUE;
 
1709
  else
 
1710
    *aHintRight = PR_FALSE;
 
1711
  return rv;
 
1712
}
 
1713
 
 
1714
#ifdef VISUALSELECTION
 
1715
 
 
1716
static nsDirection
 
1717
ReverseDirection(nsDirection aDirection)
 
1718
{
 
1719
  return (eDirNext == aDirection) ? eDirPrevious : eDirNext;
 
1720
}
 
1721
 
 
1722
static nsresult
 
1723
FindLineContaining(nsIFrame* aFrame, nsIFrame** aBlock, PRInt32* aLine)
 
1724
{
 
1725
  nsIFrame *blockFrame = aFrame;
 
1726
  nsIFrame *thisBlock = nsnull;
 
1727
  nsCOMPtr<nsILineIteratorNavigator> it; 
 
1728
  nsresult result = NS_ERROR_FAILURE;
 
1729
  while (NS_FAILED(result) && blockFrame)
 
1730
  {
 
1731
    thisBlock = blockFrame;
 
1732
    blockFrame = blockFrame->GetParent();
 
1733
    if (blockFrame) {
 
1734
      it = do_QueryInterface(blockFrame, &result);
 
1735
    }
 
1736
  }
 
1737
  if (!blockFrame || !it)
 
1738
    return NS_ERROR_FAILURE;
 
1739
  *aBlock = blockFrame;
 
1740
  return it->FindLineContaining(thisBlock, aLine);  
 
1741
}
 
1742
 
 
1743
NS_IMETHODIMP
 
1744
nsSelection::VisualSequence(nsIPresContext *aPresContext,
 
1745
                            nsIFrame* aSelectFrame,
 
1746
                            nsIFrame* aCurrentFrame,
 
1747
                            nsPeekOffsetStruct* aPos,
 
1748
                            PRBool* aNeedVisualSelection)
 
1749
{
 
1750
  nsVoidArray frameArray;
 
1751
  PRUint8 bidiLevel, currentLevel;
 
1752
  PRInt32 frameStart, frameEnd;
 
1753
  PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
 
1754
  nsresult result = nsnull;
 
1755
  
 
1756
  aCurrentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, (void**)&currentLevel, sizeof(currentLevel) );
 
1757
 
 
1758
  result = aSelectFrame->PeekOffset(aPresContext, aPos);
 
1759
  while (aCurrentFrame != (aSelectFrame = aPos->mResultFrame))
 
1760
  {
 
1761
    if (NS_FAILED(result))
 
1762
      return NS_OK; // we have passed the end of the line, and we will carry on from there
 
1763
    if (!aSelectFrame)
 
1764
      return NS_ERROR_FAILURE;
 
1765
    if (frameArray.IndexOf(aSelectFrame) > -1)
 
1766
      // If we have already seen this frame, we must be in an infinite loop
 
1767
      return NS_OK;
 
1768
    else
 
1769
      frameArray.AppendElement(aSelectFrame);
 
1770
 
 
1771
    aSelectFrame->GetOffsets(frameStart, frameEnd);
 
1772
    aSelectFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, (void**)&bidiLevel, sizeof(bidiLevel) );
 
1773
    
 
1774
    if (currentLevel != bidiLevel)
 
1775
      *aNeedVisualSelection = PR_TRUE;
 
1776
    if ((eDirNext == aPos->mDirection) == (bidiLevel & 1))
 
1777
    {
 
1778
      mDomSelections[index]->SetDirection(eDirPrevious);
 
1779
      result = TakeFocus(aPos->mResultContent, frameEnd, frameStart, PR_FALSE, PR_TRUE);
 
1780
    }
 
1781
    else
 
1782
    {
 
1783
      mDomSelections[index]->SetDirection(eDirNext);
 
1784
      result = TakeFocus(aPos->mResultContent, frameStart, frameEnd, PR_FALSE, PR_TRUE);
 
1785
    }
 
1786
    if (NS_FAILED(result))
 
1787
      return result;
 
1788
 
 
1789
    aPos->mAmount = eSelectDir; // reset this because PeekOffset will have changed it to eSelectNoAmount
 
1790
    aPos->mContentOffset = 0;
 
1791
    result = aSelectFrame->PeekOffset(aPresContext, aPos);
 
1792
  }
 
1793
  
 
1794
  return NS_OK;
 
1795
}
 
1796
 
 
1797
NS_IMETHODIMP
 
1798
nsSelection::SelectToEdge(nsIFrame *aFrame, nsIContent *aContent, PRInt32 aOffset, PRInt32 aEdge, PRBool aMultipleSelection)
 
1799
{
 
1800
  PRInt32 frameStart, frameEnd;
 
1801
  
 
1802
  aFrame->GetOffsets(frameStart, frameEnd);
 
1803
  if (0 == aEdge)
 
1804
    aEdge = frameStart;
 
1805
  else if (-1 == aEdge)
 
1806
    aEdge = frameEnd;
 
1807
  if (0 == aOffset)
 
1808
    aOffset = frameStart;
 
1809
  else if (-1 == aOffset)
 
1810
    aOffset = frameEnd;
 
1811
  return TakeFocus(aContent, aOffset, aEdge, PR_FALSE, aMultipleSelection);
 
1812
}
 
1813
 
 
1814
NS_IMETHODIMP
 
1815
nsSelection::SelectLines(nsIPresContext *aPresContext,
 
1816
                         nsDirection aSelectionDirection,
 
1817
                         nsIDOMNode *aAnchorNode,
 
1818
                         nsIFrame* aAnchorFrame,
 
1819
                         PRInt32 aAnchorOffset,
 
1820
                         nsIDOMNode *aCurrentNode,
 
1821
                         nsIFrame* aCurrentFrame,
 
1822
                         PRInt32 aCurrentOffset,
 
1823
                         nsPeekOffsetStruct aPos)
 
1824
{
 
1825
  nsIFrame *startFrame, *endFrame;
 
1826
  PRInt32 startOffset, endOffset;
 
1827
  PRInt32 relativePosition;
 
1828
  nsCOMPtr<nsIDOMNode> startNode;
 
1829
  nsCOMPtr<nsIDOMNode> endNode;
 
1830
  nsIContent *startContent;
 
1831
  nsIContent *endContent;
 
1832
  nsresult result;
 
1833
 
 
1834
  // normalize the order before we start to avoid piles of conditions later
 
1835
  relativePosition = ComparePoints(aAnchorNode, aAnchorOffset, aCurrentNode, aCurrentOffset);
 
1836
  if (0 == relativePosition)
 
1837
    return NS_ERROR_FAILURE;
 
1838
  else if (relativePosition < 0)
 
1839
  {
 
1840
    startNode = aAnchorNode;
 
1841
    startFrame = aAnchorFrame;
 
1842
    startOffset = aAnchorOffset;
 
1843
    endNode = aCurrentNode;
 
1844
    endFrame = aCurrentFrame;
 
1845
    endOffset = aCurrentOffset;
 
1846
  }
 
1847
  else
 
1848
  {
 
1849
    startNode = aCurrentNode;
 
1850
    startFrame = aCurrentFrame;
 
1851
    startOffset = aCurrentOffset;
 
1852
    endNode = aAnchorNode;
 
1853
    endFrame = aAnchorFrame;
 
1854
    endOffset = aAnchorOffset;
 
1855
  }
 
1856
 
 
1857
  aPos.mStartOffset = startOffset;
 
1858
  aPos.mDirection = eDirNext;
 
1859
  aPos.mAmount = eSelectLine;
 
1860
  result = startFrame->PeekOffset(aPresContext, &aPos);
 
1861
  if (NS_FAILED(result))
 
1862
    return result;
 
1863
  startFrame = aPos.mResultFrame;
 
1864
  
 
1865
  aPos.mStartOffset = aPos.mContentOffset;
 
1866
  aPos.mAmount = eSelectBeginLine;
 
1867
  result = startFrame->PeekOffset(aPresContext, &aPos);
 
1868
  if (NS_FAILED(result))
 
1869
    return result;
 
1870
  
 
1871
  nsIFrame *theFrame;
 
1872
  PRInt32 currentOffset, frameStart, frameEnd;
 
1873
  
 
1874
  result = GetFrameForNodeOffset(aPos.mResultContent, aPos.mContentOffset, HINTLEFT, &theFrame, &currentOffset);
 
1875
  if (NS_FAILED(result))
 
1876
    return result;
 
1877
  theFrame->GetOffsets(frameStart, frameEnd);
 
1878
  startOffset = frameStart;
 
1879
  startContent = aPos.mResultContent;
 
1880
  startNode = do_QueryInterface(startContent);
 
1881
 
 
1882
  // If we have already overshot the endpoint, back out
 
1883
  if (ComparePoints(startNode, startOffset, endNode, endOffset) >= 0)
 
1884
    return NS_ERROR_FAILURE;
 
1885
 
 
1886
  aPos.mStartOffset = endOffset;
 
1887
  aPos.mDirection = eDirPrevious;
 
1888
  aPos.mAmount = eSelectLine;
 
1889
  result = endFrame->PeekOffset(aPresContext, &aPos);
 
1890
  if (NS_FAILED(result))
 
1891
    return result;
 
1892
  endFrame = aPos.mResultFrame;
 
1893
 
 
1894
  aPos.mStartOffset = aPos.mContentOffset;
 
1895
  aPos.mAmount = eSelectEndLine;
 
1896
  result = endFrame->PeekOffset(aPresContext, &aPos);
 
1897
  if (NS_FAILED(result))
 
1898
    return result;
 
1899
 
 
1900
  result = GetFrameForNodeOffset(aPos.mResultContent, aPos.mContentOffset, HINTRIGHT, &theFrame, &currentOffset);
 
1901
  if (NS_FAILED(result))
 
1902
    return result;
 
1903
  theFrame->GetOffsets(frameStart, frameEnd);
 
1904
  endOffset = frameEnd;
 
1905
  endContent = aPos.mResultContent;
 
1906
  endNode = do_QueryInterface(endContent);
 
1907
 
 
1908
  if (ComparePoints(startNode, startOffset, endNode, endOffset) < 0)
 
1909
  {
 
1910
    TakeFocus(startContent, startOffset, startOffset, PR_FALSE, PR_TRUE);
 
1911
    return TakeFocus(endContent, endOffset, endOffset, PR_TRUE, PR_TRUE);
 
1912
  }
 
1913
  else
 
1914
    return NS_ERROR_FAILURE;
 
1915
}
 
1916
 
 
1917
NS_IMETHODIMP
 
1918
nsSelection::VisualSelectFrames(nsIPresContext *aPresContext,
 
1919
                                nsIFrame* aCurrentFrame,
 
1920
                                nsPeekOffsetStruct aPos)
 
1921
{
 
1922
  nsCOMPtr<nsIContent> anchorContent;
 
1923
  nsCOMPtr<nsIDOMNode> anchorNode;
 
1924
  PRInt32 anchorOffset;
 
1925
  nsIFrame* anchorFrame;
 
1926
  PRUint8 anchorLevel;
 
1927
  nsCOMPtr<nsIContent> focusContent;
 
1928
  nsCOMPtr<nsIDOMNode> focusNode;
 
1929
  PRInt32 focusOffset;
 
1930
  nsIFrame* focusFrame;
 
1931
  PRUint8 focusLevel;
 
1932
  nsCOMPtr<nsIContent> currentContent;
 
1933
  nsCOMPtr<nsIDOMNode> currentNode;
 
1934
  PRInt32 currentOffset;
 
1935
  PRUint8 currentLevel;
 
1936
  nsresult result;
 
1937
  nsIFrame* startFrame;
 
1938
  PRBool needVisualSelection = PR_FALSE;
 
1939
  nsDirection selectionDirection;
 
1940
  PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
 
1941
 
 
1942
  result = mDomSelections[index]->GetOriginalAnchorPoint(getter_AddRefs(anchorNode), &anchorOffset);
 
1943
  if (NS_FAILED(result))
 
1944
    return result;
 
1945
  anchorContent = do_QueryInterface(anchorNode);
 
1946
  result = GetFrameForNodeOffset(anchorContent, anchorOffset, mHint, &anchorFrame, &anchorOffset);
 
1947
  if (NS_FAILED(result))
 
1948
    return result;
 
1949
  anchorFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, (void**)&anchorLevel, sizeof(anchorLevel) );
 
1950
 
 
1951
  currentContent = aPos.mResultContent;
 
1952
  currentNode = do_QueryInterface(currentContent);
 
1953
  currentOffset = aPos.mContentOffset;
 
1954
  aCurrentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, (void**)&currentLevel, sizeof(currentLevel) );
 
1955
 
 
1956
  // Moving from simplest case to more complicated:
 
1957
  // case 1: selection starts and ends in the same frame: no special treatment
 
1958
  if (anchorFrame == aCurrentFrame) {
 
1959
    mDomSelections[index]->SetTrueDirection(!(anchorLevel & 1));
 
1960
    return TakeFocus(currentContent, anchorOffset, currentOffset, PR_FALSE, PR_FALSE);
 
1961
  }
 
1962
 
 
1963
  focusOffset = mDomSelections[index]->FetchFocusOffset();
 
1964
  focusNode = mDomSelections[index]->FetchFocusNode();
 
1965
  focusContent = do_QueryInterface(focusNode);
 
1966
  HINT hint;
 
1967
  if ((HINTLEFT == mHint) == (currentLevel & 1))
 
1968
    hint = HINTRIGHT;
 
1969
  else
 
1970
    hint = HINTLEFT;
 
1971
 
 
1972
  result = GetFrameForNodeOffset(focusContent, focusOffset, hint, &focusFrame, &focusOffset);
 
1973
  if (NS_FAILED(result))
 
1974
    return result;
 
1975
 
 
1976
  focusFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, (void**)&focusLevel, sizeof(focusLevel) );
 
1977
 
 
1978
  if (currentLevel != anchorLevel)
 
1979
    needVisualSelection = PR_TRUE;
 
1980
 
 
1981
  // Make sure of the selection direction
 
1982
  selectionDirection = mDomSelections[index]->GetDirection();
 
1983
  if (!mDomSelections[index]->GetTrueDirection()) {
 
1984
    selectionDirection = ReverseDirection(selectionDirection);
 
1985
    mDomSelections[index]->SetDirection(selectionDirection);
 
1986
  }
 
1987
 
 
1988
  PRInt32 anchorLine, currentLine;
 
1989
  nsIFrame* anchorBlock  = nsnull;
 
1990
  nsIFrame* currentBlock = nsnull;
 
1991
  FindLineContaining(anchorFrame, &anchorBlock, &anchorLine);
 
1992
  FindLineContaining(aCurrentFrame, &currentBlock, &currentLine);
 
1993
 
 
1994
  if (anchorBlock==currentBlock && anchorLine==currentLine)
 
1995
  {
 
1996
    // case 2: selection starts and ends in the same line
 
1997
 
 
1998
    // Select from the anchor point to the edge of the frame
 
1999
    // Which edge? If the selection direction is forward the right edge, if it is backward the left edge
 
2000
    // For rtl frames the right edge is the begining of the frame, for ltr frames it is the end and vice versa
 
2001
    if ((eDirNext == selectionDirection) == (anchorLevel & 1))
 
2002
      result = SelectToEdge(anchorFrame, anchorContent, anchorOffset, 0, PR_FALSE);
 
2003
    else
 
2004
      result = SelectToEdge(anchorFrame, anchorContent, anchorOffset, -1, PR_FALSE);
 
2005
    if (NS_FAILED(result))
 
2006
      return result;
 
2007
 
 
2008
    // Walk the frames in visual order until we reach the current frame, selecting each frame as we go
 
2009
    InvalidateDesiredX();
 
2010
    aPos.mAmount = eSelectDir;
 
2011
    aPos.mStartOffset = anchorOffset;
 
2012
    aPos.mDirection = selectionDirection;
 
2013
 
 
2014
    result = anchorFrame->PeekOffset(aPresContext, &aPos);
 
2015
    if (NS_FAILED(result))
 
2016
      return result;
 
2017
    
 
2018
    startFrame = aPos.mResultFrame;
 
2019
    result = VisualSequence(aPresContext, startFrame, aCurrentFrame, &aPos, &needVisualSelection);
 
2020
    if (NS_FAILED(result))
 
2021
      return result;
 
2022
 
 
2023
    if (!needVisualSelection)
 
2024
    {
 
2025
      if (currentLevel & 1)
 
2026
        mDomSelections[index]->SetDirection(ReverseDirection(selectionDirection));
 
2027
      // all the frames we passed through had the same Bidi level, so we can back out and do an ordinary selection
 
2028
      result = TakeFocus(anchorContent, anchorOffset, anchorOffset, PR_FALSE, PR_FALSE);
 
2029
      if (NS_FAILED(result))
 
2030
        return result;
 
2031
      result = TakeFocus(currentContent, currentOffset, currentOffset, PR_TRUE, PR_FALSE);
 
2032
      if (NS_FAILED(result))
 
2033
        return result;
 
2034
    }
 
2035
    else {
 
2036
      if ((currentLevel & 1) != (focusLevel & 1))
 
2037
        mDomSelections[index]->SetDirection(ReverseDirection(selectionDirection));
 
2038
      // Select from the current point to the edge of the frame
 
2039
      if ((eDirNext == selectionDirection) == (currentLevel & 1))
 
2040
        result = SelectToEdge(aCurrentFrame, currentContent, -1, currentOffset, PR_TRUE);
 
2041
      else
 
2042
        result = SelectToEdge(aCurrentFrame, currentContent, 0, currentOffset, PR_TRUE);
 
2043
      if (NS_FAILED(result))
 
2044
        return result;
 
2045
    }
 
2046
  }
 
2047
  else {
 
2048
 
 
2049
    // case 3: selection starts and ends in different lines
 
2050
 
 
2051
    // If selection direction is forwards:
 
2052
    // Select from the anchor point to the edge of the frame in the direction of the end of the line
 
2053
    // i.e. the rightmost character if the current paragraph embedding level is even (LTR paragraph)
 
2054
    // or the leftmost character if the current paragraph embedding level is odd (RTL paragraph)
 
2055
    //
 
2056
    // As before, for rtl frames the right edge is the begining of the frame, for ltr frames it is the end and vice versa
 
2057
    //
 
2058
    // If selection direction is backwards, vice versa throughout
 
2059
    //
 
2060
    PRUint8 anchorBaseLevel;
 
2061
    PRUint8 currentBaseLevel;
 
2062
    anchorFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::baseLevel, (void**)&anchorBaseLevel, sizeof(anchorBaseLevel) );
 
2063
    if ((eDirNext == selectionDirection) != ((anchorLevel & 1) == (anchorBaseLevel & 1)))
 
2064
      result = SelectToEdge(anchorFrame, anchorContent, anchorOffset, 0, PR_FALSE);
 
2065
    else
 
2066
      result = SelectToEdge(anchorFrame, anchorContent, anchorOffset, -1, PR_FALSE);
 
2067
    if (NS_FAILED(result))
 
2068
      return result;
 
2069
 
 
2070
    // Walk the frames in visual order until we reach the end of the line
 
2071
    aPos.mJumpLines = PR_FALSE;
 
2072
    aPos.mAmount = eSelectDir;
 
2073
    aPos.mStartOffset = anchorOffset;
 
2074
    aPos.mDirection = selectionDirection;
 
2075
    if (anchorBaseLevel & 1)
 
2076
      aPos.mDirection = ReverseDirection(aPos.mDirection);
 
2077
    result = VisualSequence(aPresContext, anchorFrame, aCurrentFrame, &aPos, &needVisualSelection);
 
2078
    if (NS_FAILED(result))
 
2079
      return result;
 
2080
 
 
2081
    // Select all the lines between the line containing the anchor point and the line containing the current point
 
2082
    aPos.mJumpLines = PR_TRUE;
 
2083
    result = SelectLines(aPresContext, selectionDirection,
 
2084
                         anchorNode, anchorFrame, anchorOffset,
 
2085
                         currentNode, aCurrentFrame, currentOffset, aPos);
 
2086
    if (NS_FAILED(result))
 
2087
      return result;
 
2088
 
 
2089
    // Go to the current point
 
2090
 
 
2091
    aCurrentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::baseLevel, (void**)&currentBaseLevel, sizeof(currentBaseLevel) );
 
2092
    // Walk the frames in visual order until we reach the beginning of the line
 
2093
    aPos.mJumpLines = PR_FALSE;
 
2094
    if ((currentBaseLevel & 1) == (anchorBaseLevel & 1))
 
2095
      aPos.mDirection = ReverseDirection(aPos.mDirection);
 
2096
    aPos.mStartOffset = currentOffset;
 
2097
    result = VisualSequence(aPresContext, aCurrentFrame, anchorFrame, &aPos, &needVisualSelection);
 
2098
    if (NS_FAILED(result))
 
2099
      return result;
 
2100
 
 
2101
    // Select from the current point to the edge of the frame
 
2102
    if (currentLevel & 1)
 
2103
      mDomSelections[index]->SetDirection(ReverseDirection(selectionDirection));
 
2104
 
 
2105
    if ((eDirPrevious == selectionDirection) != ((currentLevel & 1) == (currentBaseLevel & 1)))
 
2106
      result = SelectToEdge(aCurrentFrame, currentContent, 0, currentOffset, PR_TRUE);
 
2107
    else
 
2108
      result = SelectToEdge(aCurrentFrame, currentContent, -1, currentOffset, PR_TRUE);
 
2109
    if (NS_FAILED(result))
 
2110
      return result;
 
2111
    
 
2112
    // restore original selection direction
 
2113
//    mDomSelections[index]->SetDirection(selectionDirection);
 
2114
  }
 
2115
 
 
2116
  // Sometimes we have to lie about the selection direction, so we will have to remember when we are doing so
 
2117
  mDomSelections[index]->SetTrueDirection(mDomSelections[index]->GetDirection() == selectionDirection);
 
2118
  
 
2119
  mDomSelections[index]->SetOriginalAnchorPoint(anchorNode, anchorOffset);
 
2120
  NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL);
 
2121
  return NS_OK;
 
2122
}
 
2123
#endif // VISUALSELECTION
 
2124
 
 
2125
NS_IMETHODIMP
 
2126
nsSelection::GetPrevNextBidiLevels(nsIPresContext *aPresContext,
 
2127
                                   nsIContent *aNode,
 
2128
                                   PRUint32 aContentOffset,
 
2129
                                   nsIFrame **aPrevFrame,
 
2130
                                   nsIFrame **aNextFrame,
 
2131
                                   PRUint8 *aPrevLevel,
 
2132
                                   PRUint8 *aNextLevel)
 
2133
{
 
2134
  if (!aPrevFrame || !aNextFrame)
 
2135
    return NS_ERROR_NULL_POINTER;
 
2136
  // Get the level of the frames on each side
 
2137
  nsIFrame    *currentFrame;
 
2138
  PRInt32     currentOffset;
 
2139
  PRInt32     frameStart, frameEnd;
 
2140
  nsDirection direction;
 
2141
  nsresult    result;
 
2142
 
 
2143
  *aPrevLevel = *aNextLevel = 0;
 
2144
 
 
2145
  result = GetFrameForNodeOffset(aNode, aContentOffset, mHint, &currentFrame, &currentOffset);
 
2146
  if (NS_FAILED(result))
 
2147
    return result;
 
2148
  currentFrame->GetOffsets(frameStart, frameEnd);
 
2149
 
 
2150
  if (0 == frameStart && 0 == frameEnd)
 
2151
    direction = eDirPrevious;
 
2152
  else if (frameStart == currentOffset)
 
2153
    direction = eDirPrevious;
 
2154
  else if (frameEnd == currentOffset)
 
2155
    direction = eDirNext;
 
2156
  else {
 
2157
    // we are neither at the beginning nor at the end of the frame, so we have no worries
 
2158
    *aPrevFrame = *aNextFrame = currentFrame;
 
2159
    currentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel,
 
2160
                                  (void**)aNextLevel, sizeof(*aNextLevel) );
 
2161
    *aPrevLevel = *aNextLevel;
 
2162
    return NS_OK;
 
2163
  }
 
2164
 
 
2165
  /*
 
2166
  we have to find the next or previous *logical* frame.
 
2167
 
 
2168
  Unfortunately |GetFrameFromDirection| has already been munged to return the next/previous *visual* frame, so we can't use that.
 
2169
  The following code is taken from there without the Bidi changes.
 
2170
 
 
2171
  XXX is there a simpler way to do this? 
 
2172
  */
 
2173
 
 
2174
  nsIFrame *blockFrame = currentFrame;
 
2175
  nsIFrame *thisBlock = nsnull;
 
2176
  PRInt32   thisLine;
 
2177
  nsCOMPtr<nsILineIteratorNavigator> it; 
 
2178
  result = NS_ERROR_FAILURE;
 
2179
  while (NS_FAILED(result) && blockFrame)
 
2180
  {
 
2181
    thisBlock = blockFrame;
 
2182
    blockFrame = blockFrame->GetParent();
 
2183
    if (blockFrame) {
 
2184
      it = do_QueryInterface(blockFrame, &result);
 
2185
    }
 
2186
  }
 
2187
  if (!blockFrame || !it)
 
2188
    return NS_ERROR_FAILURE;
 
2189
  result = it->FindLineContaining(thisBlock, &thisLine);
 
2190
  if (NS_FAILED(result))
 
2191
    return result;
 
2192
 
 
2193
  if (thisLine < 0) 
 
2194
    return NS_ERROR_FAILURE;
 
2195
 
 
2196
  nsIFrame *firstFrame;
 
2197
  nsIFrame *lastFrame;
 
2198
  nsRect    nonUsedRect;
 
2199
  PRInt32   lineFrameCount;
 
2200
  PRUint32  lineFlags;
 
2201
 
 
2202
  result = it->GetLine(thisLine, &firstFrame, &lineFrameCount,nonUsedRect,
 
2203
                       &lineFlags);
 
2204
  if (NS_FAILED(result))
 
2205
    return result;
 
2206
 
 
2207
  lastFrame = firstFrame;
 
2208
 
 
2209
  for (;lineFrameCount > 1;lineFrameCount --) {
 
2210
    lastFrame = lastFrame->GetNextSibling();
 
2211
  }
 
2212
 
 
2213
  // GetFirstLeaf
 
2214
  nsIFrame *lookahead;
 
2215
  while (1) {
 
2216
    lookahead = firstFrame->GetFirstChild(nsnull);
 
2217
    if (!lookahead)
 
2218
      break; //nothing to do
 
2219
    firstFrame = lookahead;
 
2220
  }
 
2221
 
 
2222
  // GetLastLeaf
 
2223
  while (1) {
 
2224
    lookahead = lastFrame->GetFirstChild(nsnull);
 
2225
    if (!lookahead)
 
2226
      break; //nothing to do
 
2227
    lastFrame = lookahead;
 
2228
    while ((lookahead = lastFrame->GetNextSibling()) != nsnull)
 
2229
      lastFrame = lookahead;
 
2230
  }
 
2231
  //END LINE DATA CODE
 
2232
 
 
2233
  if (direction == eDirNext && lastFrame == currentFrame) { // End of line: set aPrevFrame to the current frame
 
2234
                                                            //              set aPrevLevel to the embedding level of the current frame
 
2235
                                                            //              set aNextFrame to null
 
2236
                                                            //              set aNextLevel to the paragraph embedding level
 
2237
    *aPrevFrame = currentFrame;
 
2238
    currentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel,
 
2239
                                  (void**)aPrevLevel, sizeof(*aPrevLevel) );
 
2240
    currentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::baseLevel,
 
2241
                                  (void**)aNextLevel, sizeof(*aNextLevel) );
 
2242
    *aNextFrame = nsnull;
 
2243
    return NS_OK;
 
2244
  }
 
2245
 
 
2246
  if (direction == eDirPrevious && firstFrame == currentFrame) { // Beginning of line: set aPrevFrame to null
 
2247
                                                                 //                    set aPrevLevel to the paragraph embedding level
 
2248
                                                                 //                    set aNextFrame to the current frame
 
2249
                                                                 //                    set aNextLevel to the embedding level of the current frame
 
2250
    *aNextFrame = currentFrame;
 
2251
    currentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel,
 
2252
                                  (void**)aNextLevel, sizeof(*aNextLevel) );
 
2253
    currentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::baseLevel,
 
2254
                                  (void**)aPrevLevel, sizeof(*aPrevLevel) );
 
2255
    *aPrevFrame = nsnull;
 
2256
    return NS_OK;
 
2257
  }
 
2258
 
 
2259
  // Find the adjacent frame
 
2260
 
 
2261
  nsCOMPtr<nsIBidirectionalEnumerator> frameTraversal;
 
2262
  nsCOMPtr<nsIFrameTraversal> trav(do_CreateInstance(kFrameTraversalCID,&result));
 
2263
  if (NS_FAILED(result))
 
2264
    return result;
 
2265
 
 
2266
  result = trav->NewFrameTraversal(getter_AddRefs(frameTraversal),LEAF, aPresContext, currentFrame);
 
2267
  if (NS_FAILED(result))
 
2268
    return result;
 
2269
  nsISupports *isupports = nsnull;
 
2270
  if (direction == eDirNext)
 
2271
    result = frameTraversal->Next();
 
2272
  else 
 
2273
    result = frameTraversal->Prev();
 
2274
 
 
2275
  if (NS_FAILED(result))
 
2276
    return result;
 
2277
  result = frameTraversal->CurrentItem(&isupports);
 
2278
  if (NS_FAILED(result))
 
2279
    return result;
 
2280
  if (!isupports)
 
2281
    return NS_ERROR_NULL_POINTER;
 
2282
  //we must CAST here to an nsIFrame. nsIFrame doesnt really follow the rules
 
2283
  //for speed reasons
 
2284
  nsIFrame *newFrame = (nsIFrame *)isupports;
 
2285
 
 
2286
  if (direction == eDirNext) {
 
2287
    *aPrevFrame = currentFrame;
 
2288
    currentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel,
 
2289
                                  (void**)aPrevLevel, sizeof(*aPrevLevel) );
 
2290
    *aNextFrame = newFrame;
 
2291
    newFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel,
 
2292
                              (void**)aNextLevel, sizeof(*aNextLevel) );
 
2293
  }
 
2294
  else {
 
2295
    *aNextFrame = currentFrame;
 
2296
    currentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel,
 
2297
                                  (void**)aNextLevel, sizeof(*aNextLevel) );
 
2298
    *aPrevFrame = newFrame;
 
2299
    newFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel,
 
2300
                              (void**)aPrevLevel, sizeof(*aPrevLevel) );
 
2301
  }
 
2302
 
 
2303
  return NS_OK;
 
2304
 
 
2305
}
 
2306
 
 
2307
NS_IMETHODIMP nsSelection::GetFrameFromLevel(nsIPresContext *aPresContext,
 
2308
                                             nsIFrame *aFrameIn,
 
2309
                                             nsDirection aDirection,
 
2310
                                             PRUint8 aBidiLevel,
 
2311
                                             nsIFrame **aFrameOut)
 
2312
{
 
2313
  PRUint8 foundLevel = 0;
 
2314
  nsIFrame *foundFrame = aFrameIn;
 
2315
 
 
2316
  nsCOMPtr<nsIBidirectionalEnumerator> frameTraversal;
 
2317
  nsresult result;
 
2318
  nsCOMPtr<nsIFrameTraversal> trav(do_CreateInstance(kFrameTraversalCID,&result));
 
2319
  if (NS_FAILED(result))
 
2320
      return result;
 
2321
 
 
2322
  result = trav->NewFrameTraversal(getter_AddRefs(frameTraversal),LEAF, aPresContext, aFrameIn);
 
2323
  if (NS_FAILED(result))
 
2324
    return result;
 
2325
  nsISupports *isupports = nsnull;
 
2326
 
 
2327
  do {
 
2328
    *aFrameOut = foundFrame;
 
2329
    if (aDirection == eDirNext)
 
2330
      result = frameTraversal->Next();
 
2331
    else 
 
2332
      result = frameTraversal->Prev();
 
2333
 
 
2334
    if (NS_FAILED(result))
 
2335
      return result;
 
2336
    result = frameTraversal->CurrentItem(&isupports);
 
2337
    if (NS_FAILED(result))
 
2338
      return result;
 
2339
    if (!isupports)
 
2340
      return NS_ERROR_NULL_POINTER;
 
2341
    //we must CAST here to an nsIFrame. nsIFrame doesnt really follow the rules
 
2342
    //for speed reasons
 
2343
    foundFrame = (nsIFrame *)isupports;
 
2344
    foundFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel,
 
2345
                                (void**)&foundLevel, sizeof(foundLevel) );
 
2346
 
 
2347
  } while (foundLevel > aBidiLevel);
 
2348
 
 
2349
  return NS_OK;
 
2350
}
 
2351
 
 
2352
 
 
2353
NS_IMETHODIMP 
 
2354
nsSelection::MaintainSelection()
 
2355
{
 
2356
  PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
 
2357
  nsCOMPtr<nsIDOMRange> range;
 
2358
  nsresult rv = mDomSelections[index]->GetRangeAt(0, getter_AddRefs(range));
 
2359
  if (NS_FAILED(rv))
 
2360
    return rv;
 
2361
  if (!range)
 
2362
    return NS_ERROR_FAILURE;
 
2363
 
 
2364
  nsCOMPtr<nsIDOMNode> startNode;
 
2365
  nsCOMPtr<nsIDOMNode> endNode;
 
2366
  PRInt32 startOffset;
 
2367
  PRInt32 endOffset;
 
2368
  range->GetStartContainer(getter_AddRefs(startNode));
 
2369
  range->GetEndContainer(getter_AddRefs(endNode));
 
2370
  range->GetStartOffset(&startOffset);
 
2371
  range->GetEndOffset(&endOffset);
 
2372
 
 
2373
  mMaintainRange = nsnull;
 
2374
  NS_NewRange(getter_AddRefs(mMaintainRange));
 
2375
  if (!mMaintainRange)
 
2376
    return NS_ERROR_OUT_OF_MEMORY;
 
2377
 
 
2378
  mMaintainRange->SetStart(startNode, startOffset);
 
2379
  return mMaintainRange->SetEnd(endNode, endOffset);
 
2380
}
 
2381
 
 
2382
 
 
2383
/** After moving the caret, its Bidi level is set according to the following rules:
 
2384
 *
 
2385
 *  After moving over a character with left/right arrow, set to the Bidi level of the last moved over character.
 
2386
 *  After Home and End, set to the paragraph embedding level.
 
2387
 *  After up/down arrow, PageUp/Down, set to the lower level of the 2 surrounding characters.
 
2388
 *  After mouse click, set to the level of the current frame.
 
2389
 *
 
2390
 *  The following two methods use GetPrevNextBidiLevels to determine the new Bidi level.
 
2391
 *  BidiLevelFromMove is called when the caret is moved in response to a keyboard event
 
2392
 *
 
2393
 * @param aContext is the presentation context
 
2394
 * @param aPresShell is the presentation shell
 
2395
 * @param aNode is the content node
 
2396
 * @param aContentOffset is the new caret position, as an offset into aNode
 
2397
 * @param aKeycode is the keyboard event that moved the caret to the new position
 
2398
 */
 
2399
void nsSelection::BidiLevelFromMove(nsIPresContext* aContext,
 
2400
                                    nsIPresShell* aPresShell,
 
2401
                                    nsIContent *aNode,
 
2402
                                    PRUint32 aContentOffset,
 
2403
                                    PRUint32 aKeycode)
 
2404
{
 
2405
  PRUint8 firstLevel;
 
2406
  PRUint8 secondLevel;
 
2407
  PRUint8 currentLevel;
 
2408
  nsIFrame* firstFrame=nsnull;
 
2409
  nsIFrame* secondFrame=nsnull;
 
2410
 
 
2411
  aPresShell->GetCaretBidiLevel(&currentLevel);
 
2412
 
 
2413
  switch (aKeycode) {
 
2414
 
 
2415
    // Right and Left: the new cursor Bidi level is the level of the character moved over
 
2416
    case nsIDOMKeyEvent::DOM_VK_RIGHT:
 
2417
    case nsIDOMKeyEvent::DOM_VK_LEFT:
 
2418
      GetPrevNextBidiLevels(aContext, aNode, aContentOffset, &firstFrame, &secondFrame, &firstLevel, &secondLevel);
 
2419
      if (HINTLEFT==mHint)
 
2420
        aPresShell->SetCaretBidiLevel(firstLevel);
 
2421
      else
 
2422
        aPresShell->SetCaretBidiLevel(secondLevel);
 
2423
      break;
 
2424
 
 
2425
      /*
 
2426
    // Up and Down: the new cursor Bidi level is the smaller of the two surrounding characters      
 
2427
    case nsIDOMKeyEvent::DOM_VK_UP:
 
2428
    case nsIDOMKeyEvent::DOM_VK_DOWN:
 
2429
      GetPrevNextBidiLevels(aContext, aNode, aContentOffset, &firstFrame, &secondFrame, &firstLevel, &secondLevel);
 
2430
      aPresShell->SetCaretBidiLevel(PR_MIN(firstLevel, secondLevel));
 
2431
      break;
 
2432
      */
 
2433
 
 
2434
    default:
 
2435
      aPresShell->UndefineCaretBidiLevel();
 
2436
  }
 
2437
}
 
2438
 
 
2439
/**
 
2440
 * BidiLevelFromClick is called when the caret is repositioned by clicking the mouse
 
2441
 *
 
2442
 * @param aNode is the content node
 
2443
 * @param aContentOffset is the new caret position, as an offset into aNode
 
2444
 */
 
2445
void nsSelection::BidiLevelFromClick(nsIContent *aNode, PRUint32 aContentOffset)
 
2446
{
 
2447
  nsCOMPtr<nsIPresContext> context;
 
2448
  nsresult result = mTracker->GetPresContext(getter_AddRefs(context));
 
2449
  if (NS_FAILED(result) || !context)
 
2450
    return;
 
2451
 
 
2452
  nsIPresShell *shell = context->GetPresShell();
 
2453
  if (!shell)
 
2454
    return;
 
2455
 
 
2456
  nsIFrame* clickInFrame=nsnull;
 
2457
  PRUint8 frameLevel;
 
2458
  PRInt32 OffsetNotUsed;
 
2459
 
 
2460
  result = GetFrameForNodeOffset(aNode, aContentOffset, mHint, &clickInFrame, &OffsetNotUsed);
 
2461
  if (NS_FAILED(result))
 
2462
    return;
 
2463
 
 
2464
  clickInFrame->GetBidiProperty(context, nsLayoutAtoms::embeddingLevel,
 
2465
                                (void**)&frameLevel, sizeof(frameLevel) );
 
2466
  shell->SetCaretBidiLevel(frameLevel);
 
2467
}
 
2468
 
 
2469
 
 
2470
PRBool
 
2471
nsSelection::AdjustForMaintainedSelection(nsIContent *aContent, PRInt32 aOffset)
 
2472
{
 
2473
  // Is the desired content and offset currently in selection?
 
2474
  // If the double click flag is set then don't continue selection if the 
 
2475
  // desired content and offset are currently inside a selection.
 
2476
  // This will stop double click then mouse-drag from undoing the desired
 
2477
  // selecting of a word.
 
2478
  if (!mMaintainRange)
 
2479
    return PR_FALSE;
 
2480
 
 
2481
  nsCOMPtr<nsIDOMNode> rangenode;
 
2482
  PRInt32 rangeOffset;
 
2483
  mMaintainRange->GetStartContainer(getter_AddRefs(rangenode));
 
2484
  mMaintainRange->GetStartOffset(&rangeOffset);
 
2485
 
 
2486
  nsCOMPtr<nsIDOMNode> domNode = do_QueryInterface(aContent);
 
2487
  if (domNode)
 
2488
  {
 
2489
    PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
 
2490
    nsCOMPtr<nsIDOMNSRange> nsrange = do_QueryInterface(mMaintainRange);
 
2491
    if (nsrange)
 
2492
    {
 
2493
      PRBool insideSelection = PR_FALSE;
 
2494
      nsrange->IsPointInRange(domNode, aOffset, &insideSelection);
 
2495
 
 
2496
      // Done when we find a range that we are in
 
2497
      if (insideSelection)
 
2498
      {
 
2499
        mDomSelections[index]->Collapse(rangenode, rangeOffset);
 
2500
        mMaintainRange->GetEndContainer(getter_AddRefs(rangenode));
 
2501
        mMaintainRange->GetEndOffset(&rangeOffset);
 
2502
        mDomSelections[index]->Extend(rangenode,rangeOffset);
 
2503
        return PR_TRUE; // dragging in selection aborted
 
2504
      }
 
2505
    }
 
2506
 
 
2507
    PRInt32 relativePosition = ComparePoints(rangenode, rangeOffset, domNode, aOffset);
 
2508
    // if == 0 or -1 do nothing if < 0 then we need to swap direction
 
2509
    if (relativePosition > 0
 
2510
        && (mDomSelections[index]->GetDirection() == eDirNext))
 
2511
    {
 
2512
      mMaintainRange->GetEndContainer(getter_AddRefs(rangenode));
 
2513
      mMaintainRange->GetEndOffset(&rangeOffset);
 
2514
      mDomSelections[index]->Collapse(rangenode, rangeOffset);
 
2515
    }
 
2516
    else if (relativePosition < 0
 
2517
             && (mDomSelections[index]->GetDirection() == eDirPrevious))
 
2518
      mDomSelections[index]->Collapse(rangenode, rangeOffset);
 
2519
  }
 
2520
 
 
2521
  return PR_FALSE;
 
2522
}
 
2523
 
 
2524
 
 
2525
NS_IMETHODIMP
 
2526
nsSelection::HandleClick(nsIContent *aNewFocus, PRUint32 aContentOffset, 
 
2527
                       PRUint32 aContentEndOffset, PRBool aContinueSelection, 
 
2528
                       PRBool aMultipleSelection, PRBool aHint) 
 
2529
{
 
2530
  if (!aNewFocus)
 
2531
    return NS_ERROR_INVALID_ARG;
 
2532
 
 
2533
  InvalidateDesiredX();
 
2534
 
 
2535
  if (!aContinueSelection)
 
2536
    mMaintainRange = nsnull;
 
2537
 
 
2538
  mHint = HINT(aHint);
 
2539
  // Don't take focus when dragging off of a table
 
2540
  if (!mDragSelectingCells)
 
2541
  {
 
2542
    BidiLevelFromClick(aNewFocus, aContentOffset);
 
2543
    PostReason(nsISelectionListener::MOUSEDOWN_REASON + nsISelectionListener::DRAG_REASON);
 
2544
    if (aContinueSelection &&
 
2545
        AdjustForMaintainedSelection(aNewFocus, aContentOffset))
 
2546
      return NS_OK; //shift clicked to maintained selection. rejected.
 
2547
 
 
2548
    return TakeFocus(aNewFocus, aContentOffset, aContentEndOffset, aContinueSelection, aMultipleSelection);
 
2549
  }
 
2550
  
 
2551
  return NS_OK;
 
2552
}
 
2553
 
 
2554
NS_IMETHODIMP
 
2555
nsSelection::HandleDrag(nsIPresContext *aPresContext, nsIFrame *aFrame, nsPoint& aPoint)
 
2556
{
 
2557
  if (!aPresContext || !aFrame)
 
2558
    return NS_ERROR_NULL_POINTER;
 
2559
 
 
2560
  nsresult result;
 
2561
  nsIFrame *newFrame = 0;
 
2562
  nsPoint   newPoint;
 
2563
 
 
2564
  result = ConstrainFrameAndPointToAnchorSubtree(aPresContext, aFrame, aPoint, &newFrame, newPoint);
 
2565
  if (NS_FAILED(result))
 
2566
    return result;
 
2567
  if (!newFrame)
 
2568
    return NS_ERROR_FAILURE;
 
2569
 
 
2570
  PRInt32 startPos = 0;
 
2571
  PRInt32 contentOffsetEnd = 0;
 
2572
  PRBool  beginOfContent;
 
2573
  nsCOMPtr<nsIContent> newContent;
 
2574
 
 
2575
  result = newFrame->GetContentAndOffsetsFromPoint(aPresContext, newPoint,
 
2576
                                                   getter_AddRefs(newContent), 
 
2577
                                                   startPos, contentOffsetEnd, beginOfContent);
 
2578
 
 
2579
  if ((newFrame->GetStateBits() & NS_FRAME_SELECTED_CONTENT) &&
 
2580
       AdjustForMaintainedSelection(newContent, startPos))
 
2581
    return NS_OK;
 
2582
 
 
2583
  // do we have CSS that changes selection behaviour?
 
2584
  {
 
2585
    //add scope for nsCOMPtr
 
2586
    PRBool    changeSelection;
 
2587
    nsCOMPtr<nsIContent>  selectContent;
 
2588
    PRInt32   newStart, newEnd;
 
2589
    if (NS_SUCCEEDED(AdjustOffsetsFromStyle(newFrame, &changeSelection, getter_AddRefs(selectContent), &newStart, &newEnd))
 
2590
      && changeSelection)
 
2591
    {
 
2592
      newContent = selectContent;
 
2593
      startPos = newStart;
 
2594
      contentOffsetEnd = newEnd;
 
2595
    }
 
2596
  }
 
2597
 
 
2598
  if (NS_SUCCEEDED(result))
 
2599
  {
 
2600
#ifdef VISUALSELECTION
 
2601
    PRBool bidiEnabled = PR_FALSE;
 
2602
    aPresContext->GetBidiEnabled(&bidiEnabled);
 
2603
    if (bidiEnabled) {
 
2604
      PRUint8 level;
 
2605
      nsPeekOffsetStruct pos;
 
2606
      //set data using mLimiter to stop on scroll views.  If we have a limiter then we stop peeking
 
2607
      //when we hit scrollable views.  If no limiter then just let it go ahead
 
2608
      pos.SetData(mTracker, 0, eSelectDir, eDirNext, startPos, PR_FALSE,
 
2609
                  PR_TRUE, PR_TRUE, mLimiter != nsnull, PR_FALSE);
 
2610
      mHint = HINT(beginOfContent);
 
2611
      HINT saveHint = mHint;
 
2612
      newFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel,
 
2613
                                (void**)&level,sizeof(level));
 
2614
      if (level & 1)
 
2615
        mHint = (mHint==HINTLEFT) ? HINTRIGHT : HINTLEFT;
 
2616
      pos.mResultContent = newContent;
 
2617
      pos.mContentOffset = contentOffsetEnd;
 
2618
      result = VisualSelectFrames(aPresContext, newFrame, pos);
 
2619
      if (NS_FAILED(result))
 
2620
        result = HandleClick(newContent, startPos, contentOffsetEnd, PR_TRUE,
 
2621
                             PR_FALSE, beginOfContent);
 
2622
      mHint = saveHint;
 
2623
    }
 
2624
    else
 
2625
#endif // VISUALSELECTION
 
2626
      result = HandleClick(newContent, startPos, contentOffsetEnd, PR_TRUE,
 
2627
                           PR_FALSE, beginOfContent);
 
2628
  }
 
2629
 
 
2630
  return result;
 
2631
}
 
2632
 
 
2633
NS_IMETHODIMP
 
2634
nsSelection::StartAutoScrollTimer(nsIPresContext *aPresContext, nsIFrame *aFrame, nsPoint& aPoint, PRUint32 aDelay)
 
2635
{
 
2636
  PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
 
2637
  return mDomSelections[index]->StartAutoScrollTimer(aPresContext, aFrame, aPoint, aDelay);
 
2638
}
 
2639
 
 
2640
NS_IMETHODIMP
 
2641
nsSelection::StopAutoScrollTimer()
 
2642
{
 
2643
  PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
 
2644
  return mDomSelections[index]->StopAutoScrollTimer();
 
2645
}
 
2646
 
 
2647
/**
 
2648
hard to go from nodes to frames, easy the other way!
 
2649
 */
 
2650
NS_IMETHODIMP
 
2651
nsSelection::TakeFocus(nsIContent *aNewFocus, PRUint32 aContentOffset, 
 
2652
                       PRUint32 aContentEndOffset, PRBool aContinueSelection, PRBool aMultipleSelection)
 
2653
{
 
2654
  if (!aNewFocus)
 
2655
    return NS_ERROR_NULL_POINTER;
 
2656
 
 
2657
  STATUS_CHECK_RETURN_MACRO();
 
2658
 
 
2659
  if (!IsValidSelectionPoint(this,aNewFocus))
 
2660
    return NS_ERROR_FAILURE;
 
2661
 
 
2662
  // Clear all table selection data
 
2663
  mSelectingTableCellMode = 0;
 
2664
  mDragSelectingCells = PR_FALSE;
 
2665
  mStartSelectedCell = nsnull;
 
2666
  mEndSelectedCell = nsnull;
 
2667
  mAppendStartSelectedCell = nsnull;
 
2668
 
 
2669
  //HACKHACKHACK
 
2670
  nsIContent * parentContent = aNewFocus->GetParent();
 
2671
  if (!parentContent)
 
2672
    return NS_ERROR_FAILURE;
 
2673
  //END HACKHACKHACK /checking for root frames/content
 
2674
 
 
2675
  nsCOMPtr<nsIContent> startContent = aNewFocus;
 
2676
  if (aNewFocus->IsContentOfType(nsIContent::eELEMENT))
 
2677
  {
 
2678
    PRInt32 childIndex  = 0;
 
2679
    PRInt32 numChildren = 0;
 
2680
 
 
2681
    if (mHint == HINTLEFT)
 
2682
    {
 
2683
      if (aContentOffset > 0)
 
2684
        childIndex = aContentOffset - 1;
 
2685
      else
 
2686
        childIndex = aContentOffset;
 
2687
    }
 
2688
    else // HINTRIGHT
 
2689
    {
 
2690
      numChildren = aNewFocus->GetChildCount();
 
2691
 
 
2692
      if (aContentOffset >= numChildren)
 
2693
      {
 
2694
        if (numChildren > 0)
 
2695
          childIndex = numChildren - 1;
 
2696
        else
 
2697
          childIndex = 0;
 
2698
      }
 
2699
      else
 
2700
        childIndex = aContentOffset;
 
2701
    }
 
2702
    startContent  = aNewFocus->GetChildAt(childIndex);
 
2703
  }
 
2704
 
 
2705
  nsIFrame * frame;
 
2706
  nsresult result = mTracker->GetPrimaryFrameFor(startContent, &frame);
 
2707
  nsIFrame*  selectNoneFrame;
 
2708
  result = FrameOrParentHasSpecialSelectionStyle(frame, NS_STYLE_USER_SELECT_NONE, &selectNoneFrame);
 
2709
  if (NS_FAILED(result)) return result;
 
2710
  if (selectNoneFrame)
 
2711
  {
 
2712
    nsIFrame * selectForceTextFrame;
 
2713
    result = FrameOrParentHasSpecialSelectionStyleBelowAncestor(frame, NS_STYLE_USER_SELECT_MOZ_FORCE_TEXT, selectNoneFrame, &selectForceTextFrame);
 
2714
    if (NS_FAILED(result)) return result;
 
2715
    if (!selectForceTextFrame) return NS_OK;
 
2716
  }
 
2717
 
 
2718
  PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
 
2719
  nsCOMPtr<nsIDOMNode> domNode = do_QueryInterface(aNewFocus);
 
2720
  //traverse through document and unselect crap here
 
2721
  if (!aContinueSelection){ //single click? setting cursor down
 
2722
    PRUint32 batching = mBatching;//hack to use the collapse code.
 
2723
    PRBool changes = mChangesDuringBatching;
 
2724
    mBatching = 1;
 
2725
 
 
2726
    if (aMultipleSelection){
 
2727
      nsCOMPtr<nsIDOMRange> newRange;
 
2728
      NS_NewRange(getter_AddRefs(newRange));
 
2729
 
 
2730
      newRange->SetStart(domNode,aContentOffset);
 
2731
      newRange->SetEnd(domNode,aContentOffset);
 
2732
      mDomSelections[index]->AddRange(newRange);
 
2733
      mBatching = batching;
 
2734
      mChangesDuringBatching = changes;
 
2735
      mDomSelections[index]->SetOriginalAnchorPoint(domNode,aContentOffset);
 
2736
    }
 
2737
    else
 
2738
    {
 
2739
      PRBool oldDesiredXSet = mDesiredXSet; //need to keep old desired X if it was set.
 
2740
      mDomSelections[index]->Collapse(domNode, aContentOffset);
 
2741
      mDesiredXSet = oldDesiredXSet; //now reset desired X back.
 
2742
      mBatching = batching;
 
2743
      mChangesDuringBatching = changes;
 
2744
    }
 
2745
    if (aContentEndOffset != aContentOffset)
 
2746
      mDomSelections[index]->Extend(domNode,aContentEndOffset);
 
2747
 
 
2748
    //find out if we are inside a table. if so, find out which one and which cell
 
2749
    //once we do that, the next time we get a takefocus, check the parent tree. 
 
2750
    //if we are no longer inside same table ,cell then switch to table selection mode.
 
2751
    // BUT only do this in an editor
 
2752
 
 
2753
    nsCOMPtr<nsIPresContext> presContext;
 
2754
    result = mTracker->GetPresContext(getter_AddRefs(presContext));
 
2755
    if (NS_FAILED(result) || !presContext)
 
2756
      return result?result:NS_ERROR_FAILURE;
 
2757
 
 
2758
    nsIPresShell *presShell = presContext->GetPresShell();
 
2759
    if (!presShell)
 
2760
      return NS_ERROR_FAILURE;
 
2761
 
 
2762
    PRInt16 displaySelection;
 
2763
    result = presShell->GetSelectionFlags(&displaySelection);
 
2764
    if (NS_FAILED(result))
 
2765
      return result;
 
2766
 
 
2767
    // Editor has DISPLAY_ALL selection type
 
2768
    if (displaySelection == nsISelectionDisplay::DISPLAY_ALL)
 
2769
    {
 
2770
      mCellParent = GetCellParent(domNode);
 
2771
#ifdef DEBUG_TABLE_SELECTION
 
2772
      if (mCellParent)
 
2773
        printf(" * TakeFocus - Collapsing into new cell\n");
 
2774
#endif
 
2775
    }
 
2776
  }
 
2777
  else {
 
2778
    // Now update the range list:
 
2779
    if (aContinueSelection && domNode)
 
2780
    {
 
2781
      PRInt32 offset;
 
2782
      nsIDOMNode *cellparent = GetCellParent(domNode);
 
2783
      if (mCellParent && cellparent && cellparent != mCellParent) //switch to cell selection mode
 
2784
      {
 
2785
#ifdef DEBUG_TABLE_SELECTION
 
2786
printf(" * TakeFocus - moving into new cell\n");
 
2787
#endif
 
2788
        nsCOMPtr<nsIDOMNode> parent;
 
2789
        nsCOMPtr<nsIContent> parentContent;
 
2790
        nsMouseEvent event;
 
2791
        nsresult result;
 
2792
 
 
2793
        // Start selecting in the cell we were in before
 
2794
        result = ParentOffset(mCellParent, getter_AddRefs(parent),&offset);
 
2795
        parentContent = do_QueryInterface(parent);
 
2796
        if (parentContent)
 
2797
          result = HandleTableSelection(parentContent, offset, nsISelectionPrivate::TABLESELECTION_CELL, &event);
 
2798
 
 
2799
        // Find the parent of this new cell and extend selection to it
 
2800
        result = ParentOffset(cellparent,getter_AddRefs(parent),&offset);
 
2801
        parentContent = do_QueryInterface(parent);
 
2802
 
 
2803
        // XXXX We need to REALLY get the current key shift state
 
2804
        //  (we'd need to add event listener -- let's not bother for now)
 
2805
        event.isShift = PR_FALSE; //aContinueSelection;
 
2806
        if (parentContent)
 
2807
        {
 
2808
          mCellParent = cellparent;
 
2809
          // Continue selection into next cell
 
2810
          result = HandleTableSelection(parentContent, offset, nsISelectionPrivate::TABLESELECTION_CELL, &event);
 
2811
        }
 
2812
      }
 
2813
      else
 
2814
      {
 
2815
        // XXXX Problem: Shift+click in browser is appending text selection to selected table!!!
 
2816
        //   is this the place to erase seleced cells ?????
 
2817
        if (mDomSelections[index]->GetDirection() == eDirNext && aContentEndOffset > aContentOffset) //didnt go far enough 
 
2818
        {
 
2819
          mDomSelections[index]->Extend(domNode, aContentEndOffset);//this will only redraw the diff 
 
2820
        }
 
2821
        else
 
2822
          mDomSelections[index]->Extend(domNode, aContentOffset);
 
2823
      }
 
2824
    }
 
2825
  }
 
2826
 
 
2827
  // Don't notify selection listeners if batching is on:
 
2828
  if (GetBatching())
 
2829
    return NS_OK;
 
2830
  return NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL);
 
2831
}
 
2832
 
 
2833
 
 
2834
 
 
2835
NS_METHOD
 
2836
nsSelection::LookUpSelection(nsIContent *aContent, PRInt32 aContentOffset, PRInt32 aContentLength,
 
2837
                             SelectionDetails **aReturnDetails, PRBool aSlowCheck)
 
2838
{
 
2839
  if (!aContent || !aReturnDetails)
 
2840
    return NS_ERROR_NULL_POINTER;
 
2841
 
 
2842
  STATUS_CHECK_RETURN_MACRO();
 
2843
 
 
2844
 
 
2845
  *aReturnDetails = nsnull;
 
2846
 
 
2847
  // if any of the additional selections are set, always do a slow check. The
 
2848
  // issue is that the NS_FRAME_SELECTED_CONTENT bit is set when any of the
 
2849
  // selections are used, and the 'non-slow' code assumes that this means that
 
2850
  // there exists something selected for all selection types.
 
2851
  PRInt8 j;
 
2852
  for (j = (PRInt8) 1; j < (PRInt8)nsISelectionController::NUM_SELECTIONTYPES; j++){
 
2853
    if (mDomSelections[j]){
 
2854
      PRBool iscollapsed;
 
2855
      mDomSelections[j]->GetIsCollapsed(&iscollapsed);
 
2856
      if (!iscollapsed){
 
2857
        aSlowCheck = PR_TRUE;
 
2858
      }
 
2859
    }
 
2860
  }
 
2861
 
 
2862
  for (j = (PRInt8) 0; j < (PRInt8)nsISelectionController::NUM_SELECTIONTYPES; j++){
 
2863
    if (mDomSelections[j])
 
2864
     mDomSelections[j]->LookUpSelection(aContent, aContentOffset, aContentLength, aReturnDetails, (SelectionType)(1<<j), aSlowCheck);
 
2865
  }
 
2866
  return NS_OK;
 
2867
}
 
2868
 
 
2869
 
 
2870
 
 
2871
NS_METHOD 
 
2872
nsSelection::SetMouseDownState(PRBool aState)
 
2873
{
 
2874
  if (mMouseDownState == aState)
 
2875
    return NS_OK;
 
2876
  mMouseDownState = aState;
 
2877
  if (!mMouseDownState)
 
2878
  {
 
2879
    PRInt16 reason;
 
2880
    if (aState)
 
2881
      reason = nsISelectionListener::MOUSEDOWN_REASON;
 
2882
    else
 
2883
      reason = nsISelectionListener::MOUSEUP_REASON;
 
2884
    PostReason(reason);//not a drag reason
 
2885
    NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL);//notify that reason is mouse up please.
 
2886
  }
 
2887
  return NS_OK;
 
2888
}
 
2889
 
 
2890
 
 
2891
 
 
2892
NS_METHOD
 
2893
nsSelection::GetMouseDownState(PRBool *aState)
 
2894
{
 
2895
  if (!aState)
 
2896
    return NS_ERROR_NULL_POINTER;
 
2897
  *aState = mMouseDownState;
 
2898
  return NS_OK;
 
2899
}
 
2900
 
 
2901
NS_IMETHODIMP
 
2902
nsSelection::GetSelection(SelectionType aType, nsISelection **aDomSelection)
 
2903
{
 
2904
  if (!aDomSelection)
 
2905
    return NS_ERROR_NULL_POINTER;
 
2906
  PRInt8 index = GetIndexFromSelectionType(aType);
 
2907
  if (index < 0)
 
2908
    return NS_ERROR_INVALID_ARG;
 
2909
  *aDomSelection = NS_REINTERPRET_CAST(nsISelection *,mDomSelections[index]);
 
2910
  (*aDomSelection)->AddRef();
 
2911
  return NS_OK;
 
2912
}
 
2913
 
 
2914
NS_IMETHODIMP
 
2915
nsSelection::ScrollSelectionIntoView(SelectionType aType, SelectionRegion aRegion, PRBool aIsSynchronous)
 
2916
{
 
2917
  PRInt8 index = GetIndexFromSelectionType(aType);
 
2918
  if (index < 0)
 
2919
    return NS_ERROR_INVALID_ARG;
 
2920
 
 
2921
  if (!mDomSelections[index])
 
2922
    return NS_ERROR_NULL_POINTER;
 
2923
 
 
2924
  return mDomSelections[index]->ScrollIntoView(aRegion, aIsSynchronous);
 
2925
}
 
2926
 
 
2927
NS_IMETHODIMP
 
2928
nsSelection::RepaintSelection(nsIPresContext* aPresContext, SelectionType aType)
 
2929
{
 
2930
  PRInt8 index = GetIndexFromSelectionType(aType);
 
2931
  if (index < 0)
 
2932
    return NS_ERROR_INVALID_ARG;
 
2933
  if (!mDomSelections[index])
 
2934
    return NS_ERROR_NULL_POINTER;
 
2935
  return mDomSelections[index]->Repaint(aPresContext);
 
2936
}
 
2937
 
 
2938
NS_IMETHODIMP
 
2939
nsSelection::GetFrameForNodeOffset(nsIContent *aNode, PRInt32 aOffset, HINT aHint, nsIFrame **aReturnFrame, PRInt32 *aReturnOffset)
 
2940
{
 
2941
  if (!aNode || !aReturnFrame || !aReturnOffset)
 
2942
    return NS_ERROR_NULL_POINTER;
 
2943
 
 
2944
  if (aOffset < 0)
 
2945
    return NS_ERROR_FAILURE;
 
2946
 
 
2947
  *aReturnOffset = aOffset;
 
2948
 
 
2949
  nsresult result = NS_OK;
 
2950
 
 
2951
  nsCOMPtr<nsIContent> theNode = aNode;
 
2952
 
 
2953
  if (aNode->IsContentOfType(nsIContent::eELEMENT))
 
2954
  {
 
2955
    PRInt32 childIndex  = 0;
 
2956
    PRInt32 numChildren = 0;
 
2957
 
 
2958
    if (aHint == HINTLEFT)
 
2959
    {
 
2960
      if (aOffset > 0)
 
2961
        childIndex = aOffset - 1;
 
2962
      else
 
2963
        childIndex = aOffset;
 
2964
    }
 
2965
    else // HINTRIGHT
 
2966
    {
 
2967
      numChildren = theNode->GetChildCount();
 
2968
 
 
2969
      if (aOffset >= numChildren)
 
2970
      {
 
2971
        if (numChildren > 0)
 
2972
          childIndex = numChildren - 1;
 
2973
        else
 
2974
          childIndex = 0;
 
2975
      }
 
2976
      else
 
2977
        childIndex = aOffset;
 
2978
    }
 
2979
    
 
2980
    nsCOMPtr<nsIContent> childNode = theNode->GetChildAt(childIndex);
 
2981
 
 
2982
    if (!childNode)
 
2983
      return NS_ERROR_FAILURE;
 
2984
 
 
2985
    theNode = childNode;
 
2986
 
 
2987
#ifdef DONT_DO_THIS_YET
 
2988
    // XXX: We can't use this code yet because the hinting
 
2989
    //      can cause us to attatch to the wrong line frame.
 
2990
 
 
2991
    // Now that we have the child node, check if it too
 
2992
    // can contain children. If so, call this method again!
 
2993
 
 
2994
    if (theNode->IsContentOfType(nsIContent::eELEMENT))
 
2995
    {
 
2996
      PRInt32 newOffset = 0;
 
2997
 
 
2998
      if (aOffset > childIndex)
 
2999
      {
 
3000
        numChildren = theNode->GetChildCount();
 
3001
 
 
3002
        newOffset = numChildren;
 
3003
      }
 
3004
 
 
3005
      return GetFrameForNodeOffset(theNode, newOffset, aHint, aReturnFrame,aReturnOffset);
 
3006
    }
 
3007
    else
 
3008
#endif // DONT_DO_THIS_YET
 
3009
    {
 
3010
      // Check to see if theNode is a text node. If it is, translate
 
3011
      // aOffset into an offset into the text node.
 
3012
 
 
3013
      nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(theNode);
 
3014
 
 
3015
      if (textNode)
 
3016
      {
 
3017
        if (aOffset > childIndex)
 
3018
        {
 
3019
          PRUint32 textLength = 0;
 
3020
 
 
3021
          result = textNode->GetLength(&textLength);
 
3022
 
 
3023
          if (NS_FAILED(result))
 
3024
            return NS_ERROR_FAILURE;
 
3025
 
 
3026
          *aReturnOffset = (PRInt32)textLength;
 
3027
        }
 
3028
        else
 
3029
          *aReturnOffset = 0;
 
3030
      }
 
3031
    }
 
3032
  }
 
3033
  
 
3034
  result = mTracker->GetPrimaryFrameFor(theNode, aReturnFrame);
 
3035
  if (NS_FAILED(result))
 
3036
    return result;
 
3037
 
 
3038
  if (!*aReturnFrame)
 
3039
    return NS_ERROR_UNEXPECTED;
 
3040
 
 
3041
  // find the child frame containing the offset we want
 
3042
  result = (*aReturnFrame)->GetChildFrameContainingOffset(*aReturnOffset, aHint, &aOffset, aReturnFrame);
 
3043
  return result;
 
3044
}
 
3045
 
 
3046
NS_IMETHODIMP
 
3047
nsSelection::CommonPageMove(PRBool aForward, 
 
3048
                          PRBool aExtend, 
 
3049
                          nsIScrollableView *aScrollableView,
 
3050
                          nsIFrameSelection *aFrameSel)
 
3051
{
 
3052
  if ( !aScrollableView || !aFrameSel)
 
3053
    return NS_ERROR_NULL_POINTER;
 
3054
  // expected behavior for PageMove is to scroll AND move the caret
 
3055
  // and remain relative position of the caret in view. see Bug 4302.
 
3056
 
 
3057
  nsresult result;
 
3058
  const nsIView* clipView;
 
3059
  //get the frame from the scrollable view
 
3060
 
 
3061
  nsIFrame* mainframe = nsnull;
 
3062
 
 
3063
  // The view's client data points back to its frame
 
3064
  nsIView *scrolledView;
 
3065
  result = aScrollableView->GetScrolledView(scrolledView);
 
3066
 
 
3067
  if (NS_FAILED(result))
 
3068
    return result;
 
3069
 
 
3070
  if (scrolledView)
 
3071
    mainframe = NS_STATIC_CAST(nsIFrame*, scrolledView->GetClientData());
 
3072
 
 
3073
  if (!mainframe)
 
3074
    return NS_ERROR_FAILURE;
 
3075
 
 
3076
  // find out where we are; determine amount to page up/down
 
3077
  if (NS_FAILED(result = aScrollableView->GetClipView(&clipView))) 
 
3078
    return result;
 
3079
  nsRect viewRect = clipView->GetBounds();
 
3080
 
 
3081
  nsCOMPtr<nsIPresContext> context;
 
3082
  result = mTracker->GetPresContext(getter_AddRefs(context));
 
3083
  
 
3084
  if (NS_FAILED(result))
 
3085
    return result;
 
3086
  
 
3087
  if (!context)
 
3088
    return NS_ERROR_NULL_POINTER;
 
3089
 
 
3090
  nsIPresShell *shell = context->GetPresShell();
 
3091
  
 
3092
  if (!shell)
 
3093
    return NS_ERROR_NULL_POINTER;
 
3094
 
 
3095
  // find out where the caret is.
 
3096
  // we should know mDesiredX value of nsSelection, but I havent seen that behavior in other windows applications yet.
 
3097
  nsCOMPtr<nsISelection> domSel;
 
3098
  aFrameSel->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(domSel));
 
3099
  
 
3100
  if (!domSel) 
 
3101
    return NS_ERROR_UNEXPECTED;
 
3102
  
 
3103
  nsCOMPtr<nsICaret> caret;
 
3104
  nsRect caretPos;
 
3105
  PRBool isCollapsed;
 
3106
  result = shell->GetCaret(getter_AddRefs(caret));
 
3107
  
 
3108
  if (NS_FAILED(result)) 
 
3109
    return result;
 
3110
  
 
3111
  nsIView *caretView;
 
3112
  result = caret->GetCaretCoordinates(nsICaret::eClosestViewCoordinates, domSel, &caretPos, &isCollapsed, &caretView);
 
3113
  
 
3114
  if (NS_FAILED(result)) 
 
3115
    return result;
 
3116
  
 
3117
  //need to adjust caret jump by percentage scroll
 
3118
  viewRect.height = (PRInt32) (viewRect.height * PAGE_SCROLL_PERCENT);
 
3119
  
 
3120
  if (aForward)
 
3121
    caretPos.y += viewRect.height;
 
3122
  else
 
3123
    caretPos.y -= viewRect.height;
 
3124
 
 
3125
  
 
3126
  if (caretView)
 
3127
  {
 
3128
    while (caretView != scrolledView)
 
3129
    {
 
3130
      caretPos += caretView->GetPosition();
 
3131
      caretView = caretView->GetParent();
 
3132
      if (!caretView) //how did we miss the scrolled view. something is very wrong
 
3133
        return NS_ERROR_FAILURE;
 
3134
    }
 
3135
  }
 
3136
    
 
3137
  // get a content at desired location
 
3138
  nsCOMPtr<nsIContent> content;
 
3139
  PRInt32 startOffset, endOffset;
 
3140
  PRBool beginFrameContent;
 
3141
  nsPoint desiredPoint;
 
3142
  desiredPoint.x = caretPos.x;
 
3143
  desiredPoint.y = caretPos.y + caretPos.height/2;
 
3144
  result = mainframe->GetContentAndOffsetsFromPoint(context, desiredPoint, getter_AddRefs(content), startOffset, endOffset, beginFrameContent);
 
3145
  
 
3146
  if (NS_FAILED(result)) 
 
3147
    return result;
 
3148
  
 
3149
  if (!content) 
 
3150
    return NS_ERROR_UNEXPECTED;
 
3151
 
 
3152
  // scroll one page
 
3153
 
 
3154
  aScrollableView->ScrollByPages(0, aForward ? 1 : -1);
 
3155
  
 
3156
  // place the caret
 
3157
  result = aFrameSel->HandleClick(content, startOffset, startOffset, aExtend, PR_FALSE, PR_TRUE);
 
3158
  
 
3159
  return result;
 
3160
}
 
3161
 
 
3162
NS_IMETHODIMP 
 
3163
nsSelection::CharacterMove(PRBool aForward, PRBool aExtend)
 
3164
{
 
3165
  if (aForward)
 
3166
    return MoveCaret(nsIDOMKeyEvent::DOM_VK_RIGHT,aExtend,eSelectCharacter);
 
3167
  else
 
3168
    return MoveCaret(nsIDOMKeyEvent::DOM_VK_LEFT,aExtend,eSelectCharacter);
 
3169
}
 
3170
 
 
3171
NS_IMETHODIMP
 
3172
nsSelection::WordMove(PRBool aForward, PRBool aExtend)
 
3173
{
 
3174
  if (aForward)
 
3175
    return MoveCaret(nsIDOMKeyEvent::DOM_VK_RIGHT,aExtend,eSelectWord);
 
3176
  else
 
3177
    return MoveCaret(nsIDOMKeyEvent::DOM_VK_LEFT,aExtend,eSelectWord);
 
3178
}
 
3179
 
 
3180
NS_IMETHODIMP
 
3181
nsSelection::LineMove(PRBool aForward, PRBool aExtend)
 
3182
{
 
3183
  if (aForward)
 
3184
    return MoveCaret(nsIDOMKeyEvent::DOM_VK_DOWN,aExtend,eSelectLine);
 
3185
  else
 
3186
    return MoveCaret(nsIDOMKeyEvent::DOM_VK_UP,aExtend,eSelectLine);
 
3187
}
 
3188
 
 
3189
NS_IMETHODIMP
 
3190
nsSelection::IntraLineMove(PRBool aForward, PRBool aExtend)
 
3191
{
 
3192
  if (aForward)
 
3193
    return MoveCaret(nsIDOMKeyEvent::DOM_VK_END,aExtend,eSelectLine);
 
3194
  else
 
3195
    return MoveCaret(nsIDOMKeyEvent::DOM_VK_HOME,aExtend,eSelectLine);
 
3196
}
 
3197
 
 
3198
NS_IMETHODIMP nsSelection::SelectAll()
 
3199
{
 
3200
  nsCOMPtr<nsIContent> rootContent;
 
3201
  if (mLimiter)
 
3202
  {
 
3203
    rootContent = mLimiter;//addrefit
 
3204
  }
 
3205
  else
 
3206
  {
 
3207
    nsresult rv;
 
3208
    nsCOMPtr<nsIPresShell> shell(do_QueryInterface(mTracker,&rv));
 
3209
    if (NS_FAILED(rv) || !shell) {
 
3210
      return NS_ERROR_FAILURE;
 
3211
    }
 
3212
 
 
3213
    nsCOMPtr<nsIDocument> doc;
 
3214
    rv = shell->GetDocument(getter_AddRefs(doc));
 
3215
    if (NS_FAILED(rv))
 
3216
      return rv;
 
3217
    if (!doc)
 
3218
      return NS_ERROR_FAILURE;
 
3219
    rootContent = doc->GetRootContent();
 
3220
    if (!rootContent)
 
3221
      return NS_ERROR_FAILURE;
 
3222
  }
 
3223
  PRInt32 numChildren = rootContent->GetChildCount();
 
3224
  PostReason(nsISelectionListener::NO_REASON);
 
3225
  return TakeFocus(mLimiter, 0, numChildren, PR_FALSE, PR_FALSE);
 
3226
}
 
3227
 
 
3228
//////////END FRAMESELECTION
 
3229
 
 
3230
NS_IMETHODIMP
 
3231
nsSelection::StartBatchChanges()
 
3232
{
 
3233
  nsresult result(NS_OK);
 
3234
  mBatching++;
 
3235
  return result;
 
3236
}
 
3237
 
 
3238
 
 
3239
NS_IMETHODIMP
 
3240
nsSelection::EndBatchChanges()
 
3241
{
 
3242
  nsresult result(NS_OK);
 
3243
  mBatching--;
 
3244
  NS_ASSERTION(mBatching >=0,"Bad mBatching");
 
3245
  if (mBatching == 0 && mChangesDuringBatching){
 
3246
    mChangesDuringBatching = PR_FALSE;
 
3247
    NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL);
 
3248
  }
 
3249
  return result;
 
3250
}
 
3251
 
 
3252
 
 
3253
nsresult
 
3254
nsSelection::NotifySelectionListeners(SelectionType aType)
 
3255
{
 
3256
  PRInt8 index = GetIndexFromSelectionType(aType);
 
3257
  if (index >=0)
 
3258
  {
 
3259
    return mDomSelections[index]->NotifySelectionListeners();
 
3260
  }
 
3261
  return NS_ERROR_FAILURE;
 
3262
}
 
3263
 
 
3264
nsresult
 
3265
nsSelection::FrameOrParentHasSpecialSelectionStyle(nsIFrame* aFrame, PRUint8 aSelectionStyle, nsIFrame* *foundFrame)
 
3266
{
 
3267
  nsIFrame* thisFrame = aFrame;
 
3268
  
 
3269
  while (thisFrame)
 
3270
  {
 
3271
    if (thisFrame->GetStyleUIReset()->mUserSelect == aSelectionStyle)
 
3272
    {
 
3273
      *foundFrame = thisFrame;
 
3274
      return NS_OK;
 
3275
    }
 
3276
  
 
3277
    thisFrame = thisFrame->GetParent();
 
3278
  }
 
3279
  
 
3280
  *foundFrame = nsnull;
 
3281
  return NS_OK;
 
3282
}
 
3283
 
 
3284
nsresult
 
3285
nsSelection::FrameOrParentHasSpecialSelectionStyleBelowAncestor(nsIFrame* aFrame, PRUint8 aSelectionStyle,
 
3286
                                                                nsIFrame * aAncestorFrame,
 
3287
                                                                nsIFrame* *foundFrame)
 
3288
{
 
3289
  nsIFrame* thisFrame = aFrame;
 
3290
  
 
3291
  while (thisFrame && thisFrame != aAncestorFrame)
 
3292
  {
 
3293
    PRUint8 frameUserSelect = thisFrame->GetStyleUIReset()->mUserSelect;
 
3294
    if (frameUserSelect == aSelectionStyle)
 
3295
    {
 
3296
      *foundFrame = thisFrame;
 
3297
      return NS_OK;
 
3298
    }
 
3299
  
 
3300
    thisFrame = thisFrame->GetParent();
 
3301
  }
 
3302
  
 
3303
  *foundFrame = nsnull;
 
3304
  return NS_OK;
 
3305
}
 
3306
 
 
3307
// Start of Table Selection methods
 
3308
 
 
3309
static PRBool IsCell(nsIContent *aContent)
 
3310
{
 
3311
  return ((aContent->Tag() == nsHTMLAtoms::td ||
 
3312
           aContent->Tag() == nsHTMLAtoms::th) &&
 
3313
          aContent->IsContentOfType(nsIContent::eHTML));
 
3314
}
 
3315
 
 
3316
nsITableCellLayout* 
 
3317
nsSelection::GetCellLayout(nsIContent *aCellContent)
 
3318
{
 
3319
  // Get frame for cell
 
3320
  nsIFrame *cellFrame = nsnull;
 
3321
  GetTracker()->GetPrimaryFrameFor(aCellContent, &cellFrame);
 
3322
  if (!cellFrame)
 
3323
    return nsnull;
 
3324
 
 
3325
  nsITableCellLayout *cellLayoutObject = nsnull;
 
3326
  CallQueryInterface(cellFrame, &cellLayoutObject);
 
3327
 
 
3328
  return cellLayoutObject;
 
3329
}
 
3330
 
 
3331
nsITableLayout* 
 
3332
nsSelection::GetTableLayout(nsIContent *aTableContent)
 
3333
{
 
3334
  // Get frame for table
 
3335
  nsIFrame *tableFrame = nsnull;
 
3336
  GetTracker()->GetPrimaryFrameFor(aTableContent, &tableFrame);
 
3337
  if (!tableFrame)
 
3338
    return nsnull;
 
3339
 
 
3340
  nsITableLayout *tableLayoutObject = nsnull;
 
3341
  CallQueryInterface(tableFrame, &tableLayoutObject);
 
3342
 
 
3343
  return tableLayoutObject;
 
3344
}
 
3345
 
 
3346
nsresult
 
3347
nsSelection::ClearNormalSelection()
 
3348
{
 
3349
  PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
 
3350
  return mDomSelections[index]->RemoveAllRanges();
 
3351
}
 
3352
 
 
3353
// Table selection support.
 
3354
// TODO: Separate table methods into a separate nsITableSelection interface
 
3355
nsresult
 
3356
nsSelection::HandleTableSelection(nsIContent *aParentContent, PRInt32 aContentOffset, PRInt32 aTarget, nsMouseEvent *aMouseEvent)
 
3357
{
 
3358
  NS_ENSURE_TRUE(aParentContent, NS_ERROR_NULL_POINTER);
 
3359
  NS_ENSURE_TRUE(aMouseEvent, NS_ERROR_NULL_POINTER);
 
3360
 
 
3361
  if (mMouseDownState && mDragSelectingCells && (aTarget & nsISelectionPrivate::TABLESELECTION_TABLE))
 
3362
  {
 
3363
    // We were selecting cells and user drags mouse in table border or inbetween cells,
 
3364
    //  just do nothing
 
3365
      return NS_OK;
 
3366
  }
 
3367
 
 
3368
  nsCOMPtr<nsIDOMNode> parentNode = do_QueryInterface(aParentContent);
 
3369
  if (!parentNode)
 
3370
    return NS_ERROR_FAILURE;
 
3371
 
 
3372
  nsresult result = NS_OK;
 
3373
 
 
3374
  nsIContent *childContent = aParentContent->GetChildAt(aContentOffset);
 
3375
  nsCOMPtr<nsIDOMNode> childNode = do_QueryInterface(childContent);
 
3376
  if (!childNode)
 
3377
    return NS_ERROR_FAILURE;
 
3378
 
 
3379
  // When doing table selection, always set the direction to next so
 
3380
  // we can be sure that anchorNode's offset always points to the
 
3381
  // selected cell
 
3382
  PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
 
3383
  mDomSelections[index]->SetDirection(eDirNext);
 
3384
 
 
3385
  // Stack-class to wrap all table selection changes in 
 
3386
  //  BeginBatchChanges() / EndBatchChanges()
 
3387
  nsSelectionBatcher selectionBatcher(mDomSelections[index]);
 
3388
 
 
3389
  PRInt32 startRowIndex, startColIndex, curRowIndex, curColIndex;
 
3390
  if (mMouseDownState && mDragSelectingCells)
 
3391
  {
 
3392
    // We are drag-selecting
 
3393
    if (aTarget != nsISelectionPrivate::TABLESELECTION_TABLE)
 
3394
    {
 
3395
      // If dragging in the same cell as last event, do nothing
 
3396
      if (mEndSelectedCell == childContent)
 
3397
        return NS_OK;
 
3398
 
 
3399
#ifdef DEBUG_TABLE_SELECTION
 
3400
printf(" mStartSelectedCell = %x, mEndSelectedCell = %x, childContent = %x \n", mStartSelectedCell, mEndSelectedCell, childContent);
 
3401
#endif
 
3402
      // aTarget can be any "cell mode",
 
3403
      //  so we can easily drag-select rows and columns 
 
3404
      // Once we are in row or column mode,
 
3405
      //  we can drift into any cell to stay in that mode
 
3406
      //  even if aTarget = TABLESELECTION_CELL
 
3407
 
 
3408
      if (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_ROW ||
 
3409
          mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_COLUMN)
 
3410
      {
 
3411
        if (mEndSelectedCell)
 
3412
        {
 
3413
          // Also check if cell is in same row/col
 
3414
          result = GetCellIndexes(mEndSelectedCell, startRowIndex, startColIndex);
 
3415
          if (NS_FAILED(result)) return result;
 
3416
          result = GetCellIndexes(childContent, curRowIndex, curColIndex);
 
3417
          if (NS_FAILED(result)) return result;
 
3418
        
 
3419
#ifdef DEBUG_TABLE_SELECTION
 
3420
printf(" curRowIndex = %d, startRowIndex = %d, curColIndex = %d, startColIndex = %d\n", curRowIndex, startRowIndex, curColIndex, startColIndex);
 
3421
#endif
 
3422
          if ((mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_ROW && startRowIndex == curRowIndex) ||
 
3423
              (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_COLUMN && startColIndex == curColIndex)) 
 
3424
            return NS_OK;
 
3425
        }
 
3426
#ifdef DEBUG_TABLE_SELECTION
 
3427
printf(" Dragged into a new column or row\n");
 
3428
#endif
 
3429
        // Continue dragging row or column selection
 
3430
        return SelectRowOrColumn(childContent, mSelectingTableCellMode);
 
3431
      }
 
3432
      else if (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_CELL)
 
3433
      {
 
3434
#ifdef DEBUG_TABLE_SELECTION
 
3435
printf("HandleTableSelection: Dragged into a new cell\n");
 
3436
#endif
 
3437
        // Trick for quick selection of rows and columns
 
3438
        // Hold down shift, then start selecting in one direction
 
3439
        // If next cell dragged into is in same row, select entire row,
 
3440
        //   if next cell is in same column, select entire column
 
3441
        if (mStartSelectedCell && aMouseEvent->isShift)
 
3442
        {
 
3443
          result = GetCellIndexes(mStartSelectedCell, startRowIndex, startColIndex);
 
3444
          if (NS_FAILED(result)) return result;
 
3445
          result = GetCellIndexes(childContent, curRowIndex, curColIndex);
 
3446
          if (NS_FAILED(result)) return result;
 
3447
          
 
3448
          if (startRowIndex == curRowIndex || 
 
3449
              startColIndex == curColIndex)
 
3450
          {
 
3451
            // Force new selection block
 
3452
            mStartSelectedCell = nsnull;
 
3453
            mDomSelections[index]->RemoveAllRanges();
 
3454
 
 
3455
            if (startRowIndex == curRowIndex)
 
3456
              mSelectingTableCellMode = nsISelectionPrivate::TABLESELECTION_ROW;
 
3457
            else
 
3458
              mSelectingTableCellMode = nsISelectionPrivate::TABLESELECTION_COLUMN;
 
3459
 
 
3460
            return SelectRowOrColumn(childContent, mSelectingTableCellMode);
 
3461
          }
 
3462
        }
 
3463
        
 
3464
        // Reselect block of cells to new end location
 
3465
        return SelectBlockOfCells(mStartSelectedCell, childContent);
 
3466
      }
 
3467
    }
 
3468
    // Do nothing if dragging in table, but outside a cell
 
3469
    return NS_OK;
 
3470
  }
 
3471
  else 
 
3472
  {
 
3473
    // Not dragging  -- mouse event is down or up
 
3474
    if (mMouseDownState)
 
3475
    {
 
3476
#ifdef DEBUG_TABLE_SELECTION
 
3477
printf("HandleTableSelection: Mouse down event\n");
 
3478
#endif
 
3479
      // Clear cell we stored in mouse-down
 
3480
      mUnselectCellOnMouseUp = nsnull;
 
3481
      
 
3482
      if (aTarget == nsISelectionPrivate::TABLESELECTION_CELL)
 
3483
      {
 
3484
        PRBool isSelected = PR_FALSE;
 
3485
 
 
3486
        // Check if we have other selected cells
 
3487
        nsCOMPtr<nsIDOMNode> previousCellNode;
 
3488
        GetFirstSelectedCellAndRange(getter_AddRefs(previousCellNode), nsnull);
 
3489
        if (previousCellNode)
 
3490
        {
 
3491
          // We have at least 1 other selected cell
 
3492
 
 
3493
          // Check if new cell is already selected
 
3494
          nsIFrame  *cellFrame = nsnull;
 
3495
          result = GetTracker()->GetPrimaryFrameFor(childContent, &cellFrame);
 
3496
          if (NS_FAILED(result)) return result;
 
3497
          if (!cellFrame) return NS_ERROR_NULL_POINTER;
 
3498
          result = cellFrame->GetSelected(&isSelected);
 
3499
          if (NS_FAILED(result)) return result;
 
3500
        }
 
3501
        else
 
3502
        {
 
3503
          // No cells selected -- remove non-cell selection
 
3504
          mDomSelections[index]->RemoveAllRanges();
 
3505
        }
 
3506
        mDragSelectingCells = PR_TRUE;    // Signal to start drag-cell-selection
 
3507
        mSelectingTableCellMode = aTarget;
 
3508
        // Set start for new drag-selection block (not appended)
 
3509
        mStartSelectedCell = childContent;
 
3510
        // The initial block end is same as the start
 
3511
        mEndSelectedCell = childContent;
 
3512
        
 
3513
        if (isSelected)
 
3514
        {
 
3515
          // Remember this cell to (possibly) unselect it on mouseup
 
3516
          mUnselectCellOnMouseUp = childContent;
 
3517
#ifdef DEBUG_TABLE_SELECTION
 
3518
printf("HandleTableSelection: Saving mUnselectCellOnMouseUp\n");
 
3519
#endif
 
3520
        }
 
3521
        else
 
3522
        {
 
3523
          // Select an unselected cell
 
3524
          // but first remove existing selection if not in same table
 
3525
          nsCOMPtr<nsIContent> previousCellContent = do_QueryInterface(previousCellNode);
 
3526
          if (previousCellContent && !IsInSameTable(previousCellContent, childContent, nsnull))
 
3527
          {
 
3528
            mDomSelections[index]->RemoveAllRanges();
 
3529
            // Reset selection mode that is cleared in RemoveAllRanges
 
3530
            mSelectingTableCellMode = aTarget;
 
3531
          }
 
3532
 
 
3533
          nsCOMPtr<nsIDOMElement> cellElement = do_QueryInterface(childContent);
 
3534
          return SelectCellElement(cellElement);
 
3535
        }
 
3536
 
 
3537
        return NS_OK;
 
3538
      }
 
3539
      else if (aTarget == nsISelectionPrivate::TABLESELECTION_TABLE)
 
3540
      {
 
3541
        //TODO: We currently select entire table when clicked between cells,
 
3542
        //  should we restrict to only around border?
 
3543
        //  *** How do we get location data for cell and click?
 
3544
        mDragSelectingCells = PR_FALSE;
 
3545
        mStartSelectedCell = nsnull;
 
3546
        mEndSelectedCell = nsnull;
 
3547
 
 
3548
        // Remove existing selection and select the table
 
3549
        mDomSelections[index]->RemoveAllRanges();
 
3550
        return CreateAndAddRange(parentNode, aContentOffset);
 
3551
      }
 
3552
      else if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW || aTarget == nsISelectionPrivate::TABLESELECTION_COLUMN)
 
3553
      {
 
3554
#ifdef DEBUG_TABLE_SELECTION
 
3555
printf("aTarget == %d\n", aTarget);
 
3556
#endif
 
3557
 
 
3558
        // Start drag-selecting mode so multiple rows/cols can be selected
 
3559
        // Note: Currently, nsFrame::GetDataForTableSelection
 
3560
        //       will never call us for row or column selection on mouse down
 
3561
        mDragSelectingCells = PR_TRUE;
 
3562
      
 
3563
        // Force new selection block
 
3564
        mStartSelectedCell = nsnull;
 
3565
        mDomSelections[index]->RemoveAllRanges();
 
3566
        // Always do this AFTER RemoveAllRanges
 
3567
        mSelectingTableCellMode = aTarget;
 
3568
        return SelectRowOrColumn(childContent, aTarget);
 
3569
      }
 
3570
    }
 
3571
    else
 
3572
    {
 
3573
#ifdef DEBUG_TABLE_SELECTION
 
3574
printf("HandleTableSelection: Mouse UP event. mDragSelectingCells=%d, mStartSelectedCell=%d\n", mDragSelectingCells, mStartSelectedCell);
 
3575
#endif
 
3576
      // First check if we are extending a block selection
 
3577
      PRInt32 rangeCount;
 
3578
      result = mDomSelections[index]->GetRangeCount(&rangeCount);
 
3579
      if (NS_FAILED(result)) 
 
3580
        return result;
 
3581
 
 
3582
      if (rangeCount > 0 && aMouseEvent->isShift && 
 
3583
          mAppendStartSelectedCell && mAppendStartSelectedCell != childContent)
 
3584
      {
 
3585
        // Shift key is down: append a block selection
 
3586
        mDragSelectingCells = PR_FALSE;
 
3587
        return SelectBlockOfCells(mAppendStartSelectedCell, childContent);
 
3588
      }
 
3589
 
 
3590
      if (mDragSelectingCells)
 
3591
        mAppendStartSelectedCell = mStartSelectedCell;
 
3592
        
 
3593
      mDragSelectingCells = PR_FALSE;
 
3594
      mStartSelectedCell = nsnull;
 
3595
      mEndSelectedCell = nsnull;
 
3596
 
 
3597
      // Any other mouseup actions require that Ctrl or Cmd key is pressed
 
3598
      //  else stop table selection mode
 
3599
      PRBool doMouseUpAction = PR_FALSE;
 
3600
#if defined(XP_MAC) || defined(XP_MACOSX)
 
3601
      doMouseUpAction = aMouseEvent->isMeta;
 
3602
#else
 
3603
      doMouseUpAction = aMouseEvent->isControl;
 
3604
#endif
 
3605
      if (!doMouseUpAction)
 
3606
      {
 
3607
#ifdef DEBUG_TABLE_SELECTION
 
3608
printf("HandleTableSelection: Ending cell selection on mouseup: mAppendStartSelectedCell=%d\n", mAppendStartSelectedCell);
 
3609
#endif
 
3610
        return NS_OK;
 
3611
      }
 
3612
      // Unselect a cell only if it wasn't
 
3613
      //  just selected on mousedown
 
3614
      if( childContent == mUnselectCellOnMouseUp)
 
3615
      {
 
3616
        // Scan ranges to find the cell to unselect (the selection range to remove)
 
3617
        nsCOMPtr<nsIDOMNode> previousCellParent;
 
3618
        nsCOMPtr<nsIDOMRange> range;
 
3619
        PRInt32 offset;
 
3620
#ifdef DEBUG_TABLE_SELECTION
 
3621
printf("HandleTableSelection: Unselecting mUnselectCellOnMouseUp; rangeCount=%d\n", rangeCount);
 
3622
#endif
 
3623
        for( PRInt32 i = 0; i < rangeCount; i++)
 
3624
        {
 
3625
          result = mDomSelections[index]->GetRangeAt(i, getter_AddRefs(range));
 
3626
          if (NS_FAILED(result)) return result;
 
3627
          if (!range) return NS_ERROR_NULL_POINTER;
 
3628
 
 
3629
          nsCOMPtr<nsIDOMNode> parent;
 
3630
          result = range->GetStartContainer(getter_AddRefs(parent));
 
3631
          if (NS_FAILED(result)) return result;
 
3632
          if (!parent) return NS_ERROR_NULL_POINTER;
 
3633
 
 
3634
          range->GetStartOffset(&offset);
 
3635
          // Be sure previous selection is a table cell
 
3636
          nsCOMPtr<nsIContent> parentContent = do_QueryInterface(parent);
 
3637
          nsCOMPtr<nsIContent> child = parentContent->GetChildAt(offset);
 
3638
          if (child && IsCell(child))
 
3639
            previousCellParent = parent;
 
3640
 
 
3641
          // We're done if we didn't find parent of a previously-selected cell
 
3642
          if (!previousCellParent) break;
 
3643
        
 
3644
          if (previousCellParent == parentNode && offset == aContentOffset)
 
3645
          {
 
3646
            // Cell is already selected
 
3647
            if (rangeCount == 1)
 
3648
            {
 
3649
#ifdef DEBUG_TABLE_SELECTION
 
3650
printf("HandleTableSelection: Unselecting single selected cell\n");
 
3651
#endif
 
3652
              // This was the only cell selected.
 
3653
              // Collapse to "normal" selection inside the cell
 
3654
              mStartSelectedCell = nsnull;
 
3655
              mEndSelectedCell = nsnull;
 
3656
              mAppendStartSelectedCell = nsnull;
 
3657
              //TODO: We need a "Collapse to just before deepest child" routine
 
3658
              // Even better, should we collapse to just after the LAST deepest child
 
3659
              //  (i.e., at the end of the cell's contents)?
 
3660
              return mDomSelections[index]->Collapse(childNode, 0);
 
3661
            }
 
3662
#ifdef DEBUG_TABLE_SELECTION
 
3663
printf("HandleTableSelection: Removing cell from multi-cell selection\n");
 
3664
#endif
 
3665
            // Unselecting the start of previous block 
 
3666
            // XXX What do we use now!
 
3667
            if (childContent == mAppendStartSelectedCell)
 
3668
               mAppendStartSelectedCell = nsnull;
 
3669
 
 
3670
            // Deselect cell by removing its range from selection
 
3671
            return mDomSelections[index]->RemoveRange(range);
 
3672
          }
 
3673
        }
 
3674
        mUnselectCellOnMouseUp = nsnull;
 
3675
      }
 
3676
    }
 
3677
  }
 
3678
  return result;
 
3679
}
 
3680
 
 
3681
nsresult
 
3682
nsSelection::SelectBlockOfCells(nsIContent *aStartCell, nsIContent *aEndCell)
 
3683
{
 
3684
  NS_ENSURE_TRUE(aStartCell, NS_ERROR_NULL_POINTER);
 
3685
  NS_ENSURE_TRUE(aEndCell, NS_ERROR_NULL_POINTER);
 
3686
  mEndSelectedCell = aEndCell;
 
3687
 
 
3688
  nsCOMPtr<nsIContent> startCell;
 
3689
  nsresult result = NS_OK;
 
3690
 
 
3691
  // If new end cell is in a different table, do nothing
 
3692
  nsCOMPtr<nsIContent> table;
 
3693
  if (!IsInSameTable(aStartCell, aEndCell, getter_AddRefs(table)))
 
3694
    return NS_OK;
 
3695
 
 
3696
  // Get starting and ending cells' location in the cellmap
 
3697
  PRInt32 startRowIndex, startColIndex, endRowIndex, endColIndex;
 
3698
  result = GetCellIndexes(aStartCell, startRowIndex, startColIndex);
 
3699
  if(NS_FAILED(result)) return result;
 
3700
  result = GetCellIndexes(aEndCell, endRowIndex, endColIndex);
 
3701
  if(NS_FAILED(result)) return result;
 
3702
 
 
3703
  // Get TableLayout interface to access cell data based on cellmap location
 
3704
  // frames are not ref counted, so don't use an nsCOMPtr
 
3705
  nsITableLayout *tableLayoutObject = GetTableLayout(table);
 
3706
  if (!tableLayoutObject) return NS_ERROR_FAILURE;
 
3707
 
 
3708
  PRInt32 curRowIndex, curColIndex;
 
3709
 
 
3710
  if (mDragSelectingCells)
 
3711
  {
 
3712
    // Drag selecting: remove selected cells outside of new block limits
 
3713
 
 
3714
    PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
 
3715
 
 
3716
    nsCOMPtr<nsIDOMNode> cellNode;
 
3717
    nsCOMPtr<nsIDOMRange> range;
 
3718
    result = GetFirstSelectedCellAndRange(getter_AddRefs(cellNode), getter_AddRefs(range));
 
3719
    if (NS_FAILED(result)) return result;
 
3720
 
 
3721
    PRInt32 minRowIndex = PR_MIN(startRowIndex, endRowIndex);
 
3722
    PRInt32 maxRowIndex = PR_MAX(startRowIndex, endRowIndex);
 
3723
    PRInt32 minColIndex = PR_MIN(startColIndex, endColIndex);
 
3724
    PRInt32 maxColIndex = PR_MAX(startColIndex, endColIndex);
 
3725
 
 
3726
    while (cellNode)
 
3727
    {
 
3728
      nsCOMPtr<nsIContent> childContent = do_QueryInterface(cellNode);
 
3729
      result = GetCellIndexes(childContent, curRowIndex, curColIndex);
 
3730
      if (NS_FAILED(result)) return result;
 
3731
 
 
3732
#ifdef DEBUG_TABLE_SELECTION
 
3733
if (!range)
 
3734
printf("SelectBlockOfCells -- range is null\n");
 
3735
#endif
 
3736
      if (range &&
 
3737
          (curRowIndex < minRowIndex || curRowIndex > maxRowIndex || 
 
3738
           curColIndex < minColIndex || curColIndex > maxColIndex))
 
3739
      {
 
3740
        mDomSelections[index]->RemoveRange(range);
 
3741
        // Since we've removed the range, decrement pointer to next range
 
3742
        mSelectedCellIndex--;
 
3743
      }    
 
3744
      result = GetNextSelectedCellAndRange(getter_AddRefs(cellNode), getter_AddRefs(range));
 
3745
      if (NS_FAILED(result)) return result;
 
3746
    }
 
3747
  }
 
3748
 
 
3749
  nsCOMPtr<nsIDOMElement> cellElement;
 
3750
  PRInt32 rowSpan, colSpan, actualRowSpan, actualColSpan;
 
3751
  PRBool  isSelected;
 
3752
 
 
3753
  // Note that we select block in the direction of user's mouse dragging,
 
3754
  //  which means start cell may be after the end cell in either row or column
 
3755
  PRInt32 row = startRowIndex;
 
3756
  while(PR_TRUE)
 
3757
  {
 
3758
    PRInt32 col = startColIndex;
 
3759
    while(PR_TRUE)
 
3760
    {
 
3761
      result = tableLayoutObject->GetCellDataAt(row, col, *getter_AddRefs(cellElement),
 
3762
                                                curRowIndex, curColIndex, rowSpan, colSpan, 
 
3763
                                                actualRowSpan, actualColSpan, isSelected);
 
3764
      if (NS_FAILED(result)) return result;
 
3765
 
 
3766
      NS_ASSERTION(actualColSpan, "!actualColSpan is 0!");
 
3767
 
 
3768
      // Skip cells that are spanned from previous locations or are already selected
 
3769
      if (!isSelected && cellElement && row == curRowIndex && col == curColIndex)
 
3770
      {
 
3771
        result = SelectCellElement(cellElement);
 
3772
        if (NS_FAILED(result)) return result;
 
3773
      }
 
3774
      // Done when we reach end column
 
3775
      if (col == endColIndex) break;
 
3776
 
 
3777
      if (startColIndex < endColIndex)
 
3778
        col ++;
 
3779
      else
 
3780
        col--;
 
3781
    };
 
3782
    if (row == endRowIndex) break;
 
3783
 
 
3784
    if (startRowIndex < endRowIndex)
 
3785
      row++;
 
3786
    else
 
3787
      row--;
 
3788
  };
 
3789
  return result;
 
3790
}
 
3791
 
 
3792
nsresult
 
3793
nsSelection::SelectRowOrColumn(nsIContent *aCellContent, PRUint32 aTarget)
 
3794
{
 
3795
  if (!aCellContent) return NS_ERROR_NULL_POINTER;
 
3796
 
 
3797
  nsCOMPtr<nsIContent> table;
 
3798
  nsresult result = GetParentTable(aCellContent, getter_AddRefs(table));
 
3799
  if (NS_FAILED(result)) return PR_FALSE;
 
3800
  if (!table) return NS_ERROR_NULL_POINTER;
 
3801
 
 
3802
  // Get table and cell layout interfaces to access 
 
3803
  //   cell data based on cellmap location
 
3804
  // Frames are not ref counted, so don't use an nsCOMPtr
 
3805
  nsITableLayout *tableLayout = GetTableLayout(table);
 
3806
  if (!tableLayout) return NS_ERROR_FAILURE;
 
3807
  nsITableCellLayout *cellLayout = GetCellLayout(aCellContent);
 
3808
  if (!cellLayout) return NS_ERROR_FAILURE;
 
3809
 
 
3810
  // Get location of target cell:      
 
3811
  PRInt32 rowIndex, colIndex, curRowIndex, curColIndex;
 
3812
  result = cellLayout->GetCellIndexes(rowIndex, colIndex);
 
3813
  if (NS_FAILED(result)) return result;
 
3814
 
 
3815
  // Be sure we start at proper beginning
 
3816
  // (This allows us to select row or col given ANY cell!)
 
3817
  if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW)
 
3818
    colIndex = 0;
 
3819
  if (aTarget == nsISelectionPrivate::TABLESELECTION_COLUMN)
 
3820
    rowIndex = 0;
 
3821
 
 
3822
  nsCOMPtr<nsIDOMElement> cellElement;
 
3823
  nsCOMPtr<nsIDOMElement> firstCell;
 
3824
  nsCOMPtr<nsIDOMElement> lastCell;
 
3825
  PRInt32 rowSpan, colSpan, actualRowSpan, actualColSpan;
 
3826
  PRBool isSelected;
 
3827
 
 
3828
  do {
 
3829
    // Loop through all cells in column or row to find first and last
 
3830
    result = tableLayout->GetCellDataAt(rowIndex, colIndex, *getter_AddRefs(cellElement),
 
3831
                                        curRowIndex, curColIndex, rowSpan, colSpan, 
 
3832
                                        actualRowSpan, actualColSpan, isSelected);
 
3833
    if (NS_FAILED(result)) return result;
 
3834
    if (cellElement)
 
3835
    {
 
3836
      NS_ASSERTION(actualRowSpan > 0 && actualColSpan> 0, "SelectRowOrColumn: Bad rowspan or colspan\n");
 
3837
      if (!firstCell)
 
3838
        firstCell = cellElement;
 
3839
 
 
3840
      lastCell = cellElement;
 
3841
 
 
3842
      // Move to next cell in cellmap, skipping spanned locations
 
3843
      if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW)
 
3844
        colIndex += actualColSpan;
 
3845
      else
 
3846
        rowIndex += actualRowSpan;
 
3847
    }
 
3848
  }
 
3849
  while (cellElement);
 
3850
 
 
3851
  // Use SelectBlockOfCells:
 
3852
  // This will replace existing selection,
 
3853
  //  but allow unselecting by dragging out of selected region
 
3854
  if (firstCell && lastCell)
 
3855
  {
 
3856
    if (!mStartSelectedCell)
 
3857
    {
 
3858
      // We are starting a new block, so select the first cell
 
3859
      result = SelectCellElement(firstCell);
 
3860
      if (NS_FAILED(result)) return result;
 
3861
      mStartSelectedCell = do_QueryInterface(firstCell);
 
3862
    }
 
3863
    nsCOMPtr<nsIContent> lastCellContent = do_QueryInterface(lastCell);
 
3864
    result = SelectBlockOfCells(mStartSelectedCell, lastCellContent);
 
3865
 
 
3866
    // This gets set to the cell at end of row/col, 
 
3867
    //   but we need it to be the cell under cursor
 
3868
    mEndSelectedCell = aCellContent;
 
3869
    return result;
 
3870
  }
 
3871
 
 
3872
#if 0
 
3873
// This is a more efficient strategy that appends row to current selection,
 
3874
//  but doesn't allow dragging OFF of an existing selection to unselect!
 
3875
  do {
 
3876
    // Loop through all cells in column or row
 
3877
    result = tableLayout->GetCellDataAt(rowIndex, colIndex,
 
3878
                                        getter_AddRefs(cellElement),
 
3879
                                        curRowIndex, curColIndex,
 
3880
                                        rowSpan, colSpan,
 
3881
                                        actualRowSpan, actualColSpan,
 
3882
                                        isSelected);
 
3883
    if (NS_FAILED(result)) return result;
 
3884
    // We're done when cell is not found
 
3885
    if (!cellElement) break;
 
3886
 
 
3887
 
 
3888
    // Check spans else we infinitely loop
 
3889
    NS_ASSERTION(actualColSpan, "actualColSpan is 0!");
 
3890
    NS_ASSERTION(actualRowSpan, "actualRowSpan is 0!");
 
3891
    
 
3892
    // Skip cells that are already selected or span from outside our region
 
3893
    if (!isSelected && rowIndex == curRowIndex && colIndex == curColIndex)
 
3894
    {
 
3895
      result = SelectCellElement(cellElement);
 
3896
      if (NS_FAILED(result)) return result;
 
3897
    }
 
3898
    // Move to next row or column in cellmap, skipping spanned locations
 
3899
    if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW)
 
3900
      colIndex += actualColSpan;
 
3901
    else
 
3902
      rowIndex += actualRowSpan;
 
3903
  }
 
3904
  while (cellElement);
 
3905
#endif
 
3906
 
 
3907
  return NS_OK;
 
3908
}
 
3909
 
 
3910
nsresult 
 
3911
nsSelection::GetFirstCellNodeInRange(nsIDOMRange *aRange, nsIDOMNode **aCellNode)
 
3912
{
 
3913
  if (!aRange || !aCellNode) return NS_ERROR_NULL_POINTER;
 
3914
 
 
3915
  *aCellNode = nsnull;
 
3916
 
 
3917
  nsCOMPtr<nsIDOMNode> startParent;
 
3918
  nsresult result = aRange->GetStartContainer(getter_AddRefs(startParent));
 
3919
  if (NS_FAILED(result))
 
3920
    return result;
 
3921
  if (!startParent)
 
3922
    return NS_ERROR_FAILURE;
 
3923
 
 
3924
  PRInt32 offset;
 
3925
  result = aRange->GetStartOffset(&offset);
 
3926
  if (NS_FAILED(result))
 
3927
    return result;
 
3928
 
 
3929
  nsCOMPtr<nsIContent> parentContent = do_QueryInterface(startParent);
 
3930
  nsCOMPtr<nsIContent> childContent = parentContent->GetChildAt(offset);
 
3931
  if (!childContent)
 
3932
    return NS_ERROR_NULL_POINTER;
 
3933
  // Don't return node if not a cell
 
3934
  if (!IsCell(childContent)) return NS_OK;
 
3935
 
 
3936
  nsCOMPtr<nsIDOMNode> childNode = do_QueryInterface(childContent);
 
3937
  if (childNode)
 
3938
  {
 
3939
    *aCellNode = childNode;
 
3940
    NS_ADDREF(*aCellNode);
 
3941
  }
 
3942
  return NS_OK;
 
3943
}
 
3944
 
 
3945
nsresult 
 
3946
nsSelection::GetFirstSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRange)
 
3947
{
 
3948
  if (!aCell) return NS_ERROR_NULL_POINTER;
 
3949
  *aCell = nsnull;
 
3950
 
 
3951
  // aRange is optional
 
3952
  if (aRange)
 
3953
    *aRange = nsnull;
 
3954
 
 
3955
  nsCOMPtr<nsIDOMRange> firstRange;
 
3956
  PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
 
3957
  nsresult result = mDomSelections[index]->GetRangeAt(0, getter_AddRefs(firstRange));
 
3958
  if (NS_FAILED(result)) return result;
 
3959
  if (!firstRange) return NS_ERROR_FAILURE;
 
3960
 
 
3961
  nsCOMPtr<nsIDOMNode> cellNode;
 
3962
  result = GetFirstCellNodeInRange(firstRange, getter_AddRefs(cellNode));
 
3963
  if (NS_FAILED(result)) return result;
 
3964
  if (!cellNode) return NS_OK;
 
3965
 
 
3966
  *aCell = cellNode;
 
3967
  NS_ADDREF(*aCell);
 
3968
  if (aRange)
 
3969
  {
 
3970
    *aRange = firstRange;
 
3971
    NS_ADDREF(*aRange);
 
3972
  }
 
3973
 
 
3974
  // Setup for next cell
 
3975
  mSelectedCellIndex = 1;
 
3976
 
 
3977
  return NS_OK;
 
3978
}
 
3979
 
 
3980
nsresult
 
3981
nsSelection::GetNextSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRange)
 
3982
{
 
3983
  if (!aCell) return NS_ERROR_NULL_POINTER;
 
3984
  *aCell = nsnull;
 
3985
 
 
3986
  // aRange is optional
 
3987
  if (aRange)
 
3988
    *aRange = nsnull;
 
3989
 
 
3990
  PRInt32 rangeCount;
 
3991
  PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
 
3992
  nsresult result = mDomSelections[index]->GetRangeCount(&rangeCount);
 
3993
  if (NS_FAILED(result)) return result;
 
3994
 
 
3995
  // Don't even try if index exceeds range count
 
3996
  if (mSelectedCellIndex >= rangeCount) 
 
3997
  {
 
3998
    // Should we reset index? 
 
3999
    // Maybe better to force recalling GetFirstSelectedCell()
 
4000
    //mSelectedCellIndex = 0;
 
4001
    return NS_OK;
 
4002
  }
 
4003
 
 
4004
  // Get first node in next range of selection - test if it's a cell
 
4005
  nsCOMPtr<nsIDOMRange> range;
 
4006
  result = mDomSelections[index]->GetRangeAt(mSelectedCellIndex, getter_AddRefs(range));
 
4007
  if (NS_FAILED(result)) return result;
 
4008
  if (!range) return NS_ERROR_FAILURE;
 
4009
 
 
4010
  nsCOMPtr<nsIDOMNode> cellNode;
 
4011
  result = GetFirstCellNodeInRange(range, getter_AddRefs(cellNode));
 
4012
  if (NS_FAILED(result)) return result;
 
4013
  // No cell in selection range
 
4014
  if (!cellNode) return NS_OK;
 
4015
 
 
4016
  *aCell = cellNode;
 
4017
  NS_ADDREF(*aCell);
 
4018
  if (aRange)
 
4019
  {
 
4020
    *aRange = range;
 
4021
    NS_ADDREF(*aRange);
 
4022
  }
 
4023
 
 
4024
  // Setup for next cell
 
4025
  mSelectedCellIndex++;
 
4026
 
 
4027
  return NS_OK;
 
4028
}
 
4029
 
 
4030
nsresult
 
4031
nsSelection::GetCellIndexes(nsIContent *aCell, PRInt32 &aRowIndex, PRInt32 &aColIndex)
 
4032
{
 
4033
  if (!aCell) return NS_ERROR_NULL_POINTER;
 
4034
 
 
4035
  aColIndex=0; // initialize out params
 
4036
  aRowIndex=0;
 
4037
 
 
4038
  nsITableCellLayout *cellLayoutObject = GetCellLayout(aCell);
 
4039
  if (!cellLayoutObject)  return NS_ERROR_FAILURE;
 
4040
  return cellLayoutObject->GetCellIndexes(aRowIndex, aColIndex);
 
4041
}
 
4042
 
 
4043
PRBool 
 
4044
nsSelection::IsInSameTable(nsIContent *aContent1, nsIContent *aContent2, nsIContent **aTable)
 
4045
{
 
4046
  if (!aContent1 || !aContent2) return PR_FALSE;
 
4047
  
 
4048
  // aTable is optional:
 
4049
  if(aTable) *aTable = nsnull;
 
4050
  
 
4051
  nsCOMPtr<nsIContent> tableNode1;
 
4052
  nsCOMPtr<nsIContent> tableNode2;
 
4053
 
 
4054
  nsresult result = GetParentTable(aContent1, getter_AddRefs(tableNode1));
 
4055
  if (NS_FAILED(result)) return PR_FALSE;
 
4056
  result = GetParentTable(aContent2, getter_AddRefs(tableNode2));
 
4057
  if (NS_FAILED(result)) return PR_FALSE;
 
4058
 
 
4059
  // Must be in the same table
 
4060
  if (tableNode1 && (tableNode1 == tableNode2))
 
4061
  {
 
4062
    if (aTable)
 
4063
    {
 
4064
      *aTable = tableNode1;
 
4065
      NS_ADDREF(*aTable);
 
4066
    }
 
4067
    return PR_TRUE;;
 
4068
  }
 
4069
  return PR_FALSE;
 
4070
}
 
4071
 
 
4072
nsresult
 
4073
nsSelection::GetParentTable(nsIContent *aCell, nsIContent **aTable)
 
4074
{
 
4075
  if (!aCell || !aTable) {
 
4076
    return NS_ERROR_NULL_POINTER;
 
4077
  }
 
4078
 
 
4079
  for (nsIContent* parent = aCell->GetParent(); parent;
 
4080
       parent = parent->GetParent()) {
 
4081
    if (parent->Tag() == nsHTMLAtoms::table &&
 
4082
        parent->IsContentOfType(nsIContent::eHTML)) {
 
4083
      *aTable = parent;
 
4084
      NS_ADDREF(*aTable);
 
4085
 
 
4086
      return NS_OK;
 
4087
    }
 
4088
  }
 
4089
 
 
4090
  return NS_OK;
 
4091
}
 
4092
 
 
4093
nsresult
 
4094
nsSelection::SelectCellElement(nsIDOMElement *aCellElement)
 
4095
{
 
4096
  nsCOMPtr<nsIContent> cellContent = do_QueryInterface(aCellElement);
 
4097
 
 
4098
  if (!cellContent) {
 
4099
    return NS_ERROR_FAILURE;
 
4100
  }
 
4101
 
 
4102
  nsIContent *parent = cellContent->GetParent();
 
4103
  nsCOMPtr<nsIDOMNode> parentNode(do_QueryInterface(parent));
 
4104
  if (!parentNode) {
 
4105
    return NS_ERROR_FAILURE;
 
4106
  }
 
4107
 
 
4108
  // Get child offset
 
4109
  PRInt32 offset = parent->IndexOf(cellContent);
 
4110
 
 
4111
  return CreateAndAddRange(parentNode, offset);
 
4112
}
 
4113
 
 
4114
nsresult
 
4115
nsTypedSelection::getTableCellLocationFromRange(nsIDOMRange *aRange, PRInt32 *aSelectionType, PRInt32 *aRow, PRInt32 *aCol)
 
4116
{
 
4117
  if (!aRange || !aSelectionType || !aRow || !aCol)
 
4118
    return NS_ERROR_NULL_POINTER;
 
4119
 
 
4120
  *aSelectionType = nsISelectionPrivate::TABLESELECTION_NONE;
 
4121
  *aRow = 0;
 
4122
  *aCol = 0;
 
4123
 
 
4124
  // Must have access to frame selection to get cell info
 
4125
  if (!mFrameSelection) return NS_OK;
 
4126
 
 
4127
  nsresult result = GetTableSelectionType(aRange, aSelectionType);
 
4128
  if (NS_FAILED(result)) return result;
 
4129
  
 
4130
  // Don't fail if range does not point to a single table cell,
 
4131
  //  let aSelectionType tell user if we don't have a cell
 
4132
  if (*aSelectionType  != nsISelectionPrivate::TABLESELECTION_CELL)
 
4133
    return NS_OK;
 
4134
 
 
4135
  // Get the child content (the cell) pointed to by starting node of range
 
4136
  // We do minimal checking since GetTableSelectionType assures
 
4137
  //   us that this really is a table cell
 
4138
  nsCOMPtr<nsIDOMNode> startNode;
 
4139
  result = aRange->GetStartContainer(getter_AddRefs(startNode));
 
4140
  if (NS_FAILED(result))
 
4141
    return result;
 
4142
 
 
4143
  nsCOMPtr<nsIContent> content(do_QueryInterface(startNode));
 
4144
  if (!content)
 
4145
    return NS_ERROR_FAILURE;
 
4146
  PRInt32 startOffset;
 
4147
  result = aRange->GetStartOffset(&startOffset);
 
4148
  if (NS_FAILED(result))
 
4149
    return result;
 
4150
 
 
4151
  nsIContent *child = content->GetChildAt(startOffset);
 
4152
  if (!child)
 
4153
    return NS_ERROR_FAILURE;
 
4154
 
 
4155
  //Note: This is a non-ref-counted pointer to the frame
 
4156
  nsITableCellLayout *cellLayout = mFrameSelection->GetCellLayout(child);
 
4157
  if (NS_FAILED(result))
 
4158
    return result;
 
4159
  if (!cellLayout)
 
4160
    return NS_ERROR_FAILURE;
 
4161
 
 
4162
  return cellLayout->GetCellIndexes(*aRow, *aCol);
 
4163
}
 
4164
 
 
4165
nsresult
 
4166
nsTypedSelection::addTableCellRange(nsIDOMRange *aRange, PRBool *aDidAddRange)
 
4167
{
 
4168
  if (!aDidAddRange)
 
4169
    return NS_ERROR_NULL_POINTER;
 
4170
 
 
4171
  *aDidAddRange = PR_FALSE;
 
4172
 
 
4173
  if (!mFrameSelection)
 
4174
    return NS_OK;
 
4175
 
 
4176
  if (!aRange)
 
4177
    return NS_ERROR_NULL_POINTER;
 
4178
 
 
4179
  nsresult result;
 
4180
 
 
4181
  // Get if we are adding a cell selection and the row, col of cell if we are
 
4182
  PRInt32 newRow, newCol, tableMode;
 
4183
  result = getTableCellLocationFromRange(aRange, &tableMode, &newRow, &newCol);
 
4184
  if (NS_FAILED(result)) return result;
 
4185
  
 
4186
  // If not adding a cell range, we are done here
 
4187
  if (tableMode != nsISelectionPrivate::TABLESELECTION_CELL)
 
4188
  {
 
4189
    mFrameSelection->mSelectingTableCellMode = tableMode;
 
4190
    // Don't fail if range isn't a selected cell, aDidAddRange tells caller if we didn't proceed
 
4191
    return NS_OK;
 
4192
  }
 
4193
  
 
4194
  // Set frame selection mode only if not already set to a table mode
 
4195
  //  so we don't loose the select row and column flags (not detected by getTableCellLocation)
 
4196
  if (mFrameSelection->mSelectingTableCellMode == TABLESELECTION_NONE)
 
4197
    mFrameSelection->mSelectingTableCellMode = tableMode;
 
4198
 
 
4199
  PRInt32 count = mRangeArray.Count();
 
4200
 
 
4201
  if (count > 0)
 
4202
  {
 
4203
    // Adding a cell range to existing list of cell ranges
 
4204
    PRInt32 index;
 
4205
    PRInt32 row, col;
 
4206
    // Insert range at appropriate location
 
4207
    for (index = 0; index < count; index++)
 
4208
    {
 
4209
      nsIDOMRange* range = mRangeArray[index];
 
4210
      if (!range) return NS_ERROR_FAILURE;
 
4211
 
 
4212
      PRInt32 selectionMode;
 
4213
      result = getTableCellLocationFromRange(range, &selectionMode, &row, &col);
 
4214
      if (NS_FAILED(result)) return result;
 
4215
 
 
4216
      // Don't proceed if range not a table cell
 
4217
      if (selectionMode != nsISelectionPrivate::TABLESELECTION_CELL)
 
4218
        return NS_OK;
 
4219
 
 
4220
      if (row > newRow ||
 
4221
          (row == newRow && col > newCol))
 
4222
      {
 
4223
        // Existing selected cell is after cell to add,
 
4224
        //  so insert at this index
 
4225
        *aDidAddRange = mRangeArray.InsertObjectAt(aRange, index);
 
4226
        return (*aDidAddRange) ? NS_OK : NS_ERROR_FAILURE;
 
4227
      }
 
4228
    }          
 
4229
  }
 
4230
  // If here, we are adding a selected cell range 
 
4231
  //   to end of range array or it's the first selected range 
 
4232
  *aDidAddRange = mRangeArray.AppendObject(aRange);
 
4233
  return (*aDidAddRange) ? NS_OK : NS_ERROR_FAILURE;
 
4234
}
 
4235
 
 
4236
//TODO: Figure out TABLESELECTION_COLUMN and TABLESELECTION_ALLCELLS
 
4237
NS_IMETHODIMP
 
4238
nsTypedSelection::GetTableSelectionType(nsIDOMRange* aRange, PRInt32* aTableSelectionType)
 
4239
{
 
4240
  if (!aRange || !aTableSelectionType)
 
4241
    return NS_ERROR_NULL_POINTER;
 
4242
  
 
4243
  *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_NONE;
 
4244
 
 
4245
  // Must have access to frame selection to get cell info
 
4246
  if(!mFrameSelection) return NS_OK;
 
4247
 
 
4248
  nsCOMPtr<nsIDOMNode> startNode;
 
4249
  nsresult result = aRange->GetStartContainer(getter_AddRefs(startNode));
 
4250
  if (NS_FAILED(result)) return result;
 
4251
  if (!startNode) return NS_ERROR_FAILURE;
 
4252
  
 
4253
  nsCOMPtr<nsIDOMNode> endNode;
 
4254
  result = aRange->GetEndContainer(getter_AddRefs(endNode));
 
4255
  if (NS_FAILED(result)) return result;
 
4256
  if (!endNode) return NS_ERROR_FAILURE;
 
4257
 
 
4258
  // Not a single selected node
 
4259
  if (startNode != endNode) return NS_OK;
 
4260
 
 
4261
  nsCOMPtr<nsIContent> content = do_QueryInterface(startNode);
 
4262
  if (!content) return NS_ERROR_FAILURE;
 
4263
 
 
4264
  // if we simply cannot have children, return NS_OK as a non-failing,
 
4265
  // non-completing case for table selection
 
4266
  if (!content->IsContentOfType(nsIContent::eELEMENT))
 
4267
    return NS_OK; //got to be a text node, definately not a table row/cell
 
4268
  
 
4269
  PRInt32 startOffset;
 
4270
  PRInt32 endOffset;
 
4271
  result = aRange->GetEndOffset(&endOffset);
 
4272
  if (NS_FAILED(result)) return result;
 
4273
  result = aRange->GetStartOffset(&startOffset);
 
4274
  if (NS_FAILED(result)) return result;
 
4275
 
 
4276
  // Not a single selected node
 
4277
  if ((endOffset - startOffset) != 1)
 
4278
    return NS_OK;
 
4279
 
 
4280
  if (!content->IsContentOfType(nsIContent::eHTML)) {
 
4281
    return NS_OK;
 
4282
  }
 
4283
 
 
4284
  nsIAtom *tag = content->Tag();
 
4285
 
 
4286
  if (tag == nsHTMLAtoms::tr)
 
4287
  {
 
4288
    *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_CELL;
 
4289
  }
 
4290
  else //check to see if we are selecting a table or row (column and all cells not done yet)
 
4291
  {
 
4292
    nsIContent *child = content->GetChildAt(startOffset);
 
4293
    if (!child)
 
4294
      return NS_ERROR_FAILURE;
 
4295
 
 
4296
    tag = child->Tag();
 
4297
 
 
4298
    if (tag == nsHTMLAtoms::table)
 
4299
      *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_TABLE;
 
4300
    else if (tag == nsHTMLAtoms::tr)
 
4301
      *aTableSelectionType = nsISelectionPrivate::TABLESELECTION_ROW;
 
4302
  }
 
4303
 
 
4304
  return result;
 
4305
}
 
4306
 
 
4307
nsresult
 
4308
nsSelection::CreateAndAddRange(nsIDOMNode *aParentNode, PRInt32 aOffset)
 
4309
{
 
4310
  if (!aParentNode) return NS_ERROR_NULL_POINTER;
 
4311
  nsCOMPtr<nsIDOMRange> range;
 
4312
  NS_NewRange(getter_AddRefs(range));
 
4313
  if (!range) return NS_ERROR_OUT_OF_MEMORY;
 
4314
 
 
4315
  // Set range around child at given offset
 
4316
  nsresult result = range->SetStart(aParentNode, aOffset);
 
4317
  if (NS_FAILED(result)) return result;
 
4318
  result = range->SetEnd(aParentNode, aOffset+1);
 
4319
  if (NS_FAILED(result)) return result;
 
4320
  
 
4321
  PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
 
4322
  return mDomSelections[index]->AddRange(range);
 
4323
}
 
4324
 
 
4325
// End of Table Selection
 
4326
 
 
4327
NS_IMETHODIMP
 
4328
nsSelection::AdjustOffsetsFromStyle(nsIFrame *aFrame, PRBool *changeSelection,
 
4329
      nsIContent** outContent, PRInt32* outStartOffset, PRInt32* outEndOffset)
 
4330
{
 
4331
  
 
4332
  *changeSelection = PR_FALSE;
 
4333
  *outContent = nsnull;
 
4334
  
 
4335
  nsresult  rv;  
 
4336
  nsIFrame*   selectAllFrame;
 
4337
  rv = FrameOrParentHasSpecialSelectionStyle(aFrame, NS_STYLE_USER_SELECT_ALL, &selectAllFrame);
 
4338
  if (NS_FAILED(rv)) return rv;
 
4339
  
 
4340
  if (!selectAllFrame)
 
4341
    return NS_OK;
 
4342
  
 
4343
  nsIContent* selectAllContent = selectAllFrame->GetContent();
 
4344
  if (selectAllContent)
 
4345
  {
 
4346
    nsCOMPtr<nsIContent> parentContent = selectAllContent->GetParent();
 
4347
    if (parentContent)
 
4348
    {
 
4349
      PRInt32 startOffset = parentContent->IndexOf(selectAllContent);
 
4350
 
 
4351
      if (startOffset < 0)
 
4352
      {
 
4353
        // hrmm, this is probably anonymous content. Let's go up another level
 
4354
        // do we need to do this if we get the right frameSelection to start with?
 
4355
        nsCOMPtr<nsIContent> superParent = parentContent->GetParent();
 
4356
        if (superParent)
 
4357
        {
 
4358
          PRInt32 superStartOffset = superParent->IndexOf(parentContent);
 
4359
          if (superStartOffset < 0)
 
4360
            return NS_ERROR_FAILURE;    // give up
 
4361
        
 
4362
          parentContent = superParent;
 
4363
          startOffset = superStartOffset;
 
4364
        }
 
4365
      }
 
4366
      
 
4367
      NS_IF_ADDREF(*outContent = parentContent);
 
4368
 
 
4369
      *outStartOffset = startOffset;
 
4370
      *outEndOffset = startOffset + 1;
 
4371
 
 
4372
      *changeSelection = PR_TRUE;
 
4373
    }    
 
4374
  }
 
4375
 
 
4376
  return NS_OK;
 
4377
}
 
4378
 
 
4379
 
 
4380
NS_IMETHODIMP
 
4381
nsSelection::SetHint(HINT aHintRight)
 
4382
{
 
4383
  mHint = aHintRight;
 
4384
  return NS_OK;
 
4385
}
 
4386
 
 
4387
NS_IMETHODIMP
 
4388
nsSelection::GetHint(HINT *aHintRight)
 
4389
{
 
4390
  *aHintRight = mHint;
 
4391
  return NS_OK; 
 
4392
}
 
4393
 
 
4394
 
 
4395
//END nsIFrameSelection methods
 
4396
 
 
4397
 
 
4398
#ifdef XP_MAC
 
4399
#pragma mark -
 
4400
#endif
 
4401
 
 
4402
//BEGIN nsISelection interface implementations
 
4403
 
 
4404
 
 
4405
 
 
4406
NS_IMETHODIMP
 
4407
nsSelection::DeleteFromDocument()
 
4408
{
 
4409
  nsresult res;
 
4410
 
 
4411
  // If we're already collapsed, then set ourselves to include the
 
4412
  // last item BEFORE the current range, rather than the range itself,
 
4413
  // before we do the delete.
 
4414
  PRBool isCollapsed;
 
4415
  PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
 
4416
  mDomSelections[index]->GetIsCollapsed( &isCollapsed);
 
4417
  if (isCollapsed)
 
4418
  {
 
4419
    // If the offset is positive, then it's easy:
 
4420
    if (mDomSelections[index]->FetchFocusOffset() > 0)
 
4421
    {
 
4422
      mDomSelections[index]->Extend(mDomSelections[index]->FetchFocusNode(), mDomSelections[index]->FetchFocusOffset() - 1);
 
4423
    }
 
4424
    else
 
4425
    {
 
4426
      // Otherwise it's harder, have to find the previous node
 
4427
      printf("Sorry, don't know how to delete across frame boundaries yet\n");
 
4428
      return NS_ERROR_NOT_IMPLEMENTED;
 
4429
    }
 
4430
  }
 
4431
 
 
4432
  // Get an iterator
 
4433
  nsSelectionIterator iter(mDomSelections[index]);
 
4434
  res = iter.First();
 
4435
  if (NS_FAILED(res))
 
4436
    return res;
 
4437
 
 
4438
  nsCOMPtr<nsIDOMRange> range;
 
4439
  while (iter.IsDone())
 
4440
  {
 
4441
    res = iter.CurrentItem(NS_STATIC_CAST(nsIDOMRange**, getter_AddRefs(range)));
 
4442
    if (NS_FAILED(res))
 
4443
      return res;
 
4444
    res = range->DeleteContents();
 
4445
    if (NS_FAILED(res))
 
4446
      return res;
 
4447
    iter.Next();
 
4448
  }
 
4449
 
 
4450
  // Collapse to the new location.
 
4451
  // If we deleted one character, then we move back one element.
 
4452
  // FIXME  We don't know how to do this past frame boundaries yet.
 
4453
  if (isCollapsed)
 
4454
    mDomSelections[index]->Collapse(mDomSelections[index]->FetchAnchorNode(), mDomSelections[index]->FetchAnchorOffset()-1);
 
4455
  else if (mDomSelections[index]->FetchAnchorOffset() > 0)
 
4456
    mDomSelections[index]->Collapse(mDomSelections[index]->FetchAnchorNode(), mDomSelections[index]->FetchAnchorOffset());
 
4457
#ifdef DEBUG
 
4458
  else
 
4459
    printf("Don't know how to set selection back past frame boundary\n");
 
4460
#endif
 
4461
 
 
4462
  return NS_OK;
 
4463
}
 
4464
 
 
4465
 
 
4466
NS_IMETHODIMP
 
4467
nsSelection::SetDisplaySelection(PRInt16 aToggle)
 
4468
{
 
4469
  mDisplaySelection = aToggle;
 
4470
  return NS_OK;
 
4471
}
 
4472
 
 
4473
NS_IMETHODIMP
 
4474
nsSelection::GetDisplaySelection(PRInt16 *aToggle)
 
4475
{
 
4476
  if (!aToggle)
 
4477
    return NS_ERROR_INVALID_ARG;
 
4478
  *aToggle = mDisplaySelection;
 
4479
  return NS_OK;
 
4480
}
 
4481
 
 
4482
NS_IMETHODIMP
 
4483
nsSelection::SetDelayCaretOverExistingSelection(PRBool aDelay)
 
4484
{
 
4485
  mDelayCaretOverExistingSelection = aDelay;
 
4486
  
 
4487
  if (! aDelay)
 
4488
    mDelayedMouseEventValid = PR_FALSE;
 
4489
 
 
4490
  return NS_OK;
 
4491
}
 
4492
 
 
4493
NS_IMETHODIMP
 
4494
nsSelection::GetDelayCaretOverExistingSelection(PRBool *aDelay)
 
4495
{
 
4496
  if (!aDelay)
 
4497
    return NS_ERROR_NULL_POINTER;
 
4498
 
 
4499
  *aDelay =   mDelayCaretOverExistingSelection;
 
4500
 
 
4501
  return NS_OK;
 
4502
}
 
4503
 
 
4504
NS_IMETHODIMP
 
4505
nsSelection::SetDelayedCaretData(nsMouseEvent *aMouseEvent)
 
4506
{
 
4507
  if (aMouseEvent)
 
4508
  {
 
4509
    mDelayedMouseEventValid = PR_TRUE;
 
4510
    mDelayedMouseEvent      = *aMouseEvent;
 
4511
 
 
4512
    // XXX: Hmmm, should we AddRef mDelayedMouseEvent->widget?
 
4513
    //      Doing so might introduce a leak if things in the app
 
4514
    //      are not released in the correct order though, so for now
 
4515
    //      don't do anything.
 
4516
  }
 
4517
  else
 
4518
    mDelayedMouseEventValid = PR_FALSE;
 
4519
 
 
4520
  return NS_OK;
 
4521
}
 
4522
 
 
4523
NS_IMETHODIMP
 
4524
nsSelection::GetDelayedCaretData(nsMouseEvent **aMouseEvent)
 
4525
{
 
4526
  if (!aMouseEvent)
 
4527
    return NS_ERROR_NULL_POINTER;
 
4528
 
 
4529
  if (mDelayedMouseEventValid)
 
4530
    *aMouseEvent = &mDelayedMouseEvent;
 
4531
  else
 
4532
    *aMouseEvent = 0;
 
4533
 
 
4534
  return NS_OK;
 
4535
}
 
4536
 
 
4537
// Frame needs to get the limiting content node for parent node searches
 
4538
NS_IMETHODIMP
 
4539
nsSelection::GetLimiter(nsIContent **aLimiterContent)
 
4540
{
 
4541
  if (!aLimiterContent) return NS_ERROR_NULL_POINTER;
 
4542
  *aLimiterContent = mLimiter;
 
4543
  NS_IF_ADDREF(*aLimiterContent);
 
4544
 
 
4545
  return NS_OK;
 
4546
}
 
4547
 
 
4548
NS_IMETHODIMP
 
4549
nsSelection::SetMouseDoubleDown(PRBool aDoubleDown)
 
4550
{
 
4551
  mMouseDoubleDownState = aDoubleDown;
 
4552
  return NS_OK;
 
4553
}
 
4554
 
 
4555
NS_IMETHODIMP
 
4556
nsSelection::GetMouseDoubleDown(PRBool *aDoubleDown)
 
4557
{
 
4558
  *aDoubleDown = mMouseDoubleDownState;
 
4559
  return NS_OK;
 
4560
}
 
4561
 
 
4562
//END nsISelection interface implementations
 
4563
 
 
4564
#if 0
 
4565
#pragma mark -
 
4566
#endif
 
4567
 
 
4568
// nsTypedSelection implementation
 
4569
 
 
4570
// note: this can return a nil anchor node
 
4571
 
 
4572
nsTypedSelection::nsTypedSelection(nsSelection *aList)
 
4573
{
 
4574
  mFrameSelection = aList;
 
4575
  mFixupState = PR_FALSE;
 
4576
  mDirection = eDirNext;
 
4577
  mAutoScrollTimer = nsnull;
 
4578
  mScrollEventPosted = PR_FALSE;
 
4579
  mCachedOffsetForFrame = nsnull;
 
4580
}
 
4581
 
 
4582
 
 
4583
nsTypedSelection::nsTypedSelection()
 
4584
{
 
4585
  mFrameSelection = nsnull;
 
4586
  mFixupState = PR_FALSE;
 
4587
  mDirection = eDirNext;
 
4588
  mAutoScrollTimer = nsnull;
 
4589
  mScrollEventPosted = PR_FALSE;
 
4590
  mCachedOffsetForFrame = nsnull;
 
4591
}
 
4592
 
 
4593
 
 
4594
 
 
4595
nsTypedSelection::~nsTypedSelection()
 
4596
{
 
4597
  setAnchorFocusRange(-1);
 
4598
 
 
4599
  if (mAutoScrollTimer) {
 
4600
    mAutoScrollTimer->Stop();
 
4601
    NS_RELEASE(mAutoScrollTimer);
 
4602
  }
 
4603
 
 
4604
  if (mEventQueue && mScrollEventPosted) {
 
4605
    mEventQueue->RevokeEvents(this);
 
4606
    mScrollEventPosted = PR_FALSE;
 
4607
  }
 
4608
 
 
4609
  delete mCachedOffsetForFrame;
 
4610
}
 
4611
 
 
4612
 
 
4613
// QueryInterface implementation for nsRange
 
4614
NS_INTERFACE_MAP_BEGIN(nsTypedSelection)
 
4615
  NS_INTERFACE_MAP_ENTRY(nsISelection)
 
4616
  NS_INTERFACE_MAP_ENTRY(nsISelectionPrivate)
 
4617
  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
 
4618
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsISelection)
 
4619
  NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(Selection)
 
4620
NS_INTERFACE_MAP_END
 
4621
 
 
4622
 
 
4623
NS_IMPL_ADDREF(nsTypedSelection)
 
4624
NS_IMPL_RELEASE(nsTypedSelection)
 
4625
 
 
4626
 
 
4627
NS_IMETHODIMP
 
4628
nsTypedSelection::SetPresShell(nsIPresShell *aPresShell)
 
4629
{
 
4630
  mPresShellWeak = do_GetWeakReference(aPresShell);
 
4631
  return NS_OK;
 
4632
}
 
4633
 
 
4634
 
 
4635
 
 
4636
NS_IMETHODIMP
 
4637
nsTypedSelection::GetAnchorNode(nsIDOMNode** aAnchorNode)
 
4638
{
 
4639
  if (!aAnchorNode)
 
4640
    return NS_ERROR_NULL_POINTER;
 
4641
  *aAnchorNode = nsnull;
 
4642
  if(!mAnchorFocusRange)
 
4643
    return NS_OK;
 
4644
   
 
4645
  nsresult result;
 
4646
  if (GetDirection() == eDirNext){
 
4647
    result = mAnchorFocusRange->GetStartContainer(aAnchorNode);
 
4648
  }
 
4649
  else{
 
4650
    result = mAnchorFocusRange->GetEndContainer(aAnchorNode);
 
4651
  }
 
4652
  return result;
 
4653
}
 
4654
 
 
4655
NS_IMETHODIMP
 
4656
nsTypedSelection::GetAnchorOffset(PRInt32* aAnchorOffset)
 
4657
{
 
4658
  if (!aAnchorOffset)
 
4659
    return NS_ERROR_NULL_POINTER;
 
4660
  *aAnchorOffset = nsnull;
 
4661
  if(!mAnchorFocusRange)
 
4662
    return NS_OK;
 
4663
 
 
4664
  nsresult result;
 
4665
  if (GetDirection() == eDirNext){
 
4666
    result = mAnchorFocusRange->GetStartOffset(aAnchorOffset);
 
4667
  }
 
4668
  else{
 
4669
    result = mAnchorFocusRange->GetEndOffset(aAnchorOffset);
 
4670
  }
 
4671
  return result;
 
4672
}
 
4673
 
 
4674
// note: this can return a nil focus node
 
4675
NS_IMETHODIMP
 
4676
nsTypedSelection::GetFocusNode(nsIDOMNode** aFocusNode)
 
4677
{
 
4678
  if (!aFocusNode)
 
4679
    return NS_ERROR_NULL_POINTER;
 
4680
  *aFocusNode = nsnull;
 
4681
  if(!mAnchorFocusRange)
 
4682
    return NS_OK;
 
4683
 
 
4684
  nsresult result;
 
4685
  if (GetDirection() == eDirNext){
 
4686
    result = mAnchorFocusRange->GetEndContainer(aFocusNode);
 
4687
  }
 
4688
  else{
 
4689
    result = mAnchorFocusRange->GetStartContainer(aFocusNode);
 
4690
  }
 
4691
 
 
4692
  return result;
 
4693
}
 
4694
 
 
4695
NS_IMETHODIMP nsTypedSelection::GetFocusOffset(PRInt32* aFocusOffset)
 
4696
{
 
4697
  if (!aFocusOffset)
 
4698
    return NS_ERROR_NULL_POINTER;
 
4699
  *aFocusOffset = nsnull;
 
4700
  if(!mAnchorFocusRange)
 
4701
    return NS_OK;
 
4702
 
 
4703
   nsresult result;
 
4704
  if (GetDirection() == eDirNext){
 
4705
    result = mAnchorFocusRange->GetEndOffset(aFocusOffset);
 
4706
  }
 
4707
  else{
 
4708
    result = mAnchorFocusRange->GetStartOffset(aFocusOffset);
 
4709
  }
 
4710
  return result;
 
4711
}
 
4712
 
 
4713
 
 
4714
void nsTypedSelection::setAnchorFocusRange(PRInt32 indx)
 
4715
{
 
4716
  if (indx >= mRangeArray.Count())
 
4717
    return;
 
4718
  if (indx < 0) //release all
 
4719
  {
 
4720
    mAnchorFocusRange = nsnull;
 
4721
  }
 
4722
  else{
 
4723
    mAnchorFocusRange = mRangeArray[indx];
 
4724
  }
 
4725
}
 
4726
 
 
4727
 
 
4728
 
 
4729
nsIDOMNode*
 
4730
nsTypedSelection::FetchAnchorNode()
 
4731
{  //where did the selection begin
 
4732
  nsCOMPtr<nsIDOMNode>returnval;
 
4733
  GetAnchorNode(getter_AddRefs(returnval));//this queries
 
4734
  return returnval;
 
4735
}//at end it will release, no addreff was called
 
4736
 
 
4737
 
 
4738
 
 
4739
PRInt32
 
4740
nsTypedSelection::FetchAnchorOffset()
 
4741
{
 
4742
  PRInt32 returnval;
 
4743
  if (NS_SUCCEEDED(GetAnchorOffset(&returnval)))//this queries
 
4744
    return returnval;
 
4745
  return 0;
 
4746
}
 
4747
 
 
4748
 
 
4749
 
 
4750
nsIDOMNode*
 
4751
nsTypedSelection::FetchOriginalAnchorNode()  //where did the ORIGINAL selection begin
 
4752
{
 
4753
  nsCOMPtr<nsIDOMNode>returnval;
 
4754
  PRInt32 unused;
 
4755
  GetOriginalAnchorPoint(getter_AddRefs(returnval),  &unused);//this queries
 
4756
  return returnval;
 
4757
}
 
4758
 
 
4759
 
 
4760
 
 
4761
PRInt32
 
4762
nsTypedSelection::FetchOriginalAnchorOffset()
 
4763
{
 
4764
  nsCOMPtr<nsIDOMNode>unused;
 
4765
  PRInt32 returnval;
 
4766
  if (NS_SUCCEEDED(GetOriginalAnchorPoint(getter_AddRefs(unused), &returnval)))//this queries
 
4767
    return returnval;
 
4768
  return NS_OK;
 
4769
}
 
4770
 
 
4771
 
 
4772
 
 
4773
nsIDOMNode*
 
4774
nsTypedSelection::FetchFocusNode()
 
4775
{   //where is the carret
 
4776
  nsCOMPtr<nsIDOMNode>returnval;
 
4777
  GetFocusNode(getter_AddRefs(returnval));//this queries
 
4778
  return returnval;
 
4779
}//at end it will release, no addreff was called
 
4780
 
 
4781
 
 
4782
 
 
4783
PRInt32
 
4784
nsTypedSelection::FetchFocusOffset()
 
4785
{
 
4786
  PRInt32 returnval;
 
4787
  if (NS_SUCCEEDED(GetFocusOffset(&returnval)))//this queries
 
4788
    return returnval;
 
4789
  return NS_OK;
 
4790
}
 
4791
 
 
4792
 
 
4793
 
 
4794
nsIDOMNode*
 
4795
nsTypedSelection::FetchStartParent(nsIDOMRange *aRange)   //skip all the com stuff and give me the start/end
 
4796
{
 
4797
  if (!aRange)
 
4798
    return nsnull;
 
4799
  nsCOMPtr<nsIDOMNode> returnval;
 
4800
  aRange->GetStartContainer(getter_AddRefs(returnval));
 
4801
  return returnval;
 
4802
}
 
4803
 
 
4804
 
 
4805
 
 
4806
PRInt32
 
4807
nsTypedSelection::FetchStartOffset(nsIDOMRange *aRange)
 
4808
{
 
4809
  if (!aRange)
 
4810
    return nsnull;
 
4811
  PRInt32 returnval;
 
4812
  if (NS_SUCCEEDED(aRange->GetStartOffset(&returnval)))
 
4813
    return returnval;
 
4814
  return 0;
 
4815
}
 
4816
 
 
4817
 
 
4818
 
 
4819
nsIDOMNode*
 
4820
nsTypedSelection::FetchEndParent(nsIDOMRange *aRange)     //skip all the com stuff and give me the start/end
 
4821
{
 
4822
  if (!aRange)
 
4823
    return nsnull;
 
4824
  nsCOMPtr<nsIDOMNode> returnval;
 
4825
  aRange->GetEndContainer(getter_AddRefs(returnval));
 
4826
  return returnval;
 
4827
}
 
4828
 
 
4829
 
 
4830
 
 
4831
PRInt32
 
4832
nsTypedSelection::FetchEndOffset(nsIDOMRange *aRange)
 
4833
{
 
4834
  if (!aRange)
 
4835
    return nsnull;
 
4836
  PRInt32 returnval;
 
4837
  if (NS_SUCCEEDED(aRange->GetEndOffset(&returnval)))
 
4838
    return returnval;
 
4839
  return 0;
 
4840
}
 
4841
 
 
4842
nsresult
 
4843
nsTypedSelection::AddItem(nsIDOMRange *aItem)
 
4844
{
 
4845
  if (!aItem)
 
4846
    return NS_ERROR_NULL_POINTER;
 
4847
  return mRangeArray.AppendObject(aItem) ? NS_OK : NS_ERROR_FAILURE;
 
4848
}
 
4849
 
 
4850
 
 
4851
 
 
4852
nsresult
 
4853
nsTypedSelection::RemoveItem(nsIDOMRange *aItem)
 
4854
{
 
4855
  if (!aItem)
 
4856
    return NS_ERROR_NULL_POINTER;
 
4857
  mRangeArray.RemoveObject(aItem);
 
4858
  return  NS_OK;
 
4859
}
 
4860
 
 
4861
 
 
4862
 
 
4863
nsresult
 
4864
nsTypedSelection::Clear(nsIPresContext* aPresContext)
 
4865
{
 
4866
  setAnchorFocusRange(-1);
 
4867
  // Get an iterator
 
4868
  while (PR_TRUE)
 
4869
  {
 
4870
    PRInt32 cnt = mRangeArray.Count();
 
4871
    if (cnt == 0)
 
4872
      break;
 
4873
    nsCOMPtr<nsIDOMRange> range = mRangeArray[0];
 
4874
    mRangeArray.RemoveObjectAt(0);
 
4875
    selectFrames(aPresContext, range, 0);
 
4876
  }
 
4877
  // Reset direction so for more dependable table selection range handling
 
4878
  SetDirection(eDirNext);
 
4879
  return NS_OK;
 
4880
}
 
4881
 
 
4882
//utility method to get the primary frame of node or use the offset to get frame of child node
 
4883
 
 
4884
#if 0
 
4885
NS_IMETHODIMP
 
4886
nsTypedSelection::GetPrimaryFrameForRangeEndpoint(nsIDOMNode *aNode, PRInt32 aOffset, PRBool aIsEndNode, nsIFrame **aReturnFrame)
 
4887
{
 
4888
  if (!aNode || !aReturnFrame)
 
4889
    return NS_ERROR_NULL_POINTER;
 
4890
  
 
4891
  if (aOffset < 0)
 
4892
    return NS_ERROR_FAILURE;
 
4893
 
 
4894
  *aReturnFrame = 0;
 
4895
  
 
4896
  nsresult  result = NS_OK;
 
4897
  
 
4898
  nsCOMPtr<nsIDOMNode> node = aNode;
 
4899
 
 
4900
  if (!node)
 
4901
    return NS_ERROR_NULL_POINTER;
 
4902
  
 
4903
  nsCOMPtr<nsIContent> content = do_QueryInterface(node, &result);
 
4904
 
 
4905
  if (NS_FAILED(result))
 
4906
    return result;
 
4907
 
 
4908
  if (!content)
 
4909
    return NS_ERROR_NULL_POINTER;
 
4910
  
 
4911
  if (content->IsContentOfType(nsIContent::eELEMENT))
 
4912
  {
 
4913
    if (aIsEndNode)
 
4914
      aOffset--;
 
4915
 
 
4916
    if (aOffset >= 0)
 
4917
    {
 
4918
      nsIContent *child = content->GetChildAt(aOffset);
 
4919
      if (!child) //out of bounds?
 
4920
        return NS_ERROR_FAILURE;
 
4921
 
 
4922
      content = child; // releases the focusnode
 
4923
    }
 
4924
  }
 
4925
  result = mFrameSelection->GetTracker()->GetPrimaryFrameFor(content,aReturnFrame);
 
4926
  return result;
 
4927
}
 
4928
#endif
 
4929
 
 
4930
 
 
4931
NS_IMETHODIMP
 
4932
nsTypedSelection::GetPrimaryFrameForAnchorNode(nsIFrame **aReturnFrame)
 
4933
{
 
4934
  if (!aReturnFrame)
 
4935
    return NS_ERROR_NULL_POINTER;
 
4936
  
 
4937
  PRInt32 frameOffset = 0;
 
4938
  *aReturnFrame = 0;
 
4939
  nsCOMPtr<nsIContent> content = do_QueryInterface(FetchAnchorNode());
 
4940
  if (content && mFrameSelection)
 
4941
  {
 
4942
    nsIFrameSelection::HINT hint;
 
4943
    mFrameSelection->GetHint(&hint);
 
4944
    return mFrameSelection->GetFrameForNodeOffset(content, FetchAnchorOffset(),hint,aReturnFrame, &frameOffset);
 
4945
  }
 
4946
  return NS_ERROR_FAILURE;
 
4947
}
 
4948
 
 
4949
NS_IMETHODIMP
 
4950
nsTypedSelection::GetPrimaryFrameForFocusNode(nsIFrame **aReturnFrame, PRInt32 *aOffsetUsed)
 
4951
{
 
4952
  if (!aReturnFrame)
 
4953
    return NS_ERROR_NULL_POINTER;
 
4954
  
 
4955
  PRInt32 frameOffset = 0;
 
4956
  *aReturnFrame = 0;
 
4957
  if (!aOffsetUsed)
 
4958
    aOffsetUsed = &frameOffset;
 
4959
    
 
4960
 
 
4961
  nsCOMPtr<nsIContent> content = do_QueryInterface(FetchFocusNode());
 
4962
  if (content && mFrameSelection)
 
4963
  {
 
4964
    nsIFrameSelection::HINT hint;
 
4965
    mFrameSelection->GetHint(&hint);
 
4966
    return mFrameSelection->GetFrameForNodeOffset(content, FetchFocusOffset(),hint,aReturnFrame, aOffsetUsed);
 
4967
  }
 
4968
  return NS_ERROR_FAILURE;
 
4969
}
 
4970
 
 
4971
 
 
4972
 
 
4973
//select all content children of aContent
 
4974
NS_IMETHODIMP
 
4975
nsTypedSelection::selectFrames(nsIPresContext* aPresContext,
 
4976
                             nsIContentIterator *aInnerIter,
 
4977
                             nsIContent *aContent,
 
4978
                             nsIDOMRange *aRange,
 
4979
                             nsIPresShell *aPresShell,
 
4980
                             PRBool aFlags)
 
4981
{
 
4982
  if (!mFrameSelection)
 
4983
    return NS_OK;//nothing to do
 
4984
  nsresult result;
 
4985
  if (!aInnerIter)
 
4986
    return NS_ERROR_NULL_POINTER;
 
4987
#ifdef USE_SELECTION_GENERATED_CONTENT_ITERATOR_CODE
 
4988
  nsCOMPtr<nsIGeneratedContentIterator> genericiter = do_QueryInterface(aInnerIter);
 
4989
  if (genericiter && aPresShell)
 
4990
  {
 
4991
    result = genericiter->Init(aPresShell,aContent);
 
4992
  }
 
4993
  else
 
4994
#endif // USE_SELECTION_GENERATED_CONTENT_ITERATOR_CODE
 
4995
    result = aInnerIter->Init(aContent);
 
4996
  nsIFrame *frame;
 
4997
  if (NS_SUCCEEDED(result))
 
4998
  {
 
4999
    // First select frame of content passed in
 
5000
    result = mFrameSelection->GetTracker()->GetPrimaryFrameFor(aContent, &frame);
 
5001
    if (NS_SUCCEEDED(result) && frame)
 
5002
    {
 
5003
      //NOTE: eSpreadDown is now IGNORED. Selected state is set only for given frame
 
5004
      frame->SetSelected(aPresContext, nsnull, aFlags, eSpreadDown);
 
5005
#ifndef OLD_TABLE_SELECTION
 
5006
      PRBool tablesel;
 
5007
      mFrameSelection->GetTableCellSelection(&tablesel);
 
5008
      if (tablesel)
 
5009
      {
 
5010
        nsITableCellLayout *tcl = nsnull;
 
5011
        CallQueryInterface(frame, &tcl);
 
5012
        if (tcl)
 
5013
        {
 
5014
          return NS_OK;
 
5015
        }
 
5016
      }
 
5017
#endif //OLD_TABLE_SELECTION
 
5018
    }
 
5019
    // Now iterated through the child frames and set them
 
5020
    while (!aInnerIter->IsDone())
 
5021
    {
 
5022
      nsIContent *innercontent = aInnerIter->GetCurrentNode();
 
5023
 
 
5024
      result = mFrameSelection->GetTracker()->GetPrimaryFrameFor(innercontent, &frame);
 
5025
      if (NS_SUCCEEDED(result) && frame)
 
5026
      {
 
5027
        //NOTE: eSpreadDown is now IGNORED. Selected state is set only
 
5028
        //for given frame
 
5029
 
 
5030
        //spread from here to hit all frames in flow
 
5031
        frame->SetSelected(aPresContext, nsnull,aFlags,eSpreadDown);
 
5032
        nsRect frameRect = frame->GetRect();
 
5033
 
 
5034
        //if a rect is 0 height/width then try to notify next
 
5035
        //available in flow of selection status.
 
5036
        while (!frameRect.width || !frameRect.height)
 
5037
        {
 
5038
          //try to notify next in flow that its content is selected.
 
5039
          if (NS_SUCCEEDED(frame->GetNextInFlow(&frame)) && frame)
 
5040
          {
 
5041
            frameRect = frame->GetRect();
 
5042
            frame->SetSelected(aPresContext, nsnull,aFlags,eSpreadDown);
 
5043
          }
 
5044
          else
 
5045
            break;
 
5046
        }
 
5047
        //if the frame is splittable and this frame is 0,0 then set
 
5048
        //the next in flow frame to be selected also
 
5049
      }
 
5050
 
 
5051
      aInnerIter->Next();
 
5052
    }
 
5053
 
 
5054
#if 0
 
5055
    result = mFrameSelection->GetTracker()->GetPrimaryFrameFor(content, &frame);
 
5056
    if (NS_SUCCEEDED(result) && frame)
 
5057
      frame->SetSelected(aRange,aFlags,eSpreadDown);//spread from here to hit all frames in flow
 
5058
#endif
 
5059
 
 
5060
    return NS_OK;
 
5061
  }
 
5062
 
 
5063
  return NS_ERROR_FAILURE;
 
5064
}
 
5065
 
 
5066
 
 
5067
 
 
5068
//the idea of this helper method is to select, deselect "top to bottom" traversing through the frames
 
5069
NS_IMETHODIMP
 
5070
nsTypedSelection::selectFrames(nsIPresContext* aPresContext, nsIDOMRange *aRange, PRBool aFlags)
 
5071
{
 
5072
  if (!mFrameSelection)
 
5073
    return NS_OK;//nothing to do
 
5074
  if (!aRange || !aPresContext) 
 
5075
    return NS_ERROR_NULL_POINTER;
 
5076
 
 
5077
  nsresult result;
 
5078
  nsCOMPtr<nsIContentIterator> iter = do_CreateInstance(
 
5079
#ifdef USE_SELECTION_GENERATED_CONTENT_ITERATOR_CODE
 
5080
                                              kCGenSubtreeIteratorCID,
 
5081
#else
 
5082
                                              kCSubtreeIteratorCID,
 
5083
#endif // USE_SELECTION_GENERATED_CONTENT_ITERATOR_CODE
 
5084
                                              &result);
 
5085
  if (NS_FAILED(result))
 
5086
    return result;
 
5087
 
 
5088
  nsCOMPtr<nsIContentIterator> inneriter = do_CreateInstance(
 
5089
#ifdef USE_SELECTION_GENERATED_CONTENT_ITERATOR_CODE
 
5090
                                              kCGenContentIteratorCID,
 
5091
#else
 
5092
                                              kCContentIteratorCID,
 
5093
#endif // USE_SELECTION_GENERATED_CONTENT_ITERATOR_CODE
 
5094
                                              &result);
 
5095
 
 
5096
  if ((NS_SUCCEEDED(result)) && iter && inneriter)
 
5097
  {
 
5098
    nsIPresShell *presShell = aPresContext->GetPresShell();
 
5099
#ifdef USE_SELECTION_GENERATED_CONTENT_ITERATOR_CODE
 
5100
    nsCOMPtr<nsIGeneratedContentIterator> genericiter = do_QueryInterface(iter);
 
5101
    if (genericiter && presShell)
 
5102
      result = genericiter->Init(presShell,aRange);
 
5103
    else
 
5104
#endif // USE_SELECTION_GENERATED_CONTENT_ITERATOR_CODE
 
5105
      result = iter->Init(aRange);
 
5106
 
 
5107
    // loop through the content iterator for each content node
 
5108
    // for each text node:
 
5109
    // get the frame for the content, and from it the style context
 
5110
    // ask the style context about the property
 
5111
    nsCOMPtr<nsIContent> content;
 
5112
    nsIFrame *frame;
 
5113
//we must call first one explicitly
 
5114
    content = do_QueryInterface(FetchStartParent(aRange), &result);
 
5115
    if (NS_FAILED(result) || !content)
 
5116
      return result;
 
5117
 
 
5118
    if (!content->IsContentOfType(nsIContent::eELEMENT))
 
5119
    {
 
5120
      result = mFrameSelection->GetTracker()->GetPrimaryFrameFor(content, &frame);
 
5121
      if (NS_SUCCEEDED(result) && frame)
 
5122
        frame->SetSelected(aPresContext, aRange,aFlags,eSpreadDown);//spread from here to hit all frames in flow
 
5123
    }
 
5124
//end start content
 
5125
    iter->First();
 
5126
 
 
5127
    while (!iter->IsDone())
 
5128
    {
 
5129
      content = iter->GetCurrentNode();
 
5130
 
 
5131
      selectFrames(aPresContext, inneriter, content, aRange, presShell,aFlags);
 
5132
 
 
5133
      iter->Next();
 
5134
    }
 
5135
//we must now do the last one  if it is not the same as the first
 
5136
    if (FetchEndParent(aRange) != FetchStartParent(aRange))
 
5137
    {
 
5138
      content = do_QueryInterface(FetchEndParent(aRange), &result);
 
5139
      if (NS_FAILED(result) || !content)
 
5140
        return result;
 
5141
 
 
5142
      if (!content->IsContentOfType(nsIContent::eELEMENT))
 
5143
      {
 
5144
        result = mFrameSelection->GetTracker()->GetPrimaryFrameFor(content, &frame);
 
5145
        if (NS_SUCCEEDED(result) && frame)
 
5146
           frame->SetSelected(aPresContext, aRange,aFlags,eSpreadDown);//spread from here to hit all frames in flow
 
5147
      }
 
5148
    }
 
5149
//end end parent
 
5150
  }
 
5151
  return result;
 
5152
}
 
5153
 
 
5154
 
 
5155
NS_IMETHODIMP
 
5156
nsTypedSelection::LookUpSelection(nsIContent *aContent, PRInt32 aContentOffset, PRInt32 aContentLength,
 
5157
                           SelectionDetails **aReturnDetails, SelectionType aType, PRBool aSlowCheck)
 
5158
{
 
5159
  PRInt32 cnt;
 
5160
  nsresult rv = GetRangeCount(&cnt);
 
5161
  if (NS_FAILED(rv)) 
 
5162
    return rv;
 
5163
  PRInt32 i;
 
5164
 
 
5165
  nsCOMPtr<nsIDOMNode> passedInNode;
 
5166
  passedInNode = do_QueryInterface(aContent);
 
5167
  if (!passedInNode)
 
5168
    return NS_ERROR_FAILURE;
 
5169
 
 
5170
  SelectionDetails *details = nsnull;
 
5171
  SelectionDetails *newDetails = details;
 
5172
 
 
5173
  for (i =0; i<cnt; i++){
 
5174
    nsCOMPtr<nsIDOMRange> range = mRangeArray[i];
 
5175
    if (range){
 
5176
      nsCOMPtr<nsIDOMNode> startNode;
 
5177
      nsCOMPtr<nsIDOMNode> endNode;
 
5178
      PRInt32 startOffset;
 
5179
      PRInt32 endOffset;
 
5180
      range->GetStartContainer(getter_AddRefs(startNode));
 
5181
      range->GetStartOffset(&startOffset);
 
5182
      range->GetEndContainer(getter_AddRefs(endNode));
 
5183
      range->GetEndOffset(&endOffset);
 
5184
      if (passedInNode == startNode && passedInNode == endNode){
 
5185
        if (startOffset < (aContentOffset + aContentLength)  &&
 
5186
            endOffset > aContentOffset){
 
5187
          if (!details){
 
5188
            details = new SelectionDetails;
 
5189
 
 
5190
            newDetails = details;
 
5191
          }
 
5192
          else{
 
5193
            newDetails->mNext = new SelectionDetails;
 
5194
            newDetails = newDetails->mNext;
 
5195
          }
 
5196
          if (!newDetails)
 
5197
            return NS_ERROR_OUT_OF_MEMORY;
 
5198
          newDetails->mNext = nsnull;
 
5199
          newDetails->mStart = PR_MAX(0,startOffset - aContentOffset);
 
5200
          newDetails->mEnd = PR_MIN(aContentLength, endOffset - aContentOffset);
 
5201
          newDetails->mType = aType;
 
5202
        }
 
5203
      }
 
5204
      else if (passedInNode == startNode){ //are we at the beginning?
 
5205
        if (startOffset < (aContentOffset + aContentLength)){
 
5206
          if (!details){
 
5207
            details = new SelectionDetails;
 
5208
            newDetails = details;
 
5209
          }
 
5210
          else{
 
5211
            newDetails->mNext = new SelectionDetails;
 
5212
            newDetails = newDetails->mNext;
 
5213
          }
 
5214
          if (!newDetails)
 
5215
            return NS_ERROR_OUT_OF_MEMORY;
 
5216
          newDetails->mNext = nsnull;
 
5217
          newDetails->mStart = PR_MAX(0,startOffset - aContentOffset);
 
5218
          newDetails->mEnd = aContentLength;
 
5219
          newDetails->mType = aType;
 
5220
        }
 
5221
      }
 
5222
      else if (passedInNode == endNode){//are we at the end?
 
5223
        if (endOffset > aContentOffset){
 
5224
          if (!details){
 
5225
            details = new SelectionDetails;
 
5226
            newDetails = details;
 
5227
          }
 
5228
          else{
 
5229
            newDetails->mNext = new SelectionDetails;
 
5230
            newDetails = newDetails->mNext;
 
5231
          }
 
5232
          if (!newDetails)
 
5233
            return NS_ERROR_OUT_OF_MEMORY;
 
5234
          newDetails->mNext = nsnull;
 
5235
          newDetails->mStart = 0;
 
5236
          newDetails->mEnd = PR_MIN(aContentLength, endOffset - aContentOffset);
 
5237
          newDetails->mType = aType;
 
5238
        }
 
5239
      }
 
5240
      else { //then we MUST be completely selected! unless someone needs us to check to make sure with slowcheck
 
5241
 
 
5242
        if (cnt > 1 || aSlowCheck){ //if more than 1 selection or we need to do slow check see if farther than start or less than end.
 
5243
          //we only have to look at start offset because anything else would have been in the range
 
5244
          PRInt32 resultnum = ComparePoints(startNode, startOffset 
 
5245
                                  ,passedInNode, aContentOffset);
 
5246
          if (resultnum > 0)
 
5247
            continue; 
 
5248
          resultnum = ComparePoints(endNode, endOffset,
 
5249
                              passedInNode, aContentOffset );
 
5250
          if (resultnum <0)
 
5251
            continue;
 
5252
        }
 
5253
 
 
5254
        if (!details){
 
5255
          details = new SelectionDetails;
 
5256
          newDetails = details;
 
5257
        }
 
5258
        else{
 
5259
          newDetails->mNext = new SelectionDetails;
 
5260
          newDetails = newDetails->mNext;
 
5261
        }
 
5262
        if (!newDetails)
 
5263
          return NS_ERROR_OUT_OF_MEMORY;
 
5264
        newDetails->mNext = nsnull;
 
5265
        newDetails->mStart = 0;
 
5266
        newDetails->mEnd = aContentLength;
 
5267
        newDetails->mType = aType;
 
5268
     }
 
5269
    }
 
5270
    else
 
5271
      continue;
 
5272
  }
 
5273
  if (*aReturnDetails && newDetails)
 
5274
    newDetails->mNext = *aReturnDetails;
 
5275
  if (details)
 
5276
    *aReturnDetails = details;
 
5277
  return NS_OK;
 
5278
}
 
5279
 
 
5280
NS_IMETHODIMP
 
5281
nsTypedSelection::Repaint(nsIPresContext* aPresContext)
 
5282
{
 
5283
  PRInt32 arrCount = mRangeArray.Count();
 
5284
 
 
5285
  if (arrCount < 1)
 
5286
    return NS_OK;
 
5287
 
 
5288
  PRInt32 i;
 
5289
  nsIDOMRange* range;
 
5290
  
 
5291
  for (i = 0; i < arrCount; i++)
 
5292
  {
 
5293
    range = mRangeArray[i];
 
5294
 
 
5295
    if (!range)
 
5296
      return NS_ERROR_UNEXPECTED;
 
5297
 
 
5298
    nsresult rv = selectFrames(aPresContext, range, PR_TRUE);
 
5299
 
 
5300
    if (NS_FAILED(rv)) {
 
5301
      return rv;
 
5302
    }
 
5303
  }
 
5304
 
 
5305
  return NS_OK;
 
5306
}
 
5307
 
 
5308
NS_IMETHODIMP
 
5309
nsTypedSelection::GetCanCacheFrameOffset(PRBool *aCanCacheFrameOffset)
 
5310
 
5311
  NS_ENSURE_ARG_POINTER(aCanCacheFrameOffset);
 
5312
 
 
5313
  if (mCachedOffsetForFrame)
 
5314
    *aCanCacheFrameOffset = mCachedOffsetForFrame->mCanCacheFrameOffset;
 
5315
  else
 
5316
    *aCanCacheFrameOffset = PR_FALSE;
 
5317
 
 
5318
  return NS_OK;
 
5319
}
 
5320
 
 
5321
NS_IMETHODIMP    
 
5322
nsTypedSelection::SetCanCacheFrameOffset(PRBool aCanCacheFrameOffset)
 
5323
{
 
5324
  if (!mCachedOffsetForFrame) {
 
5325
    mCachedOffsetForFrame = new CachedOffsetForFrame;
 
5326
  }
 
5327
 
 
5328
  mCachedOffsetForFrame->mCanCacheFrameOffset = aCanCacheFrameOffset;
 
5329
 
 
5330
  // clean up cached frame when turn off cache
 
5331
  // fix bug 207936
 
5332
  if (!aCanCacheFrameOffset) {
 
5333
    mCachedOffsetForFrame->mLastCaretFrame = nsnull;
 
5334
  }
 
5335
 
 
5336
  return NS_OK;
 
5337
}
 
5338
 
 
5339
NS_IMETHODIMP    
 
5340
nsTypedSelection::GetCachedFrameOffset(nsIFrame *aFrame, PRInt32 inOffset, nsPoint& aPoint)
 
5341
{
 
5342
  if (!mCachedOffsetForFrame) {
 
5343
    mCachedOffsetForFrame = new CachedOffsetForFrame;
 
5344
  }
 
5345
 
 
5346
  if (mCachedOffsetForFrame->mCanCacheFrameOffset &&
 
5347
      mCachedOffsetForFrame->mLastCaretFrame &&
 
5348
      (aFrame == mCachedOffsetForFrame->mLastCaretFrame) &&
 
5349
      (inOffset == mCachedOffsetForFrame->mLastContentOffset))
 
5350
  {
 
5351
     // get cached frame offset
 
5352
     aPoint = mCachedOffsetForFrame->mCachedFrameOffset;
 
5353
  } 
 
5354
  else
 
5355
  {
 
5356
     // recalculate frame offset and cache it
 
5357
     GetPointFromOffset(aFrame, inOffset, &aPoint);
 
5358
     if (mCachedOffsetForFrame->mCanCacheFrameOffset) {
 
5359
       mCachedOffsetForFrame->mCachedFrameOffset = aPoint;
 
5360
       mCachedOffsetForFrame->mLastCaretFrame = aFrame;
 
5361
       mCachedOffsetForFrame->mLastContentOffset = inOffset; 
 
5362
     }
 
5363
  }
 
5364
 
 
5365
  return NS_OK;
 
5366
}
 
5367
 
 
5368
nsresult
 
5369
nsTypedSelection::StartAutoScrollTimer(nsIPresContext *aPresContext, nsIFrame *aFrame, nsPoint& aPoint, PRUint32 aDelay)
 
5370
{
 
5371
  nsresult result;
 
5372
  if (!mFrameSelection)
 
5373
    return NS_OK;//nothing to do
 
5374
 
 
5375
  if (!mAutoScrollTimer)
 
5376
  {
 
5377
    result = NS_NewAutoScrollTimer(&mAutoScrollTimer);
 
5378
 
 
5379
    if (NS_FAILED(result))
 
5380
      return result;
 
5381
 
 
5382
    if (!mAutoScrollTimer)
 
5383
      return NS_ERROR_OUT_OF_MEMORY;
 
5384
 
 
5385
    result = mAutoScrollTimer->Init(mFrameSelection, this);
 
5386
 
 
5387
    if (NS_FAILED(result))
 
5388
      return result;
 
5389
  }
 
5390
 
 
5391
  result = mAutoScrollTimer->SetDelay(aDelay);
 
5392
 
 
5393
  if (NS_FAILED(result))
 
5394
    return result;
 
5395
 
 
5396
  return DoAutoScroll(aPresContext, aFrame, aPoint);
 
5397
}
 
5398
 
 
5399
nsresult
 
5400
nsTypedSelection::StopAutoScrollTimer()
 
5401
{
 
5402
  if (mAutoScrollTimer)
 
5403
    return mAutoScrollTimer->Stop();
 
5404
 
 
5405
  return NS_OK; 
 
5406
}
 
5407
 
 
5408
nsresult
 
5409
nsTypedSelection::GetViewAncestorOffset(nsIView *aView, nsIView *aAncestorView, nscoord *aXOffset, nscoord *aYOffset)
 
5410
{
 
5411
  // Note: A NULL aAncestorView pointer means that the caller wants
 
5412
  //       the view's global offset.
 
5413
 
 
5414
  if (!aView || !aXOffset || !aYOffset)
 
5415
    return NS_ERROR_FAILURE;
 
5416
 
 
5417
  *aXOffset = 0;
 
5418
  *aYOffset = 0;
 
5419
 
 
5420
  for (nsIView* view = aView; view && view != aAncestorView;
 
5421
       view = view->GetParent())
 
5422
  {
 
5423
    nsPoint pt = view->GetPosition();
 
5424
    *aXOffset += pt.x;
 
5425
    *aYOffset += pt.y;
 
5426
  }
 
5427
 
 
5428
  return NS_OK;
 
5429
}
 
5430
 
 
5431
nsresult
 
5432
nsTypedSelection::GetClosestScrollableView(nsIView *aView, nsIScrollableView **aScrollableView)
 
5433
{
 
5434
  if (!aView || !aScrollableView)
 
5435
    return NS_ERROR_FAILURE;
 
5436
 
 
5437
  *aScrollableView = 0;
 
5438
 
 
5439
  while (!*aScrollableView && aView)
 
5440
  {
 
5441
    CallQueryInterface(aView, aScrollableView);
 
5442
    if (!*aScrollableView)
 
5443
    {
 
5444
      aView = aView->GetParent();
 
5445
    }
 
5446
  }
 
5447
 
 
5448
  return NS_OK;
 
5449
}
 
5450
 
 
5451
nsresult
 
5452
nsTypedSelection::ScrollPointIntoClipView(nsIPresContext *aPresContext, nsIView *aView, nsPoint& aPoint, PRBool *aDidScroll)
 
5453
{
 
5454
  nsresult result;
 
5455
 
 
5456
  if (!aPresContext || !aView || !aDidScroll)
 
5457
    return NS_ERROR_NULL_POINTER;
 
5458
 
 
5459
  *aDidScroll = PR_FALSE;
 
5460
 
 
5461
  //
 
5462
  // Get aView's scrollable view.
 
5463
  //
 
5464
 
 
5465
  nsIScrollableView *scrollableView = 0;
 
5466
 
 
5467
  result = GetClosestScrollableView(aView, &scrollableView);
 
5468
 
 
5469
  if (NS_FAILED(result))
 
5470
    return result;
 
5471
 
 
5472
  if (!scrollableView)
 
5473
    return NS_OK; // Nothing to do!
 
5474
 
 
5475
  //
 
5476
  // Get the clip view.
 
5477
  //
 
5478
 
 
5479
  const nsIView *cView = 0;
 
5480
 
 
5481
  result = scrollableView->GetClipView(&cView);
 
5482
 
 
5483
  if (NS_FAILED(result))
 
5484
    return result;
 
5485
 
 
5486
  //
 
5487
  // Get the view that is being scrolled.
 
5488
  //
 
5489
 
 
5490
  nsIView *scrolledView = 0;
 
5491
 
 
5492
  result = scrollableView->GetScrolledView(scrolledView);
 
5493
  if (!cView)
 
5494
    return NS_ERROR_FAILURE;
 
5495
  
 
5496
  //
 
5497
  // Now walk up aView's hierarchy, this time keeping track of
 
5498
  // the view offsets until you hit the scrolledView.
 
5499
  //
 
5500
 
 
5501
  nsPoint viewOffset(0,0);
 
5502
 
 
5503
  result = GetViewAncestorOffset(aView, scrolledView, &viewOffset.x, &viewOffset.y);
 
5504
 
 
5505
  if (NS_FAILED(result))
 
5506
    return result;
 
5507
 
 
5508
  //
 
5509
  // See if aPoint is outside the clip view's boundaries.
 
5510
  // If it is, scroll the view till it is inside the visible area!
 
5511
  //
 
5512
 
 
5513
  nsRect bounds = cView->GetBounds();
 
5514
 
 
5515
  result = scrollableView->GetScrollPosition(bounds.x,bounds.y);
 
5516
 
 
5517
  if (NS_FAILED(result))
 
5518
    return result;
 
5519
 
 
5520
  //
 
5521
  // Calculate the amount we would have to scroll in
 
5522
  // the vertical and horizontal directions to get the point
 
5523
  // within the clip area.
 
5524
  //
 
5525
 
 
5526
  nscoord dx = 0, dy = 0;
 
5527
  nsPoint ePoint = aPoint;
 
5528
 
 
5529
  ePoint.x += viewOffset.x;
 
5530
  ePoint.y += viewOffset.y;
 
5531
  
 
5532
  nscoord x1 = bounds.x;
 
5533
  nscoord x2 = bounds.x + bounds.width;
 
5534
  nscoord y1 = bounds.y;
 
5535
  nscoord y2 = bounds.y + bounds.height;
 
5536
 
 
5537
  if (ePoint.x < x1)
 
5538
    dx = ePoint.x - x1;
 
5539
  else if (ePoint.x > x2)
 
5540
    dx = ePoint.x - x2;
 
5541
      
 
5542
  if (ePoint.y < y1)
 
5543
    dy = ePoint.y - y1;
 
5544
  else if (ePoint.y > y2)
 
5545
    dy = ePoint.y - y2;
 
5546
 
 
5547
  //
 
5548
  // Now clip the scroll amounts so that we don't scroll
 
5549
  // beyond the ends of the document.
 
5550
  //
 
5551
 
 
5552
  nscoord scrollX = 0, scrollY = 0;
 
5553
  nscoord docWidth = 0, docHeight = 0;
 
5554
 
 
5555
  result = scrollableView->GetScrollPosition(scrollX, scrollY);
 
5556
 
 
5557
  if (NS_SUCCEEDED(result))
 
5558
    result = scrollableView->GetContainerSize(&docWidth, &docHeight);
 
5559
 
 
5560
  if (NS_SUCCEEDED(result))
 
5561
  {
 
5562
    if (dx < 0 && scrollX == 0)
 
5563
      dx = 0;
 
5564
    else if (dx > 0)
 
5565
    {
 
5566
      x1 = scrollX + dx + bounds.width;
 
5567
 
 
5568
      if (x1 > docWidth)
 
5569
        dx -= x1 - docWidth;
 
5570
    }
 
5571
 
 
5572
 
 
5573
    if (dy < 0 && scrollY == 0)
 
5574
      dy = 0;
 
5575
    else if (dy > 0)
 
5576
    {
 
5577
      y1 = scrollY + dy + bounds.height;
 
5578
 
 
5579
      if (y1 > docHeight)
 
5580
        dy -= y1 - docHeight;
 
5581
    }
 
5582
 
 
5583
    //
 
5584
    // Now scroll the view if neccessary.
 
5585
    //
 
5586
 
 
5587
    if (dx != 0 || dy != 0)
 
5588
    {
 
5589
      // Make sure latest bits are available before we scroll them.
 
5590
      aPresContext->GetViewManager()->Composite();
 
5591
 
 
5592
      // Now scroll the view!
 
5593
 
 
5594
      result = scrollableView->ScrollTo(scrollX + dx, scrollY + dy, NS_VMREFRESH_NO_SYNC);
 
5595
 
 
5596
      if (NS_FAILED(result))
 
5597
        return result;
 
5598
 
 
5599
      nsPoint newPos;
 
5600
 
 
5601
      result = scrollableView->GetScrollPosition(newPos.x, newPos.y);
 
5602
 
 
5603
      if (NS_FAILED(result))
 
5604
        return result;
 
5605
 
 
5606
      *aDidScroll = (bounds.x != newPos.x || bounds.y != newPos.y);
 
5607
    }
 
5608
  }
 
5609
 
 
5610
  return result;
 
5611
}
 
5612
 
 
5613
nsresult
 
5614
nsTypedSelection::ScrollPointIntoView(nsIPresContext *aPresContext, nsIView *aView, nsPoint& aPoint, PRBool aScrollParentViews, PRBool *aDidScroll)
 
5615
{
 
5616
  if (!aPresContext || !aView || !aDidScroll)
 
5617
    return NS_ERROR_NULL_POINTER;
 
5618
 
 
5619
  nsresult result;
 
5620
 
 
5621
  *aDidScroll = PR_FALSE;
 
5622
 
 
5623
  //
 
5624
  // Calculate the global offset of the view.
 
5625
  //
 
5626
 
 
5627
  nsPoint globalOffset;
 
5628
 
 
5629
  result = GetViewAncestorOffset(aView, nsnull, &globalOffset.x, &globalOffset.y);
 
5630
 
 
5631
  if (NS_FAILED(result))
 
5632
    return result;
 
5633
 
 
5634
  //
 
5635
  // Convert aPoint into global coordinates so it is easier to map
 
5636
  // into other views.
 
5637
  //
 
5638
 
 
5639
  nsPoint globalPoint = aPoint + globalOffset;
 
5640
 
 
5641
  //
 
5642
  // Scroll the point into the visible rect of the closest
 
5643
  // scrollable view.
 
5644
  //
 
5645
  result = ScrollPointIntoClipView(aPresContext, aView, aPoint, aDidScroll);
 
5646
 
 
5647
  if (NS_FAILED(result))
 
5648
    return result;
 
5649
 
 
5650
  //
 
5651
  // Now scroll the parent scrollable views.
 
5652
  //
 
5653
 
 
5654
  if (aScrollParentViews)
 
5655
  {
 
5656
    //
 
5657
    // Find aView's parent scrollable view.
 
5658
    //
 
5659
 
 
5660
    nsIScrollableView *scrollableView = 0;
 
5661
 
 
5662
    result = GetClosestScrollableView(aView, &scrollableView);
 
5663
 
 
5664
    if (NS_FAILED(result))
 
5665
      return result;
 
5666
 
 
5667
    if (scrollableView)
 
5668
    {
 
5669
      //
 
5670
      // Convert scrollableView to nsIView.
 
5671
      //
 
5672
 
 
5673
      nsIView *scrolledView = 0;
 
5674
      nsIView *view = 0;
 
5675
 
 
5676
      CallQueryInterface(scrollableView, &view);
 
5677
 
 
5678
      if (view)
 
5679
      {
 
5680
        //
 
5681
        // Now get the scrollableView's parent, then search for it's
 
5682
        // closest scrollable view.
 
5683
        //
 
5684
 
 
5685
        view = view->GetParent();
 
5686
 
 
5687
        while (view)
 
5688
        {
 
5689
          result = GetClosestScrollableView(view, &scrollableView);
 
5690
 
 
5691
          if (NS_FAILED(result))
 
5692
            return result;
 
5693
 
 
5694
          if (!scrollableView)
 
5695
            break;
 
5696
 
 
5697
          scrolledView = 0;
 
5698
          result = scrollableView->GetScrolledView(scrolledView);
 
5699
          
 
5700
          if (NS_FAILED(result))
 
5701
            return result;
 
5702
 
 
5703
          //
 
5704
          // Map the global point into this scrolledView's coordinate space.
 
5705
          //
 
5706
 
 
5707
          result = GetViewAncestorOffset(scrolledView, nsnull, &globalOffset.x, &globalOffset.y);
 
5708
 
 
5709
          if (NS_FAILED(result))
 
5710
            return result;
 
5711
 
 
5712
          nsPoint newPoint = globalPoint - globalOffset;
 
5713
 
 
5714
          //
 
5715
          // Scroll the point into the visible rect of the scrolled view.
 
5716
          //
 
5717
 
 
5718
          PRBool parentDidScroll = PR_FALSE;
 
5719
 
 
5720
          result = ScrollPointIntoClipView(aPresContext, scrolledView, newPoint, &parentDidScroll);
 
5721
 
 
5722
          if (NS_FAILED(result))
 
5723
            return result;
 
5724
 
 
5725
          *aDidScroll = *aDidScroll || parentDidScroll;
 
5726
 
 
5727
          //
 
5728
          // Now get the parent of this scrollable view so we
 
5729
          // can scroll the next parent view.
 
5730
          //
 
5731
 
 
5732
          view = 0;
 
5733
          result = CallQueryInterface(scrollableView, &view);
 
5734
          if (!view)
 
5735
            return result;
 
5736
 
 
5737
          view = view->GetParent();
 
5738
        }
 
5739
      }
 
5740
    }
 
5741
  }
 
5742
 
 
5743
  return NS_OK;
 
5744
}
 
5745
 
 
5746
nsresult
 
5747
nsTypedSelection::DoAutoScrollView(nsIPresContext *aPresContext, nsIView *aView, nsPoint& aPoint, PRBool aScrollParentViews)
 
5748
{
 
5749
  if (!aPresContext || !aView)
 
5750
    return NS_ERROR_NULL_POINTER;
 
5751
 
 
5752
  nsresult result;
 
5753
 
 
5754
  if (mAutoScrollTimer)
 
5755
    result = mAutoScrollTimer->Stop();
 
5756
 
 
5757
  //
 
5758
  // Calculate the global offset of the view.
 
5759
  //
 
5760
 
 
5761
  nsPoint globalOffset;
 
5762
 
 
5763
  result = GetViewAncestorOffset(aView, nsnull, &globalOffset.x, &globalOffset.y);
 
5764
 
 
5765
  if (NS_FAILED(result))
 
5766
    return result;
 
5767
 
 
5768
  //
 
5769
  // Convert aPoint into global coordinates so we can get back
 
5770
  // to the same point after all the parent views have scrolled.
 
5771
  //
 
5772
 
 
5773
  nsPoint globalPoint = aPoint + globalOffset;
 
5774
 
 
5775
  //
 
5776
  // Now scroll aPoint into view.
 
5777
  //
 
5778
 
 
5779
  PRBool didScroll = PR_FALSE;
 
5780
 
 
5781
  result = ScrollPointIntoView(aPresContext, aView, aPoint, aScrollParentViews, &didScroll);
 
5782
 
 
5783
  if (NS_FAILED(result))
 
5784
    return result;
 
5785
 
 
5786
  //
 
5787
  // Start the AutoScroll timer if neccessary.
 
5788
  //
 
5789
 
 
5790
  if (didScroll && mAutoScrollTimer)
 
5791
  {
 
5792
    //
 
5793
    // Map the globalPoint back into aView's coordinate system. We
 
5794
    // have to get the globalOffsets again because aView's
 
5795
    // window and it's parents may have changed their offsets.
 
5796
    //
 
5797
    result = GetViewAncestorOffset(aView, nsnull, &globalOffset.x, &globalOffset.y);
 
5798
 
 
5799
    if (NS_FAILED(result))
 
5800
      return result;
 
5801
 
 
5802
    nsPoint svPoint = globalPoint - globalOffset;
 
5803
 
 
5804
    result = mAutoScrollTimer->Start(aPresContext, aView, svPoint);
 
5805
  }
 
5806
 
 
5807
  return NS_OK;
 
5808
}
 
5809
 
 
5810
nsresult
 
5811
nsTypedSelection::DoAutoScroll(nsIPresContext *aPresContext, nsIFrame *aFrame, nsPoint& aPoint)
 
5812
{
 
5813
  if (!aPresContext || !aFrame)
 
5814
    return NS_ERROR_NULL_POINTER;
 
5815
 
 
5816
  // Find the closest view to the frame!
 
5817
 
 
5818
  nsIView *closestView = aFrame->GetClosestView();
 
5819
  if (!closestView)
 
5820
    return NS_ERROR_FAILURE;
 
5821
 
 
5822
  return DoAutoScrollView(aPresContext, closestView, aPoint, PR_TRUE);
 
5823
}
 
5824
 
 
5825
NS_IMETHODIMP
 
5826
nsTypedSelection::GetEnumerator(nsIEnumerator **aIterator)
 
5827
{
 
5828
  nsresult status = NS_ERROR_OUT_OF_MEMORY;
 
5829
  nsSelectionIterator *iterator =  new nsSelectionIterator(this);
 
5830
  if ( iterator && NS_FAILED(status = CallQueryInterface(iterator, aIterator)) )
 
5831
    delete iterator;
 
5832
  return status;
 
5833
}
 
5834
 
 
5835
 
 
5836
 
 
5837
/** RemoveAllRanges zeroes the selection
 
5838
 */
 
5839
NS_IMETHODIMP
 
5840
nsTypedSelection::RemoveAllRanges()
 
5841
{
 
5842
  if (!mFrameSelection)
 
5843
    return NS_OK;//nothing to do
 
5844
  nsCOMPtr<nsIPresContext>  presContext;
 
5845
  GetPresContext(getter_AddRefs(presContext));
 
5846
 
 
5847
 
 
5848
  nsresult  result = Clear(presContext);
 
5849
  if (NS_FAILED(result))
 
5850
    return result;
 
5851
  
 
5852
  // Turn off signal for table selection
 
5853
  mFrameSelection->ClearTableCellSelection();
 
5854
 
 
5855
  return mFrameSelection->NotifySelectionListeners(GetType());
 
5856
  // Also need to notify the frames!
 
5857
  // PresShell::CharacterDataChanged should do that on DocumentChanged
 
5858
}
 
5859
 
 
5860
/** AddRange adds the specified range to the selection
 
5861
 *  @param aRange is the range to be added
 
5862
 */
 
5863
NS_IMETHODIMP
 
5864
nsTypedSelection::AddRange(nsIDOMRange* aRange)
 
5865
{
 
5866
  if (!aRange) return NS_ERROR_NULL_POINTER;
 
5867
 
 
5868
  // This inserts a table cell range in proper document order
 
5869
  //  and returns NS_ERROR_FAILURE if range doesn't contain just one table cell
 
5870
  PRBool didAddRange;
 
5871
  nsresult result = addTableCellRange(aRange, &didAddRange);
 
5872
  if (NS_FAILED(result)) return result;
 
5873
 
 
5874
  if (!didAddRange)
 
5875
  {
 
5876
    result = AddItem(aRange);
 
5877
    if (NS_FAILED(result)) return result;
 
5878
  }
 
5879
 
 
5880
  PRInt32 count;
 
5881
  result = GetRangeCount(&count);
 
5882
  if (NS_FAILED(result)) return result;
 
5883
 
 
5884
  if (count <= 0)
 
5885
  {
 
5886
    NS_ASSERTION(0,"bad count after additem\n");
 
5887
    return NS_ERROR_FAILURE;
 
5888
  }
 
5889
  setAnchorFocusRange(count -1);
 
5890
  
 
5891
  nsCOMPtr<nsIPresContext>  presContext;
 
5892
  GetPresContext(getter_AddRefs(presContext));
 
5893
  selectFrames(presContext, aRange, PR_TRUE);        
 
5894
 
 
5895
  //ScrollIntoView(); this should not happen automatically
 
5896
  if (!mFrameSelection)
 
5897
    return NS_OK;//nothing to do
 
5898
 
 
5899
  return mFrameSelection->NotifySelectionListeners(GetType());
 
5900
}
 
5901
 
 
5902
NS_IMETHODIMP
 
5903
nsTypedSelection::RemoveRange(nsIDOMRange* aRange)
 
5904
{
 
5905
  if (!aRange)
 
5906
    return NS_ERROR_INVALID_ARG;
 
5907
  RemoveItem(aRange);
 
5908
  
 
5909
  nsCOMPtr<nsIPresContext>  presContext;
 
5910
  GetPresContext(getter_AddRefs(presContext));
 
5911
  selectFrames(presContext, aRange, PR_FALSE);        
 
5912
  if (aRange == mAnchorFocusRange.get())
 
5913
  {
 
5914
    PRInt32 cnt = mRangeArray.Count();
 
5915
    if (cnt > 0)
 
5916
    {
 
5917
      setAnchorFocusRange(cnt - 1);//reset anchor to LAST range.
 
5918
      ScrollIntoView();
 
5919
    }
 
5920
  }
 
5921
  if (!mFrameSelection)
 
5922
    return NS_OK;//nothing to do
 
5923
  return mFrameSelection->NotifySelectionListeners(GetType());
 
5924
}
 
5925
 
 
5926
 
 
5927
 
 
5928
/*
 
5929
 * Collapse sets the whole selection to be one point.
 
5930
 */
 
5931
NS_IMETHODIMP
 
5932
nsTypedSelection::Collapse(nsIDOMNode* aParentNode, PRInt32 aOffset)
 
5933
{
 
5934
  if (!aParentNode)
 
5935
    return NS_ERROR_INVALID_ARG;
 
5936
  mFrameSelection->InvalidateDesiredX();
 
5937
  if (!IsValidSelectionPoint(mFrameSelection, aParentNode))
 
5938
    return NS_ERROR_FAILURE;
 
5939
  nsresult result;
 
5940
  // Delete all of the current ranges
 
5941
  if (NS_FAILED(SetOriginalAnchorPoint(aParentNode,aOffset)))
 
5942
    return NS_ERROR_FAILURE; //???
 
5943
  nsCOMPtr<nsIPresContext>  presContext;
 
5944
  GetPresContext(getter_AddRefs(presContext));
 
5945
  Clear(presContext);
 
5946
 
 
5947
  // Turn off signal for table selection
 
5948
  if (mFrameSelection)
 
5949
    mFrameSelection->ClearTableCellSelection();
 
5950
 
 
5951
  nsCOMPtr<nsIDOMRange> range;
 
5952
  NS_NewRange(getter_AddRefs(range));
 
5953
  if (! range){
 
5954
    NS_ASSERTION(PR_FALSE,"Couldn't make a range - nsSelection::Collapse");
 
5955
    return NS_ERROR_UNEXPECTED;
 
5956
  }
 
5957
  result = range->SetEnd(aParentNode, aOffset);
 
5958
  if (NS_FAILED(result))
 
5959
    return result;
 
5960
  result = range->SetStart(aParentNode, aOffset);
 
5961
  if (NS_FAILED(result))
 
5962
    return result;
 
5963
 
 
5964
#ifdef DEBUG_SELECTION
 
5965
  if (aParentNode)
 
5966
  {
 
5967
    nsCOMPtr<nsIContent>content;
 
5968
    content = do_QueryInterface(aParentNode);
 
5969
    if (!content)
 
5970
      return NS_ERROR_FAILURE;
 
5971
 
 
5972
    const char *tagString;
 
5973
    content->Tag()->GetUTF8String(&tagString);
 
5974
    printf ("Sel. Collapse to %p %s %d\n", content, tagString, aOffset);
 
5975
  }
 
5976
  else {
 
5977
    printf ("Sel. Collapse set to null parent.\n");
 
5978
  }
 
5979
#endif
 
5980
 
 
5981
 
 
5982
  result = AddItem(range);
 
5983
  setAnchorFocusRange(0);
 
5984
  selectFrames(presContext, range,PR_TRUE);
 
5985
  if (NS_FAILED(result))
 
5986
    return result;
 
5987
  if (!mFrameSelection)
 
5988
    return NS_OK;//nothing to do
 
5989
  return mFrameSelection->NotifySelectionListeners(GetType());
 
5990
}
 
5991
 
 
5992
/*
 
5993
 * Sets the whole selection to be one point
 
5994
 * at the start of the current selection
 
5995
 */
 
5996
NS_IMETHODIMP
 
5997
nsTypedSelection::CollapseToStart()
 
5998
{
 
5999
  PRInt32 cnt;
 
6000
  nsresult rv = GetRangeCount(&cnt);
 
6001
  if (NS_FAILED(rv) || cnt <= 0)
 
6002
    return NS_ERROR_FAILURE;
 
6003
 
 
6004
  // Get the first range
 
6005
  nsIDOMRange* firstRange = mRangeArray[0];
 
6006
  if (!firstRange)
 
6007
    return NS_ERROR_FAILURE;
 
6008
 
 
6009
  nsCOMPtr<nsIDOMNode> parent;
 
6010
  rv = firstRange->GetStartContainer(getter_AddRefs(parent));
 
6011
  if (NS_SUCCEEDED(rv))
 
6012
  {
 
6013
    if (parent)
 
6014
    {
 
6015
      PRInt32 startOffset;
 
6016
      firstRange->GetStartOffset(&startOffset);
 
6017
      rv = Collapse(parent, startOffset);
 
6018
    } else {
 
6019
      // not very likely!
 
6020
      rv = NS_ERROR_FAILURE;
 
6021
    }
 
6022
  }
 
6023
  return rv;
 
6024
}
 
6025
 
 
6026
/*
 
6027
 * Sets the whole selection to be one point
 
6028
 * at the end of the current selection
 
6029
 */
 
6030
NS_IMETHODIMP
 
6031
nsTypedSelection::CollapseToEnd()
 
6032
{
 
6033
  PRInt32 cnt;
 
6034
  nsresult rv = GetRangeCount(&cnt);
 
6035
  if (NS_FAILED(rv) || cnt <= 0)
 
6036
    return NS_ERROR_FAILURE;
 
6037
 
 
6038
  // Get the last range
 
6039
  nsIDOMRange* lastRange = mRangeArray[cnt-1];
 
6040
  if (!lastRange)
 
6041
    return NS_ERROR_FAILURE;
 
6042
 
 
6043
  nsCOMPtr<nsIDOMNode> parent;
 
6044
  rv = lastRange->GetEndContainer(getter_AddRefs(parent));
 
6045
  if (NS_SUCCEEDED(rv))
 
6046
  {
 
6047
    if (parent)
 
6048
    {
 
6049
      PRInt32 endOffset;
 
6050
      lastRange->GetEndOffset(&endOffset);
 
6051
      rv = Collapse(parent, endOffset);
 
6052
    } else {
 
6053
      // not very likely!
 
6054
      rv = NS_ERROR_FAILURE;
 
6055
    }
 
6056
  }
 
6057
  return rv;
 
6058
}
 
6059
 
 
6060
/*
 
6061
 * IsCollapsed -- is the whole selection just one point, or unset?
 
6062
 */
 
6063
NS_IMETHODIMP
 
6064
nsTypedSelection::GetIsCollapsed(PRBool* aIsCollapsed)
 
6065
{
 
6066
  if (!aIsCollapsed)
 
6067
    return NS_ERROR_NULL_POINTER;
 
6068
 
 
6069
  PRInt32 cnt = mRangeArray.Count();;
 
6070
  if (cnt == 0)
 
6071
  {
 
6072
    *aIsCollapsed = PR_TRUE;
 
6073
    return NS_OK;
 
6074
  }
 
6075
  
 
6076
  if (cnt != 1)
 
6077
  {
 
6078
    *aIsCollapsed = PR_FALSE;
 
6079
    return NS_OK;
 
6080
  }
 
6081
  
 
6082
  return mRangeArray[0]->GetCollapsed(aIsCollapsed);
 
6083
}
 
6084
 
 
6085
NS_IMETHODIMP
 
6086
nsTypedSelection::GetRangeCount(PRInt32* aRangeCount)
 
6087
{
 
6088
  if (!aRangeCount) 
 
6089
    return NS_ERROR_NULL_POINTER;
 
6090
 
 
6091
  *aRangeCount = mRangeArray.Count();
 
6092
 
 
6093
  return NS_OK;
 
6094
}
 
6095
 
 
6096
NS_IMETHODIMP
 
6097
nsTypedSelection::GetRangeAt(PRInt32 aIndex, nsIDOMRange** aReturn)
 
6098
{
 
6099
  if (!aReturn)
 
6100
    return NS_ERROR_NULL_POINTER;
 
6101
 
 
6102
  PRInt32 cnt = mRangeArray.Count();
 
6103
  if (aIndex < 0 || aIndex >= cnt)
 
6104
    return NS_ERROR_INVALID_ARG;
 
6105
 
 
6106
  *aReturn = mRangeArray[aIndex];
 
6107
  NS_IF_ADDREF(*aReturn);
 
6108
 
 
6109
  return NS_OK;
 
6110
}
 
6111
 
 
6112
#ifdef OLD_SELECTION
 
6113
 
 
6114
//may change parameters may not.
 
6115
//return NS_ERROR_FAILURE if invalid new selection between anchor and passed in parameters
 
6116
NS_IMETHODIMP
 
6117
nsTypedSelection::FixupSelectionPoints(nsIDOMRange *aRange , nsDirection *aDir, PRBool *aFixupState)
 
6118
{
 
6119
  if (!aRange || !aFixupState)
 
6120
    return NS_ERROR_NULL_POINTER;
 
6121
  *aFixupState = PR_FALSE;
 
6122
  nsresult res;
 
6123
 
 
6124
  //startNode is the beginning or "anchor" of the range
 
6125
  //end Node is the end or "focus of the range
 
6126
  nsCOMPtr<nsIDOMNode> startNode;
 
6127
  nsCOMPtr<nsIDOMNode> endNode;
 
6128
  PRInt32 startOffset;
 
6129
  PRInt32 endOffset;
 
6130
  nsresult result;
 
6131
  if (*aDir == eDirNext)
 
6132
  {
 
6133
    if (NS_FAILED(GetOriginalAnchorPoint(getter_AddRefs(startNode), &startOffset)))
 
6134
    {
 
6135
      aRange->GetStartParent(getter_AddRefs(startNode));
 
6136
      aRange->GetStartOffset(&startOffset);
 
6137
    }
 
6138
    aRange->GetEndParent(getter_AddRefs(endNode));
 
6139
    aRange->GetEndOffset(&endOffset);
 
6140
  }
 
6141
  else
 
6142
  {
 
6143
    if (NS_FAILED(GetOriginalAnchorPoint(getter_AddRefs(startNode), &startOffset)))
 
6144
    {
 
6145
      aRange->GetEndParent(getter_AddRefs(startNode));
 
6146
      aRange->GetEndOffset(&startOffset);
 
6147
    }
 
6148
    aRange->GetStartParent(getter_AddRefs(endNode));
 
6149
    aRange->GetStartOffset(&endOffset);
 
6150
  }
 
6151
  if (!startNode || !endNode)
 
6152
    return NS_ERROR_FAILURE;
 
6153
 
 
6154
  // if end node is a tbody then all bets are off we cannot select "rows"
 
6155
  nsIAtom *atom = GetTag(endNode);
 
6156
  if (atom == nsHTMLAtoms::tbody)
 
6157
    return NS_ERROR_FAILURE; //cannot select INTO row node ony cells
 
6158
 
 
6159
  //get common parent
 
6160
  nsCOMPtr<nsIDOMNode> parent;
 
6161
  nsCOMPtr<nsIDOMRange> subRange;
 
6162
  NS_NewRange(getter_AddRefs(subRange));
 
6163
  if (!subRange) return NS_ERROR_OUT_OF_MEMORY
 
6164
 
 
6165
  result = subRange->SetStart(startNode,startOffset);
 
6166
  if (NS_FAILED(result))
 
6167
    return result;
 
6168
  result = subRange->SetEnd(endNode,endOffset);
 
6169
  if (NS_FAILED(result))
 
6170
  {
 
6171
    result = subRange->SetEnd(startNode,startOffset);
 
6172
    if (NS_FAILED(result))
 
6173
      return result;
 
6174
    result = subRange->SetStart(endNode,endOffset);
 
6175
    if (NS_FAILED(result))
 
6176
      return result;
 
6177
  }
 
6178
 
 
6179
  res = subRange->GetCommonParent(getter_AddRefs(parent));
 
6180
  if (NS_FAILED(res) || !parent)
 
6181
    return res;
 
6182
 
 
6183
  //look for dest. if you see a cell you are in "cell mode"
 
6184
  //if you see a table you select "whole" table
 
6185
 
 
6186
  //src first 
 
6187
  nsCOMPtr<nsIDOMNode> tempNode;
 
6188
  nsCOMPtr<nsIDOMNode> tempNode2;
 
6189
  PRBool cellMode = PR_FALSE;
 
6190
  PRBool dirtystart = PR_FALSE;
 
6191
  PRBool dirtyend = PR_FALSE;
 
6192
  if (startNode != endNode)
 
6193
  {
 
6194
    if (parent != startNode)
 
6195
    {
 
6196
      result = startNode->GetParentNode(getter_AddRefs(tempNode));
 
6197
      if (NS_FAILED(result) || !tempNode)
 
6198
        return NS_ERROR_FAILURE;
 
6199
      while (tempNode != parent)
 
6200
      {
 
6201
        atom = GetTag(tempNode);
 
6202
        if (atom == nsHTMLAtoms::table) //select whole table  if in cell mode, wait for cell
 
6203
        {
 
6204
          result = ParentOffset(tempNode, getter_AddRefs(startNode), &startOffset);
 
6205
          if (NS_FAILED(result))
 
6206
            return NS_ERROR_FAILURE;
 
6207
          if (*aDir == eDirPrevious) //select after
 
6208
            startOffset++;
 
6209
          dirtystart = PR_TRUE;
 
6210
          cellMode = PR_FALSE;
 
6211
        }
 
6212
        else if (atom == nsHTMLAtoms::td ||
 
6213
                 atom == nsHTMLAtoms::th) //you are in "cell" mode put selection to end of cell
 
6214
        {
 
6215
          cellMode = PR_TRUE;
 
6216
          result = ParentOffset(tempNode, getter_AddRefs(startNode), &startOffset);
 
6217
          if (NS_FAILED(result))
 
6218
            return result;
 
6219
          if (*aDir == eDirPrevious) //select after
 
6220
            startOffset++;
 
6221
          dirtystart = PR_TRUE;
 
6222
        }
 
6223
        result = tempNode->GetParentNode(getter_AddRefs(tempNode2));
 
6224
        if (NS_FAILED(result) || !tempNode2)
 
6225
          return NS_ERROR_FAILURE;
 
6226
        tempNode = tempNode2;
 
6227
      }
 
6228
    }
 
6229
  
 
6230
  //now for dest node
 
6231
    if (parent != endNode)
 
6232
    {
 
6233
      result = endNode->GetParentNode(getter_AddRefs(tempNode));
 
6234
      PRBool found = !cellMode;
 
6235
      if (NS_FAILED(result) || !tempNode)
 
6236
        return NS_ERROR_FAILURE;
 
6237
      while (tempNode != parent)
 
6238
      {
 
6239
        atom = GetTag(tempNode);
 
6240
        if (atom == nsHTMLAtoms::table) //select whole table  if in cell mode, wait for cell
 
6241
        {
 
6242
          if (!cellMode)
 
6243
          {
 
6244
            result = ParentOffset(tempNode, getter_AddRefs(endNode), &endOffset);
 
6245
            if (NS_FAILED(result))
 
6246
              return result;
 
6247
            if (*aDir == eDirNext) //select after
 
6248
              endOffset++;
 
6249
            dirtyend = PR_TRUE;
 
6250
          }
 
6251
          else
 
6252
            found = PR_FALSE; //didnt find the right cell yet
 
6253
        }
 
6254
        else if (atom == nsHTMLAtoms::td ||
 
6255
                 atom == nsHTMLAtoms::th) //you are in "cell" mode put selection to end of cell
 
6256
        {
 
6257
          result = ParentOffset(tempNode, getter_AddRefs(endNode), &endOffset);
 
6258
          if (NS_FAILED(result))
 
6259
            return result;
 
6260
          if (*aDir == eDirNext) //select after
 
6261
            endOffset++;
 
6262
          found = PR_TRUE;
 
6263
          dirtyend = PR_TRUE;
 
6264
        }
 
6265
        result = tempNode->GetParentNode(getter_AddRefs(tempNode2));
 
6266
        if (NS_FAILED(result) || !tempNode2)
 
6267
          return NS_ERROR_FAILURE;
 
6268
        tempNode = tempNode2;
 
6269
      }
 
6270
      if (!found)
 
6271
        return NS_ERROR_FAILURE;
 
6272
    }
 
6273
  }
 
6274
  if (*aDir == eDirNext)
 
6275
  {
 
6276
    if (FetchAnchorNode() == startNode.get() && FetchFocusNode() == endNode.get() &&
 
6277
      FetchAnchorOffset() == startOffset && FetchFocusOffset() == endOffset)
 
6278
    {
 
6279
      *aFixupState = PR_FALSE;
 
6280
      return NS_ERROR_FAILURE;//nothing to do
 
6281
    }
 
6282
  }
 
6283
  else
 
6284
  {
 
6285
    if (FetchAnchorNode() == endNode.get() && FetchFocusNode() == startNode.get() &&
 
6286
      FetchAnchorOffset() == endOffset && FetchFocusOffset() == startOffset)
 
6287
    {
 
6288
      *aFixupState = PR_FALSE;
 
6289
      return NS_ERROR_FAILURE;//nothing to do
 
6290
    }
 
6291
  }
 
6292
  if (mFixupState && !dirtyend && !dirtystart)//no mor fixup! all bets off
 
6293
  {
 
6294
    dirtystart = PR_TRUE;//force a reset of anchor positions
 
6295
    dirtystart = PR_TRUE;
 
6296
    *aFixupState = PR_TRUE;//redraw all selection here
 
6297
    mFixupState = PR_FALSE;//no more fixup for next time
 
6298
  }
 
6299
  else
 
6300
  if ((dirtystart || dirtyend) && *aDir != mDirection) //fixup took place but new direction all bets are off
 
6301
  {
 
6302
    *aFixupState = PR_TRUE;
 
6303
    //mFixupState = PR_FALSE;
 
6304
  }
 
6305
  else
 
6306
  if (dirtystart && (FetchAnchorNode() != startNode.get() || FetchAnchorOffset() != startOffset))
 
6307
  {
 
6308
    *aFixupState = PR_TRUE;
 
6309
    mFixupState  = PR_TRUE;
 
6310
  }
 
6311
  else
 
6312
  if (dirtyend && (FetchFocusNode() != endNode.get() || FetchFocusOffset() != endOffset))
 
6313
  {
 
6314
    *aFixupState = PR_TRUE;
 
6315
    mFixupState  = PR_TRUE;
 
6316
  }
 
6317
  else
 
6318
  {
 
6319
    mFixupState = dirtystart || dirtyend;
 
6320
    *aFixupState = PR_FALSE;
 
6321
  }
 
6322
  if (dirtystart || dirtyend){
 
6323
    if (*aDir == eDirNext)
 
6324
    {
 
6325
      if (NS_FAILED(aRange->SetStart(startNode,startOffset)) || NS_FAILED(aRange->SetEnd(endNode, endOffset)))
 
6326
      {
 
6327
        *aDir = eDirPrevious;
 
6328
        aRange->SetStart(endNode, endOffset);
 
6329
        aRange->SetEnd(startNode, startOffset);
 
6330
      }
 
6331
    }
 
6332
    else
 
6333
    {
 
6334
      if (NS_FAILED(aRange->SetStart(endNode,endOffset)) || NS_FAILED(aRange->SetEnd(startNode, startOffset)))
 
6335
      {
 
6336
        *aDir = eDirNext;
 
6337
        aRange->SetStart(startNode, startOffset);
 
6338
        aRange->SetEnd(endNode, endOffset);
 
6339
      }
 
6340
    }
 
6341
  }
 
6342
  return NS_OK;
 
6343
}
 
6344
#endif //OLD_SELECTION
 
6345
 
 
6346
 
 
6347
 
 
6348
 
 
6349
NS_IMETHODIMP
 
6350
nsTypedSelection::SetOriginalAnchorPoint(nsIDOMNode *aNode, PRInt32 aOffset)
 
6351
{
 
6352
  if (!aNode){
 
6353
    mOriginalAnchorRange = 0;
 
6354
    return NS_OK;
 
6355
  }
 
6356
  nsCOMPtr<nsIDOMRange> newRange;
 
6357
  nsresult result;
 
6358
  NS_NewRange(getter_AddRefs(newRange));
 
6359
  if (!newRange) return NS_ERROR_OUT_OF_MEMORY;
 
6360
 
 
6361
  result = newRange->SetStart(aNode,aOffset);
 
6362
  if (NS_FAILED(result))
 
6363
    return result;
 
6364
  result = newRange->SetEnd(aNode,aOffset);
 
6365
  if (NS_FAILED(result))
 
6366
    return result;
 
6367
 
 
6368
  mOriginalAnchorRange = newRange;
 
6369
  return result;
 
6370
}
 
6371
 
 
6372
 
 
6373
 
 
6374
NS_IMETHODIMP
 
6375
nsTypedSelection::GetOriginalAnchorPoint(nsIDOMNode **aNode, PRInt32 *aOffset)
 
6376
{
 
6377
  if (!aNode || !aOffset || !mOriginalAnchorRange)
 
6378
    return NS_ERROR_NULL_POINTER;
 
6379
  nsresult result;
 
6380
  result = mOriginalAnchorRange->GetStartContainer(aNode);
 
6381
  if (NS_FAILED(result))
 
6382
    return result;
 
6383
  result = mOriginalAnchorRange->GetStartOffset(aOffset);
 
6384
  return result;
 
6385
}
 
6386
 
 
6387
 
 
6388
/*
 
6389
utility function
 
6390
*/
 
6391
NS_IMETHODIMP
 
6392
nsTypedSelection::CopyRangeToAnchorFocus(nsIDOMRange *aRange)
 
6393
{
 
6394
  nsCOMPtr<nsIDOMNode> startNode;
 
6395
  nsCOMPtr<nsIDOMNode> endNode;
 
6396
  PRInt32 startOffset;
 
6397
  PRInt32 endOffset;
 
6398
  aRange->GetStartContainer(getter_AddRefs(startNode));
 
6399
  aRange->GetEndContainer(getter_AddRefs(endNode));
 
6400
  aRange->GetStartOffset(&startOffset);
 
6401
  aRange->GetEndOffset(&endOffset);
 
6402
  if (NS_FAILED(mAnchorFocusRange->SetStart(startNode,startOffset)))
 
6403
  {
 
6404
    if (NS_FAILED(mAnchorFocusRange->SetEnd(endNode,endOffset)))
 
6405
      return NS_ERROR_FAILURE;//???
 
6406
    if (NS_FAILED(mAnchorFocusRange->SetStart(startNode,startOffset)))
 
6407
      return NS_ERROR_FAILURE;//???
 
6408
  }
 
6409
  else if (NS_FAILED(mAnchorFocusRange->SetEnd(endNode,endOffset)))
 
6410
          return NS_ERROR_FAILURE;//???
 
6411
  return NS_OK;
 
6412
}
 
6413
 
 
6414
/*
 
6415
Notes which might come in handy for extend:
 
6416
 
 
6417
We can tell the direction of the selection by asking for the anchors selection
 
6418
if the begin is less than the end then we know the selection is to the "right".
 
6419
else it is a backwards selection.
 
6420
a = anchor
 
6421
1 = old cursor
 
6422
2 = new cursor
 
6423
 
 
6424
  if (a <= 1 && 1 <=2)    a,1,2  or (a1,2)
 
6425
  if (a < 2 && 1 > 2)     a,2,1
 
6426
  if (1 < a && a <2)      1,a,2
 
6427
  if (a > 2 && 2 >1)      1,2,a
 
6428
  if (2 < a && a <1)      2,a,1
 
6429
  if (a > 1 && 1 >2)      2,1,a
 
6430
then execute
 
6431
a  1  2 select from 1 to 2
 
6432
a  2  1 deselect from 2 to 1
 
6433
1  a  2 deselect from 1 to a select from a to 2
 
6434
1  2  a deselect from 1 to 2
 
6435
2  1  a = continue selection from 2 to 1
 
6436
*/
 
6437
 
 
6438
 
 
6439
/*
 
6440
 * Extend extends the selection away from the anchor.
 
6441
 * We don't need to know the direction, because we always change the focus.
 
6442
 */
 
6443
NS_IMETHODIMP
 
6444
nsTypedSelection::Extend(nsIDOMNode* aParentNode, PRInt32 aOffset)
 
6445
{
 
6446
  if (!aParentNode)
 
6447
    return NS_ERROR_INVALID_ARG;
 
6448
 
 
6449
  // First, find the range containing the old focus point:
 
6450
  if (!mAnchorFocusRange)
 
6451
    return NS_ERROR_NOT_INITIALIZED;
 
6452
 
 
6453
  nsresult res;
 
6454
  if (!IsValidSelectionPoint(mFrameSelection, aParentNode))
 
6455
    return NS_ERROR_FAILURE;
 
6456
 
 
6457
  //mFrameSelection->InvalidateDesiredX();
 
6458
  nsCOMPtr<nsIDOMRange> difRange;
 
6459
  NS_NewRange(getter_AddRefs(difRange));
 
6460
  nsCOMPtr<nsIDOMRange> range;
 
6461
 
 
6462
  if (FetchFocusNode() ==  aParentNode && FetchFocusOffset() == aOffset)
 
6463
    return NS_ERROR_FAILURE;//same node nothing to do!
 
6464
 
 
6465
  res = mAnchorFocusRange->CloneRange(getter_AddRefs(range));
 
6466
  //range = mAnchorFocusRange;
 
6467
 
 
6468
  nsCOMPtr<nsIDOMNode> startNode;
 
6469
  nsCOMPtr<nsIDOMNode> endNode;
 
6470
  PRInt32 startOffset;
 
6471
  PRInt32 endOffset;
 
6472
 
 
6473
  range->GetStartContainer(getter_AddRefs(startNode));
 
6474
  range->GetEndContainer(getter_AddRefs(endNode));
 
6475
  range->GetStartOffset(&startOffset);
 
6476
  range->GetEndOffset(&endOffset);
 
6477
 
 
6478
 
 
6479
  nsDirection dir = GetDirection();
 
6480
  PRBool fixupState = PR_FALSE; //if there was a previous fixup the optimal drawing erasing will NOT work
 
6481
  if (NS_FAILED(res))
 
6482
    return res;
 
6483
 
 
6484
  NS_NewRange(getter_AddRefs(difRange));
 
6485
  //compare anchor to old cursor.
 
6486
 
 
6487
  if (NS_FAILED(res))
 
6488
    return res;
 
6489
  PRInt32 result1 = ComparePoints(FetchAnchorNode(), FetchAnchorOffset() 
 
6490
                                  ,FetchFocusNode(), FetchFocusOffset());
 
6491
  //compare old cursor to new cursor
 
6492
  PRInt32 result2 = ComparePoints(FetchFocusNode(), FetchFocusOffset(),
 
6493
                            aParentNode, aOffset );
 
6494
  //compare anchor to new cursor
 
6495
  PRInt32 result3 = ComparePoints(FetchAnchorNode(), FetchAnchorOffset(),
 
6496
                            aParentNode , aOffset );
 
6497
 
 
6498
  if (result2 == 0) //not selecting anywhere
 
6499
    return NS_OK;
 
6500
 
 
6501
  nsCOMPtr<nsIPresContext>  presContext;
 
6502
  GetPresContext(getter_AddRefs(presContext));
 
6503
  if ((result1 == 0 && result3 < 0) || (result1 <= 0 && result2 < 0)){//a1,2  a,1,2
 
6504
    //select from 1 to 2 unless they are collapsed
 
6505
    res = range->SetEnd(aParentNode,aOffset);
 
6506
    if (NS_FAILED(res))
 
6507
      return res;
 
6508
    dir = eDirNext;
 
6509
    res = difRange->SetEnd(FetchEndParent(range), FetchEndOffset(range));
 
6510
    res |= difRange->SetStart(FetchFocusNode(), FetchFocusOffset());
 
6511
    if (NS_FAILED(res))
 
6512
      return res;
 
6513
#ifdef OLD_SELECTION
 
6514
    res = FixupSelectionPoints(range, &dir, &fixupState);
 
6515
#endif
 
6516
    if (NS_FAILED(res))
 
6517
      return res;
 
6518
    if (fixupState) 
 
6519
    {
 
6520
#ifdef OLD_SELECTION
 
6521
      selectFrames(mAnchorFocusRange, PR_FALSE);
 
6522
      selectFrames(range, PR_TRUE);
 
6523
#endif
 
6524
    }
 
6525
    else{
 
6526
      selectFrames(presContext, difRange , PR_TRUE);
 
6527
    }
 
6528
    res = CopyRangeToAnchorFocus(range);
 
6529
    if (NS_FAILED(res))
 
6530
      return res;
 
6531
  }
 
6532
  else if (result1 == 0 && result3 > 0){//2, a1
 
6533
    //select from 2 to 1a
 
6534
    dir = eDirPrevious;
 
6535
    res = range->SetStart(aParentNode,aOffset);
 
6536
    if (NS_FAILED(res))
 
6537
      return res;
 
6538
#ifdef OLD_SELECTION
 
6539
    res = FixupSelectionPoints(range, &dir, &fixupState);
 
6540
    if (NS_FAILED(res))
 
6541
      return res;
 
6542
    if (fixupState) //unselect previous and select new state has changed to not fixed up
 
6543
    {
 
6544
      selectFrames(mAnchorFocusRange, PR_FALSE);
 
6545
      selectFrames(range, PR_TRUE);
 
6546
    }
 
6547
    else
 
6548
#endif
 
6549
      selectFrames(presContext, range, PR_TRUE);
 
6550
    res = CopyRangeToAnchorFocus(range);
 
6551
    if (NS_FAILED(res))
 
6552
      return res;
 
6553
  }
 
6554
  else if (result3 <= 0 && result2 >= 0) {//a,2,1 or a2,1 or a,21 or a21
 
6555
    //deselect from 2 to 1
 
6556
    res = difRange->SetEnd(FetchFocusNode(), FetchFocusOffset());
 
6557
    res |= difRange->SetStart(aParentNode, aOffset);
 
6558
    if (NS_FAILED(res))
 
6559
      return res;
 
6560
 
 
6561
    res = range->SetEnd(aParentNode,aOffset);
 
6562
    if (NS_FAILED(res))
 
6563
      return res;
 
6564
#ifdef OLD_SELECTION    
 
6565
    dir = eDirNext;
 
6566
    res = FixupSelectionPoints(range, &dir, &fixupState);
 
6567
#endif
 
6568
    if (NS_FAILED(res))
 
6569
      return res;
 
6570
    if (fixupState) //unselect previous and select new state has changed to not fixed up
 
6571
    {
 
6572
#ifdef OLD_SELECTION    
 
6573
      selectFrames(mAnchorFocusRange, PR_FALSE);
 
6574
      selectFrames(range, PR_TRUE);
 
6575
#endif
 
6576
    }
 
6577
    else 
 
6578
    {
 
6579
      res = CopyRangeToAnchorFocus(range);
 
6580
      if (NS_FAILED(res))
 
6581
        return res;
 
6582
      RemoveItem(mAnchorFocusRange);
 
6583
      selectFrames(presContext, difRange, PR_FALSE);//deselect now if fixup succeeded
 
6584
      AddItem(mAnchorFocusRange);
 
6585
      difRange->SetEnd(FetchEndParent(range),FetchEndOffset(range));
 
6586
      selectFrames(presContext, difRange, PR_TRUE);//must reselect last node maybe more if fixup did something
 
6587
    }
 
6588
  }
 
6589
  else if (result1 >= 0 && result3 <= 0) {//1,a,2 or 1a,2 or 1,a2 or 1a2
 
6590
    if (GetDirection() == eDirPrevious){
 
6591
      res = range->SetStart(endNode,endOffset);
 
6592
      if (NS_FAILED(res))
 
6593
        return res;
 
6594
    }
 
6595
    dir = eDirNext;
 
6596
    res = range->SetEnd(aParentNode,aOffset);
 
6597
    if (NS_FAILED(res))
 
6598
      return res;
 
6599
#ifdef OLD_SELECTION
 
6600
    res = FixupSelectionPoints(range, &dir, &fixupState);
 
6601
    if (NS_FAILED(res))
 
6602
      return res;
 
6603
 
 
6604
    if (fixupState) //unselect previous and select new state has changed to not fixed up
 
6605
    {
 
6606
      selectFrames(mAnchorFocusRange, PR_FALSE);
 
6607
      selectFrames(range, PR_TRUE);
 
6608
    }
 
6609
    else 
 
6610
#endif
 
6611
    {
 
6612
      if (FetchFocusNode() != FetchAnchorNode() || FetchFocusOffset() != FetchAnchorOffset() ){//if collapsed diff dont do anything
 
6613
        res = difRange->SetStart(FetchFocusNode(), FetchFocusOffset());
 
6614
        res |= difRange->SetEnd(FetchAnchorNode(), FetchAnchorOffset());
 
6615
        if (NS_FAILED(res))
 
6616
          return res;
 
6617
        res = CopyRangeToAnchorFocus(range);
 
6618
        if (NS_FAILED(res))
 
6619
          return res;
 
6620
        //deselect from 1 to a
 
6621
        RemoveItem(mAnchorFocusRange);
 
6622
        selectFrames(presContext, difRange , PR_FALSE);
 
6623
        AddItem(mAnchorFocusRange);
 
6624
      }
 
6625
      else
 
6626
      {
 
6627
        res = CopyRangeToAnchorFocus(range);
 
6628
        if (NS_FAILED(res))
 
6629
          return res;
 
6630
      }
 
6631
      //select from a to 2
 
6632
      selectFrames(presContext, range , PR_TRUE);
 
6633
    }
 
6634
  }
 
6635
  else if (result2 <= 0 && result3 >= 0) {//1,2,a or 12,a or 1,2a or 12a
 
6636
    //deselect from 1 to 2
 
6637
    res = difRange->SetEnd(aParentNode, aOffset);
 
6638
    res |= difRange->SetStart(FetchFocusNode(), FetchFocusOffset());
 
6639
    if (NS_FAILED(res))
 
6640
      return res;
 
6641
    dir = eDirPrevious;
 
6642
    res = range->SetStart(aParentNode,aOffset);
 
6643
    if (NS_FAILED(res))
 
6644
      return res;
 
6645
 
 
6646
#ifdef OLD_SELECTION
 
6647
    res = FixupSelectionPoints(range, &dir, &fixupState);
 
6648
#endif
 
6649
    if (NS_FAILED(res))
 
6650
      return res;
 
6651
    if (fixupState) //unselect previous and select new state has changed to not fixed up
 
6652
    {
 
6653
#ifdef OLD_SELECTION
 
6654
      selectFrames(mAnchorFocusRange, PR_FALSE);
 
6655
      selectFrames(range, PR_TRUE);
 
6656
#endif
 
6657
    }
 
6658
    else 
 
6659
    {
 
6660
      res = CopyRangeToAnchorFocus(range);
 
6661
      if (NS_FAILED(res))
 
6662
        return res;
 
6663
      RemoveItem(mAnchorFocusRange);
 
6664
      selectFrames(presContext, difRange , PR_FALSE);
 
6665
      AddItem(mAnchorFocusRange);
 
6666
      difRange->SetStart(FetchStartParent(range),FetchStartOffset(range));
 
6667
      selectFrames(presContext, difRange, PR_TRUE);//must reselect last node
 
6668
    }
 
6669
  }
 
6670
  else if (result3 >= 0 && result1 <= 0) {//2,a,1 or 2a,1 or 2,a1 or 2a1
 
6671
    if (GetDirection() == eDirNext){
 
6672
      range->SetEnd(startNode,startOffset);
 
6673
    }
 
6674
    dir = eDirPrevious;
 
6675
    res = range->SetStart(aParentNode,aOffset);
 
6676
    if (NS_FAILED(res))
 
6677
      return res;
 
6678
#ifdef OLD_SELECTION
 
6679
    res = FixupSelectionPoints(range, &dir, &fixupState);
 
6680
    if (NS_FAILED(res))
 
6681
      return res;
 
6682
    if (fixupState) //unselect previous and select new state has changed to not fixed up
 
6683
    {
 
6684
      selectFrames(mAnchorFocusRange, PR_FALSE);
 
6685
      selectFrames(range, PR_TRUE);
 
6686
    }
 
6687
    else
 
6688
#endif
 
6689
    {
 
6690
      //deselect from a to 1
 
6691
      if (FetchFocusNode() != FetchAnchorNode() || FetchFocusOffset() != FetchAnchorOffset() ){//if collapsed diff dont do anything
 
6692
        res = difRange->SetStart(FetchAnchorNode(), FetchAnchorOffset());
 
6693
        res |= difRange->SetEnd(FetchFocusNode(), FetchFocusOffset());
 
6694
        res = CopyRangeToAnchorFocus(range);
 
6695
        if (NS_FAILED(res))
 
6696
          return res;
 
6697
        RemoveItem(mAnchorFocusRange);
 
6698
        selectFrames(presContext, difRange, 0);
 
6699
        AddItem(mAnchorFocusRange);
 
6700
      }
 
6701
      else
 
6702
      {
 
6703
        res = CopyRangeToAnchorFocus(range);
 
6704
        if (NS_FAILED(res))
 
6705
          return res;
 
6706
      }
 
6707
      //select from 2 to a
 
6708
      selectFrames(presContext, range , PR_TRUE);
 
6709
    }
 
6710
  }
 
6711
  else if (result2 >= 0 && result1 >= 0) {//2,1,a or 21,a or 2,1a or 21a
 
6712
    //select from 2 to 1
 
6713
    res = range->SetStart(aParentNode,aOffset);
 
6714
    if (NS_FAILED(res))
 
6715
      return res;
 
6716
    dir = eDirPrevious;
 
6717
    res = difRange->SetEnd(FetchFocusNode(), FetchFocusOffset());
 
6718
    res |= difRange->SetStart(FetchStartParent(range), FetchStartOffset(range));
 
6719
    if (NS_FAILED(res))
 
6720
      return res;
 
6721
 
 
6722
#ifdef OLD_SELECTION
 
6723
    res = FixupSelectionPoints(range, &dir, &fixupState);
 
6724
#endif
 
6725
    if (NS_FAILED(res))
 
6726
      return res;
 
6727
    if (fixupState) //unselect previous and select new state has changed to not fixed up
 
6728
    {
 
6729
#ifdef OLD_SELECTION
 
6730
      selectFrames(mAnchorFocusRange, PR_FALSE);
 
6731
      selectFrames(range, PR_TRUE);
 
6732
#endif
 
6733
    }
 
6734
    else {
 
6735
      selectFrames(presContext, difRange, PR_TRUE);
 
6736
    }
 
6737
    res = CopyRangeToAnchorFocus(range);
 
6738
    if (NS_FAILED(res))
 
6739
      return res;
 
6740
  }
 
6741
 
 
6742
  DEBUG_OUT_RANGE(range);
 
6743
#if 0
 
6744
  if (eDirNext == mDirection)
 
6745
    printf("    direction = 1  LEFT TO RIGHT\n");
 
6746
  else
 
6747
    printf("    direction = 0  RIGHT TO LEFT\n");
 
6748
#endif
 
6749
  SetDirection(dir);
 
6750
#ifdef DEBUG_SELECTION
 
6751
  if (aParentNode)
 
6752
  {
 
6753
    nsCOMPtr<nsIContent>content;
 
6754
    content = do_QueryInterface(aParentNode);
 
6755
 
 
6756
    const char *tagString;
 
6757
    content->Tag()->GetUTF8String(&tagString);
 
6758
    printf ("Sel. Extend to %p %s %d\n", content, tagString, aOffset);
 
6759
  }
 
6760
  else {
 
6761
    printf ("Sel. Extend set to null parent.\n");
 
6762
  }
 
6763
#endif
 
6764
  if (!mFrameSelection)
 
6765
    return NS_OK;//nothing to do
 
6766
  return mFrameSelection->NotifySelectionListeners(GetType());
 
6767
}
 
6768
 
 
6769
static nsresult
 
6770
GetChildOffset(nsIDOMNode *aChild, nsIDOMNode *aParent, PRInt32 &aOffset)
 
6771
{
 
6772
  NS_ASSERTION((aChild && aParent), "bad args");
 
6773
  nsCOMPtr<nsIContent> content = do_QueryInterface(aParent);
 
6774
  nsCOMPtr<nsIContent> cChild = do_QueryInterface(aChild);
 
6775
 
 
6776
  if (!cChild || !content)
 
6777
    return NS_ERROR_NULL_POINTER;
 
6778
 
 
6779
  aOffset = content->IndexOf(cChild);
 
6780
 
 
6781
  return NS_OK;
 
6782
}
 
6783
 
 
6784
NS_IMETHODIMP
 
6785
nsTypedSelection::SelectAllChildren(nsIDOMNode* aParentNode)
 
6786
{
 
6787
  NS_ENSURE_ARG_POINTER(aParentNode);
 
6788
  
 
6789
  if (mFrameSelection) 
 
6790
  {
 
6791
    mFrameSelection->PostReason(nsISelectionListener::SELECTALL_REASON);
 
6792
  }
 
6793
  nsresult result = Collapse(aParentNode, 0);
 
6794
  if (NS_SUCCEEDED(result))
 
6795
  {
 
6796
    nsCOMPtr<nsIDOMNode>lastChild;
 
6797
    result = aParentNode->GetLastChild(getter_AddRefs(lastChild));
 
6798
    if ((NS_SUCCEEDED(result)) && lastChild)
 
6799
    {
 
6800
      PRInt32 numBodyChildren=0;
 
6801
      GetChildOffset(lastChild, aParentNode, numBodyChildren);
 
6802
      if (mFrameSelection) 
 
6803
      {
 
6804
        mFrameSelection->PostReason(nsISelectionListener::SELECTALL_REASON);
 
6805
      }
 
6806
      result = Extend(aParentNode, numBodyChildren+1);
 
6807
    }
 
6808
  }
 
6809
  return result;
 
6810
}
 
6811
 
 
6812
NS_IMETHODIMP
 
6813
nsTypedSelection::ContainsNode(nsIDOMNode* aNode, PRBool aRecursive, PRBool* aYes)
 
6814
{
 
6815
  if (!aYes)
 
6816
    return NS_ERROR_NULL_POINTER;
 
6817
  *aYes = PR_FALSE;
 
6818
 
 
6819
  // Iterate over the ranges in the selection checking for the content:
 
6820
  PRInt32 cnt = mRangeArray.Count();
 
6821
  for (PRInt32 i = 0; i < cnt; ++i)
 
6822
  {
 
6823
    nsIDOMRange* range = mRangeArray[i];
 
6824
    if (!range)
 
6825
      return NS_ERROR_UNEXPECTED;
 
6826
 
 
6827
    nsCOMPtr<nsIContent> content (do_QueryInterface(aNode));
 
6828
    if (content)
 
6829
    {
 
6830
      if (IsNodeIntersectsRange(content, range))
 
6831
      {
 
6832
        // If recursive, then we're done -- IsNodeIntersectsRange does the right thing
 
6833
        if (aRecursive)
 
6834
        {
 
6835
          *aYes = PR_TRUE;
 
6836
          return NS_OK;
 
6837
        }
 
6838
 
 
6839
        // else not recursive -- node itself must be contained,
 
6840
        // so we need to do more checking
 
6841
        PRBool nodeStartsBeforeRange, nodeEndsAfterRange;
 
6842
        if (NS_SUCCEEDED(nsRange::CompareNodeToRange(content, range,
 
6843
                                                     &nodeStartsBeforeRange,
 
6844
                                                     &nodeEndsAfterRange)))
 
6845
        {
 
6846
#ifdef DEBUG_ContainsNode
 
6847
          nsAutoString name, value;
 
6848
          aNode->GetNodeName(name);
 
6849
          aNode->GetNodeValue(value);
 
6850
          printf("%s [%s]: %d, %d\n", NS_LossyConvertUCS2toASCII(name).get(),
 
6851
                 NS_LossyConvertUCS2toASCII(value).get(),
 
6852
                 nodeStartsBeforeRange, nodeEndsAfterRange);
 
6853
#endif
 
6854
          PRUint16 nodeType;
 
6855
          aNode->GetNodeType(&nodeType);
 
6856
          if ((!nodeStartsBeforeRange && !nodeEndsAfterRange)
 
6857
              || (nodeType == nsIDOMNode::TEXT_NODE))
 
6858
          {
 
6859
            *aYes = PR_TRUE;
 
6860
            return NS_OK;
 
6861
          }
 
6862
        }
 
6863
      }
 
6864
    }
 
6865
  }
 
6866
  return NS_OK;
 
6867
}
 
6868
 
 
6869
 
 
6870
nsresult
 
6871
nsTypedSelection::GetPresContext(nsIPresContext **aPresContext)
 
6872
{
 
6873
  if (!mFrameSelection)
 
6874
    return NS_ERROR_FAILURE;//nothing to do
 
6875
  nsIFocusTracker *tracker = mFrameSelection->GetTracker();
 
6876
 
 
6877
  if (!tracker)
 
6878
    return NS_ERROR_NULL_POINTER;
 
6879
 
 
6880
  return tracker->GetPresContext(aPresContext);
 
6881
}
 
6882
 
 
6883
nsresult
 
6884
nsTypedSelection::GetPresShell(nsIPresShell **aPresShell)
 
6885
{
 
6886
  if (mPresShellWeak)
 
6887
  {
 
6888
    nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShellWeak);
 
6889
    if (presShell)
 
6890
      NS_ADDREF(*aPresShell = presShell);
 
6891
    return NS_OK;
 
6892
  }
 
6893
  nsresult rv = NS_OK;
 
6894
  if (!mFrameSelection)
 
6895
    return NS_ERROR_FAILURE;//nothing to do
 
6896
 
 
6897
  nsIFocusTracker *tracker = mFrameSelection->GetTracker();
 
6898
 
 
6899
  if (!tracker)
 
6900
    return NS_ERROR_NULL_POINTER;
 
6901
 
 
6902
  nsCOMPtr<nsIPresContext> presContext;
 
6903
 
 
6904
  rv = tracker->GetPresContext(getter_AddRefs(presContext));
 
6905
 
 
6906
  if (NS_FAILED(rv))
 
6907
    return rv;
 
6908
 
 
6909
  if (!presContext)
 
6910
    return NS_ERROR_NULL_POINTER;
 
6911
  
 
6912
  nsIPresShell *shell = presContext->GetPresShell();
 
6913
  mPresShellWeak = do_GetWeakReference(shell);    // the presshell owns us, so no addref
 
6914
  if (mPresShellWeak)
 
6915
    NS_ADDREF(*aPresShell = shell);
 
6916
  return rv;
 
6917
}
 
6918
 
 
6919
nsresult
 
6920
nsTypedSelection::GetRootScrollableView(nsIScrollableView **aScrollableView)
 
6921
{
 
6922
  //
 
6923
  // NOTE: This method returns a NON-AddRef'd pointer
 
6924
  //       to the scrollable view!
 
6925
  //
 
6926
  NS_ENSURE_ARG_POINTER(aScrollableView);
 
6927
 
 
6928
  if (!mFrameSelection)
 
6929
    return NS_ERROR_FAILURE;//nothing to do
 
6930
  nsresult rv;
 
6931
  nsIScrollableView *scrollView;
 
6932
  rv = mFrameSelection->GetScrollableView(&scrollView);
 
6933
  if ( NS_FAILED(rv))
 
6934
    return rv;
 
6935
 
 
6936
  if (!scrollView)
 
6937
  {
 
6938
 
 
6939
    nsCOMPtr<nsIPresShell> presShell;
 
6940
 
 
6941
    rv = GetPresShell(getter_AddRefs(presShell));
 
6942
 
 
6943
    if (NS_FAILED(rv))
 
6944
      return rv;
 
6945
 
 
6946
    if (!presShell)
 
6947
      return NS_ERROR_NULL_POINTER;
 
6948
 
 
6949
    nsIViewManager* viewManager = presShell->GetViewManager();
 
6950
 
 
6951
    if (!viewManager)
 
6952
      return NS_ERROR_NULL_POINTER;
 
6953
 
 
6954
    //
 
6955
    // nsIViewManager::GetRootScrollableView() does not
 
6956
    // AddRef the pointer it returns.
 
6957
    //
 
6958
    return viewManager->GetRootScrollableView(aScrollableView);
 
6959
  }
 
6960
  else //SCROLLVIEW_FROM_FRAME
 
6961
  {
 
6962
    *aScrollableView = scrollView;
 
6963
  }
 
6964
 
 
6965
  return rv;
 
6966
}
 
6967
 
 
6968
nsresult
 
6969
nsTypedSelection::GetFrameToScrolledViewOffsets(nsIScrollableView *aScrollableView, nsIFrame *aFrame, nscoord *aX, nscoord *aY)
 
6970
{
 
6971
  nsresult rv = NS_OK;
 
6972
  if (!mFrameSelection)
 
6973
    return NS_ERROR_FAILURE;//nothing to do
 
6974
 
 
6975
  if (!aScrollableView || !aFrame || !aX || !aY) {
 
6976
    return NS_ERROR_NULL_POINTER;
 
6977
  }
 
6978
 
 
6979
  *aX = 0;
 
6980
  *aY = 0;
 
6981
 
 
6982
  nsIView*  scrolledView;
 
6983
  nsPoint   offset;
 
6984
  nsIView*  closestView;
 
6985
          
 
6986
  // Determine the offset from aFrame to the scrolled view. We do that by
 
6987
  // getting the offset from its closest view and then walking up
 
6988
  aScrollableView->GetScrolledView(scrolledView);
 
6989
  nsIFocusTracker *tracker = mFrameSelection->GetTracker();
 
6990
 
 
6991
  if (!tracker)
 
6992
    return NS_ERROR_NULL_POINTER;
 
6993
 
 
6994
  nsCOMPtr<nsIPresContext> presContext;
 
6995
  tracker->GetPresContext(getter_AddRefs(presContext));
 
6996
  aFrame->GetOffsetFromView(presContext, offset, &closestView);
 
6997
 
 
6998
  // XXX Deal with the case where there is a scrolled element, e.g., a
 
6999
  // DIV in the middle...
 
7000
  while (closestView && closestView != scrolledView) {
 
7001
    // Update the offset
 
7002
    offset += closestView->GetPosition();
 
7003
 
 
7004
    // Get its parent view
 
7005
    closestView = closestView->GetParent();
 
7006
  }
 
7007
 
 
7008
  *aX = offset.x;
 
7009
  *aY = offset.y;
 
7010
 
 
7011
  return rv;
 
7012
}
 
7013
 
 
7014
nsresult
 
7015
nsTypedSelection::GetPointFromOffset(nsIFrame *aFrame, PRInt32 aContentOffset, nsPoint *aPoint)
 
7016
{
 
7017
  nsresult rv = NS_OK;
 
7018
  if (!mFrameSelection)
 
7019
    return NS_ERROR_FAILURE;//nothing to do
 
7020
  if (!aFrame || !aPoint)
 
7021
    return NS_ERROR_NULL_POINTER;
 
7022
 
 
7023
  aPoint->x = 0;
 
7024
  aPoint->y = 0;
 
7025
 
 
7026
  //
 
7027
  // Retrieve the device context. We need one to create
 
7028
  // a rendering context.
 
7029
  //
 
7030
 
 
7031
  nsIFocusTracker *tracker = mFrameSelection->GetTracker();
 
7032
 
 
7033
  if (!tracker)
 
7034
    return NS_ERROR_NULL_POINTER;
 
7035
 
 
7036
  nsCOMPtr<nsIPresContext> presContext;
 
7037
 
 
7038
  rv = tracker->GetPresContext(getter_AddRefs(presContext));
 
7039
 
 
7040
  if (NS_FAILED(rv))
 
7041
    return rv;
 
7042
 
 
7043
  if (!presContext)
 
7044
    return NS_ERROR_NULL_POINTER;
 
7045
  
 
7046
  //
 
7047
  // Now get the closest view with a widget so we can create
 
7048
  // a rendering context.
 
7049
  //
 
7050
 
 
7051
  nsIWidget* widget = nsnull;
 
7052
  nsIView *closestView = nsnull;
 
7053
  nsPoint offset(0, 0);
 
7054
 
 
7055
  rv = aFrame->GetOffsetFromView(presContext, offset, &closestView);
 
7056
 
 
7057
  while (!widget && closestView)
 
7058
  {
 
7059
    widget = closestView->GetWidget();
 
7060
 
 
7061
    if (!widget)
 
7062
    {
 
7063
      closestView = closestView->GetParent();
 
7064
    }
 
7065
  }
 
7066
 
 
7067
  if (!closestView)
 
7068
    return NS_ERROR_FAILURE;
 
7069
 
 
7070
  //
 
7071
  // Create a rendering context. This context is used by text frames
 
7072
  // to calculate text widths so it can figure out where the point is
 
7073
  // in the frame.
 
7074
  //
 
7075
 
 
7076
  nsCOMPtr<nsIRenderingContext> rendContext;
 
7077
 
 
7078
  rv = presContext->DeviceContext()->
 
7079
    CreateRenderingContext(closestView, *getter_AddRefs(rendContext));
 
7080
  
 
7081
  if (NS_FAILED(rv))
 
7082
    return rv;
 
7083
 
 
7084
  if (!rendContext)
 
7085
    return NS_ERROR_NULL_POINTER;
 
7086
 
 
7087
  //
 
7088
  // Now get the point and return!
 
7089
  //
 
7090
 
 
7091
  rv = aFrame->GetPointFromOffset(presContext, rendContext, aContentOffset, aPoint);
 
7092
 
 
7093
  return rv;
 
7094
}
 
7095
 
 
7096
nsresult
 
7097
nsTypedSelection::GetSelectionRegionRectAndScrollableView(SelectionRegion aRegion, nsRect *aRect, nsIScrollableView **aScrollableView)
 
7098
{
 
7099
  nsresult result = NS_OK;
 
7100
  if (!mFrameSelection)
 
7101
    return NS_ERROR_FAILURE;//nothing to do
 
7102
 
 
7103
  if (!aRect || !aScrollableView)
 
7104
    return NS_ERROR_NULL_POINTER;
 
7105
 
 
7106
  // Init aRect:
 
7107
 
 
7108
  aRect->x = 0;
 
7109
  aRect->y = 0;
 
7110
  aRect->width  = 0;
 
7111
  aRect->height = 0;
 
7112
 
 
7113
  *aScrollableView = 0;
 
7114
 
 
7115
  nsIDOMNode *node       = 0;
 
7116
  PRInt32     nodeOffset = 0;
 
7117
  PRBool      isEndNode  = PR_TRUE;
 
7118
  nsIFrame   *frame      = 0;
 
7119
 
 
7120
  switch (aRegion)
 
7121
  {
 
7122
  case nsISelectionController::SELECTION_ANCHOR_REGION:
 
7123
    node       = FetchAnchorNode();
 
7124
    nodeOffset = FetchAnchorOffset();
 
7125
    isEndNode  = GetDirection() == eDirPrevious;
 
7126
    break;
 
7127
  case nsISelectionController::SELECTION_FOCUS_REGION:
 
7128
    node       = FetchFocusNode();
 
7129
    nodeOffset = FetchFocusOffset();
 
7130
    isEndNode  = GetDirection() == eDirNext;
 
7131
    break;
 
7132
  default:
 
7133
    return NS_ERROR_FAILURE;
 
7134
  }
 
7135
 
 
7136
  if (!node)
 
7137
    return NS_ERROR_NULL_POINTER;
 
7138
 
 
7139
  nsCOMPtr<nsIContent> content = do_QueryInterface(node);
 
7140
  PRInt32 frameOffset = 0;
 
7141
 
 
7142
  if (content)
 
7143
  {
 
7144
    nsIFrameSelection::HINT hint;
 
7145
    mFrameSelection->GetHint(&hint);
 
7146
    result = mFrameSelection->GetFrameForNodeOffset(content, nodeOffset, hint, &frame, &frameOffset);
 
7147
  }
 
7148
  else
 
7149
    result = NS_ERROR_FAILURE;
 
7150
 
 
7151
  if(NS_FAILED(result))
 
7152
    return result;
 
7153
 
 
7154
  if (!frame)
 
7155
    return NS_ERROR_NULL_POINTER;
 
7156
 
 
7157
  //
 
7158
  // Get the frame's scrollable view.
 
7159
  //
 
7160
 
 
7161
  nsCOMPtr<nsIPresContext> presContext;
 
7162
 
 
7163
  result = GetPresContext(getter_AddRefs(presContext));
 
7164
 
 
7165
  if (NS_FAILED(result))
 
7166
    return result;
 
7167
 
 
7168
  if (!presContext)
 
7169
    return NS_ERROR_FAILURE;
 
7170
 
 
7171
 
 
7172
  nsIFrame *parentWithView = frame->GetAncestorWithView();
 
7173
 
 
7174
  if (!parentWithView)
 
7175
    return NS_ERROR_FAILURE;
 
7176
 
 
7177
  nsIView* view = parentWithView->GetView();
 
7178
 
 
7179
  result = GetClosestScrollableView(view, aScrollableView);
 
7180
 
 
7181
  if (NS_FAILED(result))
 
7182
    return result;
 
7183
 
 
7184
  if (!*aScrollableView)
 
7185
    return NS_OK;
 
7186
 
 
7187
  //
 
7188
  // Figure out what node type we have, then get the
 
7189
  // appropriate rect for it's nodeOffset.
 
7190
  //
 
7191
 
 
7192
  PRUint16 nodeType = nsIDOMNode::ELEMENT_NODE;
 
7193
 
 
7194
  result = node->GetNodeType(&nodeType);
 
7195
 
 
7196
  if (NS_FAILED(result))
 
7197
    return NS_ERROR_NULL_POINTER;
 
7198
 
 
7199
  if (nodeType == nsIDOMNode::TEXT_NODE)
 
7200
  {
 
7201
    nsIFrame *childFrame = 0;
 
7202
    frameOffset  = 0;
 
7203
 
 
7204
    result = frame->GetChildFrameContainingOffset(nodeOffset, mFrameSelection->mHint, &frameOffset, &childFrame);
 
7205
 
 
7206
    if (NS_FAILED(result))
 
7207
      return result;
 
7208
 
 
7209
    if (!childFrame)
 
7210
      return NS_ERROR_NULL_POINTER;
 
7211
 
 
7212
    frame = childFrame;
 
7213
 
 
7214
    //
 
7215
    // Get the x coordinate of the offset into the text frame.
 
7216
    // The x coordinate will be relative to the frame's origin,
 
7217
    // so we'll have to translate it into the root view's coordinate
 
7218
    // system.
 
7219
    //
 
7220
    nsPoint pt;
 
7221
    result = GetCachedFrameOffset(frame, nodeOffset, pt);
 
7222
 
 
7223
    if (NS_FAILED(result))
 
7224
      return result;
 
7225
    
 
7226
    //
 
7227
    // Get the frame's rect.
 
7228
    //
 
7229
    *aRect = frame->GetRect();
 
7230
 
 
7231
    //
 
7232
    // Translate the frame's rect into root view coordinates.
 
7233
    //
 
7234
    result = GetFrameToScrolledViewOffsets(*aScrollableView, frame, &aRect->x, &aRect->y);
 
7235
 
 
7236
    if (NS_FAILED(result))
 
7237
      return result;
 
7238
 
 
7239
    //
 
7240
    // Now add the offset's x coordinate.
 
7241
    //
 
7242
    aRect->x += pt.x;
 
7243
 
 
7244
    //
 
7245
    // Adjust the width of the rect to account for any neccessary
 
7246
    // padding!
 
7247
    //
 
7248
 
 
7249
    const nsIView* clipView = 0;
 
7250
 
 
7251
    result = (*aScrollableView)->GetClipView(&clipView);
 
7252
 
 
7253
    if (NS_FAILED(result))
 
7254
      return result;
 
7255
 
 
7256
    nsRect clipRect = clipView->GetBounds();
 
7257
 
 
7258
    result = (*aScrollableView)->GetScrollPosition(clipRect.x, clipRect.y);
 
7259
 
 
7260
    if (NS_FAILED(result))
 
7261
      return result;
 
7262
 
 
7263
    //
 
7264
    // If the point we are interested is outside the clip
 
7265
    // region, we will scroll it into view with a padding
 
7266
    // equal to a quarter of the clip's width.
 
7267
    //
 
7268
 
 
7269
    PRInt32 pad = clipRect.width >> 2;
 
7270
 
 
7271
    if (pad <= 0)
 
7272
      pad = 3; // Arbitrary
 
7273
 
 
7274
    if (aRect->x >= clipRect.XMost())
 
7275
      aRect->width = pad;
 
7276
    else if (aRect->x <= clipRect.x)
 
7277
    {
 
7278
      aRect->x -= pad;
 
7279
      aRect->width = pad;
 
7280
    }
 
7281
    else
 
7282
      aRect->width = 60; // Arbitrary
 
7283
 
 
7284
    //
 
7285
    // Clip the x dimensions of aRect so that they are
 
7286
    // completely within the bounds of the scrolledView.
 
7287
    // This helps avoid unnecessary scrolling of parent
 
7288
    // scrolled views.
 
7289
    //
 
7290
    // Note that aRect is in the coordinate system
 
7291
    // of the scrolledView, so there is no need to take
 
7292
    // into account scrolledView's x and y position.
 
7293
    // We can just assume that (0,0) corresponds to the
 
7294
    // upper left corner, and (svRect.width, svRect.height)
 
7295
    // the lower right corner of the scrolledView.
 
7296
    //
 
7297
 
 
7298
    nsIView* scrolledView = 0;
 
7299
 
 
7300
    result = (*aScrollableView)->GetScrolledView(scrolledView);
 
7301
 
 
7302
    if (NS_FAILED(result))
 
7303
      return result;
 
7304
 
 
7305
    nsRect svRect = scrolledView->GetBounds();
 
7306
 
 
7307
    if (aRect->x < 0)
 
7308
      aRect->x = 0;
 
7309
    else if (aRect->x >= svRect.width)
 
7310
      aRect->x = svRect.width - 1;
 
7311
 
 
7312
    if (aRect->XMost() > svRect.width)
 
7313
      aRect->width = svRect.width - aRect->x;
 
7314
  }
 
7315
  else
 
7316
  {
 
7317
    //
 
7318
    // Must be a non-text frame, just scroll the frame
 
7319
    // into view.
 
7320
    //
 
7321
    *aRect = frame->GetRect();
 
7322
 
 
7323
    result = GetFrameToScrolledViewOffsets(*aScrollableView, frame, &aRect->x, &aRect->y);
 
7324
  }
 
7325
 
 
7326
  return result;
 
7327
}
 
7328
 
 
7329
nsresult
 
7330
nsTypedSelection::ScrollRectIntoView(nsIScrollableView *aScrollableView,
 
7331
                              nsRect& aRect,
 
7332
                              PRIntn  aVPercent, 
 
7333
                              PRIntn  aHPercent,
 
7334
                              PRBool aScrollParentViews)
 
7335
{
 
7336
  nsresult rv = NS_OK;
 
7337
  if (!mFrameSelection)
 
7338
    return NS_OK;//nothing to do
 
7339
 
 
7340
  if (!aScrollableView)
 
7341
    return NS_ERROR_NULL_POINTER;
 
7342
 
 
7343
  // Determine the visible rect in the scrolled view's coordinate space.
 
7344
  // The size of the visible area is the clip view size
 
7345
  const nsIView*  clipView;
 
7346
 
 
7347
  aScrollableView->GetClipView(&clipView);
 
7348
  nsRect visibleRect = clipView->GetBounds();
 
7349
  aScrollableView->GetScrollPosition(visibleRect.x, visibleRect.y);
 
7350
 
 
7351
  // The actual scroll offsets
 
7352
  nscoord scrollOffsetX = visibleRect.x;
 
7353
  nscoord scrollOffsetY = visibleRect.y;
 
7354
 
 
7355
  // See how aRect should be positioned vertically
 
7356
  if (NS_PRESSHELL_SCROLL_ANYWHERE == aVPercent) {
 
7357
    // The caller doesn't care where aRect is positioned vertically,
 
7358
    // so long as it's fully visible
 
7359
    if (aRect.y < visibleRect.y) {
 
7360
      // Scroll up so aRect's top edge is visible
 
7361
      scrollOffsetY = aRect.y;
 
7362
    } else if (aRect.YMost() > visibleRect.YMost()) {
 
7363
      // Scroll down so aRect's bottom edge is visible. Make sure
 
7364
      // aRect's top edge is still visible
 
7365
      scrollOffsetY += aRect.YMost() - visibleRect.YMost();
 
7366
      if (scrollOffsetY > aRect.y) {
 
7367
        scrollOffsetY = aRect.y;
 
7368
      }
 
7369
    }
 
7370
  } else {
 
7371
    // Align the aRect edge according to the specified percentage
 
7372
    nscoord frameAlignY = aRect.y + (aRect.height * aVPercent) / 100;
 
7373
    scrollOffsetY = frameAlignY - (visibleRect.height * aVPercent) / 100;
 
7374
  }
 
7375
 
 
7376
  // See how the aRect should be positioned horizontally
 
7377
  if (NS_PRESSHELL_SCROLL_ANYWHERE == aHPercent) {
 
7378
    // The caller doesn't care where the aRect is positioned horizontally,
 
7379
    // so long as it's fully visible
 
7380
    if (aRect.x < visibleRect.x) {
 
7381
      // Scroll left so the aRect's left edge is visible
 
7382
      scrollOffsetX = aRect.x;
 
7383
    } else if (aRect.XMost() > visibleRect.XMost()) {
 
7384
      // Scroll right so the aRect's right edge is visible. Make sure the
 
7385
      // aRect's left edge is still visible
 
7386
      scrollOffsetX += aRect.XMost() - visibleRect.XMost();
 
7387
      if (scrollOffsetX > aRect.x) {
 
7388
        scrollOffsetX = aRect.x;
 
7389
      }
 
7390
    }
 
7391
      
 
7392
  } else {
 
7393
    // Align the aRect edge according to the specified percentage
 
7394
    nscoord frameAlignX = aRect.x + (aRect.width * aHPercent) / 100;
 
7395
    scrollOffsetX = frameAlignX - (visibleRect.width * aHPercent) / 100;
 
7396
  }
 
7397
 
 
7398
  aScrollableView->ScrollTo(scrollOffsetX, scrollOffsetY, NS_VMREFRESH_IMMEDIATE);
 
7399
 
 
7400
  if (aScrollParentViews)
 
7401
  {
 
7402
    //
 
7403
    // Get aScrollableView's scrolled view.
 
7404
    //
 
7405
 
 
7406
    nsIView *scrolledView = 0;
 
7407
 
 
7408
    rv = aScrollableView->GetScrolledView(scrolledView);
 
7409
 
 
7410
    if (NS_FAILED(rv))
 
7411
      return rv;
 
7412
 
 
7413
    if (!scrolledView)
 
7414
      return NS_ERROR_FAILURE;
 
7415
 
 
7416
    //
 
7417
    // Check if aScrollableRect has a parent scrollable view!
 
7418
    //
 
7419
 
 
7420
    nsIView *view = 0;
 
7421
    rv = CallQueryInterface(aScrollableView, &view);
 
7422
    if (!view)
 
7423
      return rv;
 
7424
 
 
7425
    view = view->GetParent();
 
7426
 
 
7427
    if (view)
 
7428
    {
 
7429
      nsIScrollableView *parentSV = 0;
 
7430
 
 
7431
      rv = GetClosestScrollableView(view, &parentSV);
 
7432
 
 
7433
      if (NS_FAILED(rv))
 
7434
        return rv;
 
7435
 
 
7436
      if (parentSV)
 
7437
      {
 
7438
        //
 
7439
        // We have a parent scrollable view, so now map aRect
 
7440
        // into it's scrolled view's coordinate space.
 
7441
        //
 
7442
        
 
7443
        nsRect newRect;
 
7444
 
 
7445
        rv = parentSV->GetScrolledView(view);
 
7446
 
 
7447
        if (NS_FAILED(rv))
 
7448
          return rv;
 
7449
 
 
7450
        if (!view)
 
7451
          return NS_ERROR_FAILURE;
 
7452
 
 
7453
        rv = GetViewAncestorOffset(scrolledView, view, &newRect.x, &newRect.y);
 
7454
 
 
7455
        if (NS_FAILED(rv))
 
7456
          return rv;
 
7457
 
 
7458
        newRect.x     += aRect.x;
 
7459
        newRect.y     += aRect.y;
 
7460
        newRect.width  = aRect.width;
 
7461
        newRect.height = aRect.height;
 
7462
 
 
7463
        //
 
7464
        // Now scroll the rect into the parent's view.
 
7465
        //
 
7466
 
 
7467
        rv = ScrollRectIntoView(parentSV, newRect, aVPercent, aHPercent, aScrollParentViews);
 
7468
      }
 
7469
    }
 
7470
  }
 
7471
 
 
7472
  return rv;
 
7473
}
 
7474
 
 
7475
static void PR_CALLBACK HandlePLEvent(nsScrollSelectionIntoViewEvent* aEvent);
 
7476
static void PR_CALLBACK DestroyPLEvent(nsScrollSelectionIntoViewEvent* aEvent);
 
7477
 
 
7478
struct nsScrollSelectionIntoViewEvent : public PLEvent {
 
7479
  nsScrollSelectionIntoViewEvent(nsTypedSelection *aTypedSelection, SelectionRegion aRegion) {
 
7480
    if (!aTypedSelection)
 
7481
      return;
 
7482
 
 
7483
    mTypedSelection = aTypedSelection;
 
7484
    mRegion = aRegion;
 
7485
 
 
7486
    PL_InitEvent(this, aTypedSelection,
 
7487
                 (PLHandleEventProc) ::HandlePLEvent,
 
7488
                 (PLDestroyEventProc) ::DestroyPLEvent);
 
7489
  }
 
7490
 
 
7491
  ~nsScrollSelectionIntoViewEvent() {}
 
7492
 
 
7493
  void HandleEvent() {
 
7494
    mTypedSelection->mScrollEventPosted = PR_FALSE;
 
7495
 
 
7496
    if (!mTypedSelection)
 
7497
      return;
 
7498
 
 
7499
    mTypedSelection->ScrollIntoView(mRegion, PR_TRUE);
 
7500
  }
 
7501
 
 
7502
  nsTypedSelection *mTypedSelection;
 
7503
  SelectionRegion   mRegion;
 
7504
};
 
7505
 
 
7506
static void PR_CALLBACK HandlePLEvent(nsScrollSelectionIntoViewEvent* aEvent)
 
7507
{
 
7508
  NS_ASSERTION(nsnull != aEvent,"Event is null");
 
7509
  aEvent->HandleEvent();
 
7510
}
 
7511
 
 
7512
static void PR_CALLBACK DestroyPLEvent(nsScrollSelectionIntoViewEvent* aEvent)
 
7513
{
 
7514
  NS_ASSERTION(nsnull != aEvent,"Event is null");
 
7515
  delete aEvent;
 
7516
}
 
7517
 
 
7518
nsresult
 
7519
nsTypedSelection::PostScrollSelectionIntoViewEvent(SelectionRegion aRegion)
 
7520
{
 
7521
  static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
 
7522
 
 
7523
  if (!mEventQueue) {
 
7524
    nsresult rv;
 
7525
 
 
7526
    // Cache the event queue of the current UI thread
 
7527
    nsCOMPtr<nsIEventQueueService> eventService = do_GetService(kEventQueueServiceCID, &rv);
 
7528
    if (NS_SUCCEEDED(rv) && (nsnull != eventService)) {  // XXX this implies that the UI is the current thread.
 
7529
      rv = eventService->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(mEventQueue));
 
7530
    }
 
7531
  }
 
7532
 
 
7533
  if (mEventQueue) {
 
7534
    if (mScrollEventPosted) {
 
7535
      // We've already posted an event, revoke it and
 
7536
      // place a new one at the end of the queue to make
 
7537
      // sure that any new pending reflow events are processed
 
7538
      // before we scroll. This will insure that we scroll
 
7539
      // to the correct place on screen.
 
7540
 
 
7541
      mEventQueue->RevokeEvents(this);
 
7542
      mScrollEventPosted = PR_FALSE;
 
7543
    }
 
7544
 
 
7545
    nsScrollSelectionIntoViewEvent *ev = new nsScrollSelectionIntoViewEvent(this, aRegion);
 
7546
    if (ev) {
 
7547
      mEventQueue->PostEvent(ev);
 
7548
      mScrollEventPosted = PR_TRUE;
 
7549
      return NS_OK;
 
7550
    }
 
7551
  }
 
7552
 
 
7553
  return NS_ERROR_FAILURE;
 
7554
}
 
7555
 
 
7556
NS_IMETHODIMP
 
7557
nsTypedSelection::ScrollIntoView(SelectionRegion aRegion, PRBool aIsSynchronous)
 
7558
{
 
7559
  nsresult result;
 
7560
  if (!mFrameSelection)
 
7561
    return NS_OK;//nothing to do
 
7562
 
 
7563
  if (mFrameSelection->GetBatching())
 
7564
    return NS_OK;
 
7565
 
 
7566
  if (!aIsSynchronous)
 
7567
    return PostScrollSelectionIntoViewEvent(aRegion);
 
7568
 
 
7569
  //
 
7570
  // Shut the caret off before scrolling to avoid
 
7571
  // leaving caret turds on the screen!
 
7572
  //
 
7573
  nsCOMPtr<nsIPresShell> presShell;
 
7574
  result = GetPresShell(getter_AddRefs(presShell));
 
7575
  if (NS_FAILED(result))
 
7576
    return result;
 
7577
  nsCOMPtr<nsICaret> caret;
 
7578
  presShell->GetCaret(getter_AddRefs(caret));
 
7579
  if (caret)
 
7580
  {
 
7581
    StCaretHider  caretHider(caret);      // stack-based class hides and shows the caret
 
7582
 
 
7583
    //
 
7584
    // Scroll the selection region into view.
 
7585
    //
 
7586
 
 
7587
    nsRect rect;
 
7588
    nsIScrollableView *scrollableView = 0;
 
7589
 
 
7590
    result = GetSelectionRegionRectAndScrollableView(aRegion, &rect, &scrollableView);
 
7591
 
 
7592
    if (NS_FAILED(result))
 
7593
      return result;
 
7594
 
 
7595
    //
 
7596
    // It's ok if we don't have a scrollable view, just return early.
 
7597
    //
 
7598
    if (!scrollableView)
 
7599
      return NS_OK;
 
7600
 
 
7601
    result = ScrollRectIntoView(scrollableView, rect, NS_PRESSHELL_SCROLL_ANYWHERE, NS_PRESSHELL_SCROLL_ANYWHERE, PR_TRUE);
 
7602
  }
 
7603
  return result;
 
7604
}
 
7605
 
 
7606
 
 
7607
 
 
7608
NS_IMETHODIMP
 
7609
nsTypedSelection::AddSelectionListener(nsISelectionListener* aNewListener)
 
7610
{
 
7611
  if (!aNewListener)
 
7612
    return NS_ERROR_NULL_POINTER;
 
7613
  return mSelectionListeners.AppendObject(aNewListener) ? NS_OK : NS_ERROR_FAILURE;      // addrefs
 
7614
}
 
7615
 
 
7616
 
 
7617
 
 
7618
NS_IMETHODIMP
 
7619
nsTypedSelection::RemoveSelectionListener(nsISelectionListener* aListenerToRemove)
 
7620
{
 
7621
  if (!aListenerToRemove )
 
7622
    return NS_ERROR_NULL_POINTER;
 
7623
  return mSelectionListeners.RemoveObject(aListenerToRemove) ? NS_OK : NS_ERROR_FAILURE; // releases
 
7624
}
 
7625
 
 
7626
 
 
7627
nsresult
 
7628
nsTypedSelection::NotifySelectionListeners()
 
7629
{
 
7630
  if (!mFrameSelection)
 
7631
    return NS_OK;//nothing to do
 
7632
 
 
7633
  if (mFrameSelection->GetBatching()){
 
7634
    mFrameSelection->SetDirty();
 
7635
    return NS_OK;
 
7636
  }
 
7637
  PRInt32 cnt = mSelectionListeners.Count();
 
7638
  
 
7639
  nsCOMPtr<nsIDOMDocument> domdoc;
 
7640
  nsCOMPtr<nsIDocument> doc;
 
7641
  nsCOMPtr<nsIPresShell> shell;
 
7642
  nsresult rv = GetPresShell(getter_AddRefs(shell));
 
7643
  if (NS_SUCCEEDED(rv) && shell)
 
7644
  {
 
7645
    rv = shell->GetDocument(getter_AddRefs(doc));
 
7646
    if (NS_FAILED(rv))
 
7647
      doc = 0;
 
7648
    domdoc = do_QueryInterface(doc);
 
7649
  }
 
7650
  short reason = mFrameSelection->PopReason();
 
7651
  for (PRInt32 i = 0; i < cnt; i++)
 
7652
  {
 
7653
    nsISelectionListener* thisListener = mSelectionListeners[i];
 
7654
    if (thisListener)
 
7655
      thisListener->NotifySelectionChanged(domdoc, this, reason);
 
7656
  }
 
7657
  return NS_OK;
 
7658
}
 
7659
 
 
7660
NS_IMETHODIMP
 
7661
nsTypedSelection::StartBatchChanges()
 
7662
{
 
7663
  if (!mFrameSelection)
 
7664
    return NS_OK;//nothing to do
 
7665
  return mFrameSelection->StartBatchChanges();
 
7666
}
 
7667
 
 
7668
 
 
7669
 
 
7670
NS_IMETHODIMP
 
7671
nsTypedSelection::EndBatchChanges()
 
7672
{
 
7673
  if (!mFrameSelection)
 
7674
    return NS_OK;//nothing to do
 
7675
  return mFrameSelection->EndBatchChanges();
 
7676
}
 
7677
 
 
7678
 
 
7679
 
 
7680
NS_IMETHODIMP
 
7681
nsTypedSelection::DeleteFromDocument()
 
7682
{
 
7683
  if (!mFrameSelection)
 
7684
    return NS_OK;//nothing to do
 
7685
  return mFrameSelection->DeleteFromDocument();
 
7686
}
 
7687
 
 
7688
/** SelectionLanguageChange modifies the cursor Bidi level after a change in keyboard direction
 
7689
 *  @param aLangRTL is PR_TRUE if the new language is right-to-left or PR_FALSE if the new language is left-to-right
 
7690
 */
 
7691
NS_IMETHODIMP
 
7692
nsTypedSelection::SelectionLanguageChange(PRBool aLangRTL)
 
7693
{
 
7694
  nsresult result;
 
7695
  nsCOMPtr<nsIDOMNode>  focusNode;
 
7696
  nsCOMPtr<nsIContent> focusContent;
 
7697
  PRInt32 focusOffset;
 
7698
  nsIFrame *focusFrame = 0;
 
7699
 
 
7700
  focusOffset = FetchFocusOffset();
 
7701
  focusNode = FetchFocusNode();
 
7702
  result = GetPrimaryFrameForFocusNode(&focusFrame, nsnull);
 
7703
  if (NS_FAILED(result) || !focusFrame)
 
7704
    return result?result:NS_ERROR_FAILURE;
 
7705
 
 
7706
  PRInt32 frameStart, frameEnd;
 
7707
  focusFrame->GetOffsets(frameStart, frameEnd);
 
7708
  nsCOMPtr<nsIPresContext> context;
 
7709
  PRUint8 level, levelBefore, levelAfter;
 
7710
  result = GetPresContext(getter_AddRefs(context));
 
7711
  if (NS_FAILED(result) || !context)
 
7712
    return result?result:NS_ERROR_FAILURE;
 
7713
 
 
7714
  focusFrame->GetBidiProperty(context, nsLayoutAtoms::embeddingLevel, (void**)&level, sizeof(level));
 
7715
  if ((focusOffset != frameStart) && (focusOffset != frameEnd))
 
7716
    // the cursor is not at a frame boundary, so the level of both the characters (logically) before and after the cursor
 
7717
    //  is equal to the frame level
 
7718
    levelBefore = levelAfter = level;
 
7719
  else {
 
7720
    // the cursor is at a frame boundary, so use GetPrevNextBidiLevels to find the level of the characters
 
7721
    //  before and after the cursor
 
7722
    nsIFrame* frameBefore = nsnull;
 
7723
    nsIFrame* frameAfter = nsnull;
 
7724
    focusContent = do_QueryInterface(focusNode);
 
7725
    /*
 
7726
    nsIFrameSelection::HINT hint;
 
7727
 
 
7728
    if ((focusOffset == frameStart && level)        // beginning of an RTL frame
 
7729
        || (focusOffset == frameEnd && !level)) {   // end of an LTR frame
 
7730
      hint = nsIFrameSelection::HINTRIGHT;
 
7731
    }
 
7732
    else {                                          // end of an RTL frame or beginning of an LTR frame
 
7733
      hint = nsIFrameSelection::HINTLEFT;
 
7734
    }
 
7735
    mFrameSelection->SetHint(hint);
 
7736
    */
 
7737
    mFrameSelection->GetPrevNextBidiLevels(context, focusContent, focusOffset, &frameBefore, &frameAfter, &levelBefore, &levelAfter);
 
7738
  }
 
7739
 
 
7740
  nsIPresShell* shell = context->GetPresShell();
 
7741
  if (!shell)
 
7742
    return NS_ERROR_FAILURE;
 
7743
 
 
7744
  if ((levelBefore & 1) == (levelAfter & 1)) {
 
7745
    // if cursor is between two characters with the same orientation, changing the keyboard language
 
7746
    //  must toggle the cursor level between the level of the character with the lowest level
 
7747
    //  (if the new language corresponds to the orientation of that character) and this level plus 1
 
7748
    //  (if the new language corresponds to the opposite orientation)
 
7749
    if ((level != levelBefore) && (level != levelAfter))
 
7750
      level = PR_MIN(levelBefore, levelAfter);
 
7751
    if ((level & 1) == aLangRTL)
 
7752
      shell->SetCaretBidiLevel(level);
 
7753
    else
 
7754
      shell->SetCaretBidiLevel(level + 1);
 
7755
  }
 
7756
  else {
 
7757
    // if cursor is between characters with opposite orientations, changing the keyboard language must change
 
7758
    //  the cursor level to that of the adjacent character with the orientation corresponding to the new language.
 
7759
    if ((levelBefore & 1) == aLangRTL)
 
7760
      shell->SetCaretBidiLevel(levelBefore);
 
7761
    else
 
7762
      shell->SetCaretBidiLevel(levelAfter);
 
7763
  }
 
7764
  return NS_OK;
 
7765
}