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 Communicator client 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.
22
* Original Author: David W. Hyatt (hyatt@netscape.com)
25
* Dean Tessman <dean_tessman@hotmail.com>
26
* Brian Ryner <bryner@brianryner.com>
28
* Alternatively, the contents of this file may be used under the terms of
29
* either the GNU General Public License Version 2 or later (the "GPL"), or
30
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31
* in which case the provisions of the GPL or the LGPL are applicable instead
32
* of those above. If you wish to allow use of your version of this file only
33
* under the terms of either the GPL or the LGPL, and not to allow others to
34
* use your version of this file under the terms of the NPL, indicate your
35
* decision by deleting the provisions above and replace them with the notice
36
* and other provisions required by the GPL or the LGPL. If you do not delete
37
* the provisions above, a recipient may use your version of this file under
38
* the terms of any one of the NPL, the GPL or the LGPL.
40
* ***** END LICENSE BLOCK ***** */
43
#include "nsLeafBoxFrame.h"
44
#include "nsITreeBoxObject.h"
45
#include "nsITreeView.h"
46
#include "nsICSSPseudoComparator.h"
47
#include "nsIScrollbarMediator.h"
48
#include "nsIRenderingContext.h"
49
#include "nsIDragSession.h"
50
#include "nsIWidget.h"
51
#include "nsHashtable.h"
53
#include "nsIReflowCallback.h"
54
#include "nsILookAndFeel.h"
55
#include "nsValueArray.h"
57
#include "imgIDecoderObserver.h"
59
class nsSupportsHashtable;
61
class nsDFAState : public nsHashKey
66
nsDFAState(PRUint32 aID) :mStateID(aID) {};
68
PRUint32 GetStateID() { return mStateID; };
70
PRUint32 HashCode(void) const {
74
PRBool Equals(const nsHashKey *aKey) const {
75
nsDFAState* key = (nsDFAState*)aKey;
76
return key->mStateID == mStateID;
79
nsHashKey *Clone(void) const {
80
return new nsDFAState(mStateID);
84
class nsTransitionKey : public nsHashKey
88
nsCOMPtr<nsIAtom> mInputSymbol;
90
nsTransitionKey(PRUint32 aState, nsIAtom* aSymbol) :mState(aState), mInputSymbol(aSymbol) {};
92
PRUint32 HashCode(void) const {
93
// Make a 32-bit integer that combines the low-order 16 bits of the state and the input symbol.
94
PRInt32 hb = mState << 16;
95
PRInt32 lb = (NS_PTR_TO_INT32(mInputSymbol.get()) << 16) >> 16;
99
PRBool Equals(const nsHashKey *aKey) const {
100
nsTransitionKey* key = (nsTransitionKey*)aKey;
101
return key->mState == mState && key->mInputSymbol == mInputSymbol;
104
nsHashKey *Clone(void) const {
105
return new nsTransitionKey(mState, mInputSymbol);
109
class nsTreeStyleCache
112
nsTreeStyleCache() :mTransitionTable(nsnull), mCache(nsnull), mNextState(0) {};
113
virtual ~nsTreeStyleCache() { Clear(); };
115
void Clear() { delete mTransitionTable; mTransitionTable = nsnull; delete mCache; mCache = nsnull; mNextState = 0; };
117
nsStyleContext* GetStyleContext(nsICSSPseudoComparator* aComparator,
118
nsIPresContext* aPresContext,
119
nsIContent* aContent,
120
nsStyleContext* aContext,
121
nsIAtom* aPseudoElement,
122
nsISupportsArray* aInputWord);
124
static PRBool PR_CALLBACK DeleteDFAState(nsHashKey *aKey, void *aData, void *closure);
126
static PRBool PR_CALLBACK ReleaseStyleContext(nsHashKey *aKey, void *aData, void *closure);
129
// A transition table for a deterministic finite automaton. The DFA
130
// takes as its input a single pseudoelement and an ordered set of properties.
131
// It transitions on an input word that is the concatenation of the pseudoelement supplied
132
// with the properties in the array.
134
// It transitions from state to state by looking up entries in the transition table (which is
135
// a mapping from (S,i)->S', where S is the current state, i is the next
136
// property in the input word, and S' is the state to transition to.
138
// If S' is not found, it is constructed and entered into the hashtable
139
// under the key (S,i).
141
// Once the entire word has been consumed, the final state is used
142
// to reference the cache table to locate the style context.
143
nsObjectHashtable* mTransitionTable;
145
// The cache of all active style contexts. This is a hash from
146
// a final state in the DFA, Sf, to the resultant style context.
147
nsObjectHashtable* mCache;
149
// An integer counter that is used when we need to make new states in the
154
// This class is our column info. We use it to iterate our columns and to obtain
155
// information about each column.
158
nsTreeColumn(nsIContent* aColElement, nsIFrame* aFrame);
159
virtual ~nsTreeColumn() { delete mNext; };
161
void SetNext(nsTreeColumn* aNext) { mNext = aNext; };
162
nsTreeColumn* GetNext() { return mNext; };
164
nsIContent* GetElement() { return mColElement; };
167
const nsAFlatString& GetID() { return mID; };
169
void GetIDAtom(nsIAtom** aResult) { *aResult = mIDAtom; NS_IF_ADDREF(*aResult); };
171
PRBool IsPrimary() { return mIsPrimaryCol; };
172
PRBool IsCycler() { return mIsCyclerCol; };
179
Type GetType() { return mType; };
181
PRInt32 GetCropStyle() { return mCropStyle; };
182
PRInt32 GetTextAlignment() { return mTextAlignment; };
184
PRInt32 GetColIndex() { return mColIndex; };
190
nsCOMPtr<nsIAtom> mIDAtom;
193
PRUint32 mTextAlignment;
195
PRPackedBool mIsPrimaryCol;
196
PRPackedBool mIsCyclerCol;
200
nsIContent* mColElement;
205
// The interface for our image listener.
206
// {90586540-2D50-403e-8DCE-981CAA778444}
207
#define NS_ITREEIMAGELISTENER_IID \
208
{ 0x90586540, 0x2d50, 0x403e, { 0x8d, 0xce, 0x98, 0x1c, 0xaa, 0x77, 0x84, 0x44 } }
210
class nsITreeImageListener : public nsISupports
213
NS_DEFINE_STATIC_IID_ACCESSOR(NS_ITREEIMAGELISTENER_IID)
216
NS_IMETHOD AddRow(int aIndex)=0;
217
NS_IMETHOD Invalidate()=0;
220
// This class handles image load observation.
221
class nsTreeImageListener : public imgIDecoderObserver, public nsITreeImageListener
224
nsTreeImageListener(nsITreeBoxObject* aTree, const PRUnichar* aColID);
225
virtual ~nsTreeImageListener();
228
NS_DECL_IMGIDECODEROBSERVER
229
NS_DECL_IMGICONTAINEROBSERVER
231
NS_IMETHOD AddRow(int aIndex);
232
NS_IMETHOD Invalidate();
238
nsITreeBoxObject* mTree;
241
// The actual frame that paints the cells and rows.
242
class nsTreeBodyFrame : public nsLeafBoxFrame, public nsITreeBoxObject, public nsICSSPseudoComparator,
243
public nsIScrollbarMediator,
244
public nsIReflowCallback
248
NS_DECL_NSITREEBOXOBJECT
251
NS_IMETHOD GetMinSize(nsBoxLayoutState& aBoxLayoutState, nsSize& aSize);
252
NS_IMETHOD SetBounds(nsBoxLayoutState& aBoxLayoutState, const nsRect& aRect);
255
NS_IMETHOD ReflowFinished(nsIPresShell* aPresShell, PRBool* aFlushFlag);
257
// nsICSSPseudoComparator
258
NS_IMETHOD PseudoMatches(nsIAtom* aTag, nsCSSSelector* aSelector, PRBool* aResult);
260
// nsIScrollbarMediator
261
NS_IMETHOD PositionChanged(PRInt32 aOldIndex, PRInt32& aNewIndex);
262
NS_IMETHOD ScrollbarButtonPressed(PRInt32 aOldIndex, PRInt32 aNewIndex);
263
NS_IMETHOD VisibilityChanged(PRBool aVisible) { Invalidate(); return NS_OK; };
265
// Overridden from nsIFrame to cache our pres context.
266
NS_IMETHOD Init(nsIPresContext* aPresContext, nsIContent* aContent,
267
nsIFrame* aParent, nsStyleContext* aContext, nsIFrame* aPrevInFlow);
268
NS_IMETHOD Destroy(nsIPresContext* aPresContext);
271
// Paint is the generic nsIFrame paint method. We override this method
272
// to paint our contents (our rows and cells).
273
NS_IMETHOD Paint(nsIPresContext* aPresContext,
274
nsIRenderingContext& aRenderingContext,
275
const nsRect& aDirtyRect,
276
nsFramePaintLayer aWhichLayer,
277
PRUint32 aFlags = 0);
279
// This method paints a specific column background of the tree.
280
nsresult PaintColumn(nsTreeColumn* aColumn,
281
const nsRect& aColumnRect,
282
nsIPresContext* aPresContext,
283
nsIRenderingContext& aRenderingContext,
284
const nsRect& aDirtyRect);
286
// This method paints a single row in the tree.
287
nsresult PaintRow(PRInt32 aRowIndex,
288
const nsRect& aRowRect,
289
nsIPresContext* aPresContext,
290
nsIRenderingContext& aRenderingContext,
291
const nsRect& aDirtyRect);
293
// This method paints a specific cell in a given row of the tree.
294
nsresult PaintCell(PRInt32 aRowIndex,
295
nsTreeColumn* aColumn,
296
const nsRect& aCellRect,
297
nsIPresContext* aPresContext,
298
nsIRenderingContext& aRenderingContext,
299
const nsRect& aDirtyRect,
302
// This method paints the twisty inside a cell in the primary column of an tree.
303
nsresult PaintTwisty(PRInt32 aRowIndex,
304
nsTreeColumn* aColumn,
305
const nsRect& aTwistyRect,
306
nsIPresContext* aPresContext,
307
nsIRenderingContext& aRenderingContext,
308
const nsRect& aDirtyRect,
309
nscoord& aRemainingWidth,
312
// This method paints the image inside the cell of an tree.
313
nsresult PaintImage(PRInt32 aRowIndex,
314
nsTreeColumn* aColumn,
315
const nsRect& aImageRect,
316
nsIPresContext* aPresContext,
317
nsIRenderingContext& aRenderingContext,
318
const nsRect& aDirtyRect,
319
nscoord& aRemainingWidth,
322
// This method paints the text string inside a particular cell of the tree.
323
nsresult PaintText(PRInt32 aRowIndex,
324
nsTreeColumn* aColumn,
325
const nsRect& aTextRect,
326
nsIPresContext* aPresContext,
327
nsIRenderingContext& aRenderingContext,
328
const nsRect& aDirtyRect,
331
// This method paints the checkbox inside a particular cell of the tree.
332
nsresult PaintCheckbox(PRInt32 aRowIndex,
333
nsTreeColumn* aColumn,
334
const nsRect& aCheckboxRect,
335
nsIPresContext* aPresContext,
336
nsIRenderingContext& aRenderingContext,
337
const nsRect& aDirtyRect);
339
// This method paints the progress meter inside a particular cell of the tree.
340
nsresult PaintProgressMeter(PRInt32 aRowIndex,
341
nsTreeColumn* aColumn,
342
const nsRect& aProgressMeterRect,
343
nsIPresContext* aPresContext,
344
nsIRenderingContext& aRenderingContext,
345
const nsRect& aDirtyRect);
347
// This method paints a drop feedback of the tree.
348
nsresult PaintDropFeedback(const nsRect& aDropFeedbackRect,
349
nsIPresContext* aPresContext,
350
nsIRenderingContext& aRenderingContext,
351
const nsRect& aDirtyRect);
353
// This method is called with a specific style context and rect to
354
// paint the background rect as if it were a full-blown frame.
355
nsresult PaintBackgroundLayer(nsStyleContext* aStyleContext,
356
nsIPresContext* aPresContext,
357
nsIRenderingContext& aRenderingContext,
359
const nsRect& aDirtyRect);
361
// This method is called whenever an treecol is added or removed and
362
// the column cache needs to be rebuilt.
363
void InvalidateColumnCache();
365
friend nsresult NS_NewTreeBodyFrame(nsIPresShell* aPresShell,
366
nsIFrame** aNewFrame);
368
friend class nsTreeBoxObject;
371
nsTreeBodyFrame(nsIPresShell* aPresShell);
372
virtual ~nsTreeBodyFrame();
374
// Caches our box object.
375
void SetBoxObject(nsITreeBoxObject* aBoxObject) { mTreeBoxObject = aBoxObject; };
377
// A helper used when hit testing.
378
nsresult GetItemWithinCellAt(PRInt32 aX, const nsRect& aCellRect, PRInt32 aRowIndex,
379
nsTreeColumn* aColumn, PRUnichar** aChildElt);
381
// Fetch an image from the image cache.
382
nsresult GetImage(PRInt32 aRowIndex, const PRUnichar* aColID, PRBool aUseContext,
383
nsStyleContext* aStyleContext, PRBool& aAllowImageRegions, imgIContainer** aResult);
385
// Returns the size of a given image. This size *includes* border and
386
// padding. It does not include margins.
387
nsRect GetImageSize(PRInt32 aRowIndex, const PRUnichar* aColID, PRBool aUseContext, nsStyleContext* aStyleContext);
389
// Returns the height of rows in the tree.
390
PRInt32 GetRowHeight();
392
// Returns our indentation width.
393
PRInt32 GetIndentation();
395
// Calculates our width/height once border and padding have been removed.
398
// Looks up a style context in the style cache. On a cache miss we resolve
399
// the pseudo-styles passed in and place them into the cache.
400
nsStyleContext* GetPseudoStyleContext(nsIAtom* aPseudoElement);
402
// Builds our cache of column info.
403
void EnsureColumns();
405
// Makes |mScrollbar| non-null if at all possible, and returns it.
406
nsIFrame* EnsureScrollbar();
408
// Update the curpos of the scrollbar.
409
void UpdateScrollbar();
411
// Update the maxpos of the scrollbar.
412
void InvalidateScrollbar();
414
// Check vertical overflow.
415
nsresult CheckVerticalOverflow();
417
// Use to auto-fill some of the common properties without the view having to do it.
418
// Examples include container, open, selected, and focus.
419
void PrefillPropertyArray(PRInt32 aRowIndex, nsTreeColumn* aCol);
421
// Our internal scroll method, used by all the public scroll methods.
422
nsresult ScrollInternal(PRInt32 aRow);
424
// Convert pixels, probably from an event, into twips in our coordinate space.
425
void AdjustEventCoordsToBoxCoordSpace (PRInt32 aX, PRInt32 aY, PRInt32* aResultX, PRInt32* aResultY);
427
// Convert a border style into line style.
428
nsLineStyle ConvertBorderStyleToLineStyle(PRUint8 aBorderStyle);
430
// Cache the box object
431
void EnsureBoxObject();
435
// Get the base element, <tree> or <select>
436
nsresult GetBaseElement(nsIContent** aElement);
438
void GetCellWidth(PRInt32 aRow, const nsAString& aColID,
439
nsIRenderingContext* aRenderingContext,
440
nscoord& aDesiredSize, nscoord& aCurrentSize);
441
nscoord CalcMaxRowWidth(nsBoxLayoutState& aState);
443
PRBool CanAutoScroll(PRInt32 aRowIndex);
445
// Calc the row and above/below/on status given where the mouse currently is hovering.
446
// Also calc if we're in the region in which we want to auto-scroll the tree.
447
// A positive value of |aScrollLines| means scroll down, a negative value
448
// means scroll up, a zero value means that we aren't in drag scroll region.
449
void ComputeDropPosition(nsIDOMEvent* aEvent, PRInt32* aRow, PRInt16* aOrient,
450
PRInt16* aScrollLines);
452
// Mark ourselves dirty if we're a select widget
453
void MarkDirtyIfSelect();
455
// Create a new timer. This method is used to delay various actions like
456
// opening/closing folders or tree scrolling.
457
// aID is type of the action, aFunc is the function to be called when
458
// the timer fires and aType is type of timer - one shot or repeating.
459
nsresult CreateTimer(const nsILookAndFeel::nsMetricID aID,
460
nsTimerCallbackFunc aFunc, PRInt32 aType,
463
static void OpenCallback(nsITimer *aTimer, void *aClosure);
465
static void CloseCallback(nsITimer *aTimer, void *aClosure);
467
static void LazyScrollCallback(nsITimer *aTimer, void *aClosure);
469
static void ScrollCallback(nsITimer *aTimer, void *aClosure);
471
protected: // Data Members
472
// Our cached pres context.
473
nsIPresContext* mPresContext;
475
// The cached box object parent.
476
nsCOMPtr<nsITreeBoxObject> mTreeBoxObject;
478
// The current view for this tree widget. We get all of our row and cell data
480
nsCOMPtr<nsITreeView> mView;
482
// A cache of all the style contexts we have seen for rows and cells of the tree. This is a mapping from
483
// a list of atoms to a corresponding style context. This cache stores every combination that
484
// occurs in the tree, so for n distinct properties, this cache could have 2 to the n entries
485
// (the power set of all row properties).
486
nsTreeStyleCache mStyleCache;
488
// A hashtable that maps from URLs to image requests. The URL is provided
489
// by the view or by the style context. The style context represents
490
// a resolved :-moz-tree-cell-image (or twisty) pseudo-element.
491
// It maps directly to an imgIRequest.
492
nsSupportsHashtable* mImageCache;
494
// Cached column information.
495
nsTreeColumn* mColumns;
497
// Our vertical scrollbar.
498
nsIFrame* mScrollbar;
501
nsCOMPtr<nsIWidget> mTreeWidget;
503
// The index of the first visible row and the # of rows visible onscreen.
504
// The tree only examines onscreen rows, starting from
505
// this index and going up to index+pageCount.
506
PRInt32 mTopRowIndex;
509
// Cached heights and indent info.
512
PRInt32 mIndentation;
513
nscoord mStringWidth;
515
// A scratch array used when looking up cached style contexts.
516
nsCOMPtr<nsISupportsArray> mScratchArray;
518
// Whether or not we're currently focused.
519
PRPackedBool mFocused;
521
// An indicator that columns have changed and need to be rebuilt
522
PRPackedBool mColumnsDirty;
524
// If the drop is actually allowed here or not.
525
PRPackedBool mDropAllowed;
527
// Do we have a fixed number of onscreen rows?
528
PRPackedBool mHasFixedRowCount;
530
PRPackedBool mVerticalOverflow;
532
// A guard that prevents us from recursive painting.
533
PRPackedBool mImageGuard;
535
PRPackedBool mReflowCallbackPosted;
537
// The row the mouse is hovering over during a drop.
540
// Where we want to draw feedback (above/on this row/below) if allowed.
543
// Number of lines to be scrolled.
544
PRInt16 mScrollLines;
546
nsCOMPtr<nsIDragSession> mDragSession;
548
// Timer for opening/closing spring loaded folders or scrolling the tree.
549
nsCOMPtr<nsITimer> mTimer;
551
// A value array used to keep track of all spring loaded folders.
552
nsValueArray mValueArray;
554
PRInt32 mUpdateBatchNest;
559
}; // class nsTreeBodyFrame