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
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/
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
15
* The Original Code is mozilla.org code.
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.
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.
37
* ***** END LICENSE BLOCK ***** */
40
* Implementation of selection: nsISelection,nsISelectionPrivate and nsIFrameSelection
44
#include "nsWeakReference.h"
45
#include "nsIFactory.h"
46
#include "nsIEnumerator.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"
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"
69
#include "nsISelectionListener.h"
70
#include "nsIContentIterator.h"
71
#include "nsIDocumentEncoder.h"
72
#include "nsIPrefBranch.h"
73
#include "nsIPrefService.h"
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);
83
#include "nsIDOMText.h"
85
#include "nsContentUtils.h"
87
//included for desired x position;
88
#include "nsIPresContext.h"
89
#include "nsIPresShell.h"
93
// included for view scrolling
94
#include "nsIViewManager.h"
95
#include "nsIScrollableView.h"
96
#include "nsIDeviceContext.h"
98
#include "nsIServiceManager.h"
99
#include "nsIAutoCopy.h"
100
#include "nsIEventQueue.h"
101
#include "nsIEventQueueService.h"
104
#include "nsIDOMDocument.h"
105
#include "nsIDocument.h"
107
#include "nsISelectionController.h"//for the enums
108
#include "nsHTMLAtoms.h"
110
#define STATUS_CHECK_RETURN_MACRO() {if (!mTracker) return NS_ERROR_FAILURE;}
114
//#define DEBUG_TABLE 1
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.
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.
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);
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
139
#undef OLD_TABLE_SELECTION
143
class nsSelectionIterator;
145
class nsAutoScrollTimer;
146
struct nsScrollSelectionIntoViewEvent;
148
PRBool IsValidSelectionPoint(nsSelection *aFrameSel, nsIContent *aContent);
149
PRBool IsValidSelectionPoint(nsSelection *aFrameSel, nsIDOMNode *aDomNode);
151
static nsIAtom *GetTag(nsIDOMNode *aNode);
152
static nsresult ParentOffset(nsIDOMNode *aNode, nsIDOMNode **aParent, PRInt32 *aChildOffset);
153
static nsIDOMNode *GetCellParent(nsIDOMNode *aDomNode);
157
static void printRange(nsIDOMRange *aDomRange);
158
#define DEBUG_OUT_RANGE(x) printRange(x)
160
#define DEBUG_OUT_RANGE(x)
165
//#define DEBUG_SELECTION // uncomment for printf describing every collapse and extend.
166
//#define DEBUG_NAVIGATION
169
//#define DEBUG_TABLE_SELECTION 1
172
struct CachedOffsetForFrame {
173
CachedOffsetForFrame()
174
: mCachedFrameOffset(0, 0) // nsPoint ctor
175
, mLastCaretFrame(nsnull)
176
, mLastContentOffset(0)
177
, mCanCacheFrameOffset(PR_FALSE)
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?
186
class nsTypedSelection : public nsISelection,
187
public nsISelectionPrivate,
188
public nsSupportsWeakReference
192
nsTypedSelection(nsSelection *aList);
193
virtual ~nsTypedSelection();
197
NS_DECL_NSISELECTIONPRIVATE
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);
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);
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();
218
nsIDOMNode* FetchOriginalAnchorNode(); //where did the ORIGINAL selection begin
219
PRInt32 FetchOriginalAnchorOffset();
221
nsIDOMNode* FetchFocusNode(); //where is the carret
222
PRInt32 FetchFocusOffset();
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);
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);
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);
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);
254
SelectionType GetType(){return mType;}
255
void SetType(SelectionType aType){mType = aType;}
257
nsresult NotifySelectionListeners();
260
friend class nsSelectionIterator;
261
friend struct nsScrollSelectionIntoViewEvent;
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);
271
NS_IMETHOD FixupSelectionPoints(nsIDOMRange *aRange, nsDirection *aDir, PRBool *aFixupState);
272
#endif //OLD_SELECTION
274
nsCOMArray<nsIDOMRange> mRangeArray;
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?
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;
292
// Stack-class to turn on/off selection batching for table selection
293
class nsSelectionBatcher
296
nsCOMPtr<nsISelectionPrivate> mSelection;
298
nsSelectionBatcher(nsISelectionPrivate *aSelection) : mSelection(aSelection)
300
if (mSelection) mSelection->StartBatchChanges();
302
virtual ~nsSelectionBatcher()
304
if (mSelection) mSelection->EndBatchChanges();
308
class nsSelection : public nsIFrameSelection
312
/*interfaces for addref and release and queryinterface*/
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;}
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);
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;}
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);
345
NS_IMETHOD AdjustOffsetsFromStyle(nsIFrame *aFrame, PRBool *changeSelection,
346
nsIContent** outContent, PRInt32* outStartOffset, PRInt32* outEndOffset);
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,
366
PRUint32 aContentOffset,
367
nsIFrame **aPrevFrame,
368
nsIFrame **aNextFrame,
370
PRUint8 *aNextLevel);
371
NS_IMETHOD GetFrameFromLevel(nsIPresContext *aPresContext,
373
nsDirection aDirection,
375
nsIFrame **aFrameOut);
376
NS_IMETHOD MaintainSelection();
377
/*END nsIFrameSelection interfaces */
382
virtual ~nsSelection();
384
NS_IMETHOD StartBatchChanges();
385
NS_IMETHOD EndBatchChanges();
386
NS_IMETHOD DeleteFromDocument();
388
nsIFocusTracker *GetTracker(){return mTracker;}
391
NS_IMETHOD TakeFocus(nsIContent *aNewFocus, PRUint32 aContentOffset, PRUint32 aContentEndOffset,
392
PRBool aContinueSelection, PRBool aMultipleSelection);
394
void BidiLevelFromMove(nsIPresContext* aContext,
395
nsIPresShell* aPresShell,
397
PRUint32 aContentOffset,
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,
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
425
PRBool AdjustForMaintainedSelection(nsIContent *aContent, PRInt32 aOffset);
427
// post and pop reasons for notifications. we may stack these later
428
void PostReason(PRInt16 aReason) { mSelectionChangeReason = aReason; }
431
PRInt16 retval = mSelectionChangeReason;
432
mSelectionChangeReason = 0;
436
friend class nsTypedSelection;
438
void printSelection(); // for debugging
441
void ResizeBuffer(PRUint32 aNewBufSize);
443
nsresult MoveCaret(PRUint32 aKeycode, PRBool aContinue, nsSelectionAmount aAmount);
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
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);
453
PRUint32 GetBatching(){return mBatching;}
454
PRBool GetNotifyFrames(){return mNotifyFrames;}
455
void SetDirty(PRBool aDirty=PR_TRUE){if (mBatching) mChangesDuringBatching = aDirty;}
457
nsresult NotifySelectionListeners(SelectionType aType); // add parameters to say collapsed etc?
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);
465
nsTypedSelection *mDomSelections[nsISelectionController::NUM_SELECTIONTYPES];
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);
472
nsresult SelectBlockOfCells(nsIContent *aStartNode, nsIContent *aEndNode);
473
nsresult SelectRowOrColumn(nsIContent *aCellContent, PRUint32 aTarget);
474
nsresult GetCellIndexes(nsIContent *aCell, PRInt32 &aRowIndex, PRInt32 &aColIndex);
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();
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;
494
// maintain selection
495
nsCOMPtr<nsIDOMRange> mMaintainRange;
500
nsIContent *mLimiter; //limit selection navigation to a child of this node.
501
nsIFocusTracker *mTracker;
503
PRInt16 mSelectionChangeReason; // reason for notifications of selection changing
504
PRInt16 mDisplaySelection; //for visual display purposes.
506
HINT mHint; //hint to tell if the selection is at the end of this line or beginning of next
509
nsIScrollableView *mScrollView;
511
nsMouseEvent mDelayedMouseEvent;
513
PRPackedBool mDelayCaretOverExistingSelection;
514
PRPackedBool mDelayedMouseEventValid;
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;
525
class nsSelectionIterator : public nsIBidirectionalEnumerator
528
/*BEGIN nsIEnumerator interfaces
529
see the nsIEnumerator for more details*/
533
NS_DECL_NSIENUMERATOR
535
NS_DECL_NSIBIDIRECTIONALENUMERATOR
537
/*END nsIEnumerator interfaces*/
538
/*BEGIN Helper Methods*/
539
NS_IMETHOD CurrentItem(nsIDOMRange **aRange);
540
/*END Helper Methods*/
542
friend class nsTypedSelection;
544
//lame lame lame if delete from document goes away then get rid of this unless its debug
545
friend class nsSelection;
547
nsSelectionIterator(nsTypedSelection *);
548
virtual ~nsSelectionIterator();
550
nsTypedSelection *mDomSelection;
554
class nsAutoScrollTimer : public nsITimerCallback
561
: mSelection(0), mView(0), mPresContext(0), mPoint(0,0), mDelay(30)
565
virtual ~nsAutoScrollTimer()
571
nsresult Start(nsIPresContext *aPresContext, nsIView *aView, nsPoint &aPoint)
574
mPresContext = aPresContext;
580
mTimer = do_CreateInstance("@mozilla.org/timer;1", &result);
582
if (NS_FAILED(result))
586
return mTimer->InitWithCallback(this, mDelay, nsITimer::TYPE_ONE_SHOT);
591
nsresult result = NS_OK;
602
nsresult Init(nsSelection *aFrameSelection, nsTypedSelection *aSelection)
604
mFrameSelection = aFrameSelection;
605
mSelection = aSelection;
609
nsresult SetDelay(PRUint32 aDelay)
615
NS_IMETHOD Notify(nsITimer *timer)
617
if (mSelection && mPresContext && mView)
619
nsIFrame *frame = NS_STATIC_CAST(nsIFrame*, mView->GetClientData());
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);
634
if (NS_SUCCEEDED(result))
635
result = mFrameSelection->HandleClick(newContent, startPos, contentOffsetEnd , PR_TRUE, PR_FALSE, beginOfContent);
637
//mFrameSelection->HandleDrag(mPresContext, mFrame, mPoint);
638
mSelection->DoAutoScrollView(mPresContext, mView, mPoint, PR_TRUE);
643
nsSelection *mFrameSelection;
644
nsTypedSelection *mSelection;
645
nsCOMPtr<nsITimer> mTimer;
647
nsIPresContext *mPresContext;
652
NS_IMPL_ADDREF(nsAutoScrollTimer)
653
NS_IMPL_RELEASE(nsAutoScrollTimer)
654
NS_IMPL_QUERY_INTERFACE1(nsAutoScrollTimer, nsITimerCallback)
656
nsresult NS_NewAutoScrollTimer(nsAutoScrollTimer **aResult);
658
nsresult NS_NewAutoScrollTimer(nsAutoScrollTimer **aResult)
661
return NS_ERROR_NULL_POINTER;
663
*aResult = (nsAutoScrollTimer*) new nsAutoScrollTimer;
666
return NS_ERROR_OUT_OF_MEMORY;
673
nsresult NS_NewSelection(nsIFrameSelection **aFrameSelection);
675
nsresult NS_NewSelection(nsIFrameSelection **aFrameSelection)
677
nsSelection *rlist = new nsSelection;
679
return NS_ERROR_OUT_OF_MEMORY;
680
*aFrameSelection = (nsIFrameSelection *)rlist;
685
nsresult NS_NewDomSelection(nsISelection **aDomSelection);
687
nsresult NS_NewDomSelection(nsISelection **aDomSelection)
689
nsTypedSelection *rlist = new nsTypedSelection;
691
return NS_ERROR_OUT_OF_MEMORY;
692
*aDomSelection = (nsISelection *)rlist;
698
GetIndexFromSelectionType(SelectionType aType)
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;
716
GetSelectionTypeFromIndex(PRInt8 aIndex)
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;
728
return nsISelectionController::SELECTION_NORMAL;break;
734
//utility methods to check the content vs the limiter that will hold selection to a piece of the dom
736
IsValidSelectionPoint(nsSelection *aFrameSel, nsIDOMNode *aDomNode)
738
nsCOMPtr<nsIContent> passedContent;
739
passedContent = do_QueryInterface(aDomNode);
742
return IsValidSelectionPoint(aFrameSel,passedContent);
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
759
IsValidSelectionPoint(nsSelection *aFrameSel, nsIContent *aContent)
761
if (!aFrameSel || !aContent)
766
nsCOMPtr<nsIContent> tLimiter;
767
result = aFrameSel->GetLimiter(getter_AddRefs(tLimiter));
768
if (NS_FAILED(result))
770
if (tLimiter && tLimiter != aContent)
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
780
NS_IMPL_ADDREF(nsSelectionIterator)
781
NS_IMPL_RELEASE(nsSelectionIterator)
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)
789
///////////BEGIN nsSelectionIterator methods
791
nsSelectionIterator::nsSelectionIterator(nsTypedSelection *aList)
796
NS_NOTREACHED("nsSelection");
799
mDomSelection = aList;
804
nsSelectionIterator::~nsSelectionIterator()
810
////////////END nsSelectionIterator methods
812
////////////BEGIN nsIFrameSelectionIterator methods
817
nsSelectionIterator::Next()
820
PRInt32 cnt = mDomSelection->mRangeArray.Count();
823
return NS_ERROR_FAILURE;
829
nsSelectionIterator::Prev()
834
return NS_ERROR_FAILURE;
840
nsSelectionIterator::First()
843
return NS_ERROR_NULL_POINTER;
851
nsSelectionIterator::Last()
854
return NS_ERROR_NULL_POINTER;
855
mIndex = mDomSelection->mRangeArray.Count() - 1;
862
nsSelectionIterator::CurrentItem(nsISupports **aItem)
865
return NS_ERROR_NULL_POINTER;
867
if (mIndex < 0 || mIndex >= mDomSelection->mRangeArray.Count()) {
868
return NS_ERROR_FAILURE;
871
return CallQueryInterface(mDomSelection->mRangeArray[mIndex],
877
nsSelectionIterator::CurrentItem(nsIDOMRange **aItem)
880
return NS_ERROR_NULL_POINTER;
881
if (mIndex < 0 || mIndex >= mDomSelection->mRangeArray.Count()) {
882
return NS_ERROR_FAILURE;
885
*aItem = mDomSelection->mRangeArray[mIndex];
886
NS_IF_ADDREF(*aItem);
893
nsSelectionIterator::IsDone()
895
PRInt32 cnt = mDomSelection->mRangeArray.Count();
896
if (mIndex >= 0 && mIndex < cnt) {
897
return NS_ENUMERATOR_FALSE;
903
////////////END nsIFrameSelectionIterator methods
909
////////////BEGIN nsSelection methods
911
nsSelection::nsSelection()
914
for (i = 0;i<nsISelectionController::NUM_SELECTIONTYPES;i++){
915
mDomSelections[i] = nsnull;
917
for (i = 0;i<nsISelectionController::NUM_SELECTIONTYPES;i++){
918
mDomSelections[i] = new nsTypedSelection(this);
919
if (!mDomSelections[i])
921
mDomSelections[i]->AddRef();
922
mDomSelections[i]->SetType(GetSelectionTypeFromIndex(i));
925
mChangesDuringBatching = PR_FALSE;
926
mNotifyFrames = PR_TRUE;
927
mLimiter = nsnull; //no default limiter.
929
mMouseDoubleDownState = PR_FALSE;
932
mDragSelectingCells = PR_FALSE;
933
mSelectingTableCellMode = 0;
934
mSelectedCellIndex = 0;
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));
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");
948
if (autoCopyService) {
950
GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
951
if (mDomSelections[index]) {
952
autoCopyService->Listen(mDomSelections[index]);
958
mDisplaySelection = nsISelectionController::SELECTION_OFF;
960
mDelayCaretOverExistingSelection = PR_TRUE;
961
mDelayedMouseEventValid = PR_FALSE;
962
mSelectionChangeReason = nsISelectionListener::NO_REASON;
966
nsSelection::~nsSelection()
969
for (i = 0;i<nsISelectionController::NUM_SELECTIONTYPES;i++){
970
if (mDomSelections[i])
971
NS_IF_RELEASE(mDomSelections[i]);
976
NS_IMPL_ISUPPORTS1(nsSelection, nsIFrameSelection)
980
nsSelection::FetchDesiredX(nscoord &aDesiredX) //the x position requested by the Key Handling for up down
984
NS_ASSERTION(0,"fetch desired X failed\n");
985
return NS_ERROR_FAILURE;
989
aDesiredX = mDesiredX;
993
nsCOMPtr<nsIPresContext> context;
994
nsresult result = mTracker->GetPresContext(getter_AddRefs(context));
995
if (NS_FAILED(result))
998
return NS_ERROR_NULL_POINTER;
1000
nsIPresShell *shell = context->GetPresShell();
1002
return NS_ERROR_NULL_POINTER;
1004
nsCOMPtr<nsICaret> caret;
1005
result = shell->GetCaret(getter_AddRefs(caret));
1006
if (NS_FAILED(result))
1009
return NS_ERROR_NULL_POINTER;
1013
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
1014
result = caret->SetCaretDOMSelection(mDomSelections[index]);
1015
if (NS_FAILED(result))
1018
result = caret->GetCaretCoordinates(nsICaret::eClosestViewCoordinates, mDomSelections[index], &coord, &collapsed, nsnull);
1019
if (NS_FAILED(result))
1022
aDesiredX = coord.x;
1029
nsSelection::InvalidateDesiredX() //do not listen to mDesiredX you must get another.
1031
mDesiredXSet = PR_FALSE;
1037
nsSelection::SetDesiredX(nscoord aX) //set the mDesiredX
1040
mDesiredXSet = PR_TRUE;
1044
nsSelection::GetRootForContentSubtree(nsIContent *aContent, nsIContent **aParent)
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.
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.
1056
if (!aContent || !aParent)
1057
return NS_ERROR_NULL_POINTER;
1061
nsIContent* child = aContent;
1065
nsIContent* parent = child->GetParent();
1070
PRUint32 childCount = parent->GetChildCount();
1075
PRInt32 childIndex = parent->IndexOf(child);
1077
if (childIndex < 0 || ((PRUint32)childIndex) >= childCount)
1083
NS_IF_ADDREF(*aParent = child);
1089
nsSelection::GetGlobalViewOffsetsFromFrame(nsIPresContext *aPresContext, nsIFrame *aFrame, nscoord *offsetX, nscoord *offsetY)
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.
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.
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.
1104
if (!aPresContext || !aFrame || !offsetX || !offsetY)
1105
return NS_ERROR_NULL_POINTER;
1107
*offsetX = *offsetY = 0;
1109
nsIFrame *frame = aFrame;
1112
frame = frame->GetAncestorWithView();
1115
nsIView *view = frame->GetView();
1119
nsPoint pt = view->GetPosition();
1130
nsSelection::ConstrainFrameAndPointToAnchorSubtree(nsIPresContext *aPresContext, nsIFrame *aFrame, nsPoint& aPoint, nsIFrame **aRetFrame, nsPoint& aRetPoint)
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().
1137
// A valid subtree is defined to be one where all the content nodes in
1138
// the tree have a valid parent-child relationship.
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.
1145
if (!aFrame || !aRetFrame)
1146
return NS_ERROR_NULL_POINTER;
1148
*aRetFrame = aFrame;
1152
// Get the frame and content for the selection's anchor point!
1156
nsCOMPtr<nsIDOMNode> anchorNode;
1157
PRInt32 anchorOffset = 0;
1158
PRInt32 anchorFrameOffset = 0;
1160
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
1161
if (! mDomSelections[index])
1162
return NS_ERROR_NULL_POINTER;
1164
result = mDomSelections[index]->GetAnchorNode(getter_AddRefs(anchorNode));
1166
if (NS_FAILED(result))
1172
result = mDomSelections[index]->GetAnchorOffset(&anchorOffset);
1174
if (NS_FAILED(result))
1177
nsIFrame *anchorFrame = 0;
1178
nsCOMPtr<nsIContent> anchorContent = do_QueryInterface(anchorNode);
1181
return NS_ERROR_FAILURE;
1183
result = GetFrameForNodeOffset(anchorContent, anchorOffset, mHint, &anchorFrame, &anchorFrameOffset);
1186
// Now find the root of the subtree containing the anchor's content.
1189
nsCOMPtr<nsIContent> anchorRoot;
1190
result = GetRootForContentSubtree(anchorContent, getter_AddRefs(anchorRoot));
1192
if (NS_FAILED(result))
1196
// Now find the root of the subtree containing aFrame's content.
1199
nsIContent* content = aFrame->GetContent();
1203
nsCOMPtr<nsIContent> contentRoot;
1205
result = GetRootForContentSubtree(content, getter_AddRefs(contentRoot));
1207
if (anchorRoot == contentRoot)
1210
// The anchor and AFrame's root are the same. There
1211
// is no need to constrain, simply return aFrame.
1213
*aRetFrame = aFrame;
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.
1225
result = mTracker->GetPrimaryFrameFor(anchorRoot, aRetFrame);
1227
if (NS_FAILED(result))
1231
return NS_ERROR_FAILURE;
1234
// Now make sure that aRetPoint is converted to the same coordinate
1235
// system used by aRetFrame.
1238
nsPoint frameOffset;
1239
nsPoint retFrameOffset;
1241
result = GetGlobalViewOffsetsFromFrame(aPresContext, aFrame, &frameOffset.x, &frameOffset.y);
1243
if (NS_FAILED(result))
1246
result = GetGlobalViewOffsetsFromFrame(aPresContext, *aRetFrame, &retFrameOffset.x, &retFrameOffset.y);
1248
if (NS_FAILED(result))
1251
aRetPoint = aPoint + frameOffset - retFrameOffset;
1261
void printRange(nsIDOMRange *aDomRange)
1265
printf("NULL nsIDOMRange\n");
1267
nsCOMPtr<nsIDOMNode> startNode;
1268
nsCOMPtr<nsIDOMNode> endNode;
1269
PRInt32 startOffset;
1271
aDomRange->GetStartParent(getter_AddRefs(startNode));
1272
aDomRange->GetStartOffset(&startOffset);
1273
aDomRange->GetEndParent(getter_AddRefs(endNode));
1274
aDomRange->GetEndOffset(&endOffset);
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);
1282
#endif /* PRINT_RANGE */
1285
nsIAtom *GetTag(nsIDOMNode *aNode)
1287
nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
1290
NS_NOTREACHED("bad node passed to GetTag()");
1294
return content->Tag();
1298
ParentOffset(nsIDOMNode *aNode, nsIDOMNode **aParent, PRInt32 *aChildOffset)
1300
if (!aNode || !aParent || !aChildOffset)
1301
return NS_ERROR_NULL_POINTER;
1303
nsCOMPtr<nsIContent> content = do_QueryInterface(aNode);
1306
nsIContent* parent = content->GetParent();
1309
*aChildOffset = parent->IndexOf(content);
1311
return CallQueryInterface(parent, aParent);
1319
GetCellParent(nsIDOMNode *aDomNode)
1323
nsCOMPtr<nsIDOMNode> parent(aDomNode);
1324
nsCOMPtr<nsIDOMNode> current(aDomNode);
1325
PRInt32 childOffset;
1327
// Start with current node and look for a table cell
1330
tag = GetTag(current);
1331
if (tag == nsHTMLAtoms::td || tag == nsHTMLAtoms::th)
1333
if (NS_FAILED(ParentOffset(current,getter_AddRefs(parent),&childOffset)) || !parent)
1342
nsSelection::Init(nsIFocusTracker *aTracker, nsIContent *aLimiter)
1344
mTracker = aTracker;
1345
mMouseDownState = PR_FALSE;
1346
mDesiredXSet = PR_FALSE;
1347
mLimiter = aLimiter;
1348
mScrollView = nsnull;
1353
nsSelection::SetScrollableView(nsIScrollableView *aScrollView)
1355
mScrollView = aScrollView;
1361
nsSelection::ShutDown()
1369
nsSelection::HandleTextEvent(nsGUIEvent *aGUIEvent)
1372
return NS_ERROR_NULL_POINTER;
1375
printf("nsSelection: HandleTextEvent\n");
1377
nsresult result(NS_OK);
1378
if (NS_TEXT_TEXT == aGUIEvent->message) {
1379
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
1380
result = mDomSelections[index]->ScrollIntoView();
1387
nsSelection::MoveCaret(PRUint32 aKeycode, PRBool aContinue, nsSelectionAmount aAmount)
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;
1394
nsCOMPtr<nsIDOMNode> weakNodeUsed;
1395
PRInt32 offsetused = 0;
1398
nscoord desiredX = 0; //we must keep this around and revalidate it when its just UP/DOWN
1400
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
1401
result = mDomSelections[index]->GetIsCollapsed(&isCollapsed);
1402
if (NS_FAILED(result))
1404
if (aKeycode == nsIDOMKeyEvent::DOM_VK_UP || aKeycode == nsIDOMKeyEvent::DOM_VK_DOWN)
1406
result = FetchDesiredX(desiredX);
1407
if (NS_FAILED(result))
1409
SetDesiredX(desiredX);
1412
if (!isCollapsed && !aContinue) {
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();
1421
offsetused = mDomSelections[index]->FetchAnchorOffset();
1422
weakNodeUsed = mDomSelections[index]->FetchAnchorNode();
1424
result = mDomSelections[index]->Collapse(weakNodeUsed,offsetused);
1425
mDomSelections[index]->ScrollIntoView();
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();
1436
offsetused = mDomSelections[index]->FetchFocusOffset();
1437
weakNodeUsed = mDomSelections[index]->FetchFocusNode();
1439
result = mDomSelections[index]->Collapse(weakNodeUsed,offsetused);
1440
mDomSelections[index]->ScrollIntoView();
1446
// if (keyEvent->keyCode == nsIDOMKeyEvent::DOM_VK_UP || keyEvent->keyCode == nsIDOMKeyEvent::DOM_VK_DOWN)
1447
// SetDesiredX(desiredX);
1450
nsCOMPtr<nsICaret> caret;
1451
nsIPresShell *shell = context->GetPresShell();
1454
result = shell->GetCaret(getter_AddRefs(caret));
1455
if (NS_FAILED(result) || !caret)
1458
offsetused = mDomSelections[index]->FetchFocusOffset();
1459
weakNodeUsed = mDomSelections[index]->FetchFocusNode();
1462
result = mDomSelections[index]->GetPrimaryFrameForFocusNode(&frame, &offsetused);
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;
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);
1475
HINT tHint(mHint); //temporary variable so we dont set mHint until it is necessary
1477
case nsIDOMKeyEvent::DOM_VK_RIGHT :
1478
InvalidateDesiredX();
1479
pos.mDirection = eDirNext;
1480
tHint = HINTLEFT;//stick to this line
1481
PostReason(nsISelectionListener::KEYPRESS_REASON);
1483
case nsIDOMKeyEvent::DOM_VK_LEFT : //no break
1484
InvalidateDesiredX();
1485
tHint = HINTRIGHT;//stick to opposite of movement
1486
PostReason(nsISelectionListener::KEYPRESS_REASON);
1488
case nsIDOMKeyEvent::DOM_VK_DOWN :
1489
pos.mAmount = eSelectLine;
1490
pos.mDirection = eDirNext;//no break here
1491
PostReason(nsISelectionListener::KEYPRESS_REASON);
1493
case nsIDOMKeyEvent::DOM_VK_UP :
1494
pos.mAmount = eSelectLine;
1495
PostReason(nsISelectionListener::KEYPRESS_REASON);
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);
1503
case nsIDOMKeyEvent::DOM_VK_END :
1504
InvalidateDesiredX();
1505
pos.mAmount = eSelectEndLine;
1506
tHint = HINTLEFT;//stick to this line
1507
PostReason(nsISelectionListener::KEYPRESS_REASON);
1509
default :return NS_ERROR_FAILURE;
1511
pos.mPreferLeft = tHint;
1512
if (NS_SUCCEEDED(result) && NS_SUCCEEDED(result = frame->PeekOffset(context, &pos)) && pos.mResultContent)
1514
tHint = (HINT)pos.mPreferLeft;
1515
PRBool bidiEnabled = PR_FALSE;
1516
context->GetBidiEnabled(&bidiEnabled);
1520
PRInt32 currentOffset, frameStart, frameEnd;
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, ¤tOffset);
1528
theFrame->GetOffsets(frameStart, frameEnd);
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
1539
case nsIDOMKeyEvent::DOM_VK_HOME:
1540
case nsIDOMKeyEvent::DOM_VK_END:
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;
1547
pos.mContentOffset = frameEnd;
1549
// set the cursor Bidi level to the paragraph embedding level
1550
theFrame->GetBidiProperty(context, nsLayoutAtoms::baseLevel, (void**)&level,
1552
shell->SetCaretBidiLevel(level);
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))
1561
theFrame->GetBidiProperty(context, nsLayoutAtoms::embeddingLevel, (void**)&level,
1563
shell->SetCaretBidiLevel(level);
1566
BidiLevelFromMove(context, shell, pos.mResultContent, pos.mContentOffset, aKeycode);
1569
#ifdef VISUALSELECTION
1570
// Handle visual selection
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);
1578
result = TakeFocus(pos.mResultContent, pos.mContentOffset, pos.mContentOffset, aContinue, PR_FALSE);
1583
#endif // VISUALSELECTION
1584
result = TakeFocus(pos.mResultContent, pos.mContentOffset, pos.mContentOffset, aContinue, PR_FALSE);
1586
if (NS_SUCCEEDED(result))
1588
mHint = tHint; //save the hint parameter now for the next time
1589
result = mDomSelections[index]->ScrollIntoView();
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.
1601
nsSelection::HandleKeyEvent(nsIPresContext* aPresContext, nsGUIEvent *aGuiEvent)
1604
return NS_ERROR_NULL_POINTER;
1605
STATUS_CHECK_RETURN_MACRO();
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)
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 :
1620
return NS_ERROR_FAILURE;
1623
//XXX Need xp way get platfrom specific behavior into key navigation.
1624
//XXX This really shouldn't have to use an ifdef
1626
if (keyEvent->isAlt) {
1627
return NS_ERROR_FAILURE;
1630
nsSelectionAmount amount = eSelectCharacter;
1631
if (keyEvent->isControl)
1632
amount = eSelectWord;
1633
return MoveCaret(keyEvent->keyCode, keyEvent->isShift, amount);
1638
//END nsSelection methods
1641
//BEGIN nsIFrameSelection methods
1644
nsTypedSelection::ToString(PRUnichar **aReturn)
1646
return ToStringWithFormat("text/plain", 0, 0, aReturn);
1651
nsTypedSelection::ToStringWithFormat(const char * aFormatType, PRUint32 aFlags,
1652
PRInt32 aWrapCol, PRUnichar **aReturn)
1654
nsresult rv = NS_OK;
1656
return NS_ERROR_NULL_POINTER;
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);
1664
nsCOMPtr<nsIPresShell> shell;
1665
rv = GetPresShell(getter_AddRefs(shell));
1666
if (NS_FAILED(rv) || !shell) {
1667
return NS_ERROR_FAILURE;
1670
nsCOMPtr<nsIDocument> doc;
1671
rv = shell->GetDocument(getter_AddRefs(doc));
1672
NS_ENSURE_SUCCESS(rv, rv);
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);
1681
encoder->SetSelection(this);
1683
encoder->SetWrapColumn(aWrapCol);
1686
rv = encoder->EncodeToString(tmp);
1687
*aReturn = ToNewUnicode(tmp);//get the unicode pointer from it. this is temporary
1692
nsTypedSelection::SetInterlinePosition(PRBool aHintRight)
1694
nsIFrameSelection::HINT hint;
1696
hint = nsIFrameSelection::HINTRIGHT;
1698
hint = nsIFrameSelection::HINTLEFT;
1699
return mFrameSelection->SetHint(hint);
1703
nsTypedSelection::GetInterlinePosition(PRBool *aHintRight)
1705
nsIFrameSelection::HINT hint;
1706
nsresult rv = mFrameSelection->GetHint(&hint);
1707
if (hint == nsIFrameSelection::HINTRIGHT)
1708
*aHintRight = PR_TRUE;
1710
*aHintRight = PR_FALSE;
1714
#ifdef VISUALSELECTION
1717
ReverseDirection(nsDirection aDirection)
1719
return (eDirNext == aDirection) ? eDirPrevious : eDirNext;
1723
FindLineContaining(nsIFrame* aFrame, nsIFrame** aBlock, PRInt32* aLine)
1725
nsIFrame *blockFrame = aFrame;
1726
nsIFrame *thisBlock = nsnull;
1727
nsCOMPtr<nsILineIteratorNavigator> it;
1728
nsresult result = NS_ERROR_FAILURE;
1729
while (NS_FAILED(result) && blockFrame)
1731
thisBlock = blockFrame;
1732
blockFrame = blockFrame->GetParent();
1734
it = do_QueryInterface(blockFrame, &result);
1737
if (!blockFrame || !it)
1738
return NS_ERROR_FAILURE;
1739
*aBlock = blockFrame;
1740
return it->FindLineContaining(thisBlock, aLine);
1744
nsSelection::VisualSequence(nsIPresContext *aPresContext,
1745
nsIFrame* aSelectFrame,
1746
nsIFrame* aCurrentFrame,
1747
nsPeekOffsetStruct* aPos,
1748
PRBool* aNeedVisualSelection)
1750
nsVoidArray frameArray;
1751
PRUint8 bidiLevel, currentLevel;
1752
PRInt32 frameStart, frameEnd;
1753
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
1754
nsresult result = nsnull;
1756
aCurrentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, (void**)¤tLevel, sizeof(currentLevel) );
1758
result = aSelectFrame->PeekOffset(aPresContext, aPos);
1759
while (aCurrentFrame != (aSelectFrame = aPos->mResultFrame))
1761
if (NS_FAILED(result))
1762
return NS_OK; // we have passed the end of the line, and we will carry on from there
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
1769
frameArray.AppendElement(aSelectFrame);
1771
aSelectFrame->GetOffsets(frameStart, frameEnd);
1772
aSelectFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, (void**)&bidiLevel, sizeof(bidiLevel) );
1774
if (currentLevel != bidiLevel)
1775
*aNeedVisualSelection = PR_TRUE;
1776
if ((eDirNext == aPos->mDirection) == (bidiLevel & 1))
1778
mDomSelections[index]->SetDirection(eDirPrevious);
1779
result = TakeFocus(aPos->mResultContent, frameEnd, frameStart, PR_FALSE, PR_TRUE);
1783
mDomSelections[index]->SetDirection(eDirNext);
1784
result = TakeFocus(aPos->mResultContent, frameStart, frameEnd, PR_FALSE, PR_TRUE);
1786
if (NS_FAILED(result))
1789
aPos->mAmount = eSelectDir; // reset this because PeekOffset will have changed it to eSelectNoAmount
1790
aPos->mContentOffset = 0;
1791
result = aSelectFrame->PeekOffset(aPresContext, aPos);
1798
nsSelection::SelectToEdge(nsIFrame *aFrame, nsIContent *aContent, PRInt32 aOffset, PRInt32 aEdge, PRBool aMultipleSelection)
1800
PRInt32 frameStart, frameEnd;
1802
aFrame->GetOffsets(frameStart, frameEnd);
1805
else if (-1 == aEdge)
1808
aOffset = frameStart;
1809
else if (-1 == aOffset)
1811
return TakeFocus(aContent, aOffset, aEdge, PR_FALSE, aMultipleSelection);
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)
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;
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)
1840
startNode = aAnchorNode;
1841
startFrame = aAnchorFrame;
1842
startOffset = aAnchorOffset;
1843
endNode = aCurrentNode;
1844
endFrame = aCurrentFrame;
1845
endOffset = aCurrentOffset;
1849
startNode = aCurrentNode;
1850
startFrame = aCurrentFrame;
1851
startOffset = aCurrentOffset;
1852
endNode = aAnchorNode;
1853
endFrame = aAnchorFrame;
1854
endOffset = aAnchorOffset;
1857
aPos.mStartOffset = startOffset;
1858
aPos.mDirection = eDirNext;
1859
aPos.mAmount = eSelectLine;
1860
result = startFrame->PeekOffset(aPresContext, &aPos);
1861
if (NS_FAILED(result))
1863
startFrame = aPos.mResultFrame;
1865
aPos.mStartOffset = aPos.mContentOffset;
1866
aPos.mAmount = eSelectBeginLine;
1867
result = startFrame->PeekOffset(aPresContext, &aPos);
1868
if (NS_FAILED(result))
1872
PRInt32 currentOffset, frameStart, frameEnd;
1874
result = GetFrameForNodeOffset(aPos.mResultContent, aPos.mContentOffset, HINTLEFT, &theFrame, ¤tOffset);
1875
if (NS_FAILED(result))
1877
theFrame->GetOffsets(frameStart, frameEnd);
1878
startOffset = frameStart;
1879
startContent = aPos.mResultContent;
1880
startNode = do_QueryInterface(startContent);
1882
// If we have already overshot the endpoint, back out
1883
if (ComparePoints(startNode, startOffset, endNode, endOffset) >= 0)
1884
return NS_ERROR_FAILURE;
1886
aPos.mStartOffset = endOffset;
1887
aPos.mDirection = eDirPrevious;
1888
aPos.mAmount = eSelectLine;
1889
result = endFrame->PeekOffset(aPresContext, &aPos);
1890
if (NS_FAILED(result))
1892
endFrame = aPos.mResultFrame;
1894
aPos.mStartOffset = aPos.mContentOffset;
1895
aPos.mAmount = eSelectEndLine;
1896
result = endFrame->PeekOffset(aPresContext, &aPos);
1897
if (NS_FAILED(result))
1900
result = GetFrameForNodeOffset(aPos.mResultContent, aPos.mContentOffset, HINTRIGHT, &theFrame, ¤tOffset);
1901
if (NS_FAILED(result))
1903
theFrame->GetOffsets(frameStart, frameEnd);
1904
endOffset = frameEnd;
1905
endContent = aPos.mResultContent;
1906
endNode = do_QueryInterface(endContent);
1908
if (ComparePoints(startNode, startOffset, endNode, endOffset) < 0)
1910
TakeFocus(startContent, startOffset, startOffset, PR_FALSE, PR_TRUE);
1911
return TakeFocus(endContent, endOffset, endOffset, PR_TRUE, PR_TRUE);
1914
return NS_ERROR_FAILURE;
1918
nsSelection::VisualSelectFrames(nsIPresContext *aPresContext,
1919
nsIFrame* aCurrentFrame,
1920
nsPeekOffsetStruct aPos)
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;
1932
nsCOMPtr<nsIContent> currentContent;
1933
nsCOMPtr<nsIDOMNode> currentNode;
1934
PRInt32 currentOffset;
1935
PRUint8 currentLevel;
1937
nsIFrame* startFrame;
1938
PRBool needVisualSelection = PR_FALSE;
1939
nsDirection selectionDirection;
1940
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
1942
result = mDomSelections[index]->GetOriginalAnchorPoint(getter_AddRefs(anchorNode), &anchorOffset);
1943
if (NS_FAILED(result))
1945
anchorContent = do_QueryInterface(anchorNode);
1946
result = GetFrameForNodeOffset(anchorContent, anchorOffset, mHint, &anchorFrame, &anchorOffset);
1947
if (NS_FAILED(result))
1949
anchorFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, (void**)&anchorLevel, sizeof(anchorLevel) );
1951
currentContent = aPos.mResultContent;
1952
currentNode = do_QueryInterface(currentContent);
1953
currentOffset = aPos.mContentOffset;
1954
aCurrentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, (void**)¤tLevel, sizeof(currentLevel) );
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);
1963
focusOffset = mDomSelections[index]->FetchFocusOffset();
1964
focusNode = mDomSelections[index]->FetchFocusNode();
1965
focusContent = do_QueryInterface(focusNode);
1967
if ((HINTLEFT == mHint) == (currentLevel & 1))
1972
result = GetFrameForNodeOffset(focusContent, focusOffset, hint, &focusFrame, &focusOffset);
1973
if (NS_FAILED(result))
1976
focusFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel, (void**)&focusLevel, sizeof(focusLevel) );
1978
if (currentLevel != anchorLevel)
1979
needVisualSelection = PR_TRUE;
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);
1988
PRInt32 anchorLine, currentLine;
1989
nsIFrame* anchorBlock = nsnull;
1990
nsIFrame* currentBlock = nsnull;
1991
FindLineContaining(anchorFrame, &anchorBlock, &anchorLine);
1992
FindLineContaining(aCurrentFrame, ¤tBlock, ¤tLine);
1994
if (anchorBlock==currentBlock && anchorLine==currentLine)
1996
// case 2: selection starts and ends in the same line
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);
2004
result = SelectToEdge(anchorFrame, anchorContent, anchorOffset, -1, PR_FALSE);
2005
if (NS_FAILED(result))
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;
2014
result = anchorFrame->PeekOffset(aPresContext, &aPos);
2015
if (NS_FAILED(result))
2018
startFrame = aPos.mResultFrame;
2019
result = VisualSequence(aPresContext, startFrame, aCurrentFrame, &aPos, &needVisualSelection);
2020
if (NS_FAILED(result))
2023
if (!needVisualSelection)
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))
2031
result = TakeFocus(currentContent, currentOffset, currentOffset, PR_TRUE, PR_FALSE);
2032
if (NS_FAILED(result))
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);
2042
result = SelectToEdge(aCurrentFrame, currentContent, 0, currentOffset, PR_TRUE);
2043
if (NS_FAILED(result))
2049
// case 3: selection starts and ends in different lines
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)
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
2058
// If selection direction is backwards, vice versa throughout
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);
2066
result = SelectToEdge(anchorFrame, anchorContent, anchorOffset, -1, PR_FALSE);
2067
if (NS_FAILED(result))
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))
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))
2089
// Go to the current point
2091
aCurrentFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::baseLevel, (void**)¤tBaseLevel, 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))
2101
// Select from the current point to the edge of the frame
2102
if (currentLevel & 1)
2103
mDomSelections[index]->SetDirection(ReverseDirection(selectionDirection));
2105
if ((eDirPrevious == selectionDirection) != ((currentLevel & 1) == (currentBaseLevel & 1)))
2106
result = SelectToEdge(aCurrentFrame, currentContent, 0, currentOffset, PR_TRUE);
2108
result = SelectToEdge(aCurrentFrame, currentContent, -1, currentOffset, PR_TRUE);
2109
if (NS_FAILED(result))
2112
// restore original selection direction
2113
// mDomSelections[index]->SetDirection(selectionDirection);
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);
2119
mDomSelections[index]->SetOriginalAnchorPoint(anchorNode, anchorOffset);
2120
NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL);
2123
#endif // VISUALSELECTION
2126
nsSelection::GetPrevNextBidiLevels(nsIPresContext *aPresContext,
2128
PRUint32 aContentOffset,
2129
nsIFrame **aPrevFrame,
2130
nsIFrame **aNextFrame,
2131
PRUint8 *aPrevLevel,
2132
PRUint8 *aNextLevel)
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;
2143
*aPrevLevel = *aNextLevel = 0;
2145
result = GetFrameForNodeOffset(aNode, aContentOffset, mHint, ¤tFrame, ¤tOffset);
2146
if (NS_FAILED(result))
2148
currentFrame->GetOffsets(frameStart, frameEnd);
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;
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;
2166
we have to find the next or previous *logical* frame.
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.
2171
XXX is there a simpler way to do this?
2174
nsIFrame *blockFrame = currentFrame;
2175
nsIFrame *thisBlock = nsnull;
2177
nsCOMPtr<nsILineIteratorNavigator> it;
2178
result = NS_ERROR_FAILURE;
2179
while (NS_FAILED(result) && blockFrame)
2181
thisBlock = blockFrame;
2182
blockFrame = blockFrame->GetParent();
2184
it = do_QueryInterface(blockFrame, &result);
2187
if (!blockFrame || !it)
2188
return NS_ERROR_FAILURE;
2189
result = it->FindLineContaining(thisBlock, &thisLine);
2190
if (NS_FAILED(result))
2194
return NS_ERROR_FAILURE;
2196
nsIFrame *firstFrame;
2197
nsIFrame *lastFrame;
2199
PRInt32 lineFrameCount;
2202
result = it->GetLine(thisLine, &firstFrame, &lineFrameCount,nonUsedRect,
2204
if (NS_FAILED(result))
2207
lastFrame = firstFrame;
2209
for (;lineFrameCount > 1;lineFrameCount --) {
2210
lastFrame = lastFrame->GetNextSibling();
2214
nsIFrame *lookahead;
2216
lookahead = firstFrame->GetFirstChild(nsnull);
2218
break; //nothing to do
2219
firstFrame = lookahead;
2224
lookahead = lastFrame->GetFirstChild(nsnull);
2226
break; //nothing to do
2227
lastFrame = lookahead;
2228
while ((lookahead = lastFrame->GetNextSibling()) != nsnull)
2229
lastFrame = lookahead;
2231
//END LINE DATA CODE
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;
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;
2259
// Find the adjacent frame
2261
nsCOMPtr<nsIBidirectionalEnumerator> frameTraversal;
2262
nsCOMPtr<nsIFrameTraversal> trav(do_CreateInstance(kFrameTraversalCID,&result));
2263
if (NS_FAILED(result))
2266
result = trav->NewFrameTraversal(getter_AddRefs(frameTraversal),LEAF, aPresContext, currentFrame);
2267
if (NS_FAILED(result))
2269
nsISupports *isupports = nsnull;
2270
if (direction == eDirNext)
2271
result = frameTraversal->Next();
2273
result = frameTraversal->Prev();
2275
if (NS_FAILED(result))
2277
result = frameTraversal->CurrentItem(&isupports);
2278
if (NS_FAILED(result))
2281
return NS_ERROR_NULL_POINTER;
2282
//we must CAST here to an nsIFrame. nsIFrame doesnt really follow the rules
2284
nsIFrame *newFrame = (nsIFrame *)isupports;
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) );
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) );
2307
NS_IMETHODIMP nsSelection::GetFrameFromLevel(nsIPresContext *aPresContext,
2309
nsDirection aDirection,
2311
nsIFrame **aFrameOut)
2313
PRUint8 foundLevel = 0;
2314
nsIFrame *foundFrame = aFrameIn;
2316
nsCOMPtr<nsIBidirectionalEnumerator> frameTraversal;
2318
nsCOMPtr<nsIFrameTraversal> trav(do_CreateInstance(kFrameTraversalCID,&result));
2319
if (NS_FAILED(result))
2322
result = trav->NewFrameTraversal(getter_AddRefs(frameTraversal),LEAF, aPresContext, aFrameIn);
2323
if (NS_FAILED(result))
2325
nsISupports *isupports = nsnull;
2328
*aFrameOut = foundFrame;
2329
if (aDirection == eDirNext)
2330
result = frameTraversal->Next();
2332
result = frameTraversal->Prev();
2334
if (NS_FAILED(result))
2336
result = frameTraversal->CurrentItem(&isupports);
2337
if (NS_FAILED(result))
2340
return NS_ERROR_NULL_POINTER;
2341
//we must CAST here to an nsIFrame. nsIFrame doesnt really follow the rules
2343
foundFrame = (nsIFrame *)isupports;
2344
foundFrame->GetBidiProperty(aPresContext, nsLayoutAtoms::embeddingLevel,
2345
(void**)&foundLevel, sizeof(foundLevel) );
2347
} while (foundLevel > aBidiLevel);
2354
nsSelection::MaintainSelection()
2356
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
2357
nsCOMPtr<nsIDOMRange> range;
2358
nsresult rv = mDomSelections[index]->GetRangeAt(0, getter_AddRefs(range));
2362
return NS_ERROR_FAILURE;
2364
nsCOMPtr<nsIDOMNode> startNode;
2365
nsCOMPtr<nsIDOMNode> endNode;
2366
PRInt32 startOffset;
2368
range->GetStartContainer(getter_AddRefs(startNode));
2369
range->GetEndContainer(getter_AddRefs(endNode));
2370
range->GetStartOffset(&startOffset);
2371
range->GetEndOffset(&endOffset);
2373
mMaintainRange = nsnull;
2374
NS_NewRange(getter_AddRefs(mMaintainRange));
2375
if (!mMaintainRange)
2376
return NS_ERROR_OUT_OF_MEMORY;
2378
mMaintainRange->SetStart(startNode, startOffset);
2379
return mMaintainRange->SetEnd(endNode, endOffset);
2383
/** After moving the caret, its Bidi level is set according to the following rules:
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.
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
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
2399
void nsSelection::BidiLevelFromMove(nsIPresContext* aContext,
2400
nsIPresShell* aPresShell,
2402
PRUint32 aContentOffset,
2406
PRUint8 secondLevel;
2407
PRUint8 currentLevel;
2408
nsIFrame* firstFrame=nsnull;
2409
nsIFrame* secondFrame=nsnull;
2411
aPresShell->GetCaretBidiLevel(¤tLevel);
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);
2422
aPresShell->SetCaretBidiLevel(secondLevel);
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));
2435
aPresShell->UndefineCaretBidiLevel();
2440
* BidiLevelFromClick is called when the caret is repositioned by clicking the mouse
2442
* @param aNode is the content node
2443
* @param aContentOffset is the new caret position, as an offset into aNode
2445
void nsSelection::BidiLevelFromClick(nsIContent *aNode, PRUint32 aContentOffset)
2447
nsCOMPtr<nsIPresContext> context;
2448
nsresult result = mTracker->GetPresContext(getter_AddRefs(context));
2449
if (NS_FAILED(result) || !context)
2452
nsIPresShell *shell = context->GetPresShell();
2456
nsIFrame* clickInFrame=nsnull;
2458
PRInt32 OffsetNotUsed;
2460
result = GetFrameForNodeOffset(aNode, aContentOffset, mHint, &clickInFrame, &OffsetNotUsed);
2461
if (NS_FAILED(result))
2464
clickInFrame->GetBidiProperty(context, nsLayoutAtoms::embeddingLevel,
2465
(void**)&frameLevel, sizeof(frameLevel) );
2466
shell->SetCaretBidiLevel(frameLevel);
2471
nsSelection::AdjustForMaintainedSelection(nsIContent *aContent, PRInt32 aOffset)
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)
2481
nsCOMPtr<nsIDOMNode> rangenode;
2482
PRInt32 rangeOffset;
2483
mMaintainRange->GetStartContainer(getter_AddRefs(rangenode));
2484
mMaintainRange->GetStartOffset(&rangeOffset);
2486
nsCOMPtr<nsIDOMNode> domNode = do_QueryInterface(aContent);
2489
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
2490
nsCOMPtr<nsIDOMNSRange> nsrange = do_QueryInterface(mMaintainRange);
2493
PRBool insideSelection = PR_FALSE;
2494
nsrange->IsPointInRange(domNode, aOffset, &insideSelection);
2496
// Done when we find a range that we are in
2497
if (insideSelection)
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
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))
2512
mMaintainRange->GetEndContainer(getter_AddRefs(rangenode));
2513
mMaintainRange->GetEndOffset(&rangeOffset);
2514
mDomSelections[index]->Collapse(rangenode, rangeOffset);
2516
else if (relativePosition < 0
2517
&& (mDomSelections[index]->GetDirection() == eDirPrevious))
2518
mDomSelections[index]->Collapse(rangenode, rangeOffset);
2526
nsSelection::HandleClick(nsIContent *aNewFocus, PRUint32 aContentOffset,
2527
PRUint32 aContentEndOffset, PRBool aContinueSelection,
2528
PRBool aMultipleSelection, PRBool aHint)
2531
return NS_ERROR_INVALID_ARG;
2533
InvalidateDesiredX();
2535
if (!aContinueSelection)
2536
mMaintainRange = nsnull;
2538
mHint = HINT(aHint);
2539
// Don't take focus when dragging off of a table
2540
if (!mDragSelectingCells)
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.
2548
return TakeFocus(aNewFocus, aContentOffset, aContentEndOffset, aContinueSelection, aMultipleSelection);
2555
nsSelection::HandleDrag(nsIPresContext *aPresContext, nsIFrame *aFrame, nsPoint& aPoint)
2557
if (!aPresContext || !aFrame)
2558
return NS_ERROR_NULL_POINTER;
2561
nsIFrame *newFrame = 0;
2564
result = ConstrainFrameAndPointToAnchorSubtree(aPresContext, aFrame, aPoint, &newFrame, newPoint);
2565
if (NS_FAILED(result))
2568
return NS_ERROR_FAILURE;
2570
PRInt32 startPos = 0;
2571
PRInt32 contentOffsetEnd = 0;
2572
PRBool beginOfContent;
2573
nsCOMPtr<nsIContent> newContent;
2575
result = newFrame->GetContentAndOffsetsFromPoint(aPresContext, newPoint,
2576
getter_AddRefs(newContent),
2577
startPos, contentOffsetEnd, beginOfContent);
2579
if ((newFrame->GetStateBits() & NS_FRAME_SELECTED_CONTENT) &&
2580
AdjustForMaintainedSelection(newContent, startPos))
2583
// do we have CSS that changes selection behaviour?
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))
2592
newContent = selectContent;
2593
startPos = newStart;
2594
contentOffsetEnd = newEnd;
2598
if (NS_SUCCEEDED(result))
2600
#ifdef VISUALSELECTION
2601
PRBool bidiEnabled = PR_FALSE;
2602
aPresContext->GetBidiEnabled(&bidiEnabled);
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));
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);
2625
#endif // VISUALSELECTION
2626
result = HandleClick(newContent, startPos, contentOffsetEnd, PR_TRUE,
2627
PR_FALSE, beginOfContent);
2634
nsSelection::StartAutoScrollTimer(nsIPresContext *aPresContext, nsIFrame *aFrame, nsPoint& aPoint, PRUint32 aDelay)
2636
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
2637
return mDomSelections[index]->StartAutoScrollTimer(aPresContext, aFrame, aPoint, aDelay);
2641
nsSelection::StopAutoScrollTimer()
2643
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
2644
return mDomSelections[index]->StopAutoScrollTimer();
2648
hard to go from nodes to frames, easy the other way!
2651
nsSelection::TakeFocus(nsIContent *aNewFocus, PRUint32 aContentOffset,
2652
PRUint32 aContentEndOffset, PRBool aContinueSelection, PRBool aMultipleSelection)
2655
return NS_ERROR_NULL_POINTER;
2657
STATUS_CHECK_RETURN_MACRO();
2659
if (!IsValidSelectionPoint(this,aNewFocus))
2660
return NS_ERROR_FAILURE;
2662
// Clear all table selection data
2663
mSelectingTableCellMode = 0;
2664
mDragSelectingCells = PR_FALSE;
2665
mStartSelectedCell = nsnull;
2666
mEndSelectedCell = nsnull;
2667
mAppendStartSelectedCell = nsnull;
2670
nsIContent * parentContent = aNewFocus->GetParent();
2672
return NS_ERROR_FAILURE;
2673
//END HACKHACKHACK /checking for root frames/content
2675
nsCOMPtr<nsIContent> startContent = aNewFocus;
2676
if (aNewFocus->IsContentOfType(nsIContent::eELEMENT))
2678
PRInt32 childIndex = 0;
2679
PRInt32 numChildren = 0;
2681
if (mHint == HINTLEFT)
2683
if (aContentOffset > 0)
2684
childIndex = aContentOffset - 1;
2686
childIndex = aContentOffset;
2690
numChildren = aNewFocus->GetChildCount();
2692
if (aContentOffset >= numChildren)
2694
if (numChildren > 0)
2695
childIndex = numChildren - 1;
2700
childIndex = aContentOffset;
2702
startContent = aNewFocus->GetChildAt(childIndex);
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)
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;
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;
2726
if (aMultipleSelection){
2727
nsCOMPtr<nsIDOMRange> newRange;
2728
NS_NewRange(getter_AddRefs(newRange));
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);
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;
2745
if (aContentEndOffset != aContentOffset)
2746
mDomSelections[index]->Extend(domNode,aContentEndOffset);
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
2753
nsCOMPtr<nsIPresContext> presContext;
2754
result = mTracker->GetPresContext(getter_AddRefs(presContext));
2755
if (NS_FAILED(result) || !presContext)
2756
return result?result:NS_ERROR_FAILURE;
2758
nsIPresShell *presShell = presContext->GetPresShell();
2760
return NS_ERROR_FAILURE;
2762
PRInt16 displaySelection;
2763
result = presShell->GetSelectionFlags(&displaySelection);
2764
if (NS_FAILED(result))
2767
// Editor has DISPLAY_ALL selection type
2768
if (displaySelection == nsISelectionDisplay::DISPLAY_ALL)
2770
mCellParent = GetCellParent(domNode);
2771
#ifdef DEBUG_TABLE_SELECTION
2773
printf(" * TakeFocus - Collapsing into new cell\n");
2778
// Now update the range list:
2779
if (aContinueSelection && domNode)
2782
nsIDOMNode *cellparent = GetCellParent(domNode);
2783
if (mCellParent && cellparent && cellparent != mCellParent) //switch to cell selection mode
2785
#ifdef DEBUG_TABLE_SELECTION
2786
printf(" * TakeFocus - moving into new cell\n");
2788
nsCOMPtr<nsIDOMNode> parent;
2789
nsCOMPtr<nsIContent> parentContent;
2793
// Start selecting in the cell we were in before
2794
result = ParentOffset(mCellParent, getter_AddRefs(parent),&offset);
2795
parentContent = do_QueryInterface(parent);
2797
result = HandleTableSelection(parentContent, offset, nsISelectionPrivate::TABLESELECTION_CELL, &event);
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);
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;
2808
mCellParent = cellparent;
2809
// Continue selection into next cell
2810
result = HandleTableSelection(parentContent, offset, nsISelectionPrivate::TABLESELECTION_CELL, &event);
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
2819
mDomSelections[index]->Extend(domNode, aContentEndOffset);//this will only redraw the diff
2822
mDomSelections[index]->Extend(domNode, aContentOffset);
2827
// Don't notify selection listeners if batching is on:
2830
return NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL);
2836
nsSelection::LookUpSelection(nsIContent *aContent, PRInt32 aContentOffset, PRInt32 aContentLength,
2837
SelectionDetails **aReturnDetails, PRBool aSlowCheck)
2839
if (!aContent || !aReturnDetails)
2840
return NS_ERROR_NULL_POINTER;
2842
STATUS_CHECK_RETURN_MACRO();
2845
*aReturnDetails = nsnull;
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.
2852
for (j = (PRInt8) 1; j < (PRInt8)nsISelectionController::NUM_SELECTIONTYPES; j++){
2853
if (mDomSelections[j]){
2855
mDomSelections[j]->GetIsCollapsed(&iscollapsed);
2857
aSlowCheck = PR_TRUE;
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);
2872
nsSelection::SetMouseDownState(PRBool aState)
2874
if (mMouseDownState == aState)
2876
mMouseDownState = aState;
2877
if (!mMouseDownState)
2881
reason = nsISelectionListener::MOUSEDOWN_REASON;
2883
reason = nsISelectionListener::MOUSEUP_REASON;
2884
PostReason(reason);//not a drag reason
2885
NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL);//notify that reason is mouse up please.
2893
nsSelection::GetMouseDownState(PRBool *aState)
2896
return NS_ERROR_NULL_POINTER;
2897
*aState = mMouseDownState;
2902
nsSelection::GetSelection(SelectionType aType, nsISelection **aDomSelection)
2905
return NS_ERROR_NULL_POINTER;
2906
PRInt8 index = GetIndexFromSelectionType(aType);
2908
return NS_ERROR_INVALID_ARG;
2909
*aDomSelection = NS_REINTERPRET_CAST(nsISelection *,mDomSelections[index]);
2910
(*aDomSelection)->AddRef();
2915
nsSelection::ScrollSelectionIntoView(SelectionType aType, SelectionRegion aRegion, PRBool aIsSynchronous)
2917
PRInt8 index = GetIndexFromSelectionType(aType);
2919
return NS_ERROR_INVALID_ARG;
2921
if (!mDomSelections[index])
2922
return NS_ERROR_NULL_POINTER;
2924
return mDomSelections[index]->ScrollIntoView(aRegion, aIsSynchronous);
2928
nsSelection::RepaintSelection(nsIPresContext* aPresContext, SelectionType aType)
2930
PRInt8 index = GetIndexFromSelectionType(aType);
2932
return NS_ERROR_INVALID_ARG;
2933
if (!mDomSelections[index])
2934
return NS_ERROR_NULL_POINTER;
2935
return mDomSelections[index]->Repaint(aPresContext);
2939
nsSelection::GetFrameForNodeOffset(nsIContent *aNode, PRInt32 aOffset, HINT aHint, nsIFrame **aReturnFrame, PRInt32 *aReturnOffset)
2941
if (!aNode || !aReturnFrame || !aReturnOffset)
2942
return NS_ERROR_NULL_POINTER;
2945
return NS_ERROR_FAILURE;
2947
*aReturnOffset = aOffset;
2949
nsresult result = NS_OK;
2951
nsCOMPtr<nsIContent> theNode = aNode;
2953
if (aNode->IsContentOfType(nsIContent::eELEMENT))
2955
PRInt32 childIndex = 0;
2956
PRInt32 numChildren = 0;
2958
if (aHint == HINTLEFT)
2961
childIndex = aOffset - 1;
2963
childIndex = aOffset;
2967
numChildren = theNode->GetChildCount();
2969
if (aOffset >= numChildren)
2971
if (numChildren > 0)
2972
childIndex = numChildren - 1;
2977
childIndex = aOffset;
2980
nsCOMPtr<nsIContent> childNode = theNode->GetChildAt(childIndex);
2983
return NS_ERROR_FAILURE;
2985
theNode = childNode;
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.
2991
// Now that we have the child node, check if it too
2992
// can contain children. If so, call this method again!
2994
if (theNode->IsContentOfType(nsIContent::eELEMENT))
2996
PRInt32 newOffset = 0;
2998
if (aOffset > childIndex)
3000
numChildren = theNode->GetChildCount();
3002
newOffset = numChildren;
3005
return GetFrameForNodeOffset(theNode, newOffset, aHint, aReturnFrame,aReturnOffset);
3008
#endif // DONT_DO_THIS_YET
3010
// Check to see if theNode is a text node. If it is, translate
3011
// aOffset into an offset into the text node.
3013
nsCOMPtr<nsIDOMText> textNode = do_QueryInterface(theNode);
3017
if (aOffset > childIndex)
3019
PRUint32 textLength = 0;
3021
result = textNode->GetLength(&textLength);
3023
if (NS_FAILED(result))
3024
return NS_ERROR_FAILURE;
3026
*aReturnOffset = (PRInt32)textLength;
3034
result = mTracker->GetPrimaryFrameFor(theNode, aReturnFrame);
3035
if (NS_FAILED(result))
3039
return NS_ERROR_UNEXPECTED;
3041
// find the child frame containing the offset we want
3042
result = (*aReturnFrame)->GetChildFrameContainingOffset(*aReturnOffset, aHint, &aOffset, aReturnFrame);
3047
nsSelection::CommonPageMove(PRBool aForward,
3049
nsIScrollableView *aScrollableView,
3050
nsIFrameSelection *aFrameSel)
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.
3058
const nsIView* clipView;
3059
//get the frame from the scrollable view
3061
nsIFrame* mainframe = nsnull;
3063
// The view's client data points back to its frame
3064
nsIView *scrolledView;
3065
result = aScrollableView->GetScrolledView(scrolledView);
3067
if (NS_FAILED(result))
3071
mainframe = NS_STATIC_CAST(nsIFrame*, scrolledView->GetClientData());
3074
return NS_ERROR_FAILURE;
3076
// find out where we are; determine amount to page up/down
3077
if (NS_FAILED(result = aScrollableView->GetClipView(&clipView)))
3079
nsRect viewRect = clipView->GetBounds();
3081
nsCOMPtr<nsIPresContext> context;
3082
result = mTracker->GetPresContext(getter_AddRefs(context));
3084
if (NS_FAILED(result))
3088
return NS_ERROR_NULL_POINTER;
3090
nsIPresShell *shell = context->GetPresShell();
3093
return NS_ERROR_NULL_POINTER;
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));
3101
return NS_ERROR_UNEXPECTED;
3103
nsCOMPtr<nsICaret> caret;
3106
result = shell->GetCaret(getter_AddRefs(caret));
3108
if (NS_FAILED(result))
3112
result = caret->GetCaretCoordinates(nsICaret::eClosestViewCoordinates, domSel, &caretPos, &isCollapsed, &caretView);
3114
if (NS_FAILED(result))
3117
//need to adjust caret jump by percentage scroll
3118
viewRect.height = (PRInt32) (viewRect.height * PAGE_SCROLL_PERCENT);
3121
caretPos.y += viewRect.height;
3123
caretPos.y -= viewRect.height;
3128
while (caretView != scrolledView)
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;
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);
3146
if (NS_FAILED(result))
3150
return NS_ERROR_UNEXPECTED;
3154
aScrollableView->ScrollByPages(0, aForward ? 1 : -1);
3157
result = aFrameSel->HandleClick(content, startOffset, startOffset, aExtend, PR_FALSE, PR_TRUE);
3163
nsSelection::CharacterMove(PRBool aForward, PRBool aExtend)
3166
return MoveCaret(nsIDOMKeyEvent::DOM_VK_RIGHT,aExtend,eSelectCharacter);
3168
return MoveCaret(nsIDOMKeyEvent::DOM_VK_LEFT,aExtend,eSelectCharacter);
3172
nsSelection::WordMove(PRBool aForward, PRBool aExtend)
3175
return MoveCaret(nsIDOMKeyEvent::DOM_VK_RIGHT,aExtend,eSelectWord);
3177
return MoveCaret(nsIDOMKeyEvent::DOM_VK_LEFT,aExtend,eSelectWord);
3181
nsSelection::LineMove(PRBool aForward, PRBool aExtend)
3184
return MoveCaret(nsIDOMKeyEvent::DOM_VK_DOWN,aExtend,eSelectLine);
3186
return MoveCaret(nsIDOMKeyEvent::DOM_VK_UP,aExtend,eSelectLine);
3190
nsSelection::IntraLineMove(PRBool aForward, PRBool aExtend)
3193
return MoveCaret(nsIDOMKeyEvent::DOM_VK_END,aExtend,eSelectLine);
3195
return MoveCaret(nsIDOMKeyEvent::DOM_VK_HOME,aExtend,eSelectLine);
3198
NS_IMETHODIMP nsSelection::SelectAll()
3200
nsCOMPtr<nsIContent> rootContent;
3203
rootContent = mLimiter;//addrefit
3208
nsCOMPtr<nsIPresShell> shell(do_QueryInterface(mTracker,&rv));
3209
if (NS_FAILED(rv) || !shell) {
3210
return NS_ERROR_FAILURE;
3213
nsCOMPtr<nsIDocument> doc;
3214
rv = shell->GetDocument(getter_AddRefs(doc));
3218
return NS_ERROR_FAILURE;
3219
rootContent = doc->GetRootContent();
3221
return NS_ERROR_FAILURE;
3223
PRInt32 numChildren = rootContent->GetChildCount();
3224
PostReason(nsISelectionListener::NO_REASON);
3225
return TakeFocus(mLimiter, 0, numChildren, PR_FALSE, PR_FALSE);
3228
//////////END FRAMESELECTION
3231
nsSelection::StartBatchChanges()
3233
nsresult result(NS_OK);
3240
nsSelection::EndBatchChanges()
3242
nsresult result(NS_OK);
3244
NS_ASSERTION(mBatching >=0,"Bad mBatching");
3245
if (mBatching == 0 && mChangesDuringBatching){
3246
mChangesDuringBatching = PR_FALSE;
3247
NotifySelectionListeners(nsISelectionController::SELECTION_NORMAL);
3254
nsSelection::NotifySelectionListeners(SelectionType aType)
3256
PRInt8 index = GetIndexFromSelectionType(aType);
3259
return mDomSelections[index]->NotifySelectionListeners();
3261
return NS_ERROR_FAILURE;
3265
nsSelection::FrameOrParentHasSpecialSelectionStyle(nsIFrame* aFrame, PRUint8 aSelectionStyle, nsIFrame* *foundFrame)
3267
nsIFrame* thisFrame = aFrame;
3271
if (thisFrame->GetStyleUIReset()->mUserSelect == aSelectionStyle)
3273
*foundFrame = thisFrame;
3277
thisFrame = thisFrame->GetParent();
3280
*foundFrame = nsnull;
3285
nsSelection::FrameOrParentHasSpecialSelectionStyleBelowAncestor(nsIFrame* aFrame, PRUint8 aSelectionStyle,
3286
nsIFrame * aAncestorFrame,
3287
nsIFrame* *foundFrame)
3289
nsIFrame* thisFrame = aFrame;
3291
while (thisFrame && thisFrame != aAncestorFrame)
3293
PRUint8 frameUserSelect = thisFrame->GetStyleUIReset()->mUserSelect;
3294
if (frameUserSelect == aSelectionStyle)
3296
*foundFrame = thisFrame;
3300
thisFrame = thisFrame->GetParent();
3303
*foundFrame = nsnull;
3307
// Start of Table Selection methods
3309
static PRBool IsCell(nsIContent *aContent)
3311
return ((aContent->Tag() == nsHTMLAtoms::td ||
3312
aContent->Tag() == nsHTMLAtoms::th) &&
3313
aContent->IsContentOfType(nsIContent::eHTML));
3317
nsSelection::GetCellLayout(nsIContent *aCellContent)
3319
// Get frame for cell
3320
nsIFrame *cellFrame = nsnull;
3321
GetTracker()->GetPrimaryFrameFor(aCellContent, &cellFrame);
3325
nsITableCellLayout *cellLayoutObject = nsnull;
3326
CallQueryInterface(cellFrame, &cellLayoutObject);
3328
return cellLayoutObject;
3332
nsSelection::GetTableLayout(nsIContent *aTableContent)
3334
// Get frame for table
3335
nsIFrame *tableFrame = nsnull;
3336
GetTracker()->GetPrimaryFrameFor(aTableContent, &tableFrame);
3340
nsITableLayout *tableLayoutObject = nsnull;
3341
CallQueryInterface(tableFrame, &tableLayoutObject);
3343
return tableLayoutObject;
3347
nsSelection::ClearNormalSelection()
3349
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
3350
return mDomSelections[index]->RemoveAllRanges();
3353
// Table selection support.
3354
// TODO: Separate table methods into a separate nsITableSelection interface
3356
nsSelection::HandleTableSelection(nsIContent *aParentContent, PRInt32 aContentOffset, PRInt32 aTarget, nsMouseEvent *aMouseEvent)
3358
NS_ENSURE_TRUE(aParentContent, NS_ERROR_NULL_POINTER);
3359
NS_ENSURE_TRUE(aMouseEvent, NS_ERROR_NULL_POINTER);
3361
if (mMouseDownState && mDragSelectingCells && (aTarget & nsISelectionPrivate::TABLESELECTION_TABLE))
3363
// We were selecting cells and user drags mouse in table border or inbetween cells,
3368
nsCOMPtr<nsIDOMNode> parentNode = do_QueryInterface(aParentContent);
3370
return NS_ERROR_FAILURE;
3372
nsresult result = NS_OK;
3374
nsIContent *childContent = aParentContent->GetChildAt(aContentOffset);
3375
nsCOMPtr<nsIDOMNode> childNode = do_QueryInterface(childContent);
3377
return NS_ERROR_FAILURE;
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
3382
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
3383
mDomSelections[index]->SetDirection(eDirNext);
3385
// Stack-class to wrap all table selection changes in
3386
// BeginBatchChanges() / EndBatchChanges()
3387
nsSelectionBatcher selectionBatcher(mDomSelections[index]);
3389
PRInt32 startRowIndex, startColIndex, curRowIndex, curColIndex;
3390
if (mMouseDownState && mDragSelectingCells)
3392
// We are drag-selecting
3393
if (aTarget != nsISelectionPrivate::TABLESELECTION_TABLE)
3395
// If dragging in the same cell as last event, do nothing
3396
if (mEndSelectedCell == childContent)
3399
#ifdef DEBUG_TABLE_SELECTION
3400
printf(" mStartSelectedCell = %x, mEndSelectedCell = %x, childContent = %x \n", mStartSelectedCell, mEndSelectedCell, childContent);
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
3408
if (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_ROW ||
3409
mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_COLUMN)
3411
if (mEndSelectedCell)
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;
3419
#ifdef DEBUG_TABLE_SELECTION
3420
printf(" curRowIndex = %d, startRowIndex = %d, curColIndex = %d, startColIndex = %d\n", curRowIndex, startRowIndex, curColIndex, startColIndex);
3422
if ((mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_ROW && startRowIndex == curRowIndex) ||
3423
(mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_COLUMN && startColIndex == curColIndex))
3426
#ifdef DEBUG_TABLE_SELECTION
3427
printf(" Dragged into a new column or row\n");
3429
// Continue dragging row or column selection
3430
return SelectRowOrColumn(childContent, mSelectingTableCellMode);
3432
else if (mSelectingTableCellMode == nsISelectionPrivate::TABLESELECTION_CELL)
3434
#ifdef DEBUG_TABLE_SELECTION
3435
printf("HandleTableSelection: Dragged into a new cell\n");
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)
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;
3448
if (startRowIndex == curRowIndex ||
3449
startColIndex == curColIndex)
3451
// Force new selection block
3452
mStartSelectedCell = nsnull;
3453
mDomSelections[index]->RemoveAllRanges();
3455
if (startRowIndex == curRowIndex)
3456
mSelectingTableCellMode = nsISelectionPrivate::TABLESELECTION_ROW;
3458
mSelectingTableCellMode = nsISelectionPrivate::TABLESELECTION_COLUMN;
3460
return SelectRowOrColumn(childContent, mSelectingTableCellMode);
3464
// Reselect block of cells to new end location
3465
return SelectBlockOfCells(mStartSelectedCell, childContent);
3468
// Do nothing if dragging in table, but outside a cell
3473
// Not dragging -- mouse event is down or up
3474
if (mMouseDownState)
3476
#ifdef DEBUG_TABLE_SELECTION
3477
printf("HandleTableSelection: Mouse down event\n");
3479
// Clear cell we stored in mouse-down
3480
mUnselectCellOnMouseUp = nsnull;
3482
if (aTarget == nsISelectionPrivate::TABLESELECTION_CELL)
3484
PRBool isSelected = PR_FALSE;
3486
// Check if we have other selected cells
3487
nsCOMPtr<nsIDOMNode> previousCellNode;
3488
GetFirstSelectedCellAndRange(getter_AddRefs(previousCellNode), nsnull);
3489
if (previousCellNode)
3491
// We have at least 1 other selected cell
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;
3503
// No cells selected -- remove non-cell selection
3504
mDomSelections[index]->RemoveAllRanges();
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;
3515
// Remember this cell to (possibly) unselect it on mouseup
3516
mUnselectCellOnMouseUp = childContent;
3517
#ifdef DEBUG_TABLE_SELECTION
3518
printf("HandleTableSelection: Saving mUnselectCellOnMouseUp\n");
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))
3528
mDomSelections[index]->RemoveAllRanges();
3529
// Reset selection mode that is cleared in RemoveAllRanges
3530
mSelectingTableCellMode = aTarget;
3533
nsCOMPtr<nsIDOMElement> cellElement = do_QueryInterface(childContent);
3534
return SelectCellElement(cellElement);
3539
else if (aTarget == nsISelectionPrivate::TABLESELECTION_TABLE)
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;
3548
// Remove existing selection and select the table
3549
mDomSelections[index]->RemoveAllRanges();
3550
return CreateAndAddRange(parentNode, aContentOffset);
3552
else if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW || aTarget == nsISelectionPrivate::TABLESELECTION_COLUMN)
3554
#ifdef DEBUG_TABLE_SELECTION
3555
printf("aTarget == %d\n", aTarget);
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;
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);
3573
#ifdef DEBUG_TABLE_SELECTION
3574
printf("HandleTableSelection: Mouse UP event. mDragSelectingCells=%d, mStartSelectedCell=%d\n", mDragSelectingCells, mStartSelectedCell);
3576
// First check if we are extending a block selection
3578
result = mDomSelections[index]->GetRangeCount(&rangeCount);
3579
if (NS_FAILED(result))
3582
if (rangeCount > 0 && aMouseEvent->isShift &&
3583
mAppendStartSelectedCell && mAppendStartSelectedCell != childContent)
3585
// Shift key is down: append a block selection
3586
mDragSelectingCells = PR_FALSE;
3587
return SelectBlockOfCells(mAppendStartSelectedCell, childContent);
3590
if (mDragSelectingCells)
3591
mAppendStartSelectedCell = mStartSelectedCell;
3593
mDragSelectingCells = PR_FALSE;
3594
mStartSelectedCell = nsnull;
3595
mEndSelectedCell = nsnull;
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;
3603
doMouseUpAction = aMouseEvent->isControl;
3605
if (!doMouseUpAction)
3607
#ifdef DEBUG_TABLE_SELECTION
3608
printf("HandleTableSelection: Ending cell selection on mouseup: mAppendStartSelectedCell=%d\n", mAppendStartSelectedCell);
3612
// Unselect a cell only if it wasn't
3613
// just selected on mousedown
3614
if( childContent == mUnselectCellOnMouseUp)
3616
// Scan ranges to find the cell to unselect (the selection range to remove)
3617
nsCOMPtr<nsIDOMNode> previousCellParent;
3618
nsCOMPtr<nsIDOMRange> range;
3620
#ifdef DEBUG_TABLE_SELECTION
3621
printf("HandleTableSelection: Unselecting mUnselectCellOnMouseUp; rangeCount=%d\n", rangeCount);
3623
for( PRInt32 i = 0; i < rangeCount; i++)
3625
result = mDomSelections[index]->GetRangeAt(i, getter_AddRefs(range));
3626
if (NS_FAILED(result)) return result;
3627
if (!range) return NS_ERROR_NULL_POINTER;
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;
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;
3641
// We're done if we didn't find parent of a previously-selected cell
3642
if (!previousCellParent) break;
3644
if (previousCellParent == parentNode && offset == aContentOffset)
3646
// Cell is already selected
3647
if (rangeCount == 1)
3649
#ifdef DEBUG_TABLE_SELECTION
3650
printf("HandleTableSelection: Unselecting single selected cell\n");
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);
3662
#ifdef DEBUG_TABLE_SELECTION
3663
printf("HandleTableSelection: Removing cell from multi-cell selection\n");
3665
// Unselecting the start of previous block
3666
// XXX What do we use now!
3667
if (childContent == mAppendStartSelectedCell)
3668
mAppendStartSelectedCell = nsnull;
3670
// Deselect cell by removing its range from selection
3671
return mDomSelections[index]->RemoveRange(range);
3674
mUnselectCellOnMouseUp = nsnull;
3682
nsSelection::SelectBlockOfCells(nsIContent *aStartCell, nsIContent *aEndCell)
3684
NS_ENSURE_TRUE(aStartCell, NS_ERROR_NULL_POINTER);
3685
NS_ENSURE_TRUE(aEndCell, NS_ERROR_NULL_POINTER);
3686
mEndSelectedCell = aEndCell;
3688
nsCOMPtr<nsIContent> startCell;
3689
nsresult result = NS_OK;
3691
// If new end cell is in a different table, do nothing
3692
nsCOMPtr<nsIContent> table;
3693
if (!IsInSameTable(aStartCell, aEndCell, getter_AddRefs(table)))
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;
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;
3708
PRInt32 curRowIndex, curColIndex;
3710
if (mDragSelectingCells)
3712
// Drag selecting: remove selected cells outside of new block limits
3714
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
3716
nsCOMPtr<nsIDOMNode> cellNode;
3717
nsCOMPtr<nsIDOMRange> range;
3718
result = GetFirstSelectedCellAndRange(getter_AddRefs(cellNode), getter_AddRefs(range));
3719
if (NS_FAILED(result)) return result;
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);
3728
nsCOMPtr<nsIContent> childContent = do_QueryInterface(cellNode);
3729
result = GetCellIndexes(childContent, curRowIndex, curColIndex);
3730
if (NS_FAILED(result)) return result;
3732
#ifdef DEBUG_TABLE_SELECTION
3734
printf("SelectBlockOfCells -- range is null\n");
3737
(curRowIndex < minRowIndex || curRowIndex > maxRowIndex ||
3738
curColIndex < minColIndex || curColIndex > maxColIndex))
3740
mDomSelections[index]->RemoveRange(range);
3741
// Since we've removed the range, decrement pointer to next range
3742
mSelectedCellIndex--;
3744
result = GetNextSelectedCellAndRange(getter_AddRefs(cellNode), getter_AddRefs(range));
3745
if (NS_FAILED(result)) return result;
3749
nsCOMPtr<nsIDOMElement> cellElement;
3750
PRInt32 rowSpan, colSpan, actualRowSpan, actualColSpan;
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;
3758
PRInt32 col = startColIndex;
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;
3766
NS_ASSERTION(actualColSpan, "!actualColSpan is 0!");
3768
// Skip cells that are spanned from previous locations or are already selected
3769
if (!isSelected && cellElement && row == curRowIndex && col == curColIndex)
3771
result = SelectCellElement(cellElement);
3772
if (NS_FAILED(result)) return result;
3774
// Done when we reach end column
3775
if (col == endColIndex) break;
3777
if (startColIndex < endColIndex)
3782
if (row == endRowIndex) break;
3784
if (startRowIndex < endRowIndex)
3793
nsSelection::SelectRowOrColumn(nsIContent *aCellContent, PRUint32 aTarget)
3795
if (!aCellContent) return NS_ERROR_NULL_POINTER;
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;
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;
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;
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)
3819
if (aTarget == nsISelectionPrivate::TABLESELECTION_COLUMN)
3822
nsCOMPtr<nsIDOMElement> cellElement;
3823
nsCOMPtr<nsIDOMElement> firstCell;
3824
nsCOMPtr<nsIDOMElement> lastCell;
3825
PRInt32 rowSpan, colSpan, actualRowSpan, actualColSpan;
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;
3836
NS_ASSERTION(actualRowSpan > 0 && actualColSpan> 0, "SelectRowOrColumn: Bad rowspan or colspan\n");
3838
firstCell = cellElement;
3840
lastCell = cellElement;
3842
// Move to next cell in cellmap, skipping spanned locations
3843
if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW)
3844
colIndex += actualColSpan;
3846
rowIndex += actualRowSpan;
3849
while (cellElement);
3851
// Use SelectBlockOfCells:
3852
// This will replace existing selection,
3853
// but allow unselecting by dragging out of selected region
3854
if (firstCell && lastCell)
3856
if (!mStartSelectedCell)
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);
3863
nsCOMPtr<nsIContent> lastCellContent = do_QueryInterface(lastCell);
3864
result = SelectBlockOfCells(mStartSelectedCell, lastCellContent);
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;
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!
3876
// Loop through all cells in column or row
3877
result = tableLayout->GetCellDataAt(rowIndex, colIndex,
3878
getter_AddRefs(cellElement),
3879
curRowIndex, curColIndex,
3881
actualRowSpan, actualColSpan,
3883
if (NS_FAILED(result)) return result;
3884
// We're done when cell is not found
3885
if (!cellElement) break;
3888
// Check spans else we infinitely loop
3889
NS_ASSERTION(actualColSpan, "actualColSpan is 0!");
3890
NS_ASSERTION(actualRowSpan, "actualRowSpan is 0!");
3892
// Skip cells that are already selected or span from outside our region
3893
if (!isSelected && rowIndex == curRowIndex && colIndex == curColIndex)
3895
result = SelectCellElement(cellElement);
3896
if (NS_FAILED(result)) return result;
3898
// Move to next row or column in cellmap, skipping spanned locations
3899
if (aTarget == nsISelectionPrivate::TABLESELECTION_ROW)
3900
colIndex += actualColSpan;
3902
rowIndex += actualRowSpan;
3904
while (cellElement);
3911
nsSelection::GetFirstCellNodeInRange(nsIDOMRange *aRange, nsIDOMNode **aCellNode)
3913
if (!aRange || !aCellNode) return NS_ERROR_NULL_POINTER;
3915
*aCellNode = nsnull;
3917
nsCOMPtr<nsIDOMNode> startParent;
3918
nsresult result = aRange->GetStartContainer(getter_AddRefs(startParent));
3919
if (NS_FAILED(result))
3922
return NS_ERROR_FAILURE;
3925
result = aRange->GetStartOffset(&offset);
3926
if (NS_FAILED(result))
3929
nsCOMPtr<nsIContent> parentContent = do_QueryInterface(startParent);
3930
nsCOMPtr<nsIContent> childContent = parentContent->GetChildAt(offset);
3932
return NS_ERROR_NULL_POINTER;
3933
// Don't return node if not a cell
3934
if (!IsCell(childContent)) return NS_OK;
3936
nsCOMPtr<nsIDOMNode> childNode = do_QueryInterface(childContent);
3939
*aCellNode = childNode;
3940
NS_ADDREF(*aCellNode);
3946
nsSelection::GetFirstSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRange)
3948
if (!aCell) return NS_ERROR_NULL_POINTER;
3951
// aRange is optional
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;
3961
nsCOMPtr<nsIDOMNode> cellNode;
3962
result = GetFirstCellNodeInRange(firstRange, getter_AddRefs(cellNode));
3963
if (NS_FAILED(result)) return result;
3964
if (!cellNode) return NS_OK;
3970
*aRange = firstRange;
3974
// Setup for next cell
3975
mSelectedCellIndex = 1;
3981
nsSelection::GetNextSelectedCellAndRange(nsIDOMNode **aCell, nsIDOMRange **aRange)
3983
if (!aCell) return NS_ERROR_NULL_POINTER;
3986
// aRange is optional
3991
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
3992
nsresult result = mDomSelections[index]->GetRangeCount(&rangeCount);
3993
if (NS_FAILED(result)) return result;
3995
// Don't even try if index exceeds range count
3996
if (mSelectedCellIndex >= rangeCount)
3998
// Should we reset index?
3999
// Maybe better to force recalling GetFirstSelectedCell()
4000
//mSelectedCellIndex = 0;
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;
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;
4024
// Setup for next cell
4025
mSelectedCellIndex++;
4031
nsSelection::GetCellIndexes(nsIContent *aCell, PRInt32 &aRowIndex, PRInt32 &aColIndex)
4033
if (!aCell) return NS_ERROR_NULL_POINTER;
4035
aColIndex=0; // initialize out params
4038
nsITableCellLayout *cellLayoutObject = GetCellLayout(aCell);
4039
if (!cellLayoutObject) return NS_ERROR_FAILURE;
4040
return cellLayoutObject->GetCellIndexes(aRowIndex, aColIndex);
4044
nsSelection::IsInSameTable(nsIContent *aContent1, nsIContent *aContent2, nsIContent **aTable)
4046
if (!aContent1 || !aContent2) return PR_FALSE;
4048
// aTable is optional:
4049
if(aTable) *aTable = nsnull;
4051
nsCOMPtr<nsIContent> tableNode1;
4052
nsCOMPtr<nsIContent> tableNode2;
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;
4059
// Must be in the same table
4060
if (tableNode1 && (tableNode1 == tableNode2))
4064
*aTable = tableNode1;
4073
nsSelection::GetParentTable(nsIContent *aCell, nsIContent **aTable)
4075
if (!aCell || !aTable) {
4076
return NS_ERROR_NULL_POINTER;
4079
for (nsIContent* parent = aCell->GetParent(); parent;
4080
parent = parent->GetParent()) {
4081
if (parent->Tag() == nsHTMLAtoms::table &&
4082
parent->IsContentOfType(nsIContent::eHTML)) {
4094
nsSelection::SelectCellElement(nsIDOMElement *aCellElement)
4096
nsCOMPtr<nsIContent> cellContent = do_QueryInterface(aCellElement);
4099
return NS_ERROR_FAILURE;
4102
nsIContent *parent = cellContent->GetParent();
4103
nsCOMPtr<nsIDOMNode> parentNode(do_QueryInterface(parent));
4105
return NS_ERROR_FAILURE;
4109
PRInt32 offset = parent->IndexOf(cellContent);
4111
return CreateAndAddRange(parentNode, offset);
4115
nsTypedSelection::getTableCellLocationFromRange(nsIDOMRange *aRange, PRInt32 *aSelectionType, PRInt32 *aRow, PRInt32 *aCol)
4117
if (!aRange || !aSelectionType || !aRow || !aCol)
4118
return NS_ERROR_NULL_POINTER;
4120
*aSelectionType = nsISelectionPrivate::TABLESELECTION_NONE;
4124
// Must have access to frame selection to get cell info
4125
if (!mFrameSelection) return NS_OK;
4127
nsresult result = GetTableSelectionType(aRange, aSelectionType);
4128
if (NS_FAILED(result)) return result;
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)
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))
4143
nsCOMPtr<nsIContent> content(do_QueryInterface(startNode));
4145
return NS_ERROR_FAILURE;
4146
PRInt32 startOffset;
4147
result = aRange->GetStartOffset(&startOffset);
4148
if (NS_FAILED(result))
4151
nsIContent *child = content->GetChildAt(startOffset);
4153
return NS_ERROR_FAILURE;
4155
//Note: This is a non-ref-counted pointer to the frame
4156
nsITableCellLayout *cellLayout = mFrameSelection->GetCellLayout(child);
4157
if (NS_FAILED(result))
4160
return NS_ERROR_FAILURE;
4162
return cellLayout->GetCellIndexes(*aRow, *aCol);
4166
nsTypedSelection::addTableCellRange(nsIDOMRange *aRange, PRBool *aDidAddRange)
4169
return NS_ERROR_NULL_POINTER;
4171
*aDidAddRange = PR_FALSE;
4173
if (!mFrameSelection)
4177
return NS_ERROR_NULL_POINTER;
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;
4186
// If not adding a cell range, we are done here
4187
if (tableMode != nsISelectionPrivate::TABLESELECTION_CELL)
4189
mFrameSelection->mSelectingTableCellMode = tableMode;
4190
// Don't fail if range isn't a selected cell, aDidAddRange tells caller if we didn't proceed
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;
4199
PRInt32 count = mRangeArray.Count();
4203
// Adding a cell range to existing list of cell ranges
4206
// Insert range at appropriate location
4207
for (index = 0; index < count; index++)
4209
nsIDOMRange* range = mRangeArray[index];
4210
if (!range) return NS_ERROR_FAILURE;
4212
PRInt32 selectionMode;
4213
result = getTableCellLocationFromRange(range, &selectionMode, &row, &col);
4214
if (NS_FAILED(result)) return result;
4216
// Don't proceed if range not a table cell
4217
if (selectionMode != nsISelectionPrivate::TABLESELECTION_CELL)
4221
(row == newRow && col > newCol))
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;
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;
4236
//TODO: Figure out TABLESELECTION_COLUMN and TABLESELECTION_ALLCELLS
4238
nsTypedSelection::GetTableSelectionType(nsIDOMRange* aRange, PRInt32* aTableSelectionType)
4240
if (!aRange || !aTableSelectionType)
4241
return NS_ERROR_NULL_POINTER;
4243
*aTableSelectionType = nsISelectionPrivate::TABLESELECTION_NONE;
4245
// Must have access to frame selection to get cell info
4246
if(!mFrameSelection) return NS_OK;
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;
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;
4258
// Not a single selected node
4259
if (startNode != endNode) return NS_OK;
4261
nsCOMPtr<nsIContent> content = do_QueryInterface(startNode);
4262
if (!content) return NS_ERROR_FAILURE;
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
4269
PRInt32 startOffset;
4271
result = aRange->GetEndOffset(&endOffset);
4272
if (NS_FAILED(result)) return result;
4273
result = aRange->GetStartOffset(&startOffset);
4274
if (NS_FAILED(result)) return result;
4276
// Not a single selected node
4277
if ((endOffset - startOffset) != 1)
4280
if (!content->IsContentOfType(nsIContent::eHTML)) {
4284
nsIAtom *tag = content->Tag();
4286
if (tag == nsHTMLAtoms::tr)
4288
*aTableSelectionType = nsISelectionPrivate::TABLESELECTION_CELL;
4290
else //check to see if we are selecting a table or row (column and all cells not done yet)
4292
nsIContent *child = content->GetChildAt(startOffset);
4294
return NS_ERROR_FAILURE;
4298
if (tag == nsHTMLAtoms::table)
4299
*aTableSelectionType = nsISelectionPrivate::TABLESELECTION_TABLE;
4300
else if (tag == nsHTMLAtoms::tr)
4301
*aTableSelectionType = nsISelectionPrivate::TABLESELECTION_ROW;
4308
nsSelection::CreateAndAddRange(nsIDOMNode *aParentNode, PRInt32 aOffset)
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;
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;
4321
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
4322
return mDomSelections[index]->AddRange(range);
4325
// End of Table Selection
4328
nsSelection::AdjustOffsetsFromStyle(nsIFrame *aFrame, PRBool *changeSelection,
4329
nsIContent** outContent, PRInt32* outStartOffset, PRInt32* outEndOffset)
4332
*changeSelection = PR_FALSE;
4333
*outContent = nsnull;
4336
nsIFrame* selectAllFrame;
4337
rv = FrameOrParentHasSpecialSelectionStyle(aFrame, NS_STYLE_USER_SELECT_ALL, &selectAllFrame);
4338
if (NS_FAILED(rv)) return rv;
4340
if (!selectAllFrame)
4343
nsIContent* selectAllContent = selectAllFrame->GetContent();
4344
if (selectAllContent)
4346
nsCOMPtr<nsIContent> parentContent = selectAllContent->GetParent();
4349
PRInt32 startOffset = parentContent->IndexOf(selectAllContent);
4351
if (startOffset < 0)
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();
4358
PRInt32 superStartOffset = superParent->IndexOf(parentContent);
4359
if (superStartOffset < 0)
4360
return NS_ERROR_FAILURE; // give up
4362
parentContent = superParent;
4363
startOffset = superStartOffset;
4367
NS_IF_ADDREF(*outContent = parentContent);
4369
*outStartOffset = startOffset;
4370
*outEndOffset = startOffset + 1;
4372
*changeSelection = PR_TRUE;
4381
nsSelection::SetHint(HINT aHintRight)
4388
nsSelection::GetHint(HINT *aHintRight)
4390
*aHintRight = mHint;
4395
//END nsIFrameSelection methods
4402
//BEGIN nsISelection interface implementations
4407
nsSelection::DeleteFromDocument()
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.
4415
PRInt8 index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
4416
mDomSelections[index]->GetIsCollapsed( &isCollapsed);
4419
// If the offset is positive, then it's easy:
4420
if (mDomSelections[index]->FetchFocusOffset() > 0)
4422
mDomSelections[index]->Extend(mDomSelections[index]->FetchFocusNode(), mDomSelections[index]->FetchFocusOffset() - 1);
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;
4433
nsSelectionIterator iter(mDomSelections[index]);
4438
nsCOMPtr<nsIDOMRange> range;
4439
while (iter.IsDone())
4441
res = iter.CurrentItem(NS_STATIC_CAST(nsIDOMRange**, getter_AddRefs(range)));
4444
res = range->DeleteContents();
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.
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());
4459
printf("Don't know how to set selection back past frame boundary\n");
4467
nsSelection::SetDisplaySelection(PRInt16 aToggle)
4469
mDisplaySelection = aToggle;
4474
nsSelection::GetDisplaySelection(PRInt16 *aToggle)
4477
return NS_ERROR_INVALID_ARG;
4478
*aToggle = mDisplaySelection;
4483
nsSelection::SetDelayCaretOverExistingSelection(PRBool aDelay)
4485
mDelayCaretOverExistingSelection = aDelay;
4488
mDelayedMouseEventValid = PR_FALSE;
4494
nsSelection::GetDelayCaretOverExistingSelection(PRBool *aDelay)
4497
return NS_ERROR_NULL_POINTER;
4499
*aDelay = mDelayCaretOverExistingSelection;
4505
nsSelection::SetDelayedCaretData(nsMouseEvent *aMouseEvent)
4509
mDelayedMouseEventValid = PR_TRUE;
4510
mDelayedMouseEvent = *aMouseEvent;
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.
4518
mDelayedMouseEventValid = PR_FALSE;
4524
nsSelection::GetDelayedCaretData(nsMouseEvent **aMouseEvent)
4527
return NS_ERROR_NULL_POINTER;
4529
if (mDelayedMouseEventValid)
4530
*aMouseEvent = &mDelayedMouseEvent;
4537
// Frame needs to get the limiting content node for parent node searches
4539
nsSelection::GetLimiter(nsIContent **aLimiterContent)
4541
if (!aLimiterContent) return NS_ERROR_NULL_POINTER;
4542
*aLimiterContent = mLimiter;
4543
NS_IF_ADDREF(*aLimiterContent);
4549
nsSelection::SetMouseDoubleDown(PRBool aDoubleDown)
4551
mMouseDoubleDownState = aDoubleDown;
4556
nsSelection::GetMouseDoubleDown(PRBool *aDoubleDown)
4558
*aDoubleDown = mMouseDoubleDownState;
4562
//END nsISelection interface implementations
4568
// nsTypedSelection implementation
4570
// note: this can return a nil anchor node
4572
nsTypedSelection::nsTypedSelection(nsSelection *aList)
4574
mFrameSelection = aList;
4575
mFixupState = PR_FALSE;
4576
mDirection = eDirNext;
4577
mAutoScrollTimer = nsnull;
4578
mScrollEventPosted = PR_FALSE;
4579
mCachedOffsetForFrame = nsnull;
4583
nsTypedSelection::nsTypedSelection()
4585
mFrameSelection = nsnull;
4586
mFixupState = PR_FALSE;
4587
mDirection = eDirNext;
4588
mAutoScrollTimer = nsnull;
4589
mScrollEventPosted = PR_FALSE;
4590
mCachedOffsetForFrame = nsnull;
4595
nsTypedSelection::~nsTypedSelection()
4597
setAnchorFocusRange(-1);
4599
if (mAutoScrollTimer) {
4600
mAutoScrollTimer->Stop();
4601
NS_RELEASE(mAutoScrollTimer);
4604
if (mEventQueue && mScrollEventPosted) {
4605
mEventQueue->RevokeEvents(this);
4606
mScrollEventPosted = PR_FALSE;
4609
delete mCachedOffsetForFrame;
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
4623
NS_IMPL_ADDREF(nsTypedSelection)
4624
NS_IMPL_RELEASE(nsTypedSelection)
4628
nsTypedSelection::SetPresShell(nsIPresShell *aPresShell)
4630
mPresShellWeak = do_GetWeakReference(aPresShell);
4637
nsTypedSelection::GetAnchorNode(nsIDOMNode** aAnchorNode)
4640
return NS_ERROR_NULL_POINTER;
4641
*aAnchorNode = nsnull;
4642
if(!mAnchorFocusRange)
4646
if (GetDirection() == eDirNext){
4647
result = mAnchorFocusRange->GetStartContainer(aAnchorNode);
4650
result = mAnchorFocusRange->GetEndContainer(aAnchorNode);
4656
nsTypedSelection::GetAnchorOffset(PRInt32* aAnchorOffset)
4659
return NS_ERROR_NULL_POINTER;
4660
*aAnchorOffset = nsnull;
4661
if(!mAnchorFocusRange)
4665
if (GetDirection() == eDirNext){
4666
result = mAnchorFocusRange->GetStartOffset(aAnchorOffset);
4669
result = mAnchorFocusRange->GetEndOffset(aAnchorOffset);
4674
// note: this can return a nil focus node
4676
nsTypedSelection::GetFocusNode(nsIDOMNode** aFocusNode)
4679
return NS_ERROR_NULL_POINTER;
4680
*aFocusNode = nsnull;
4681
if(!mAnchorFocusRange)
4685
if (GetDirection() == eDirNext){
4686
result = mAnchorFocusRange->GetEndContainer(aFocusNode);
4689
result = mAnchorFocusRange->GetStartContainer(aFocusNode);
4695
NS_IMETHODIMP nsTypedSelection::GetFocusOffset(PRInt32* aFocusOffset)
4698
return NS_ERROR_NULL_POINTER;
4699
*aFocusOffset = nsnull;
4700
if(!mAnchorFocusRange)
4704
if (GetDirection() == eDirNext){
4705
result = mAnchorFocusRange->GetEndOffset(aFocusOffset);
4708
result = mAnchorFocusRange->GetStartOffset(aFocusOffset);
4714
void nsTypedSelection::setAnchorFocusRange(PRInt32 indx)
4716
if (indx >= mRangeArray.Count())
4718
if (indx < 0) //release all
4720
mAnchorFocusRange = nsnull;
4723
mAnchorFocusRange = mRangeArray[indx];
4730
nsTypedSelection::FetchAnchorNode()
4731
{ //where did the selection begin
4732
nsCOMPtr<nsIDOMNode>returnval;
4733
GetAnchorNode(getter_AddRefs(returnval));//this queries
4735
}//at end it will release, no addreff was called
4740
nsTypedSelection::FetchAnchorOffset()
4743
if (NS_SUCCEEDED(GetAnchorOffset(&returnval)))//this queries
4751
nsTypedSelection::FetchOriginalAnchorNode() //where did the ORIGINAL selection begin
4753
nsCOMPtr<nsIDOMNode>returnval;
4755
GetOriginalAnchorPoint(getter_AddRefs(returnval), &unused);//this queries
4762
nsTypedSelection::FetchOriginalAnchorOffset()
4764
nsCOMPtr<nsIDOMNode>unused;
4766
if (NS_SUCCEEDED(GetOriginalAnchorPoint(getter_AddRefs(unused), &returnval)))//this queries
4774
nsTypedSelection::FetchFocusNode()
4775
{ //where is the carret
4776
nsCOMPtr<nsIDOMNode>returnval;
4777
GetFocusNode(getter_AddRefs(returnval));//this queries
4779
}//at end it will release, no addreff was called
4784
nsTypedSelection::FetchFocusOffset()
4787
if (NS_SUCCEEDED(GetFocusOffset(&returnval)))//this queries
4795
nsTypedSelection::FetchStartParent(nsIDOMRange *aRange) //skip all the com stuff and give me the start/end
4799
nsCOMPtr<nsIDOMNode> returnval;
4800
aRange->GetStartContainer(getter_AddRefs(returnval));
4807
nsTypedSelection::FetchStartOffset(nsIDOMRange *aRange)
4812
if (NS_SUCCEEDED(aRange->GetStartOffset(&returnval)))
4820
nsTypedSelection::FetchEndParent(nsIDOMRange *aRange) //skip all the com stuff and give me the start/end
4824
nsCOMPtr<nsIDOMNode> returnval;
4825
aRange->GetEndContainer(getter_AddRefs(returnval));
4832
nsTypedSelection::FetchEndOffset(nsIDOMRange *aRange)
4837
if (NS_SUCCEEDED(aRange->GetEndOffset(&returnval)))
4843
nsTypedSelection::AddItem(nsIDOMRange *aItem)
4846
return NS_ERROR_NULL_POINTER;
4847
return mRangeArray.AppendObject(aItem) ? NS_OK : NS_ERROR_FAILURE;
4853
nsTypedSelection::RemoveItem(nsIDOMRange *aItem)
4856
return NS_ERROR_NULL_POINTER;
4857
mRangeArray.RemoveObject(aItem);
4864
nsTypedSelection::Clear(nsIPresContext* aPresContext)
4866
setAnchorFocusRange(-1);
4870
PRInt32 cnt = mRangeArray.Count();
4873
nsCOMPtr<nsIDOMRange> range = mRangeArray[0];
4874
mRangeArray.RemoveObjectAt(0);
4875
selectFrames(aPresContext, range, 0);
4877
// Reset direction so for more dependable table selection range handling
4878
SetDirection(eDirNext);
4882
//utility method to get the primary frame of node or use the offset to get frame of child node
4886
nsTypedSelection::GetPrimaryFrameForRangeEndpoint(nsIDOMNode *aNode, PRInt32 aOffset, PRBool aIsEndNode, nsIFrame **aReturnFrame)
4888
if (!aNode || !aReturnFrame)
4889
return NS_ERROR_NULL_POINTER;
4892
return NS_ERROR_FAILURE;
4896
nsresult result = NS_OK;
4898
nsCOMPtr<nsIDOMNode> node = aNode;
4901
return NS_ERROR_NULL_POINTER;
4903
nsCOMPtr<nsIContent> content = do_QueryInterface(node, &result);
4905
if (NS_FAILED(result))
4909
return NS_ERROR_NULL_POINTER;
4911
if (content->IsContentOfType(nsIContent::eELEMENT))
4918
nsIContent *child = content->GetChildAt(aOffset);
4919
if (!child) //out of bounds?
4920
return NS_ERROR_FAILURE;
4922
content = child; // releases the focusnode
4925
result = mFrameSelection->GetTracker()->GetPrimaryFrameFor(content,aReturnFrame);
4932
nsTypedSelection::GetPrimaryFrameForAnchorNode(nsIFrame **aReturnFrame)
4935
return NS_ERROR_NULL_POINTER;
4937
PRInt32 frameOffset = 0;
4939
nsCOMPtr<nsIContent> content = do_QueryInterface(FetchAnchorNode());
4940
if (content && mFrameSelection)
4942
nsIFrameSelection::HINT hint;
4943
mFrameSelection->GetHint(&hint);
4944
return mFrameSelection->GetFrameForNodeOffset(content, FetchAnchorOffset(),hint,aReturnFrame, &frameOffset);
4946
return NS_ERROR_FAILURE;
4950
nsTypedSelection::GetPrimaryFrameForFocusNode(nsIFrame **aReturnFrame, PRInt32 *aOffsetUsed)
4953
return NS_ERROR_NULL_POINTER;
4955
PRInt32 frameOffset = 0;
4958
aOffsetUsed = &frameOffset;
4961
nsCOMPtr<nsIContent> content = do_QueryInterface(FetchFocusNode());
4962
if (content && mFrameSelection)
4964
nsIFrameSelection::HINT hint;
4965
mFrameSelection->GetHint(&hint);
4966
return mFrameSelection->GetFrameForNodeOffset(content, FetchFocusOffset(),hint,aReturnFrame, aOffsetUsed);
4968
return NS_ERROR_FAILURE;
4973
//select all content children of aContent
4975
nsTypedSelection::selectFrames(nsIPresContext* aPresContext,
4976
nsIContentIterator *aInnerIter,
4977
nsIContent *aContent,
4978
nsIDOMRange *aRange,
4979
nsIPresShell *aPresShell,
4982
if (!mFrameSelection)
4983
return NS_OK;//nothing to do
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)
4991
result = genericiter->Init(aPresShell,aContent);
4994
#endif // USE_SELECTION_GENERATED_CONTENT_ITERATOR_CODE
4995
result = aInnerIter->Init(aContent);
4997
if (NS_SUCCEEDED(result))
4999
// First select frame of content passed in
5000
result = mFrameSelection->GetTracker()->GetPrimaryFrameFor(aContent, &frame);
5001
if (NS_SUCCEEDED(result) && frame)
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
5007
mFrameSelection->GetTableCellSelection(&tablesel);
5010
nsITableCellLayout *tcl = nsnull;
5011
CallQueryInterface(frame, &tcl);
5017
#endif //OLD_TABLE_SELECTION
5019
// Now iterated through the child frames and set them
5020
while (!aInnerIter->IsDone())
5022
nsIContent *innercontent = aInnerIter->GetCurrentNode();
5024
result = mFrameSelection->GetTracker()->GetPrimaryFrameFor(innercontent, &frame);
5025
if (NS_SUCCEEDED(result) && frame)
5027
//NOTE: eSpreadDown is now IGNORED. Selected state is set only
5030
//spread from here to hit all frames in flow
5031
frame->SetSelected(aPresContext, nsnull,aFlags,eSpreadDown);
5032
nsRect frameRect = frame->GetRect();
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)
5038
//try to notify next in flow that its content is selected.
5039
if (NS_SUCCEEDED(frame->GetNextInFlow(&frame)) && frame)
5041
frameRect = frame->GetRect();
5042
frame->SetSelected(aPresContext, nsnull,aFlags,eSpreadDown);
5047
//if the frame is splittable and this frame is 0,0 then set
5048
//the next in flow frame to be selected also
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
5063
return NS_ERROR_FAILURE;
5068
//the idea of this helper method is to select, deselect "top to bottom" traversing through the frames
5070
nsTypedSelection::selectFrames(nsIPresContext* aPresContext, nsIDOMRange *aRange, PRBool aFlags)
5072
if (!mFrameSelection)
5073
return NS_OK;//nothing to do
5074
if (!aRange || !aPresContext)
5075
return NS_ERROR_NULL_POINTER;
5078
nsCOMPtr<nsIContentIterator> iter = do_CreateInstance(
5079
#ifdef USE_SELECTION_GENERATED_CONTENT_ITERATOR_CODE
5080
kCGenSubtreeIteratorCID,
5082
kCSubtreeIteratorCID,
5083
#endif // USE_SELECTION_GENERATED_CONTENT_ITERATOR_CODE
5085
if (NS_FAILED(result))
5088
nsCOMPtr<nsIContentIterator> inneriter = do_CreateInstance(
5089
#ifdef USE_SELECTION_GENERATED_CONTENT_ITERATOR_CODE
5090
kCGenContentIteratorCID,
5092
kCContentIteratorCID,
5093
#endif // USE_SELECTION_GENERATED_CONTENT_ITERATOR_CODE
5096
if ((NS_SUCCEEDED(result)) && iter && inneriter)
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);
5104
#endif // USE_SELECTION_GENERATED_CONTENT_ITERATOR_CODE
5105
result = iter->Init(aRange);
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;
5113
//we must call first one explicitly
5114
content = do_QueryInterface(FetchStartParent(aRange), &result);
5115
if (NS_FAILED(result) || !content)
5118
if (!content->IsContentOfType(nsIContent::eELEMENT))
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
5127
while (!iter->IsDone())
5129
content = iter->GetCurrentNode();
5131
selectFrames(aPresContext, inneriter, content, aRange, presShell,aFlags);
5135
//we must now do the last one if it is not the same as the first
5136
if (FetchEndParent(aRange) != FetchStartParent(aRange))
5138
content = do_QueryInterface(FetchEndParent(aRange), &result);
5139
if (NS_FAILED(result) || !content)
5142
if (!content->IsContentOfType(nsIContent::eELEMENT))
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
5156
nsTypedSelection::LookUpSelection(nsIContent *aContent, PRInt32 aContentOffset, PRInt32 aContentLength,
5157
SelectionDetails **aReturnDetails, SelectionType aType, PRBool aSlowCheck)
5160
nsresult rv = GetRangeCount(&cnt);
5165
nsCOMPtr<nsIDOMNode> passedInNode;
5166
passedInNode = do_QueryInterface(aContent);
5168
return NS_ERROR_FAILURE;
5170
SelectionDetails *details = nsnull;
5171
SelectionDetails *newDetails = details;
5173
for (i =0; i<cnt; i++){
5174
nsCOMPtr<nsIDOMRange> range = mRangeArray[i];
5176
nsCOMPtr<nsIDOMNode> startNode;
5177
nsCOMPtr<nsIDOMNode> endNode;
5178
PRInt32 startOffset;
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){
5188
details = new SelectionDetails;
5190
newDetails = details;
5193
newDetails->mNext = new SelectionDetails;
5194
newDetails = newDetails->mNext;
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;
5204
else if (passedInNode == startNode){ //are we at the beginning?
5205
if (startOffset < (aContentOffset + aContentLength)){
5207
details = new SelectionDetails;
5208
newDetails = details;
5211
newDetails->mNext = new SelectionDetails;
5212
newDetails = newDetails->mNext;
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;
5222
else if (passedInNode == endNode){//are we at the end?
5223
if (endOffset > aContentOffset){
5225
details = new SelectionDetails;
5226
newDetails = details;
5229
newDetails->mNext = new SelectionDetails;
5230
newDetails = newDetails->mNext;
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;
5240
else { //then we MUST be completely selected! unless someone needs us to check to make sure with slowcheck
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);
5248
resultnum = ComparePoints(endNode, endOffset,
5249
passedInNode, aContentOffset );
5255
details = new SelectionDetails;
5256
newDetails = details;
5259
newDetails->mNext = new SelectionDetails;
5260
newDetails = newDetails->mNext;
5263
return NS_ERROR_OUT_OF_MEMORY;
5264
newDetails->mNext = nsnull;
5265
newDetails->mStart = 0;
5266
newDetails->mEnd = aContentLength;
5267
newDetails->mType = aType;
5273
if (*aReturnDetails && newDetails)
5274
newDetails->mNext = *aReturnDetails;
5276
*aReturnDetails = details;
5281
nsTypedSelection::Repaint(nsIPresContext* aPresContext)
5283
PRInt32 arrCount = mRangeArray.Count();
5291
for (i = 0; i < arrCount; i++)
5293
range = mRangeArray[i];
5296
return NS_ERROR_UNEXPECTED;
5298
nsresult rv = selectFrames(aPresContext, range, PR_TRUE);
5300
if (NS_FAILED(rv)) {
5309
nsTypedSelection::GetCanCacheFrameOffset(PRBool *aCanCacheFrameOffset)
5311
NS_ENSURE_ARG_POINTER(aCanCacheFrameOffset);
5313
if (mCachedOffsetForFrame)
5314
*aCanCacheFrameOffset = mCachedOffsetForFrame->mCanCacheFrameOffset;
5316
*aCanCacheFrameOffset = PR_FALSE;
5322
nsTypedSelection::SetCanCacheFrameOffset(PRBool aCanCacheFrameOffset)
5324
if (!mCachedOffsetForFrame) {
5325
mCachedOffsetForFrame = new CachedOffsetForFrame;
5328
mCachedOffsetForFrame->mCanCacheFrameOffset = aCanCacheFrameOffset;
5330
// clean up cached frame when turn off cache
5332
if (!aCanCacheFrameOffset) {
5333
mCachedOffsetForFrame->mLastCaretFrame = nsnull;
5340
nsTypedSelection::GetCachedFrameOffset(nsIFrame *aFrame, PRInt32 inOffset, nsPoint& aPoint)
5342
if (!mCachedOffsetForFrame) {
5343
mCachedOffsetForFrame = new CachedOffsetForFrame;
5346
if (mCachedOffsetForFrame->mCanCacheFrameOffset &&
5347
mCachedOffsetForFrame->mLastCaretFrame &&
5348
(aFrame == mCachedOffsetForFrame->mLastCaretFrame) &&
5349
(inOffset == mCachedOffsetForFrame->mLastContentOffset))
5351
// get cached frame offset
5352
aPoint = mCachedOffsetForFrame->mCachedFrameOffset;
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;
5369
nsTypedSelection::StartAutoScrollTimer(nsIPresContext *aPresContext, nsIFrame *aFrame, nsPoint& aPoint, PRUint32 aDelay)
5372
if (!mFrameSelection)
5373
return NS_OK;//nothing to do
5375
if (!mAutoScrollTimer)
5377
result = NS_NewAutoScrollTimer(&mAutoScrollTimer);
5379
if (NS_FAILED(result))
5382
if (!mAutoScrollTimer)
5383
return NS_ERROR_OUT_OF_MEMORY;
5385
result = mAutoScrollTimer->Init(mFrameSelection, this);
5387
if (NS_FAILED(result))
5391
result = mAutoScrollTimer->SetDelay(aDelay);
5393
if (NS_FAILED(result))
5396
return DoAutoScroll(aPresContext, aFrame, aPoint);
5400
nsTypedSelection::StopAutoScrollTimer()
5402
if (mAutoScrollTimer)
5403
return mAutoScrollTimer->Stop();
5409
nsTypedSelection::GetViewAncestorOffset(nsIView *aView, nsIView *aAncestorView, nscoord *aXOffset, nscoord *aYOffset)
5411
// Note: A NULL aAncestorView pointer means that the caller wants
5412
// the view's global offset.
5414
if (!aView || !aXOffset || !aYOffset)
5415
return NS_ERROR_FAILURE;
5420
for (nsIView* view = aView; view && view != aAncestorView;
5421
view = view->GetParent())
5423
nsPoint pt = view->GetPosition();
5432
nsTypedSelection::GetClosestScrollableView(nsIView *aView, nsIScrollableView **aScrollableView)
5434
if (!aView || !aScrollableView)
5435
return NS_ERROR_FAILURE;
5437
*aScrollableView = 0;
5439
while (!*aScrollableView && aView)
5441
CallQueryInterface(aView, aScrollableView);
5442
if (!*aScrollableView)
5444
aView = aView->GetParent();
5452
nsTypedSelection::ScrollPointIntoClipView(nsIPresContext *aPresContext, nsIView *aView, nsPoint& aPoint, PRBool *aDidScroll)
5456
if (!aPresContext || !aView || !aDidScroll)
5457
return NS_ERROR_NULL_POINTER;
5459
*aDidScroll = PR_FALSE;
5462
// Get aView's scrollable view.
5465
nsIScrollableView *scrollableView = 0;
5467
result = GetClosestScrollableView(aView, &scrollableView);
5469
if (NS_FAILED(result))
5472
if (!scrollableView)
5473
return NS_OK; // Nothing to do!
5476
// Get the clip view.
5479
const nsIView *cView = 0;
5481
result = scrollableView->GetClipView(&cView);
5483
if (NS_FAILED(result))
5487
// Get the view that is being scrolled.
5490
nsIView *scrolledView = 0;
5492
result = scrollableView->GetScrolledView(scrolledView);
5494
return NS_ERROR_FAILURE;
5497
// Now walk up aView's hierarchy, this time keeping track of
5498
// the view offsets until you hit the scrolledView.
5501
nsPoint viewOffset(0,0);
5503
result = GetViewAncestorOffset(aView, scrolledView, &viewOffset.x, &viewOffset.y);
5505
if (NS_FAILED(result))
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!
5513
nsRect bounds = cView->GetBounds();
5515
result = scrollableView->GetScrollPosition(bounds.x,bounds.y);
5517
if (NS_FAILED(result))
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.
5526
nscoord dx = 0, dy = 0;
5527
nsPoint ePoint = aPoint;
5529
ePoint.x += viewOffset.x;
5530
ePoint.y += viewOffset.y;
5532
nscoord x1 = bounds.x;
5533
nscoord x2 = bounds.x + bounds.width;
5534
nscoord y1 = bounds.y;
5535
nscoord y2 = bounds.y + bounds.height;
5539
else if (ePoint.x > x2)
5544
else if (ePoint.y > y2)
5548
// Now clip the scroll amounts so that we don't scroll
5549
// beyond the ends of the document.
5552
nscoord scrollX = 0, scrollY = 0;
5553
nscoord docWidth = 0, docHeight = 0;
5555
result = scrollableView->GetScrollPosition(scrollX, scrollY);
5557
if (NS_SUCCEEDED(result))
5558
result = scrollableView->GetContainerSize(&docWidth, &docHeight);
5560
if (NS_SUCCEEDED(result))
5562
if (dx < 0 && scrollX == 0)
5566
x1 = scrollX + dx + bounds.width;
5569
dx -= x1 - docWidth;
5573
if (dy < 0 && scrollY == 0)
5577
y1 = scrollY + dy + bounds.height;
5580
dy -= y1 - docHeight;
5584
// Now scroll the view if neccessary.
5587
if (dx != 0 || dy != 0)
5589
// Make sure latest bits are available before we scroll them.
5590
aPresContext->GetViewManager()->Composite();
5592
// Now scroll the view!
5594
result = scrollableView->ScrollTo(scrollX + dx, scrollY + dy, NS_VMREFRESH_NO_SYNC);
5596
if (NS_FAILED(result))
5601
result = scrollableView->GetScrollPosition(newPos.x, newPos.y);
5603
if (NS_FAILED(result))
5606
*aDidScroll = (bounds.x != newPos.x || bounds.y != newPos.y);
5614
nsTypedSelection::ScrollPointIntoView(nsIPresContext *aPresContext, nsIView *aView, nsPoint& aPoint, PRBool aScrollParentViews, PRBool *aDidScroll)
5616
if (!aPresContext || !aView || !aDidScroll)
5617
return NS_ERROR_NULL_POINTER;
5621
*aDidScroll = PR_FALSE;
5624
// Calculate the global offset of the view.
5627
nsPoint globalOffset;
5629
result = GetViewAncestorOffset(aView, nsnull, &globalOffset.x, &globalOffset.y);
5631
if (NS_FAILED(result))
5635
// Convert aPoint into global coordinates so it is easier to map
5636
// into other views.
5639
nsPoint globalPoint = aPoint + globalOffset;
5642
// Scroll the point into the visible rect of the closest
5645
result = ScrollPointIntoClipView(aPresContext, aView, aPoint, aDidScroll);
5647
if (NS_FAILED(result))
5651
// Now scroll the parent scrollable views.
5654
if (aScrollParentViews)
5657
// Find aView's parent scrollable view.
5660
nsIScrollableView *scrollableView = 0;
5662
result = GetClosestScrollableView(aView, &scrollableView);
5664
if (NS_FAILED(result))
5670
// Convert scrollableView to nsIView.
5673
nsIView *scrolledView = 0;
5676
CallQueryInterface(scrollableView, &view);
5681
// Now get the scrollableView's parent, then search for it's
5682
// closest scrollable view.
5685
view = view->GetParent();
5689
result = GetClosestScrollableView(view, &scrollableView);
5691
if (NS_FAILED(result))
5694
if (!scrollableView)
5698
result = scrollableView->GetScrolledView(scrolledView);
5700
if (NS_FAILED(result))
5704
// Map the global point into this scrolledView's coordinate space.
5707
result = GetViewAncestorOffset(scrolledView, nsnull, &globalOffset.x, &globalOffset.y);
5709
if (NS_FAILED(result))
5712
nsPoint newPoint = globalPoint - globalOffset;
5715
// Scroll the point into the visible rect of the scrolled view.
5718
PRBool parentDidScroll = PR_FALSE;
5720
result = ScrollPointIntoClipView(aPresContext, scrolledView, newPoint, &parentDidScroll);
5722
if (NS_FAILED(result))
5725
*aDidScroll = *aDidScroll || parentDidScroll;
5728
// Now get the parent of this scrollable view so we
5729
// can scroll the next parent view.
5733
result = CallQueryInterface(scrollableView, &view);
5737
view = view->GetParent();
5747
nsTypedSelection::DoAutoScrollView(nsIPresContext *aPresContext, nsIView *aView, nsPoint& aPoint, PRBool aScrollParentViews)
5749
if (!aPresContext || !aView)
5750
return NS_ERROR_NULL_POINTER;
5754
if (mAutoScrollTimer)
5755
result = mAutoScrollTimer->Stop();
5758
// Calculate the global offset of the view.
5761
nsPoint globalOffset;
5763
result = GetViewAncestorOffset(aView, nsnull, &globalOffset.x, &globalOffset.y);
5765
if (NS_FAILED(result))
5769
// Convert aPoint into global coordinates so we can get back
5770
// to the same point after all the parent views have scrolled.
5773
nsPoint globalPoint = aPoint + globalOffset;
5776
// Now scroll aPoint into view.
5779
PRBool didScroll = PR_FALSE;
5781
result = ScrollPointIntoView(aPresContext, aView, aPoint, aScrollParentViews, &didScroll);
5783
if (NS_FAILED(result))
5787
// Start the AutoScroll timer if neccessary.
5790
if (didScroll && mAutoScrollTimer)
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.
5797
result = GetViewAncestorOffset(aView, nsnull, &globalOffset.x, &globalOffset.y);
5799
if (NS_FAILED(result))
5802
nsPoint svPoint = globalPoint - globalOffset;
5804
result = mAutoScrollTimer->Start(aPresContext, aView, svPoint);
5811
nsTypedSelection::DoAutoScroll(nsIPresContext *aPresContext, nsIFrame *aFrame, nsPoint& aPoint)
5813
if (!aPresContext || !aFrame)
5814
return NS_ERROR_NULL_POINTER;
5816
// Find the closest view to the frame!
5818
nsIView *closestView = aFrame->GetClosestView();
5820
return NS_ERROR_FAILURE;
5822
return DoAutoScrollView(aPresContext, closestView, aPoint, PR_TRUE);
5826
nsTypedSelection::GetEnumerator(nsIEnumerator **aIterator)
5828
nsresult status = NS_ERROR_OUT_OF_MEMORY;
5829
nsSelectionIterator *iterator = new nsSelectionIterator(this);
5830
if ( iterator && NS_FAILED(status = CallQueryInterface(iterator, aIterator)) )
5837
/** RemoveAllRanges zeroes the selection
5840
nsTypedSelection::RemoveAllRanges()
5842
if (!mFrameSelection)
5843
return NS_OK;//nothing to do
5844
nsCOMPtr<nsIPresContext> presContext;
5845
GetPresContext(getter_AddRefs(presContext));
5848
nsresult result = Clear(presContext);
5849
if (NS_FAILED(result))
5852
// Turn off signal for table selection
5853
mFrameSelection->ClearTableCellSelection();
5855
return mFrameSelection->NotifySelectionListeners(GetType());
5856
// Also need to notify the frames!
5857
// PresShell::CharacterDataChanged should do that on DocumentChanged
5860
/** AddRange adds the specified range to the selection
5861
* @param aRange is the range to be added
5864
nsTypedSelection::AddRange(nsIDOMRange* aRange)
5866
if (!aRange) return NS_ERROR_NULL_POINTER;
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
5871
nsresult result = addTableCellRange(aRange, &didAddRange);
5872
if (NS_FAILED(result)) return result;
5876
result = AddItem(aRange);
5877
if (NS_FAILED(result)) return result;
5881
result = GetRangeCount(&count);
5882
if (NS_FAILED(result)) return result;
5886
NS_ASSERTION(0,"bad count after additem\n");
5887
return NS_ERROR_FAILURE;
5889
setAnchorFocusRange(count -1);
5891
nsCOMPtr<nsIPresContext> presContext;
5892
GetPresContext(getter_AddRefs(presContext));
5893
selectFrames(presContext, aRange, PR_TRUE);
5895
//ScrollIntoView(); this should not happen automatically
5896
if (!mFrameSelection)
5897
return NS_OK;//nothing to do
5899
return mFrameSelection->NotifySelectionListeners(GetType());
5903
nsTypedSelection::RemoveRange(nsIDOMRange* aRange)
5906
return NS_ERROR_INVALID_ARG;
5909
nsCOMPtr<nsIPresContext> presContext;
5910
GetPresContext(getter_AddRefs(presContext));
5911
selectFrames(presContext, aRange, PR_FALSE);
5912
if (aRange == mAnchorFocusRange.get())
5914
PRInt32 cnt = mRangeArray.Count();
5917
setAnchorFocusRange(cnt - 1);//reset anchor to LAST range.
5921
if (!mFrameSelection)
5922
return NS_OK;//nothing to do
5923
return mFrameSelection->NotifySelectionListeners(GetType());
5929
* Collapse sets the whole selection to be one point.
5932
nsTypedSelection::Collapse(nsIDOMNode* aParentNode, PRInt32 aOffset)
5935
return NS_ERROR_INVALID_ARG;
5936
mFrameSelection->InvalidateDesiredX();
5937
if (!IsValidSelectionPoint(mFrameSelection, aParentNode))
5938
return NS_ERROR_FAILURE;
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));
5947
// Turn off signal for table selection
5948
if (mFrameSelection)
5949
mFrameSelection->ClearTableCellSelection();
5951
nsCOMPtr<nsIDOMRange> range;
5952
NS_NewRange(getter_AddRefs(range));
5954
NS_ASSERTION(PR_FALSE,"Couldn't make a range - nsSelection::Collapse");
5955
return NS_ERROR_UNEXPECTED;
5957
result = range->SetEnd(aParentNode, aOffset);
5958
if (NS_FAILED(result))
5960
result = range->SetStart(aParentNode, aOffset);
5961
if (NS_FAILED(result))
5964
#ifdef DEBUG_SELECTION
5967
nsCOMPtr<nsIContent>content;
5968
content = do_QueryInterface(aParentNode);
5970
return NS_ERROR_FAILURE;
5972
const char *tagString;
5973
content->Tag()->GetUTF8String(&tagString);
5974
printf ("Sel. Collapse to %p %s %d\n", content, tagString, aOffset);
5977
printf ("Sel. Collapse set to null parent.\n");
5982
result = AddItem(range);
5983
setAnchorFocusRange(0);
5984
selectFrames(presContext, range,PR_TRUE);
5985
if (NS_FAILED(result))
5987
if (!mFrameSelection)
5988
return NS_OK;//nothing to do
5989
return mFrameSelection->NotifySelectionListeners(GetType());
5993
* Sets the whole selection to be one point
5994
* at the start of the current selection
5997
nsTypedSelection::CollapseToStart()
6000
nsresult rv = GetRangeCount(&cnt);
6001
if (NS_FAILED(rv) || cnt <= 0)
6002
return NS_ERROR_FAILURE;
6004
// Get the first range
6005
nsIDOMRange* firstRange = mRangeArray[0];
6007
return NS_ERROR_FAILURE;
6009
nsCOMPtr<nsIDOMNode> parent;
6010
rv = firstRange->GetStartContainer(getter_AddRefs(parent));
6011
if (NS_SUCCEEDED(rv))
6015
PRInt32 startOffset;
6016
firstRange->GetStartOffset(&startOffset);
6017
rv = Collapse(parent, startOffset);
6020
rv = NS_ERROR_FAILURE;
6027
* Sets the whole selection to be one point
6028
* at the end of the current selection
6031
nsTypedSelection::CollapseToEnd()
6034
nsresult rv = GetRangeCount(&cnt);
6035
if (NS_FAILED(rv) || cnt <= 0)
6036
return NS_ERROR_FAILURE;
6038
// Get the last range
6039
nsIDOMRange* lastRange = mRangeArray[cnt-1];
6041
return NS_ERROR_FAILURE;
6043
nsCOMPtr<nsIDOMNode> parent;
6044
rv = lastRange->GetEndContainer(getter_AddRefs(parent));
6045
if (NS_SUCCEEDED(rv))
6050
lastRange->GetEndOffset(&endOffset);
6051
rv = Collapse(parent, endOffset);
6054
rv = NS_ERROR_FAILURE;
6061
* IsCollapsed -- is the whole selection just one point, or unset?
6064
nsTypedSelection::GetIsCollapsed(PRBool* aIsCollapsed)
6067
return NS_ERROR_NULL_POINTER;
6069
PRInt32 cnt = mRangeArray.Count();;
6072
*aIsCollapsed = PR_TRUE;
6078
*aIsCollapsed = PR_FALSE;
6082
return mRangeArray[0]->GetCollapsed(aIsCollapsed);
6086
nsTypedSelection::GetRangeCount(PRInt32* aRangeCount)
6089
return NS_ERROR_NULL_POINTER;
6091
*aRangeCount = mRangeArray.Count();
6097
nsTypedSelection::GetRangeAt(PRInt32 aIndex, nsIDOMRange** aReturn)
6100
return NS_ERROR_NULL_POINTER;
6102
PRInt32 cnt = mRangeArray.Count();
6103
if (aIndex < 0 || aIndex >= cnt)
6104
return NS_ERROR_INVALID_ARG;
6106
*aReturn = mRangeArray[aIndex];
6107
NS_IF_ADDREF(*aReturn);
6112
#ifdef OLD_SELECTION
6114
//may change parameters may not.
6115
//return NS_ERROR_FAILURE if invalid new selection between anchor and passed in parameters
6117
nsTypedSelection::FixupSelectionPoints(nsIDOMRange *aRange , nsDirection *aDir, PRBool *aFixupState)
6119
if (!aRange || !aFixupState)
6120
return NS_ERROR_NULL_POINTER;
6121
*aFixupState = PR_FALSE;
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;
6131
if (*aDir == eDirNext)
6133
if (NS_FAILED(GetOriginalAnchorPoint(getter_AddRefs(startNode), &startOffset)))
6135
aRange->GetStartParent(getter_AddRefs(startNode));
6136
aRange->GetStartOffset(&startOffset);
6138
aRange->GetEndParent(getter_AddRefs(endNode));
6139
aRange->GetEndOffset(&endOffset);
6143
if (NS_FAILED(GetOriginalAnchorPoint(getter_AddRefs(startNode), &startOffset)))
6145
aRange->GetEndParent(getter_AddRefs(startNode));
6146
aRange->GetEndOffset(&startOffset);
6148
aRange->GetStartParent(getter_AddRefs(endNode));
6149
aRange->GetStartOffset(&endOffset);
6151
if (!startNode || !endNode)
6152
return NS_ERROR_FAILURE;
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
6160
nsCOMPtr<nsIDOMNode> parent;
6161
nsCOMPtr<nsIDOMRange> subRange;
6162
NS_NewRange(getter_AddRefs(subRange));
6163
if (!subRange) return NS_ERROR_OUT_OF_MEMORY
6165
result = subRange->SetStart(startNode,startOffset);
6166
if (NS_FAILED(result))
6168
result = subRange->SetEnd(endNode,endOffset);
6169
if (NS_FAILED(result))
6171
result = subRange->SetEnd(startNode,startOffset);
6172
if (NS_FAILED(result))
6174
result = subRange->SetStart(endNode,endOffset);
6175
if (NS_FAILED(result))
6179
res = subRange->GetCommonParent(getter_AddRefs(parent));
6180
if (NS_FAILED(res) || !parent)
6183
//look for dest. if you see a cell you are in "cell mode"
6184
//if you see a table you select "whole" table
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)
6194
if (parent != startNode)
6196
result = startNode->GetParentNode(getter_AddRefs(tempNode));
6197
if (NS_FAILED(result) || !tempNode)
6198
return NS_ERROR_FAILURE;
6199
while (tempNode != parent)
6201
atom = GetTag(tempNode);
6202
if (atom == nsHTMLAtoms::table) //select whole table if in cell mode, wait for cell
6204
result = ParentOffset(tempNode, getter_AddRefs(startNode), &startOffset);
6205
if (NS_FAILED(result))
6206
return NS_ERROR_FAILURE;
6207
if (*aDir == eDirPrevious) //select after
6209
dirtystart = PR_TRUE;
6210
cellMode = PR_FALSE;
6212
else if (atom == nsHTMLAtoms::td ||
6213
atom == nsHTMLAtoms::th) //you are in "cell" mode put selection to end of cell
6216
result = ParentOffset(tempNode, getter_AddRefs(startNode), &startOffset);
6217
if (NS_FAILED(result))
6219
if (*aDir == eDirPrevious) //select after
6221
dirtystart = PR_TRUE;
6223
result = tempNode->GetParentNode(getter_AddRefs(tempNode2));
6224
if (NS_FAILED(result) || !tempNode2)
6225
return NS_ERROR_FAILURE;
6226
tempNode = tempNode2;
6231
if (parent != endNode)
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)
6239
atom = GetTag(tempNode);
6240
if (atom == nsHTMLAtoms::table) //select whole table if in cell mode, wait for cell
6244
result = ParentOffset(tempNode, getter_AddRefs(endNode), &endOffset);
6245
if (NS_FAILED(result))
6247
if (*aDir == eDirNext) //select after
6252
found = PR_FALSE; //didnt find the right cell yet
6254
else if (atom == nsHTMLAtoms::td ||
6255
atom == nsHTMLAtoms::th) //you are in "cell" mode put selection to end of cell
6257
result = ParentOffset(tempNode, getter_AddRefs(endNode), &endOffset);
6258
if (NS_FAILED(result))
6260
if (*aDir == eDirNext) //select after
6265
result = tempNode->GetParentNode(getter_AddRefs(tempNode2));
6266
if (NS_FAILED(result) || !tempNode2)
6267
return NS_ERROR_FAILURE;
6268
tempNode = tempNode2;
6271
return NS_ERROR_FAILURE;
6274
if (*aDir == eDirNext)
6276
if (FetchAnchorNode() == startNode.get() && FetchFocusNode() == endNode.get() &&
6277
FetchAnchorOffset() == startOffset && FetchFocusOffset() == endOffset)
6279
*aFixupState = PR_FALSE;
6280
return NS_ERROR_FAILURE;//nothing to do
6285
if (FetchAnchorNode() == endNode.get() && FetchFocusNode() == startNode.get() &&
6286
FetchAnchorOffset() == endOffset && FetchFocusOffset() == startOffset)
6288
*aFixupState = PR_FALSE;
6289
return NS_ERROR_FAILURE;//nothing to do
6292
if (mFixupState && !dirtyend && !dirtystart)//no mor fixup! all bets off
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
6300
if ((dirtystart || dirtyend) && *aDir != mDirection) //fixup took place but new direction all bets are off
6302
*aFixupState = PR_TRUE;
6303
//mFixupState = PR_FALSE;
6306
if (dirtystart && (FetchAnchorNode() != startNode.get() || FetchAnchorOffset() != startOffset))
6308
*aFixupState = PR_TRUE;
6309
mFixupState = PR_TRUE;
6312
if (dirtyend && (FetchFocusNode() != endNode.get() || FetchFocusOffset() != endOffset))
6314
*aFixupState = PR_TRUE;
6315
mFixupState = PR_TRUE;
6319
mFixupState = dirtystart || dirtyend;
6320
*aFixupState = PR_FALSE;
6322
if (dirtystart || dirtyend){
6323
if (*aDir == eDirNext)
6325
if (NS_FAILED(aRange->SetStart(startNode,startOffset)) || NS_FAILED(aRange->SetEnd(endNode, endOffset)))
6327
*aDir = eDirPrevious;
6328
aRange->SetStart(endNode, endOffset);
6329
aRange->SetEnd(startNode, startOffset);
6334
if (NS_FAILED(aRange->SetStart(endNode,endOffset)) || NS_FAILED(aRange->SetEnd(startNode, startOffset)))
6337
aRange->SetStart(startNode, startOffset);
6338
aRange->SetEnd(endNode, endOffset);
6344
#endif //OLD_SELECTION
6350
nsTypedSelection::SetOriginalAnchorPoint(nsIDOMNode *aNode, PRInt32 aOffset)
6353
mOriginalAnchorRange = 0;
6356
nsCOMPtr<nsIDOMRange> newRange;
6358
NS_NewRange(getter_AddRefs(newRange));
6359
if (!newRange) return NS_ERROR_OUT_OF_MEMORY;
6361
result = newRange->SetStart(aNode,aOffset);
6362
if (NS_FAILED(result))
6364
result = newRange->SetEnd(aNode,aOffset);
6365
if (NS_FAILED(result))
6368
mOriginalAnchorRange = newRange;
6375
nsTypedSelection::GetOriginalAnchorPoint(nsIDOMNode **aNode, PRInt32 *aOffset)
6377
if (!aNode || !aOffset || !mOriginalAnchorRange)
6378
return NS_ERROR_NULL_POINTER;
6380
result = mOriginalAnchorRange->GetStartContainer(aNode);
6381
if (NS_FAILED(result))
6383
result = mOriginalAnchorRange->GetStartOffset(aOffset);
6392
nsTypedSelection::CopyRangeToAnchorFocus(nsIDOMRange *aRange)
6394
nsCOMPtr<nsIDOMNode> startNode;
6395
nsCOMPtr<nsIDOMNode> endNode;
6396
PRInt32 startOffset;
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)))
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;//???
6409
else if (NS_FAILED(mAnchorFocusRange->SetEnd(endNode,endOffset)))
6410
return NS_ERROR_FAILURE;//???
6415
Notes which might come in handy for extend:
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.
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
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
6440
* Extend extends the selection away from the anchor.
6441
* We don't need to know the direction, because we always change the focus.
6444
nsTypedSelection::Extend(nsIDOMNode* aParentNode, PRInt32 aOffset)
6447
return NS_ERROR_INVALID_ARG;
6449
// First, find the range containing the old focus point:
6450
if (!mAnchorFocusRange)
6451
return NS_ERROR_NOT_INITIALIZED;
6454
if (!IsValidSelectionPoint(mFrameSelection, aParentNode))
6455
return NS_ERROR_FAILURE;
6457
//mFrameSelection->InvalidateDesiredX();
6458
nsCOMPtr<nsIDOMRange> difRange;
6459
NS_NewRange(getter_AddRefs(difRange));
6460
nsCOMPtr<nsIDOMRange> range;
6462
if (FetchFocusNode() == aParentNode && FetchFocusOffset() == aOffset)
6463
return NS_ERROR_FAILURE;//same node nothing to do!
6465
res = mAnchorFocusRange->CloneRange(getter_AddRefs(range));
6466
//range = mAnchorFocusRange;
6468
nsCOMPtr<nsIDOMNode> startNode;
6469
nsCOMPtr<nsIDOMNode> endNode;
6470
PRInt32 startOffset;
6473
range->GetStartContainer(getter_AddRefs(startNode));
6474
range->GetEndContainer(getter_AddRefs(endNode));
6475
range->GetStartOffset(&startOffset);
6476
range->GetEndOffset(&endOffset);
6479
nsDirection dir = GetDirection();
6480
PRBool fixupState = PR_FALSE; //if there was a previous fixup the optimal drawing erasing will NOT work
6484
NS_NewRange(getter_AddRefs(difRange));
6485
//compare anchor to old cursor.
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 );
6498
if (result2 == 0) //not selecting anywhere
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);
6509
res = difRange->SetEnd(FetchEndParent(range), FetchEndOffset(range));
6510
res |= difRange->SetStart(FetchFocusNode(), FetchFocusOffset());
6513
#ifdef OLD_SELECTION
6514
res = FixupSelectionPoints(range, &dir, &fixupState);
6520
#ifdef OLD_SELECTION
6521
selectFrames(mAnchorFocusRange, PR_FALSE);
6522
selectFrames(range, PR_TRUE);
6526
selectFrames(presContext, difRange , PR_TRUE);
6528
res = CopyRangeToAnchorFocus(range);
6532
else if (result1 == 0 && result3 > 0){//2, a1
6533
//select from 2 to 1a
6535
res = range->SetStart(aParentNode,aOffset);
6538
#ifdef OLD_SELECTION
6539
res = FixupSelectionPoints(range, &dir, &fixupState);
6542
if (fixupState) //unselect previous and select new state has changed to not fixed up
6544
selectFrames(mAnchorFocusRange, PR_FALSE);
6545
selectFrames(range, PR_TRUE);
6549
selectFrames(presContext, range, PR_TRUE);
6550
res = CopyRangeToAnchorFocus(range);
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);
6561
res = range->SetEnd(aParentNode,aOffset);
6564
#ifdef OLD_SELECTION
6566
res = FixupSelectionPoints(range, &dir, &fixupState);
6570
if (fixupState) //unselect previous and select new state has changed to not fixed up
6572
#ifdef OLD_SELECTION
6573
selectFrames(mAnchorFocusRange, PR_FALSE);
6574
selectFrames(range, PR_TRUE);
6579
res = CopyRangeToAnchorFocus(range);
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
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);
6596
res = range->SetEnd(aParentNode,aOffset);
6599
#ifdef OLD_SELECTION
6600
res = FixupSelectionPoints(range, &dir, &fixupState);
6604
if (fixupState) //unselect previous and select new state has changed to not fixed up
6606
selectFrames(mAnchorFocusRange, PR_FALSE);
6607
selectFrames(range, PR_TRUE);
6612
if (FetchFocusNode() != FetchAnchorNode() || FetchFocusOffset() != FetchAnchorOffset() ){//if collapsed diff dont do anything
6613
res = difRange->SetStart(FetchFocusNode(), FetchFocusOffset());
6614
res |= difRange->SetEnd(FetchAnchorNode(), FetchAnchorOffset());
6617
res = CopyRangeToAnchorFocus(range);
6620
//deselect from 1 to a
6621
RemoveItem(mAnchorFocusRange);
6622
selectFrames(presContext, difRange , PR_FALSE);
6623
AddItem(mAnchorFocusRange);
6627
res = CopyRangeToAnchorFocus(range);
6631
//select from a to 2
6632
selectFrames(presContext, range , PR_TRUE);
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());
6642
res = range->SetStart(aParentNode,aOffset);
6646
#ifdef OLD_SELECTION
6647
res = FixupSelectionPoints(range, &dir, &fixupState);
6651
if (fixupState) //unselect previous and select new state has changed to not fixed up
6653
#ifdef OLD_SELECTION
6654
selectFrames(mAnchorFocusRange, PR_FALSE);
6655
selectFrames(range, PR_TRUE);
6660
res = CopyRangeToAnchorFocus(range);
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
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);
6675
res = range->SetStart(aParentNode,aOffset);
6678
#ifdef OLD_SELECTION
6679
res = FixupSelectionPoints(range, &dir, &fixupState);
6682
if (fixupState) //unselect previous and select new state has changed to not fixed up
6684
selectFrames(mAnchorFocusRange, PR_FALSE);
6685
selectFrames(range, PR_TRUE);
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);
6697
RemoveItem(mAnchorFocusRange);
6698
selectFrames(presContext, difRange, 0);
6699
AddItem(mAnchorFocusRange);
6703
res = CopyRangeToAnchorFocus(range);
6707
//select from 2 to a
6708
selectFrames(presContext, range , PR_TRUE);
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);
6717
res = difRange->SetEnd(FetchFocusNode(), FetchFocusOffset());
6718
res |= difRange->SetStart(FetchStartParent(range), FetchStartOffset(range));
6722
#ifdef OLD_SELECTION
6723
res = FixupSelectionPoints(range, &dir, &fixupState);
6727
if (fixupState) //unselect previous and select new state has changed to not fixed up
6729
#ifdef OLD_SELECTION
6730
selectFrames(mAnchorFocusRange, PR_FALSE);
6731
selectFrames(range, PR_TRUE);
6735
selectFrames(presContext, difRange, PR_TRUE);
6737
res = CopyRangeToAnchorFocus(range);
6742
DEBUG_OUT_RANGE(range);
6744
if (eDirNext == mDirection)
6745
printf(" direction = 1 LEFT TO RIGHT\n");
6747
printf(" direction = 0 RIGHT TO LEFT\n");
6750
#ifdef DEBUG_SELECTION
6753
nsCOMPtr<nsIContent>content;
6754
content = do_QueryInterface(aParentNode);
6756
const char *tagString;
6757
content->Tag()->GetUTF8String(&tagString);
6758
printf ("Sel. Extend to %p %s %d\n", content, tagString, aOffset);
6761
printf ("Sel. Extend set to null parent.\n");
6764
if (!mFrameSelection)
6765
return NS_OK;//nothing to do
6766
return mFrameSelection->NotifySelectionListeners(GetType());
6770
GetChildOffset(nsIDOMNode *aChild, nsIDOMNode *aParent, PRInt32 &aOffset)
6772
NS_ASSERTION((aChild && aParent), "bad args");
6773
nsCOMPtr<nsIContent> content = do_QueryInterface(aParent);
6774
nsCOMPtr<nsIContent> cChild = do_QueryInterface(aChild);
6776
if (!cChild || !content)
6777
return NS_ERROR_NULL_POINTER;
6779
aOffset = content->IndexOf(cChild);
6785
nsTypedSelection::SelectAllChildren(nsIDOMNode* aParentNode)
6787
NS_ENSURE_ARG_POINTER(aParentNode);
6789
if (mFrameSelection)
6791
mFrameSelection->PostReason(nsISelectionListener::SELECTALL_REASON);
6793
nsresult result = Collapse(aParentNode, 0);
6794
if (NS_SUCCEEDED(result))
6796
nsCOMPtr<nsIDOMNode>lastChild;
6797
result = aParentNode->GetLastChild(getter_AddRefs(lastChild));
6798
if ((NS_SUCCEEDED(result)) && lastChild)
6800
PRInt32 numBodyChildren=0;
6801
GetChildOffset(lastChild, aParentNode, numBodyChildren);
6802
if (mFrameSelection)
6804
mFrameSelection->PostReason(nsISelectionListener::SELECTALL_REASON);
6806
result = Extend(aParentNode, numBodyChildren+1);
6813
nsTypedSelection::ContainsNode(nsIDOMNode* aNode, PRBool aRecursive, PRBool* aYes)
6816
return NS_ERROR_NULL_POINTER;
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)
6823
nsIDOMRange* range = mRangeArray[i];
6825
return NS_ERROR_UNEXPECTED;
6827
nsCOMPtr<nsIContent> content (do_QueryInterface(aNode));
6830
if (IsNodeIntersectsRange(content, range))
6832
// If recursive, then we're done -- IsNodeIntersectsRange does the right thing
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)))
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);
6855
aNode->GetNodeType(&nodeType);
6856
if ((!nodeStartsBeforeRange && !nodeEndsAfterRange)
6857
|| (nodeType == nsIDOMNode::TEXT_NODE))
6871
nsTypedSelection::GetPresContext(nsIPresContext **aPresContext)
6873
if (!mFrameSelection)
6874
return NS_ERROR_FAILURE;//nothing to do
6875
nsIFocusTracker *tracker = mFrameSelection->GetTracker();
6878
return NS_ERROR_NULL_POINTER;
6880
return tracker->GetPresContext(aPresContext);
6884
nsTypedSelection::GetPresShell(nsIPresShell **aPresShell)
6888
nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShellWeak);
6890
NS_ADDREF(*aPresShell = presShell);
6893
nsresult rv = NS_OK;
6894
if (!mFrameSelection)
6895
return NS_ERROR_FAILURE;//nothing to do
6897
nsIFocusTracker *tracker = mFrameSelection->GetTracker();
6900
return NS_ERROR_NULL_POINTER;
6902
nsCOMPtr<nsIPresContext> presContext;
6904
rv = tracker->GetPresContext(getter_AddRefs(presContext));
6910
return NS_ERROR_NULL_POINTER;
6912
nsIPresShell *shell = presContext->GetPresShell();
6913
mPresShellWeak = do_GetWeakReference(shell); // the presshell owns us, so no addref
6915
NS_ADDREF(*aPresShell = shell);
6920
nsTypedSelection::GetRootScrollableView(nsIScrollableView **aScrollableView)
6923
// NOTE: This method returns a NON-AddRef'd pointer
6924
// to the scrollable view!
6926
NS_ENSURE_ARG_POINTER(aScrollableView);
6928
if (!mFrameSelection)
6929
return NS_ERROR_FAILURE;//nothing to do
6931
nsIScrollableView *scrollView;
6932
rv = mFrameSelection->GetScrollableView(&scrollView);
6939
nsCOMPtr<nsIPresShell> presShell;
6941
rv = GetPresShell(getter_AddRefs(presShell));
6947
return NS_ERROR_NULL_POINTER;
6949
nsIViewManager* viewManager = presShell->GetViewManager();
6952
return NS_ERROR_NULL_POINTER;
6955
// nsIViewManager::GetRootScrollableView() does not
6956
// AddRef the pointer it returns.
6958
return viewManager->GetRootScrollableView(aScrollableView);
6960
else //SCROLLVIEW_FROM_FRAME
6962
*aScrollableView = scrollView;
6969
nsTypedSelection::GetFrameToScrolledViewOffsets(nsIScrollableView *aScrollableView, nsIFrame *aFrame, nscoord *aX, nscoord *aY)
6971
nsresult rv = NS_OK;
6972
if (!mFrameSelection)
6973
return NS_ERROR_FAILURE;//nothing to do
6975
if (!aScrollableView || !aFrame || !aX || !aY) {
6976
return NS_ERROR_NULL_POINTER;
6982
nsIView* scrolledView;
6984
nsIView* closestView;
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();
6992
return NS_ERROR_NULL_POINTER;
6994
nsCOMPtr<nsIPresContext> presContext;
6995
tracker->GetPresContext(getter_AddRefs(presContext));
6996
aFrame->GetOffsetFromView(presContext, offset, &closestView);
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();
7004
// Get its parent view
7005
closestView = closestView->GetParent();
7015
nsTypedSelection::GetPointFromOffset(nsIFrame *aFrame, PRInt32 aContentOffset, nsPoint *aPoint)
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;
7027
// Retrieve the device context. We need one to create
7028
// a rendering context.
7031
nsIFocusTracker *tracker = mFrameSelection->GetTracker();
7034
return NS_ERROR_NULL_POINTER;
7036
nsCOMPtr<nsIPresContext> presContext;
7038
rv = tracker->GetPresContext(getter_AddRefs(presContext));
7044
return NS_ERROR_NULL_POINTER;
7047
// Now get the closest view with a widget so we can create
7048
// a rendering context.
7051
nsIWidget* widget = nsnull;
7052
nsIView *closestView = nsnull;
7053
nsPoint offset(0, 0);
7055
rv = aFrame->GetOffsetFromView(presContext, offset, &closestView);
7057
while (!widget && closestView)
7059
widget = closestView->GetWidget();
7063
closestView = closestView->GetParent();
7068
return NS_ERROR_FAILURE;
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
7076
nsCOMPtr<nsIRenderingContext> rendContext;
7078
rv = presContext->DeviceContext()->
7079
CreateRenderingContext(closestView, *getter_AddRefs(rendContext));
7085
return NS_ERROR_NULL_POINTER;
7088
// Now get the point and return!
7091
rv = aFrame->GetPointFromOffset(presContext, rendContext, aContentOffset, aPoint);
7097
nsTypedSelection::GetSelectionRegionRectAndScrollableView(SelectionRegion aRegion, nsRect *aRect, nsIScrollableView **aScrollableView)
7099
nsresult result = NS_OK;
7100
if (!mFrameSelection)
7101
return NS_ERROR_FAILURE;//nothing to do
7103
if (!aRect || !aScrollableView)
7104
return NS_ERROR_NULL_POINTER;
7113
*aScrollableView = 0;
7115
nsIDOMNode *node = 0;
7116
PRInt32 nodeOffset = 0;
7117
PRBool isEndNode = PR_TRUE;
7118
nsIFrame *frame = 0;
7122
case nsISelectionController::SELECTION_ANCHOR_REGION:
7123
node = FetchAnchorNode();
7124
nodeOffset = FetchAnchorOffset();
7125
isEndNode = GetDirection() == eDirPrevious;
7127
case nsISelectionController::SELECTION_FOCUS_REGION:
7128
node = FetchFocusNode();
7129
nodeOffset = FetchFocusOffset();
7130
isEndNode = GetDirection() == eDirNext;
7133
return NS_ERROR_FAILURE;
7137
return NS_ERROR_NULL_POINTER;
7139
nsCOMPtr<nsIContent> content = do_QueryInterface(node);
7140
PRInt32 frameOffset = 0;
7144
nsIFrameSelection::HINT hint;
7145
mFrameSelection->GetHint(&hint);
7146
result = mFrameSelection->GetFrameForNodeOffset(content, nodeOffset, hint, &frame, &frameOffset);
7149
result = NS_ERROR_FAILURE;
7151
if(NS_FAILED(result))
7155
return NS_ERROR_NULL_POINTER;
7158
// Get the frame's scrollable view.
7161
nsCOMPtr<nsIPresContext> presContext;
7163
result = GetPresContext(getter_AddRefs(presContext));
7165
if (NS_FAILED(result))
7169
return NS_ERROR_FAILURE;
7172
nsIFrame *parentWithView = frame->GetAncestorWithView();
7174
if (!parentWithView)
7175
return NS_ERROR_FAILURE;
7177
nsIView* view = parentWithView->GetView();
7179
result = GetClosestScrollableView(view, aScrollableView);
7181
if (NS_FAILED(result))
7184
if (!*aScrollableView)
7188
// Figure out what node type we have, then get the
7189
// appropriate rect for it's nodeOffset.
7192
PRUint16 nodeType = nsIDOMNode::ELEMENT_NODE;
7194
result = node->GetNodeType(&nodeType);
7196
if (NS_FAILED(result))
7197
return NS_ERROR_NULL_POINTER;
7199
if (nodeType == nsIDOMNode::TEXT_NODE)
7201
nsIFrame *childFrame = 0;
7204
result = frame->GetChildFrameContainingOffset(nodeOffset, mFrameSelection->mHint, &frameOffset, &childFrame);
7206
if (NS_FAILED(result))
7210
return NS_ERROR_NULL_POINTER;
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
7221
result = GetCachedFrameOffset(frame, nodeOffset, pt);
7223
if (NS_FAILED(result))
7227
// Get the frame's rect.
7229
*aRect = frame->GetRect();
7232
// Translate the frame's rect into root view coordinates.
7234
result = GetFrameToScrolledViewOffsets(*aScrollableView, frame, &aRect->x, &aRect->y);
7236
if (NS_FAILED(result))
7240
// Now add the offset's x coordinate.
7245
// Adjust the width of the rect to account for any neccessary
7249
const nsIView* clipView = 0;
7251
result = (*aScrollableView)->GetClipView(&clipView);
7253
if (NS_FAILED(result))
7256
nsRect clipRect = clipView->GetBounds();
7258
result = (*aScrollableView)->GetScrollPosition(clipRect.x, clipRect.y);
7260
if (NS_FAILED(result))
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.
7269
PRInt32 pad = clipRect.width >> 2;
7272
pad = 3; // Arbitrary
7274
if (aRect->x >= clipRect.XMost())
7276
else if (aRect->x <= clipRect.x)
7282
aRect->width = 60; // Arbitrary
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
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.
7298
nsIView* scrolledView = 0;
7300
result = (*aScrollableView)->GetScrolledView(scrolledView);
7302
if (NS_FAILED(result))
7305
nsRect svRect = scrolledView->GetBounds();
7309
else if (aRect->x >= svRect.width)
7310
aRect->x = svRect.width - 1;
7312
if (aRect->XMost() > svRect.width)
7313
aRect->width = svRect.width - aRect->x;
7318
// Must be a non-text frame, just scroll the frame
7321
*aRect = frame->GetRect();
7323
result = GetFrameToScrolledViewOffsets(*aScrollableView, frame, &aRect->x, &aRect->y);
7330
nsTypedSelection::ScrollRectIntoView(nsIScrollableView *aScrollableView,
7334
PRBool aScrollParentViews)
7336
nsresult rv = NS_OK;
7337
if (!mFrameSelection)
7338
return NS_OK;//nothing to do
7340
if (!aScrollableView)
7341
return NS_ERROR_NULL_POINTER;
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;
7347
aScrollableView->GetClipView(&clipView);
7348
nsRect visibleRect = clipView->GetBounds();
7349
aScrollableView->GetScrollPosition(visibleRect.x, visibleRect.y);
7351
// The actual scroll offsets
7352
nscoord scrollOffsetX = visibleRect.x;
7353
nscoord scrollOffsetY = visibleRect.y;
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;
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;
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;
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;
7398
aScrollableView->ScrollTo(scrollOffsetX, scrollOffsetY, NS_VMREFRESH_IMMEDIATE);
7400
if (aScrollParentViews)
7403
// Get aScrollableView's scrolled view.
7406
nsIView *scrolledView = 0;
7408
rv = aScrollableView->GetScrolledView(scrolledView);
7414
return NS_ERROR_FAILURE;
7417
// Check if aScrollableRect has a parent scrollable view!
7421
rv = CallQueryInterface(aScrollableView, &view);
7425
view = view->GetParent();
7429
nsIScrollableView *parentSV = 0;
7431
rv = GetClosestScrollableView(view, &parentSV);
7439
// We have a parent scrollable view, so now map aRect
7440
// into it's scrolled view's coordinate space.
7445
rv = parentSV->GetScrolledView(view);
7451
return NS_ERROR_FAILURE;
7453
rv = GetViewAncestorOffset(scrolledView, view, &newRect.x, &newRect.y);
7458
newRect.x += aRect.x;
7459
newRect.y += aRect.y;
7460
newRect.width = aRect.width;
7461
newRect.height = aRect.height;
7464
// Now scroll the rect into the parent's view.
7467
rv = ScrollRectIntoView(parentSV, newRect, aVPercent, aHPercent, aScrollParentViews);
7475
static void PR_CALLBACK HandlePLEvent(nsScrollSelectionIntoViewEvent* aEvent);
7476
static void PR_CALLBACK DestroyPLEvent(nsScrollSelectionIntoViewEvent* aEvent);
7478
struct nsScrollSelectionIntoViewEvent : public PLEvent {
7479
nsScrollSelectionIntoViewEvent(nsTypedSelection *aTypedSelection, SelectionRegion aRegion) {
7480
if (!aTypedSelection)
7483
mTypedSelection = aTypedSelection;
7486
PL_InitEvent(this, aTypedSelection,
7487
(PLHandleEventProc) ::HandlePLEvent,
7488
(PLDestroyEventProc) ::DestroyPLEvent);
7491
~nsScrollSelectionIntoViewEvent() {}
7493
void HandleEvent() {
7494
mTypedSelection->mScrollEventPosted = PR_FALSE;
7496
if (!mTypedSelection)
7499
mTypedSelection->ScrollIntoView(mRegion, PR_TRUE);
7502
nsTypedSelection *mTypedSelection;
7503
SelectionRegion mRegion;
7506
static void PR_CALLBACK HandlePLEvent(nsScrollSelectionIntoViewEvent* aEvent)
7508
NS_ASSERTION(nsnull != aEvent,"Event is null");
7509
aEvent->HandleEvent();
7512
static void PR_CALLBACK DestroyPLEvent(nsScrollSelectionIntoViewEvent* aEvent)
7514
NS_ASSERTION(nsnull != aEvent,"Event is null");
7519
nsTypedSelection::PostScrollSelectionIntoViewEvent(SelectionRegion aRegion)
7521
static NS_DEFINE_CID(kEventQueueServiceCID, NS_EVENTQUEUESERVICE_CID);
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));
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.
7541
mEventQueue->RevokeEvents(this);
7542
mScrollEventPosted = PR_FALSE;
7545
nsScrollSelectionIntoViewEvent *ev = new nsScrollSelectionIntoViewEvent(this, aRegion);
7547
mEventQueue->PostEvent(ev);
7548
mScrollEventPosted = PR_TRUE;
7553
return NS_ERROR_FAILURE;
7557
nsTypedSelection::ScrollIntoView(SelectionRegion aRegion, PRBool aIsSynchronous)
7560
if (!mFrameSelection)
7561
return NS_OK;//nothing to do
7563
if (mFrameSelection->GetBatching())
7566
if (!aIsSynchronous)
7567
return PostScrollSelectionIntoViewEvent(aRegion);
7570
// Shut the caret off before scrolling to avoid
7571
// leaving caret turds on the screen!
7573
nsCOMPtr<nsIPresShell> presShell;
7574
result = GetPresShell(getter_AddRefs(presShell));
7575
if (NS_FAILED(result))
7577
nsCOMPtr<nsICaret> caret;
7578
presShell->GetCaret(getter_AddRefs(caret));
7581
StCaretHider caretHider(caret); // stack-based class hides and shows the caret
7584
// Scroll the selection region into view.
7588
nsIScrollableView *scrollableView = 0;
7590
result = GetSelectionRegionRectAndScrollableView(aRegion, &rect, &scrollableView);
7592
if (NS_FAILED(result))
7596
// It's ok if we don't have a scrollable view, just return early.
7598
if (!scrollableView)
7601
result = ScrollRectIntoView(scrollableView, rect, NS_PRESSHELL_SCROLL_ANYWHERE, NS_PRESSHELL_SCROLL_ANYWHERE, PR_TRUE);
7609
nsTypedSelection::AddSelectionListener(nsISelectionListener* aNewListener)
7612
return NS_ERROR_NULL_POINTER;
7613
return mSelectionListeners.AppendObject(aNewListener) ? NS_OK : NS_ERROR_FAILURE; // addrefs
7619
nsTypedSelection::RemoveSelectionListener(nsISelectionListener* aListenerToRemove)
7621
if (!aListenerToRemove )
7622
return NS_ERROR_NULL_POINTER;
7623
return mSelectionListeners.RemoveObject(aListenerToRemove) ? NS_OK : NS_ERROR_FAILURE; // releases
7628
nsTypedSelection::NotifySelectionListeners()
7630
if (!mFrameSelection)
7631
return NS_OK;//nothing to do
7633
if (mFrameSelection->GetBatching()){
7634
mFrameSelection->SetDirty();
7637
PRInt32 cnt = mSelectionListeners.Count();
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)
7645
rv = shell->GetDocument(getter_AddRefs(doc));
7648
domdoc = do_QueryInterface(doc);
7650
short reason = mFrameSelection->PopReason();
7651
for (PRInt32 i = 0; i < cnt; i++)
7653
nsISelectionListener* thisListener = mSelectionListeners[i];
7655
thisListener->NotifySelectionChanged(domdoc, this, reason);
7661
nsTypedSelection::StartBatchChanges()
7663
if (!mFrameSelection)
7664
return NS_OK;//nothing to do
7665
return mFrameSelection->StartBatchChanges();
7671
nsTypedSelection::EndBatchChanges()
7673
if (!mFrameSelection)
7674
return NS_OK;//nothing to do
7675
return mFrameSelection->EndBatchChanges();
7681
nsTypedSelection::DeleteFromDocument()
7683
if (!mFrameSelection)
7684
return NS_OK;//nothing to do
7685
return mFrameSelection->DeleteFromDocument();
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
7692
nsTypedSelection::SelectionLanguageChange(PRBool aLangRTL)
7695
nsCOMPtr<nsIDOMNode> focusNode;
7696
nsCOMPtr<nsIContent> focusContent;
7697
PRInt32 focusOffset;
7698
nsIFrame *focusFrame = 0;
7700
focusOffset = FetchFocusOffset();
7701
focusNode = FetchFocusNode();
7702
result = GetPrimaryFrameForFocusNode(&focusFrame, nsnull);
7703
if (NS_FAILED(result) || !focusFrame)
7704
return result?result:NS_ERROR_FAILURE;
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;
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;
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);
7726
nsIFrameSelection::HINT hint;
7728
if ((focusOffset == frameStart && level) // beginning of an RTL frame
7729
|| (focusOffset == frameEnd && !level)) { // end of an LTR frame
7730
hint = nsIFrameSelection::HINTRIGHT;
7732
else { // end of an RTL frame or beginning of an LTR frame
7733
hint = nsIFrameSelection::HINTLEFT;
7735
mFrameSelection->SetHint(hint);
7737
mFrameSelection->GetPrevNextBidiLevels(context, focusContent, focusOffset, &frameBefore, &frameAfter, &levelBefore, &levelAfter);
7740
nsIPresShell* shell = context->GetPresShell();
7742
return NS_ERROR_FAILURE;
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);
7754
shell->SetCaretBidiLevel(level + 1);
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);
7762
shell->SetCaretBidiLevel(levelAfter);