layout/xul/tree/nsTreeBodyFrame.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include "mozilla/AsyncEventDispatcher.h"
michael@0 7 #include "mozilla/ContentEvents.h"
michael@0 8 #include "mozilla/DebugOnly.h"
michael@0 9 #include "mozilla/EventDispatcher.h"
michael@0 10 #include "mozilla/MathAlgorithms.h"
michael@0 11 #include "mozilla/MouseEvents.h"
michael@0 12 #include "mozilla/Likely.h"
michael@0 13
michael@0 14 #include "nsCOMPtr.h"
michael@0 15 #include "nsPresContext.h"
michael@0 16 #include "nsNameSpaceManager.h"
michael@0 17
michael@0 18 #include "nsTreeBodyFrame.h"
michael@0 19 #include "nsTreeSelection.h"
michael@0 20 #include "nsTreeImageListener.h"
michael@0 21
michael@0 22 #include "nsGkAtoms.h"
michael@0 23 #include "nsCSSAnonBoxes.h"
michael@0 24
michael@0 25 #include "nsIContent.h"
michael@0 26 #include "nsStyleContext.h"
michael@0 27 #include "nsIBoxObject.h"
michael@0 28 #include "nsIDOMCustomEvent.h"
michael@0 29 #include "nsIDOMMouseEvent.h"
michael@0 30 #include "nsIDOMElement.h"
michael@0 31 #include "nsIDOMNodeList.h"
michael@0 32 #include "nsIDOMDocument.h"
michael@0 33 #include "nsIDOMXULElement.h"
michael@0 34 #include "nsIDocument.h"
michael@0 35 #include "mozilla/css/StyleRule.h"
michael@0 36 #include "nsCSSRendering.h"
michael@0 37 #include "nsIXULTemplateBuilder.h"
michael@0 38 #include "nsXPIDLString.h"
michael@0 39 #include "nsContainerFrame.h"
michael@0 40 #include "nsView.h"
michael@0 41 #include "nsViewManager.h"
michael@0 42 #include "nsWidgetsCID.h"
michael@0 43 #include "nsBoxFrame.h"
michael@0 44 #include "nsBoxObject.h"
michael@0 45 #include "nsIURL.h"
michael@0 46 #include "nsNetUtil.h"
michael@0 47 #include "nsBoxLayoutState.h"
michael@0 48 #include "nsTreeContentView.h"
michael@0 49 #include "nsTreeUtils.h"
michael@0 50 #include "nsITheme.h"
michael@0 51 #include "imgIRequest.h"
michael@0 52 #include "imgIContainer.h"
michael@0 53 #include "imgILoader.h"
michael@0 54 #include "nsINodeInfo.h"
michael@0 55 #include "nsContentUtils.h"
michael@0 56 #include "nsLayoutUtils.h"
michael@0 57 #include "nsIScrollableFrame.h"
michael@0 58 #include "nsDisplayList.h"
michael@0 59 #include "nsTreeBoxObject.h"
michael@0 60 #include "nsRenderingContext.h"
michael@0 61 #include "nsIScriptableRegion.h"
michael@0 62 #include <algorithm>
michael@0 63 #include "ScrollbarActivity.h"
michael@0 64
michael@0 65 #ifdef ACCESSIBILITY
michael@0 66 #include "nsAccessibilityService.h"
michael@0 67 #include "nsIWritablePropertyBag2.h"
michael@0 68 #endif
michael@0 69 #include "nsBidiUtils.h"
michael@0 70
michael@0 71 using namespace mozilla;
michael@0 72 using namespace mozilla::layout;
michael@0 73
michael@0 74 // Enumeration function that cancels all the image requests in our cache
michael@0 75 static PLDHashOperator
michael@0 76 CancelImageRequest(const nsAString& aKey,
michael@0 77 nsTreeImageCacheEntry aEntry, void* aData)
michael@0 78 {
michael@0 79
michael@0 80 // If our imgIRequest object was registered with the refresh driver,
michael@0 81 // then we need to deregister it.
michael@0 82 nsTreeBodyFrame* frame = static_cast<nsTreeBodyFrame*>(aData);
michael@0 83
michael@0 84 nsLayoutUtils::DeregisterImageRequest(frame->PresContext(), aEntry.request,
michael@0 85 nullptr);
michael@0 86
michael@0 87 aEntry.request->CancelAndForgetObserver(NS_BINDING_ABORTED);
michael@0 88 return PL_DHASH_NEXT;
michael@0 89 }
michael@0 90
michael@0 91 //
michael@0 92 // NS_NewTreeFrame
michael@0 93 //
michael@0 94 // Creates a new tree frame
michael@0 95 //
michael@0 96 nsIFrame*
michael@0 97 NS_NewTreeBodyFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
michael@0 98 {
michael@0 99 return new (aPresShell) nsTreeBodyFrame(aPresShell, aContext);
michael@0 100 }
michael@0 101
michael@0 102 NS_IMPL_FRAMEARENA_HELPERS(nsTreeBodyFrame)
michael@0 103
michael@0 104 NS_QUERYFRAME_HEAD(nsTreeBodyFrame)
michael@0 105 NS_QUERYFRAME_ENTRY(nsIScrollbarMediator)
michael@0 106 NS_QUERYFRAME_ENTRY(nsIScrollbarOwner)
michael@0 107 NS_QUERYFRAME_ENTRY(nsTreeBodyFrame)
michael@0 108 NS_QUERYFRAME_TAIL_INHERITING(nsLeafBoxFrame)
michael@0 109
michael@0 110 // Constructor
michael@0 111 nsTreeBodyFrame::nsTreeBodyFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
michael@0 112 :nsLeafBoxFrame(aPresShell, aContext),
michael@0 113 mSlots(nullptr),
michael@0 114 mImageCache(16),
michael@0 115 mTopRowIndex(0),
michael@0 116 mPageLength(0),
michael@0 117 mHorzPosition(0),
michael@0 118 mOriginalHorzWidth(-1),
michael@0 119 mHorzWidth(0),
michael@0 120 mAdjustWidth(0),
michael@0 121 mRowHeight(0),
michael@0 122 mIndentation(0),
michael@0 123 mStringWidth(-1),
michael@0 124 mUpdateBatchNest(0),
michael@0 125 mRowCount(0),
michael@0 126 mMouseOverRow(-1),
michael@0 127 mFocused(false),
michael@0 128 mHasFixedRowCount(false),
michael@0 129 mVerticalOverflow(false),
michael@0 130 mHorizontalOverflow(false),
michael@0 131 mReflowCallbackPosted(false),
michael@0 132 mCheckingOverflow(false)
michael@0 133 {
michael@0 134 mColumns = new nsTreeColumns(this);
michael@0 135 }
michael@0 136
michael@0 137 // Destructor
michael@0 138 nsTreeBodyFrame::~nsTreeBodyFrame()
michael@0 139 {
michael@0 140 mImageCache.EnumerateRead(CancelImageRequest, this);
michael@0 141 DetachImageListeners();
michael@0 142 delete mSlots;
michael@0 143 }
michael@0 144
michael@0 145 static void
michael@0 146 GetBorderPadding(nsStyleContext* aContext, nsMargin& aMargin)
michael@0 147 {
michael@0 148 aMargin.SizeTo(0, 0, 0, 0);
michael@0 149 if (!aContext->StylePadding()->GetPadding(aMargin)) {
michael@0 150 NS_NOTYETIMPLEMENTED("percentage padding");
michael@0 151 }
michael@0 152 aMargin += aContext->StyleBorder()->GetComputedBorder();
michael@0 153 }
michael@0 154
michael@0 155 static void
michael@0 156 AdjustForBorderPadding(nsStyleContext* aContext, nsRect& aRect)
michael@0 157 {
michael@0 158 nsMargin borderPadding(0, 0, 0, 0);
michael@0 159 GetBorderPadding(aContext, borderPadding);
michael@0 160 aRect.Deflate(borderPadding);
michael@0 161 }
michael@0 162
michael@0 163 void
michael@0 164 nsTreeBodyFrame::Init(nsIContent* aContent,
michael@0 165 nsIFrame* aParent,
michael@0 166 nsIFrame* aPrevInFlow)
michael@0 167 {
michael@0 168 nsLeafBoxFrame::Init(aContent, aParent, aPrevInFlow);
michael@0 169
michael@0 170 mIndentation = GetIndentation();
michael@0 171 mRowHeight = GetRowHeight();
michael@0 172
michael@0 173 EnsureBoxObject();
michael@0 174
michael@0 175 if (LookAndFeel::GetInt(LookAndFeel::eIntID_UseOverlayScrollbars) != 0) {
michael@0 176 mScrollbarActivity = new ScrollbarActivity(
michael@0 177 static_cast<nsIScrollbarOwner*>(this));
michael@0 178 }
michael@0 179 }
michael@0 180
michael@0 181 nsSize
michael@0 182 nsTreeBodyFrame::GetMinSize(nsBoxLayoutState& aBoxLayoutState)
michael@0 183 {
michael@0 184 EnsureView();
michael@0 185
michael@0 186 nsIContent* baseElement = GetBaseElement();
michael@0 187
michael@0 188 nsSize min(0,0);
michael@0 189 int32_t desiredRows;
michael@0 190 if (MOZ_UNLIKELY(!baseElement)) {
michael@0 191 desiredRows = 0;
michael@0 192 }
michael@0 193 else if (baseElement->Tag() == nsGkAtoms::select &&
michael@0 194 baseElement->IsHTML()) {
michael@0 195 min.width = CalcMaxRowWidth();
michael@0 196 nsAutoString size;
michael@0 197 baseElement->GetAttr(kNameSpaceID_None, nsGkAtoms::size, size);
michael@0 198 if (!size.IsEmpty()) {
michael@0 199 nsresult err;
michael@0 200 desiredRows = size.ToInteger(&err);
michael@0 201 mHasFixedRowCount = true;
michael@0 202 mPageLength = desiredRows;
michael@0 203 }
michael@0 204 else {
michael@0 205 desiredRows = 1;
michael@0 206 }
michael@0 207 }
michael@0 208 else {
michael@0 209 // tree
michael@0 210 nsAutoString rows;
michael@0 211 baseElement->GetAttr(kNameSpaceID_None, nsGkAtoms::rows, rows);
michael@0 212 if (!rows.IsEmpty()) {
michael@0 213 nsresult err;
michael@0 214 desiredRows = rows.ToInteger(&err);
michael@0 215 mPageLength = desiredRows;
michael@0 216 }
michael@0 217 else {
michael@0 218 desiredRows = 0;
michael@0 219 }
michael@0 220 }
michael@0 221
michael@0 222 min.height = mRowHeight * desiredRows;
michael@0 223
michael@0 224 AddBorderAndPadding(min);
michael@0 225 bool widthSet, heightSet;
michael@0 226 nsIFrame::AddCSSMinSize(aBoxLayoutState, this, min, widthSet, heightSet);
michael@0 227
michael@0 228 return min;
michael@0 229 }
michael@0 230
michael@0 231 nscoord
michael@0 232 nsTreeBodyFrame::CalcMaxRowWidth()
michael@0 233 {
michael@0 234 if (mStringWidth != -1)
michael@0 235 return mStringWidth;
michael@0 236
michael@0 237 if (!mView)
michael@0 238 return 0;
michael@0 239
michael@0 240 nsStyleContext* rowContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreerow);
michael@0 241 nsMargin rowMargin(0,0,0,0);
michael@0 242 GetBorderPadding(rowContext, rowMargin);
michael@0 243
michael@0 244 nscoord rowWidth;
michael@0 245 nsTreeColumn* col;
michael@0 246
michael@0 247 nsRefPtr<nsRenderingContext> rc =
michael@0 248 PresContext()->PresShell()->CreateReferenceRenderingContext();
michael@0 249
michael@0 250 for (int32_t row = 0; row < mRowCount; ++row) {
michael@0 251 rowWidth = 0;
michael@0 252
michael@0 253 for (col = mColumns->GetFirstColumn(); col; col = col->GetNext()) {
michael@0 254 nscoord desiredWidth, currentWidth;
michael@0 255 nsresult rv = GetCellWidth(row, col, rc, desiredWidth, currentWidth);
michael@0 256 if (NS_FAILED(rv)) {
michael@0 257 NS_NOTREACHED("invalid column");
michael@0 258 continue;
michael@0 259 }
michael@0 260 rowWidth += desiredWidth;
michael@0 261 }
michael@0 262
michael@0 263 if (rowWidth > mStringWidth)
michael@0 264 mStringWidth = rowWidth;
michael@0 265 }
michael@0 266
michael@0 267 mStringWidth += rowMargin.left + rowMargin.right;
michael@0 268 return mStringWidth;
michael@0 269 }
michael@0 270
michael@0 271 void
michael@0 272 nsTreeBodyFrame::DestroyFrom(nsIFrame* aDestructRoot)
michael@0 273 {
michael@0 274 if (mScrollbarActivity) {
michael@0 275 mScrollbarActivity->Destroy();
michael@0 276 mScrollbarActivity = nullptr;
michael@0 277 }
michael@0 278
michael@0 279 mScrollEvent.Revoke();
michael@0 280 // Make sure we cancel any posted callbacks.
michael@0 281 if (mReflowCallbackPosted) {
michael@0 282 PresContext()->PresShell()->CancelReflowCallback(this);
michael@0 283 mReflowCallbackPosted = false;
michael@0 284 }
michael@0 285
michael@0 286 if (mColumns)
michael@0 287 mColumns->SetTree(nullptr);
michael@0 288
michael@0 289 // Save off our info into the box object.
michael@0 290 nsCOMPtr<nsPIBoxObject> box(do_QueryInterface(mTreeBoxObject));
michael@0 291 if (box) {
michael@0 292 if (mTopRowIndex > 0) {
michael@0 293 nsAutoString topRowStr; topRowStr.AssignLiteral("topRow");
michael@0 294 nsAutoString topRow;
michael@0 295 topRow.AppendInt(mTopRowIndex);
michael@0 296 box->SetProperty(topRowStr.get(), topRow.get());
michael@0 297 }
michael@0 298
michael@0 299 // Always null out the cached tree body frame.
michael@0 300 box->ClearCachedValues();
michael@0 301
michael@0 302 mTreeBoxObject = nullptr; // Drop our ref here.
michael@0 303 }
michael@0 304
michael@0 305 if (mView) {
michael@0 306 nsCOMPtr<nsITreeSelection> sel;
michael@0 307 mView->GetSelection(getter_AddRefs(sel));
michael@0 308 if (sel)
michael@0 309 sel->SetTree(nullptr);
michael@0 310 mView->SetTree(nullptr);
michael@0 311 mView = nullptr;
michael@0 312 }
michael@0 313
michael@0 314 nsLeafBoxFrame::DestroyFrom(aDestructRoot);
michael@0 315 }
michael@0 316
michael@0 317 void
michael@0 318 nsTreeBodyFrame::EnsureBoxObject()
michael@0 319 {
michael@0 320 if (!mTreeBoxObject) {
michael@0 321 nsIContent* parent = GetBaseElement();
michael@0 322 if (parent) {
michael@0 323 nsIDocument* nsDoc = parent->GetDocument();
michael@0 324 if (!nsDoc) // there may be no document, if we're called from Destroy()
michael@0 325 return;
michael@0 326 ErrorResult ignored;
michael@0 327 nsCOMPtr<nsIBoxObject> box =
michael@0 328 nsDoc->GetBoxObjectFor(parent->AsElement(), ignored);
michael@0 329 // Ensure that we got a native box object.
michael@0 330 nsCOMPtr<nsPIBoxObject> pBox = do_QueryInterface(box);
michael@0 331 if (pBox) {
michael@0 332 nsCOMPtr<nsITreeBoxObject> realTreeBoxObject = do_QueryInterface(pBox);
michael@0 333 if (realTreeBoxObject) {
michael@0 334 nsTreeBodyFrame* innerTreeBoxObject =
michael@0 335 static_cast<nsTreeBoxObject*>(realTreeBoxObject.get())
michael@0 336 ->GetCachedTreeBody();
michael@0 337 ENSURE_TRUE(!innerTreeBoxObject || innerTreeBoxObject == this);
michael@0 338 mTreeBoxObject = realTreeBoxObject;
michael@0 339 }
michael@0 340 }
michael@0 341 }
michael@0 342 }
michael@0 343 }
michael@0 344
michael@0 345 void
michael@0 346 nsTreeBodyFrame::EnsureView()
michael@0 347 {
michael@0 348 if (!mView) {
michael@0 349 if (PresContext()->PresShell()->IsReflowLocked()) {
michael@0 350 if (!mReflowCallbackPosted) {
michael@0 351 mReflowCallbackPosted = true;
michael@0 352 PresContext()->PresShell()->PostReflowCallback(this);
michael@0 353 }
michael@0 354 return;
michael@0 355 }
michael@0 356 nsCOMPtr<nsIBoxObject> box = do_QueryInterface(mTreeBoxObject);
michael@0 357 if (box) {
michael@0 358 nsWeakFrame weakFrame(this);
michael@0 359 nsCOMPtr<nsITreeView> treeView;
michael@0 360 mTreeBoxObject->GetView(getter_AddRefs(treeView));
michael@0 361 if (treeView && weakFrame.IsAlive()) {
michael@0 362 nsXPIDLString rowStr;
michael@0 363 box->GetProperty(MOZ_UTF16("topRow"),
michael@0 364 getter_Copies(rowStr));
michael@0 365 nsAutoString rowStr2(rowStr);
michael@0 366 nsresult error;
michael@0 367 int32_t rowIndex = rowStr2.ToInteger(&error);
michael@0 368
michael@0 369 // Set our view.
michael@0 370 SetView(treeView);
michael@0 371 ENSURE_TRUE(weakFrame.IsAlive());
michael@0 372
michael@0 373 // Scroll to the given row.
michael@0 374 // XXX is this optimal if we haven't laid out yet?
michael@0 375 ScrollToRow(rowIndex);
michael@0 376 ENSURE_TRUE(weakFrame.IsAlive());
michael@0 377
michael@0 378 // Clear out the property info for the top row, but we always keep the
michael@0 379 // view current.
michael@0 380 box->RemoveProperty(MOZ_UTF16("topRow"));
michael@0 381 }
michael@0 382 }
michael@0 383 }
michael@0 384 }
michael@0 385
michael@0 386 void
michael@0 387 nsTreeBodyFrame::ManageReflowCallback(const nsRect& aRect, nscoord aHorzWidth)
michael@0 388 {
michael@0 389 if (!mReflowCallbackPosted &&
michael@0 390 (!aRect.IsEqualEdges(mRect) || mHorzWidth != aHorzWidth)) {
michael@0 391 PresContext()->PresShell()->PostReflowCallback(this);
michael@0 392 mReflowCallbackPosted = true;
michael@0 393 mOriginalHorzWidth = mHorzWidth;
michael@0 394 }
michael@0 395 else if (mReflowCallbackPosted &&
michael@0 396 mHorzWidth != aHorzWidth && mOriginalHorzWidth == aHorzWidth) {
michael@0 397 PresContext()->PresShell()->CancelReflowCallback(this);
michael@0 398 mReflowCallbackPosted = false;
michael@0 399 mOriginalHorzWidth = -1;
michael@0 400 }
michael@0 401 }
michael@0 402
michael@0 403 void
michael@0 404 nsTreeBodyFrame::SetBounds(nsBoxLayoutState& aBoxLayoutState, const nsRect& aRect,
michael@0 405 bool aRemoveOverflowArea)
michael@0 406 {
michael@0 407 nscoord horzWidth = CalcHorzWidth(GetScrollParts());
michael@0 408 ManageReflowCallback(aRect, horzWidth);
michael@0 409 mHorzWidth = horzWidth;
michael@0 410
michael@0 411 nsLeafBoxFrame::SetBounds(aBoxLayoutState, aRect, aRemoveOverflowArea);
michael@0 412 }
michael@0 413
michael@0 414
michael@0 415 bool
michael@0 416 nsTreeBodyFrame::ReflowFinished()
michael@0 417 {
michael@0 418 if (!mView) {
michael@0 419 nsWeakFrame weakFrame(this);
michael@0 420 EnsureView();
michael@0 421 NS_ENSURE_TRUE(weakFrame.IsAlive(), false);
michael@0 422 }
michael@0 423 if (mView) {
michael@0 424 CalcInnerBox();
michael@0 425 ScrollParts parts = GetScrollParts();
michael@0 426 mHorzWidth = CalcHorzWidth(parts);
michael@0 427 if (!mHasFixedRowCount) {
michael@0 428 mPageLength = mInnerBox.height / mRowHeight;
michael@0 429 }
michael@0 430
michael@0 431 int32_t lastPageTopRow = std::max(0, mRowCount - mPageLength);
michael@0 432 if (mTopRowIndex > lastPageTopRow)
michael@0 433 ScrollToRowInternal(parts, lastPageTopRow);
michael@0 434
michael@0 435 nsIContent *treeContent = GetBaseElement();
michael@0 436 if (treeContent &&
michael@0 437 treeContent->AttrValueIs(kNameSpaceID_None,
michael@0 438 nsGkAtoms::keepcurrentinview,
michael@0 439 nsGkAtoms::_true, eCaseMatters)) {
michael@0 440 // make sure that the current selected item is still
michael@0 441 // visible after the tree changes size.
michael@0 442 nsCOMPtr<nsITreeSelection> sel;
michael@0 443 mView->GetSelection(getter_AddRefs(sel));
michael@0 444 if (sel) {
michael@0 445 int32_t currentIndex;
michael@0 446 sel->GetCurrentIndex(&currentIndex);
michael@0 447 if (currentIndex != -1)
michael@0 448 EnsureRowIsVisibleInternal(parts, currentIndex);
michael@0 449 }
michael@0 450 }
michael@0 451
michael@0 452 if (!FullScrollbarsUpdate(false)) {
michael@0 453 return false;
michael@0 454 }
michael@0 455 }
michael@0 456
michael@0 457 mReflowCallbackPosted = false;
michael@0 458 return false;
michael@0 459 }
michael@0 460
michael@0 461 void
michael@0 462 nsTreeBodyFrame::ReflowCallbackCanceled()
michael@0 463 {
michael@0 464 mReflowCallbackPosted = false;
michael@0 465 }
michael@0 466
michael@0 467 nsresult
michael@0 468 nsTreeBodyFrame::GetView(nsITreeView * *aView)
michael@0 469 {
michael@0 470 *aView = nullptr;
michael@0 471 nsWeakFrame weakFrame(this);
michael@0 472 EnsureView();
michael@0 473 NS_ENSURE_STATE(weakFrame.IsAlive());
michael@0 474 NS_IF_ADDREF(*aView = mView);
michael@0 475 return NS_OK;
michael@0 476 }
michael@0 477
michael@0 478 nsresult
michael@0 479 nsTreeBodyFrame::SetView(nsITreeView * aView)
michael@0 480 {
michael@0 481 // First clear out the old view.
michael@0 482 if (mView) {
michael@0 483 nsCOMPtr<nsITreeSelection> sel;
michael@0 484 mView->GetSelection(getter_AddRefs(sel));
michael@0 485 if (sel)
michael@0 486 sel->SetTree(nullptr);
michael@0 487 mView->SetTree(nullptr);
michael@0 488
michael@0 489 // Only reset the top row index and delete the columns if we had an old non-null view.
michael@0 490 mTopRowIndex = 0;
michael@0 491 }
michael@0 492
michael@0 493 // Tree, meet the view.
michael@0 494 mView = aView;
michael@0 495
michael@0 496 // Changing the view causes us to refetch our data. This will
michael@0 497 // necessarily entail a full invalidation of the tree.
michael@0 498 Invalidate();
michael@0 499
michael@0 500 nsIContent *treeContent = GetBaseElement();
michael@0 501 if (treeContent) {
michael@0 502 #ifdef ACCESSIBILITY
michael@0 503 nsAccessibilityService* accService = nsIPresShell::AccService();
michael@0 504 if (accService)
michael@0 505 accService->TreeViewChanged(PresContext()->GetPresShell(), treeContent, mView);
michael@0 506 #endif
michael@0 507 FireDOMEvent(NS_LITERAL_STRING("TreeViewChanged"), treeContent);
michael@0 508 }
michael@0 509
michael@0 510 if (mView) {
michael@0 511 // Give the view a new empty selection object to play with, but only if it
michael@0 512 // doesn't have one already.
michael@0 513 nsCOMPtr<nsITreeSelection> sel;
michael@0 514 mView->GetSelection(getter_AddRefs(sel));
michael@0 515 if (sel) {
michael@0 516 sel->SetTree(mTreeBoxObject);
michael@0 517 }
michael@0 518 else {
michael@0 519 NS_NewTreeSelection(mTreeBoxObject, getter_AddRefs(sel));
michael@0 520 mView->SetSelection(sel);
michael@0 521 }
michael@0 522
michael@0 523 // View, meet the tree.
michael@0 524 nsWeakFrame weakFrame(this);
michael@0 525 mView->SetTree(mTreeBoxObject);
michael@0 526 NS_ENSURE_STATE(weakFrame.IsAlive());
michael@0 527 mView->GetRowCount(&mRowCount);
michael@0 528
michael@0 529 if (!PresContext()->PresShell()->IsReflowLocked()) {
michael@0 530 // The scrollbar will need to be updated.
michael@0 531 FullScrollbarsUpdate(false);
michael@0 532 } else if (!mReflowCallbackPosted) {
michael@0 533 mReflowCallbackPosted = true;
michael@0 534 PresContext()->PresShell()->PostReflowCallback(this);
michael@0 535 }
michael@0 536 }
michael@0 537
michael@0 538 return NS_OK;
michael@0 539 }
michael@0 540
michael@0 541 nsresult
michael@0 542 nsTreeBodyFrame::GetFocused(bool* aFocused)
michael@0 543 {
michael@0 544 *aFocused = mFocused;
michael@0 545 return NS_OK;
michael@0 546 }
michael@0 547
michael@0 548 nsresult
michael@0 549 nsTreeBodyFrame::SetFocused(bool aFocused)
michael@0 550 {
michael@0 551 if (mFocused != aFocused) {
michael@0 552 mFocused = aFocused;
michael@0 553 if (mView) {
michael@0 554 nsCOMPtr<nsITreeSelection> sel;
michael@0 555 mView->GetSelection(getter_AddRefs(sel));
michael@0 556 if (sel)
michael@0 557 sel->InvalidateSelection();
michael@0 558 }
michael@0 559 }
michael@0 560 return NS_OK;
michael@0 561 }
michael@0 562
michael@0 563 nsresult
michael@0 564 nsTreeBodyFrame::GetTreeBody(nsIDOMElement** aElement)
michael@0 565 {
michael@0 566 //NS_ASSERTION(mContent, "no content, see bug #104878");
michael@0 567 if (!mContent)
michael@0 568 return NS_ERROR_NULL_POINTER;
michael@0 569
michael@0 570 return mContent->QueryInterface(NS_GET_IID(nsIDOMElement), (void**)aElement);
michael@0 571 }
michael@0 572
michael@0 573 nsresult
michael@0 574 nsTreeBodyFrame::GetRowHeight(int32_t* _retval)
michael@0 575 {
michael@0 576 *_retval = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight);
michael@0 577 return NS_OK;
michael@0 578 }
michael@0 579
michael@0 580 nsresult
michael@0 581 nsTreeBodyFrame::GetRowWidth(int32_t *aRowWidth)
michael@0 582 {
michael@0 583 *aRowWidth = nsPresContext::AppUnitsToIntCSSPixels(CalcHorzWidth(GetScrollParts()));
michael@0 584 return NS_OK;
michael@0 585 }
michael@0 586
michael@0 587 nsresult
michael@0 588 nsTreeBodyFrame::GetHorizontalPosition(int32_t *aHorizontalPosition)
michael@0 589 {
michael@0 590 *aHorizontalPosition = nsPresContext::AppUnitsToIntCSSPixels(mHorzPosition);
michael@0 591 return NS_OK;
michael@0 592 }
michael@0 593
michael@0 594 nsresult
michael@0 595 nsTreeBodyFrame::GetSelectionRegion(nsIScriptableRegion **aRegion)
michael@0 596 {
michael@0 597 *aRegion = nullptr;
michael@0 598
michael@0 599 nsCOMPtr<nsITreeSelection> selection;
michael@0 600 mView->GetSelection(getter_AddRefs(selection));
michael@0 601 NS_ENSURE_TRUE(selection, NS_OK);
michael@0 602
michael@0 603 nsCOMPtr<nsIScriptableRegion> region = do_CreateInstance("@mozilla.org/gfx/region;1");
michael@0 604 NS_ENSURE_TRUE(region, NS_ERROR_FAILURE);
michael@0 605 region->Init();
michael@0 606
michael@0 607 nsRefPtr<nsPresContext> presContext = PresContext();
michael@0 608 nsIntRect rect = mRect.ToOutsidePixels(presContext->AppUnitsPerCSSPixel());
michael@0 609
michael@0 610 nsIFrame* rootFrame = presContext->PresShell()->GetRootFrame();
michael@0 611 nsPoint origin = GetOffsetTo(rootFrame);
michael@0 612
michael@0 613 // iterate through the visible rows and add the selected ones to the
michael@0 614 // drag region
michael@0 615 int32_t x = nsPresContext::AppUnitsToIntCSSPixels(origin.x);
michael@0 616 int32_t y = nsPresContext::AppUnitsToIntCSSPixels(origin.y);
michael@0 617 int32_t top = y;
michael@0 618 int32_t end = LastVisibleRow();
michael@0 619 int32_t rowHeight = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight);
michael@0 620 for (int32_t i = mTopRowIndex; i <= end; i++) {
michael@0 621 bool isSelected;
michael@0 622 selection->IsSelected(i, &isSelected);
michael@0 623 if (isSelected)
michael@0 624 region->UnionRect(x, y, rect.width, rowHeight);
michael@0 625 y += rowHeight;
michael@0 626 }
michael@0 627
michael@0 628 // clip to the tree boundary in case one row extends past it
michael@0 629 region->IntersectRect(x, top, rect.width, rect.height);
michael@0 630
michael@0 631 NS_ADDREF(*aRegion = region);
michael@0 632 return NS_OK;
michael@0 633 }
michael@0 634
michael@0 635 nsresult
michael@0 636 nsTreeBodyFrame::Invalidate()
michael@0 637 {
michael@0 638 if (mUpdateBatchNest)
michael@0 639 return NS_OK;
michael@0 640
michael@0 641 InvalidateFrame();
michael@0 642
michael@0 643 return NS_OK;
michael@0 644 }
michael@0 645
michael@0 646 nsresult
michael@0 647 nsTreeBodyFrame::InvalidateColumn(nsITreeColumn* aCol)
michael@0 648 {
michael@0 649 if (mUpdateBatchNest)
michael@0 650 return NS_OK;
michael@0 651
michael@0 652 nsRefPtr<nsTreeColumn> col = GetColumnImpl(aCol);
michael@0 653 if (!col)
michael@0 654 return NS_ERROR_INVALID_ARG;
michael@0 655
michael@0 656 #ifdef ACCESSIBILITY
michael@0 657 if (nsIPresShell::IsAccessibilityActive())
michael@0 658 FireInvalidateEvent(-1, -1, aCol, aCol);
michael@0 659 #endif
michael@0 660
michael@0 661 nsRect columnRect;
michael@0 662 nsresult rv = col->GetRect(this, mInnerBox.y, mInnerBox.height, &columnRect);
michael@0 663 NS_ENSURE_SUCCESS(rv, rv);
michael@0 664
michael@0 665 // When false then column is out of view
michael@0 666 if (OffsetForHorzScroll(columnRect, true))
michael@0 667 InvalidateFrameWithRect(columnRect);
michael@0 668
michael@0 669 return NS_OK;
michael@0 670 }
michael@0 671
michael@0 672 nsresult
michael@0 673 nsTreeBodyFrame::InvalidateRow(int32_t aIndex)
michael@0 674 {
michael@0 675 if (mUpdateBatchNest)
michael@0 676 return NS_OK;
michael@0 677
michael@0 678 #ifdef ACCESSIBILITY
michael@0 679 if (nsIPresShell::IsAccessibilityActive())
michael@0 680 FireInvalidateEvent(aIndex, aIndex, nullptr, nullptr);
michael@0 681 #endif
michael@0 682
michael@0 683 aIndex -= mTopRowIndex;
michael@0 684 if (aIndex < 0 || aIndex > mPageLength)
michael@0 685 return NS_OK;
michael@0 686
michael@0 687 nsRect rowRect(mInnerBox.x, mInnerBox.y+mRowHeight*aIndex, mInnerBox.width, mRowHeight);
michael@0 688 InvalidateFrameWithRect(rowRect);
michael@0 689
michael@0 690 return NS_OK;
michael@0 691 }
michael@0 692
michael@0 693 nsresult
michael@0 694 nsTreeBodyFrame::InvalidateCell(int32_t aIndex, nsITreeColumn* aCol)
michael@0 695 {
michael@0 696 if (mUpdateBatchNest)
michael@0 697 return NS_OK;
michael@0 698
michael@0 699 #ifdef ACCESSIBILITY
michael@0 700 if (nsIPresShell::IsAccessibilityActive())
michael@0 701 FireInvalidateEvent(aIndex, aIndex, aCol, aCol);
michael@0 702 #endif
michael@0 703
michael@0 704 aIndex -= mTopRowIndex;
michael@0 705 if (aIndex < 0 || aIndex > mPageLength)
michael@0 706 return NS_OK;
michael@0 707
michael@0 708 nsRefPtr<nsTreeColumn> col = GetColumnImpl(aCol);
michael@0 709 if (!col)
michael@0 710 return NS_ERROR_INVALID_ARG;
michael@0 711
michael@0 712 nsRect cellRect;
michael@0 713 nsresult rv = col->GetRect(this, mInnerBox.y+mRowHeight*aIndex, mRowHeight,
michael@0 714 &cellRect);
michael@0 715 NS_ENSURE_SUCCESS(rv, rv);
michael@0 716
michael@0 717 if (OffsetForHorzScroll(cellRect, true))
michael@0 718 InvalidateFrameWithRect(cellRect);
michael@0 719
michael@0 720 return NS_OK;
michael@0 721 }
michael@0 722
michael@0 723 nsresult
michael@0 724 nsTreeBodyFrame::InvalidateRange(int32_t aStart, int32_t aEnd)
michael@0 725 {
michael@0 726 if (mUpdateBatchNest)
michael@0 727 return NS_OK;
michael@0 728
michael@0 729 if (aStart == aEnd)
michael@0 730 return InvalidateRow(aStart);
michael@0 731
michael@0 732 int32_t last = LastVisibleRow();
michael@0 733 if (aStart > aEnd || aEnd < mTopRowIndex || aStart > last)
michael@0 734 return NS_OK;
michael@0 735
michael@0 736 if (aStart < mTopRowIndex)
michael@0 737 aStart = mTopRowIndex;
michael@0 738
michael@0 739 if (aEnd > last)
michael@0 740 aEnd = last;
michael@0 741
michael@0 742 #ifdef ACCESSIBILITY
michael@0 743 if (nsIPresShell::IsAccessibilityActive()) {
michael@0 744 int32_t end =
michael@0 745 mRowCount > 0 ? ((mRowCount <= aEnd) ? mRowCount - 1 : aEnd) : 0;
michael@0 746 FireInvalidateEvent(aStart, end, nullptr, nullptr);
michael@0 747 }
michael@0 748 #endif
michael@0 749
michael@0 750 nsRect rangeRect(mInnerBox.x, mInnerBox.y+mRowHeight*(aStart-mTopRowIndex), mInnerBox.width, mRowHeight*(aEnd-aStart+1));
michael@0 751 InvalidateFrameWithRect(rangeRect);
michael@0 752
michael@0 753 return NS_OK;
michael@0 754 }
michael@0 755
michael@0 756 nsresult
michael@0 757 nsTreeBodyFrame::InvalidateColumnRange(int32_t aStart, int32_t aEnd, nsITreeColumn* aCol)
michael@0 758 {
michael@0 759 if (mUpdateBatchNest)
michael@0 760 return NS_OK;
michael@0 761
michael@0 762 nsRefPtr<nsTreeColumn> col = GetColumnImpl(aCol);
michael@0 763 if (!col)
michael@0 764 return NS_ERROR_INVALID_ARG;
michael@0 765
michael@0 766 if (aStart == aEnd)
michael@0 767 return InvalidateCell(aStart, col);
michael@0 768
michael@0 769 int32_t last = LastVisibleRow();
michael@0 770 if (aStart > aEnd || aEnd < mTopRowIndex || aStart > last)
michael@0 771 return NS_OK;
michael@0 772
michael@0 773 if (aStart < mTopRowIndex)
michael@0 774 aStart = mTopRowIndex;
michael@0 775
michael@0 776 if (aEnd > last)
michael@0 777 aEnd = last;
michael@0 778
michael@0 779 #ifdef ACCESSIBILITY
michael@0 780 if (nsIPresShell::IsAccessibilityActive()) {
michael@0 781 int32_t end =
michael@0 782 mRowCount > 0 ? ((mRowCount <= aEnd) ? mRowCount - 1 : aEnd) : 0;
michael@0 783 FireInvalidateEvent(aStart, end, aCol, aCol);
michael@0 784 }
michael@0 785 #endif
michael@0 786
michael@0 787 nsRect rangeRect;
michael@0 788 nsresult rv = col->GetRect(this,
michael@0 789 mInnerBox.y+mRowHeight*(aStart-mTopRowIndex),
michael@0 790 mRowHeight*(aEnd-aStart+1),
michael@0 791 &rangeRect);
michael@0 792 NS_ENSURE_SUCCESS(rv, rv);
michael@0 793
michael@0 794 InvalidateFrameWithRect(rangeRect);
michael@0 795
michael@0 796 return NS_OK;
michael@0 797 }
michael@0 798
michael@0 799 static void
michael@0 800 FindScrollParts(nsIFrame* aCurrFrame, nsTreeBodyFrame::ScrollParts* aResult)
michael@0 801 {
michael@0 802 if (!aResult->mColumnsScrollFrame) {
michael@0 803 nsIScrollableFrame* f = do_QueryFrame(aCurrFrame);
michael@0 804 if (f) {
michael@0 805 aResult->mColumnsFrame = aCurrFrame;
michael@0 806 aResult->mColumnsScrollFrame = f;
michael@0 807 }
michael@0 808 }
michael@0 809
michael@0 810 nsScrollbarFrame *sf = do_QueryFrame(aCurrFrame);
michael@0 811 if (sf) {
michael@0 812 if (!aCurrFrame->IsHorizontal()) {
michael@0 813 if (!aResult->mVScrollbar) {
michael@0 814 aResult->mVScrollbar = sf;
michael@0 815 }
michael@0 816 } else {
michael@0 817 if (!aResult->mHScrollbar) {
michael@0 818 aResult->mHScrollbar = sf;
michael@0 819 }
michael@0 820 }
michael@0 821 // don't bother searching inside a scrollbar
michael@0 822 return;
michael@0 823 }
michael@0 824
michael@0 825 nsIFrame* child = aCurrFrame->GetFirstPrincipalChild();
michael@0 826 while (child &&
michael@0 827 !child->GetContent()->IsRootOfNativeAnonymousSubtree() &&
michael@0 828 (!aResult->mVScrollbar || !aResult->mHScrollbar ||
michael@0 829 !aResult->mColumnsScrollFrame)) {
michael@0 830 FindScrollParts(child, aResult);
michael@0 831 child = child->GetNextSibling();
michael@0 832 }
michael@0 833 }
michael@0 834
michael@0 835 nsTreeBodyFrame::ScrollParts nsTreeBodyFrame::GetScrollParts()
michael@0 836 {
michael@0 837 ScrollParts result = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr };
michael@0 838 nsIContent* baseElement = GetBaseElement();
michael@0 839 nsIFrame* treeFrame =
michael@0 840 baseElement ? baseElement->GetPrimaryFrame() : nullptr;
michael@0 841 if (treeFrame) {
michael@0 842 // The way we do this, searching through the entire frame subtree, is pretty
michael@0 843 // dumb! We should know where these frames are.
michael@0 844 FindScrollParts(treeFrame, &result);
michael@0 845 if (result.mHScrollbar) {
michael@0 846 result.mHScrollbar->SetScrollbarMediatorContent(GetContent());
michael@0 847 nsIFrame* f = do_QueryFrame(result.mHScrollbar);
michael@0 848 result.mHScrollbarContent = f->GetContent();
michael@0 849 }
michael@0 850 if (result.mVScrollbar) {
michael@0 851 result.mVScrollbar->SetScrollbarMediatorContent(GetContent());
michael@0 852 nsIFrame* f = do_QueryFrame(result.mVScrollbar);
michael@0 853 result.mVScrollbarContent = f->GetContent();
michael@0 854 }
michael@0 855 }
michael@0 856 return result;
michael@0 857 }
michael@0 858
michael@0 859 void
michael@0 860 nsTreeBodyFrame::UpdateScrollbars(const ScrollParts& aParts)
michael@0 861 {
michael@0 862 nscoord rowHeightAsPixels = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight);
michael@0 863
michael@0 864 nsWeakFrame weakFrame(this);
michael@0 865
michael@0 866 if (aParts.mVScrollbar) {
michael@0 867 nsAutoString curPos;
michael@0 868 curPos.AppendInt(mTopRowIndex*rowHeightAsPixels);
michael@0 869 aParts.mVScrollbarContent->
michael@0 870 SetAttr(kNameSpaceID_None, nsGkAtoms::curpos, curPos, true);
michael@0 871 // 'this' might be deleted here
michael@0 872 }
michael@0 873
michael@0 874 if (weakFrame.IsAlive() && aParts.mHScrollbar) {
michael@0 875 nsAutoString curPos;
michael@0 876 curPos.AppendInt(mHorzPosition);
michael@0 877 aParts.mHScrollbarContent->
michael@0 878 SetAttr(kNameSpaceID_None, nsGkAtoms::curpos, curPos, true);
michael@0 879 // 'this' might be deleted here
michael@0 880 }
michael@0 881
michael@0 882 if (weakFrame.IsAlive() && mScrollbarActivity) {
michael@0 883 mScrollbarActivity->ActivityOccurred();
michael@0 884 }
michael@0 885 }
michael@0 886
michael@0 887 void
michael@0 888 nsTreeBodyFrame::CheckOverflow(const ScrollParts& aParts)
michael@0 889 {
michael@0 890 bool verticalOverflowChanged = false;
michael@0 891 bool horizontalOverflowChanged = false;
michael@0 892
michael@0 893 if (!mVerticalOverflow && mRowCount > mPageLength) {
michael@0 894 mVerticalOverflow = true;
michael@0 895 verticalOverflowChanged = true;
michael@0 896 }
michael@0 897 else if (mVerticalOverflow && mRowCount <= mPageLength) {
michael@0 898 mVerticalOverflow = false;
michael@0 899 verticalOverflowChanged = true;
michael@0 900 }
michael@0 901
michael@0 902 if (aParts.mColumnsFrame) {
michael@0 903 nsRect bounds = aParts.mColumnsFrame->GetRect();
michael@0 904 if (bounds.width != 0) {
michael@0 905 /* Ignore overflows that are less than half a pixel. Yes these happen
michael@0 906 all over the place when flex boxes are compressed real small.
michael@0 907 Probably a result of a rounding errors somewhere in the layout code. */
michael@0 908 bounds.width += nsPresContext::CSSPixelsToAppUnits(0.5f);
michael@0 909 if (!mHorizontalOverflow && bounds.width < mHorzWidth) {
michael@0 910 mHorizontalOverflow = true;
michael@0 911 horizontalOverflowChanged = true;
michael@0 912 } else if (mHorizontalOverflow && bounds.width >= mHorzWidth) {
michael@0 913 mHorizontalOverflow = false;
michael@0 914 horizontalOverflowChanged = true;
michael@0 915 }
michael@0 916 }
michael@0 917 }
michael@0 918
michael@0 919 nsWeakFrame weakFrame(this);
michael@0 920
michael@0 921 nsRefPtr<nsPresContext> presContext = PresContext();
michael@0 922 nsCOMPtr<nsIPresShell> presShell = presContext->GetPresShell();
michael@0 923 nsCOMPtr<nsIContent> content = mContent;
michael@0 924
michael@0 925 if (verticalOverflowChanged) {
michael@0 926 InternalScrollPortEvent event(true,
michael@0 927 mVerticalOverflow ? NS_SCROLLPORT_OVERFLOW : NS_SCROLLPORT_UNDERFLOW,
michael@0 928 nullptr);
michael@0 929 event.orient = InternalScrollPortEvent::vertical;
michael@0 930 EventDispatcher::Dispatch(content, presContext, &event);
michael@0 931 }
michael@0 932
michael@0 933 if (horizontalOverflowChanged) {
michael@0 934 InternalScrollPortEvent event(true,
michael@0 935 mHorizontalOverflow ? NS_SCROLLPORT_OVERFLOW : NS_SCROLLPORT_UNDERFLOW,
michael@0 936 nullptr);
michael@0 937 event.orient = InternalScrollPortEvent::horizontal;
michael@0 938 EventDispatcher::Dispatch(content, presContext, &event);
michael@0 939 }
michael@0 940
michael@0 941 // The synchronous event dispatch above can trigger reflow notifications.
michael@0 942 // Flush those explicitly now, so that we can guard against potential infinite
michael@0 943 // recursion. See bug 905909.
michael@0 944 if (!weakFrame.IsAlive()) {
michael@0 945 return;
michael@0 946 }
michael@0 947 NS_ASSERTION(!mCheckingOverflow, "mCheckingOverflow should not already be set");
michael@0 948 // Don't use AutoRestore since we want to not touch mCheckingOverflow if we fail
michael@0 949 // the weakFrame.IsAlive() check below
michael@0 950 mCheckingOverflow = true;
michael@0 951 presShell->FlushPendingNotifications(Flush_Layout);
michael@0 952 if (!weakFrame.IsAlive()) {
michael@0 953 return;
michael@0 954 }
michael@0 955 mCheckingOverflow = false;
michael@0 956 }
michael@0 957
michael@0 958 void
michael@0 959 nsTreeBodyFrame::InvalidateScrollbars(const ScrollParts& aParts, nsWeakFrame& aWeakColumnsFrame)
michael@0 960 {
michael@0 961 if (mUpdateBatchNest || !mView)
michael@0 962 return;
michael@0 963 nsWeakFrame weakFrame(this);
michael@0 964
michael@0 965 if (aParts.mVScrollbar) {
michael@0 966 // Do Vertical Scrollbar
michael@0 967 nsAutoString maxposStr;
michael@0 968
michael@0 969 nscoord rowHeightAsPixels = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight);
michael@0 970
michael@0 971 int32_t size = rowHeightAsPixels * (mRowCount > mPageLength ? mRowCount - mPageLength : 0);
michael@0 972 maxposStr.AppendInt(size);
michael@0 973 aParts.mVScrollbarContent->
michael@0 974 SetAttr(kNameSpaceID_None, nsGkAtoms::maxpos, maxposStr, true);
michael@0 975 ENSURE_TRUE(weakFrame.IsAlive());
michael@0 976
michael@0 977 // Also set our page increment and decrement.
michael@0 978 nscoord pageincrement = mPageLength*rowHeightAsPixels;
michael@0 979 nsAutoString pageStr;
michael@0 980 pageStr.AppendInt(pageincrement);
michael@0 981 aParts.mVScrollbarContent->
michael@0 982 SetAttr(kNameSpaceID_None, nsGkAtoms::pageincrement, pageStr, true);
michael@0 983 ENSURE_TRUE(weakFrame.IsAlive());
michael@0 984 }
michael@0 985
michael@0 986 if (aParts.mHScrollbar && aParts.mColumnsFrame && aWeakColumnsFrame.IsAlive()) {
michael@0 987 // And now Horizontal scrollbar
michael@0 988 nsRect bounds = aParts.mColumnsFrame->GetRect();
michael@0 989 nsAutoString maxposStr;
michael@0 990
michael@0 991 maxposStr.AppendInt(mHorzWidth > bounds.width ? mHorzWidth - bounds.width : 0);
michael@0 992 aParts.mHScrollbarContent->
michael@0 993 SetAttr(kNameSpaceID_None, nsGkAtoms::maxpos, maxposStr, true);
michael@0 994 ENSURE_TRUE(weakFrame.IsAlive());
michael@0 995
michael@0 996 nsAutoString pageStr;
michael@0 997 pageStr.AppendInt(bounds.width);
michael@0 998 aParts.mHScrollbarContent->
michael@0 999 SetAttr(kNameSpaceID_None, nsGkAtoms::pageincrement, pageStr, true);
michael@0 1000 ENSURE_TRUE(weakFrame.IsAlive());
michael@0 1001
michael@0 1002 pageStr.Truncate();
michael@0 1003 pageStr.AppendInt(nsPresContext::CSSPixelsToAppUnits(16));
michael@0 1004 aParts.mHScrollbarContent->
michael@0 1005 SetAttr(kNameSpaceID_None, nsGkAtoms::increment, pageStr, true);
michael@0 1006 }
michael@0 1007
michael@0 1008 if (weakFrame.IsAlive() && mScrollbarActivity) {
michael@0 1009 mScrollbarActivity->ActivityOccurred();
michael@0 1010 }
michael@0 1011 }
michael@0 1012
michael@0 1013 // Takes client x/y in pixels, converts them to appunits, and converts into
michael@0 1014 // values relative to this nsTreeBodyFrame frame.
michael@0 1015 nsPoint
michael@0 1016 nsTreeBodyFrame::AdjustClientCoordsToBoxCoordSpace(int32_t aX, int32_t aY)
michael@0 1017 {
michael@0 1018 nsPoint point(nsPresContext::CSSPixelsToAppUnits(aX),
michael@0 1019 nsPresContext::CSSPixelsToAppUnits(aY));
michael@0 1020
michael@0 1021 nsPresContext* presContext = PresContext();
michael@0 1022 point -= GetOffsetTo(presContext->GetPresShell()->GetRootFrame());
michael@0 1023
michael@0 1024 // Adjust by the inner box coords, so that we're in the inner box's
michael@0 1025 // coordinate space.
michael@0 1026 point -= mInnerBox.TopLeft();
michael@0 1027 return point;
michael@0 1028 } // AdjustClientCoordsToBoxCoordSpace
michael@0 1029
michael@0 1030 nsresult
michael@0 1031 nsTreeBodyFrame::GetRowAt(int32_t aX, int32_t aY, int32_t* _retval)
michael@0 1032 {
michael@0 1033 if (!mView)
michael@0 1034 return NS_OK;
michael@0 1035
michael@0 1036 nsPoint point = AdjustClientCoordsToBoxCoordSpace(aX, aY);
michael@0 1037
michael@0 1038 // Check if the coordinates are above our visible space.
michael@0 1039 if (point.y < 0) {
michael@0 1040 *_retval = -1;
michael@0 1041 return NS_OK;
michael@0 1042 }
michael@0 1043
michael@0 1044 *_retval = GetRowAt(point.x, point.y);
michael@0 1045
michael@0 1046 return NS_OK;
michael@0 1047 }
michael@0 1048
michael@0 1049 nsresult
michael@0 1050 nsTreeBodyFrame::GetCellAt(int32_t aX, int32_t aY, int32_t* aRow, nsITreeColumn** aCol,
michael@0 1051 nsACString& aChildElt)
michael@0 1052 {
michael@0 1053 if (!mView)
michael@0 1054 return NS_OK;
michael@0 1055
michael@0 1056 nsPoint point = AdjustClientCoordsToBoxCoordSpace(aX, aY);
michael@0 1057
michael@0 1058 // Check if the coordinates are above our visible space.
michael@0 1059 if (point.y < 0) {
michael@0 1060 *aRow = -1;
michael@0 1061 return NS_OK;
michael@0 1062 }
michael@0 1063
michael@0 1064 nsTreeColumn* col;
michael@0 1065 nsIAtom* child;
michael@0 1066 GetCellAt(point.x, point.y, aRow, &col, &child);
michael@0 1067
michael@0 1068 if (col) {
michael@0 1069 NS_ADDREF(*aCol = col);
michael@0 1070 if (child == nsCSSAnonBoxes::moztreecell)
michael@0 1071 aChildElt.AssignLiteral("cell");
michael@0 1072 else if (child == nsCSSAnonBoxes::moztreetwisty)
michael@0 1073 aChildElt.AssignLiteral("twisty");
michael@0 1074 else if (child == nsCSSAnonBoxes::moztreeimage)
michael@0 1075 aChildElt.AssignLiteral("image");
michael@0 1076 else if (child == nsCSSAnonBoxes::moztreecelltext)
michael@0 1077 aChildElt.AssignLiteral("text");
michael@0 1078 }
michael@0 1079
michael@0 1080 return NS_OK;
michael@0 1081 }
michael@0 1082
michael@0 1083
michael@0 1084 //
michael@0 1085 // GetCoordsForCellItem
michael@0 1086 //
michael@0 1087 // Find the x/y location and width/height (all in PIXELS) of the given object
michael@0 1088 // in the given column.
michael@0 1089 //
michael@0 1090 // XXX IMPORTANT XXX:
michael@0 1091 // Hyatt says in the bug for this, that the following needs to be done:
michael@0 1092 // (1) You need to deal with overflow when computing cell rects. See other column
michael@0 1093 // iteration examples... if you don't deal with this, you'll mistakenly extend the
michael@0 1094 // cell into the scrollbar's rect.
michael@0 1095 //
michael@0 1096 // (2) You are adjusting the cell rect by the *row" border padding. That's
michael@0 1097 // wrong. You need to first adjust a row rect by its border/padding, and then the
michael@0 1098 // cell rect fits inside the adjusted row rect. It also can have border/padding
michael@0 1099 // as well as margins. The vertical direction isn't that important, but you need
michael@0 1100 // to get the horizontal direction right.
michael@0 1101 //
michael@0 1102 // (3) GetImageSize() does not include margins (but it does include border/padding).
michael@0 1103 // You need to make sure to add in the image's margins as well.
michael@0 1104 //
michael@0 1105 nsresult
michael@0 1106 nsTreeBodyFrame::GetCoordsForCellItem(int32_t aRow, nsITreeColumn* aCol, const nsACString& aElement,
michael@0 1107 int32_t *aX, int32_t *aY, int32_t *aWidth, int32_t *aHeight)
michael@0 1108 {
michael@0 1109 *aX = 0;
michael@0 1110 *aY = 0;
michael@0 1111 *aWidth = 0;
michael@0 1112 *aHeight = 0;
michael@0 1113
michael@0 1114 bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
michael@0 1115 nscoord currX = mInnerBox.x - mHorzPosition;
michael@0 1116
michael@0 1117 // The Rect for the requested item.
michael@0 1118 nsRect theRect;
michael@0 1119
michael@0 1120 nsPresContext* presContext = PresContext();
michael@0 1121
michael@0 1122 for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol; currCol = currCol->GetNext()) {
michael@0 1123
michael@0 1124 // The Rect for the current cell.
michael@0 1125 nscoord colWidth;
michael@0 1126 #ifdef DEBUG
michael@0 1127 nsresult rv =
michael@0 1128 #endif
michael@0 1129 currCol->GetWidthInTwips(this, &colWidth);
michael@0 1130 NS_ASSERTION(NS_SUCCEEDED(rv), "invalid column");
michael@0 1131
michael@0 1132 nsRect cellRect(currX, mInnerBox.y + mRowHeight * (aRow - mTopRowIndex),
michael@0 1133 colWidth, mRowHeight);
michael@0 1134
michael@0 1135 // Check the ID of the current column to see if it matches. If it doesn't
michael@0 1136 // increment the current X value and continue to the next column.
michael@0 1137 if (currCol != aCol) {
michael@0 1138 currX += cellRect.width;
michael@0 1139 continue;
michael@0 1140 }
michael@0 1141 // Now obtain the properties for our cell.
michael@0 1142 PrefillPropertyArray(aRow, currCol);
michael@0 1143
michael@0 1144 nsAutoString properties;
michael@0 1145 mView->GetCellProperties(aRow, currCol, properties);
michael@0 1146 nsTreeUtils::TokenizeProperties(properties, mScratchArray);
michael@0 1147
michael@0 1148 nsStyleContext* rowContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreerow);
michael@0 1149
michael@0 1150 // We don't want to consider any of the decorations that may be present
michael@0 1151 // on the current row, so we have to deflate the rect by the border and
michael@0 1152 // padding and offset its left and top coordinates appropriately.
michael@0 1153 AdjustForBorderPadding(rowContext, cellRect);
michael@0 1154
michael@0 1155 nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell);
michael@0 1156
michael@0 1157 NS_NAMED_LITERAL_CSTRING(cell, "cell");
michael@0 1158 if (currCol->IsCycler() || cell.Equals(aElement)) {
michael@0 1159 // If the current Column is a Cycler, then the Rect is just the cell - the margins.
michael@0 1160 // Similarly, if we're just being asked for the cell rect, provide it.
michael@0 1161
michael@0 1162 theRect = cellRect;
michael@0 1163 nsMargin cellMargin;
michael@0 1164 cellContext->StyleMargin()->GetMargin(cellMargin);
michael@0 1165 theRect.Deflate(cellMargin);
michael@0 1166 break;
michael@0 1167 }
michael@0 1168
michael@0 1169 // Since we're not looking for the cell, and since the cell isn't a cycler,
michael@0 1170 // we're looking for some subcomponent, and now we need to subtract the
michael@0 1171 // borders and padding of the cell from cellRect so this does not
michael@0 1172 // interfere with our computations.
michael@0 1173 AdjustForBorderPadding(cellContext, cellRect);
michael@0 1174
michael@0 1175 nsRefPtr<nsRenderingContext> rc =
michael@0 1176 presContext->PresShell()->CreateReferenceRenderingContext();
michael@0 1177
michael@0 1178 // Now we'll start making our way across the cell, starting at the edge of
michael@0 1179 // the cell and proceeding until we hit the right edge. |cellX| is the
michael@0 1180 // working X value that we will increment as we crawl from left to right.
michael@0 1181 nscoord cellX = cellRect.x;
michael@0 1182 nscoord remainWidth = cellRect.width;
michael@0 1183
michael@0 1184 if (currCol->IsPrimary()) {
michael@0 1185 // If the current Column is a Primary, then we need to take into account the indentation
michael@0 1186 // and possibly a twisty.
michael@0 1187
michael@0 1188 // The amount of indentation is the indentation width (|mIndentation|) by the level.
michael@0 1189 int32_t level;
michael@0 1190 mView->GetLevel(aRow, &level);
michael@0 1191 if (!isRTL)
michael@0 1192 cellX += mIndentation * level;
michael@0 1193 remainWidth -= mIndentation * level;
michael@0 1194
michael@0 1195 // Find the twisty rect by computing its size.
michael@0 1196 nsRect imageRect;
michael@0 1197 nsRect twistyRect(cellRect);
michael@0 1198 nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty);
michael@0 1199 GetTwistyRect(aRow, currCol, imageRect, twistyRect, presContext,
michael@0 1200 *rc, twistyContext);
michael@0 1201
michael@0 1202 if (NS_LITERAL_CSTRING("twisty").Equals(aElement)) {
michael@0 1203 // If we're looking for the twisty Rect, just return the size
michael@0 1204 theRect = twistyRect;
michael@0 1205 break;
michael@0 1206 }
michael@0 1207
michael@0 1208 // Now we need to add in the margins of the twisty element, so that we
michael@0 1209 // can find the offset of the next element in the cell.
michael@0 1210 nsMargin twistyMargin;
michael@0 1211 twistyContext->StyleMargin()->GetMargin(twistyMargin);
michael@0 1212 twistyRect.Inflate(twistyMargin);
michael@0 1213
michael@0 1214 // Adjust our working X value with the twisty width (image size, margins,
michael@0 1215 // borders, padding.
michael@0 1216 if (!isRTL)
michael@0 1217 cellX += twistyRect.width;
michael@0 1218 }
michael@0 1219
michael@0 1220 // Cell Image
michael@0 1221 nsStyleContext* imageContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeimage);
michael@0 1222
michael@0 1223 nsRect imageSize = GetImageSize(aRow, currCol, false, imageContext);
michael@0 1224 if (NS_LITERAL_CSTRING("image").Equals(aElement)) {
michael@0 1225 theRect = imageSize;
michael@0 1226 theRect.x = cellX;
michael@0 1227 theRect.y = cellRect.y;
michael@0 1228 break;
michael@0 1229 }
michael@0 1230
michael@0 1231 // Add in the margins of the cell image.
michael@0 1232 nsMargin imageMargin;
michael@0 1233 imageContext->StyleMargin()->GetMargin(imageMargin);
michael@0 1234 imageSize.Inflate(imageMargin);
michael@0 1235
michael@0 1236 // Increment cellX by the image width
michael@0 1237 if (!isRTL)
michael@0 1238 cellX += imageSize.width;
michael@0 1239
michael@0 1240 // Cell Text
michael@0 1241 nsAutoString cellText;
michael@0 1242 mView->GetCellText(aRow, currCol, cellText);
michael@0 1243 // We're going to measure this text so we need to ensure bidi is enabled if
michael@0 1244 // necessary
michael@0 1245 CheckTextForBidi(cellText);
michael@0 1246
michael@0 1247 // Create a scratch rect to represent the text rectangle, with the current
michael@0 1248 // X and Y coords, and a guess at the width and height. The width is the
michael@0 1249 // remaining width we have left to traverse in the cell, which will be the
michael@0 1250 // widest possible value for the text rect, and the row height.
michael@0 1251 nsRect textRect(cellX, cellRect.y, remainWidth, cellRect.height);
michael@0 1252
michael@0 1253 // Measure the width of the text. If the width of the text is greater than
michael@0 1254 // the remaining width available, then we just assume that the text has
michael@0 1255 // been cropped and use the remaining rect as the text Rect. Otherwise,
michael@0 1256 // we add in borders and padding to the text dimension and give that back.
michael@0 1257 nsStyleContext* textContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecelltext);
michael@0 1258
michael@0 1259 nsRefPtr<nsFontMetrics> fm;
michael@0 1260 nsLayoutUtils::GetFontMetricsForStyleContext(textContext,
michael@0 1261 getter_AddRefs(fm));
michael@0 1262 nscoord height = fm->MaxHeight();
michael@0 1263
michael@0 1264 nsMargin textMargin;
michael@0 1265 textContext->StyleMargin()->GetMargin(textMargin);
michael@0 1266 textRect.Deflate(textMargin);
michael@0 1267
michael@0 1268 // Center the text. XXX Obey vertical-align style prop?
michael@0 1269 if (height < textRect.height) {
michael@0 1270 textRect.y += (textRect.height - height) / 2;
michael@0 1271 textRect.height = height;
michael@0 1272 }
michael@0 1273
michael@0 1274 nsMargin bp(0,0,0,0);
michael@0 1275 GetBorderPadding(textContext, bp);
michael@0 1276 textRect.height += bp.top + bp.bottom;
michael@0 1277
michael@0 1278 rc->SetFont(fm);
michael@0 1279 AdjustForCellText(cellText, aRow, currCol, *rc, textRect);
michael@0 1280
michael@0 1281 theRect = textRect;
michael@0 1282 }
michael@0 1283
michael@0 1284 if (isRTL)
michael@0 1285 theRect.x = mInnerBox.width - theRect.x - theRect.width;
michael@0 1286
michael@0 1287 *aX = nsPresContext::AppUnitsToIntCSSPixels(theRect.x);
michael@0 1288 *aY = nsPresContext::AppUnitsToIntCSSPixels(theRect.y);
michael@0 1289 *aWidth = nsPresContext::AppUnitsToIntCSSPixels(theRect.width);
michael@0 1290 *aHeight = nsPresContext::AppUnitsToIntCSSPixels(theRect.height);
michael@0 1291
michael@0 1292 return NS_OK;
michael@0 1293 }
michael@0 1294
michael@0 1295 int32_t
michael@0 1296 nsTreeBodyFrame::GetRowAt(int32_t aX, int32_t aY)
michael@0 1297 {
michael@0 1298 // Now just mod by our total inner box height and add to our top row index.
michael@0 1299 int32_t row = (aY/mRowHeight)+mTopRowIndex;
michael@0 1300
michael@0 1301 // Check if the coordinates are below our visible space (or within our visible
michael@0 1302 // space but below any row).
michael@0 1303 if (row > mTopRowIndex + mPageLength || row >= mRowCount)
michael@0 1304 return -1;
michael@0 1305
michael@0 1306 return row;
michael@0 1307 }
michael@0 1308
michael@0 1309 void
michael@0 1310 nsTreeBodyFrame::CheckTextForBidi(nsAutoString& aText)
michael@0 1311 {
michael@0 1312 // We could check to see whether the prescontext already has bidi enabled,
michael@0 1313 // but usually it won't, so it's probably faster to avoid the call to
michael@0 1314 // GetPresContext() when it's not needed.
michael@0 1315 if (HasRTLChars(aText)) {
michael@0 1316 PresContext()->SetBidiEnabled();
michael@0 1317 }
michael@0 1318 }
michael@0 1319
michael@0 1320 void
michael@0 1321 nsTreeBodyFrame::AdjustForCellText(nsAutoString& aText,
michael@0 1322 int32_t aRowIndex, nsTreeColumn* aColumn,
michael@0 1323 nsRenderingContext& aRenderingContext,
michael@0 1324 nsRect& aTextRect)
michael@0 1325 {
michael@0 1326 NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
michael@0 1327
michael@0 1328 nscoord width =
michael@0 1329 nsLayoutUtils::GetStringWidth(this, &aRenderingContext, aText.get(), aText.Length());
michael@0 1330 nscoord maxWidth = aTextRect.width;
michael@0 1331
michael@0 1332 if (aColumn->Overflow()) {
michael@0 1333 DebugOnly<nsresult> rv;
michael@0 1334 nsTreeColumn* nextColumn = aColumn->GetNext();
michael@0 1335 while (nextColumn && width > maxWidth) {
michael@0 1336 while (nextColumn) {
michael@0 1337 nscoord width;
michael@0 1338 rv = nextColumn->GetWidthInTwips(this, &width);
michael@0 1339 NS_ASSERTION(NS_SUCCEEDED(rv), "nextColumn is invalid");
michael@0 1340
michael@0 1341 if (width != 0)
michael@0 1342 break;
michael@0 1343
michael@0 1344 nextColumn = nextColumn->GetNext();
michael@0 1345 }
michael@0 1346
michael@0 1347 if (nextColumn) {
michael@0 1348 nsAutoString nextText;
michael@0 1349 mView->GetCellText(aRowIndex, nextColumn, nextText);
michael@0 1350 // We don't measure or draw this text so no need to check it for
michael@0 1351 // bidi-ness
michael@0 1352
michael@0 1353 if (nextText.Length() == 0) {
michael@0 1354 nscoord width;
michael@0 1355 rv = nextColumn->GetWidthInTwips(this, &width);
michael@0 1356 NS_ASSERTION(NS_SUCCEEDED(rv), "nextColumn is invalid");
michael@0 1357
michael@0 1358 maxWidth += width;
michael@0 1359
michael@0 1360 nextColumn = nextColumn->GetNext();
michael@0 1361 }
michael@0 1362 else {
michael@0 1363 nextColumn = nullptr;
michael@0 1364 }
michael@0 1365 }
michael@0 1366 }
michael@0 1367 }
michael@0 1368
michael@0 1369 if (width > maxWidth) {
michael@0 1370 // See if the width is even smaller than the ellipsis
michael@0 1371 // If so, clear the text completely.
michael@0 1372 const nsDependentString& kEllipsis = nsContentUtils::GetLocalizedEllipsis();
michael@0 1373 aRenderingContext.SetTextRunRTL(false);
michael@0 1374 nscoord ellipsisWidth = aRenderingContext.GetWidth(kEllipsis);
michael@0 1375
michael@0 1376 width = maxWidth;
michael@0 1377 if (ellipsisWidth > width)
michael@0 1378 aText.SetLength(0);
michael@0 1379 else if (ellipsisWidth == width)
michael@0 1380 aText.Assign(kEllipsis);
michael@0 1381 else {
michael@0 1382 // We will be drawing an ellipsis, thank you very much.
michael@0 1383 // Subtract out the required width of the ellipsis.
michael@0 1384 // This is the total remaining width we have to play with.
michael@0 1385 width -= ellipsisWidth;
michael@0 1386
michael@0 1387 // Now we crop.
michael@0 1388 switch (aColumn->GetCropStyle()) {
michael@0 1389 default:
michael@0 1390 case 0: {
michael@0 1391 // Crop right.
michael@0 1392 nscoord cwidth;
michael@0 1393 nscoord twidth = 0;
michael@0 1394 uint32_t length = aText.Length();
michael@0 1395 uint32_t i;
michael@0 1396 for (i = 0; i < length; ++i) {
michael@0 1397 char16_t ch = aText[i];
michael@0 1398 // XXX this is horrible and doesn't handle clusters
michael@0 1399 cwidth = aRenderingContext.GetWidth(ch);
michael@0 1400 if (twidth + cwidth > width)
michael@0 1401 break;
michael@0 1402 twidth += cwidth;
michael@0 1403 }
michael@0 1404 aText.Truncate(i);
michael@0 1405 aText.Append(kEllipsis);
michael@0 1406 }
michael@0 1407 break;
michael@0 1408
michael@0 1409 case 2: {
michael@0 1410 // Crop left.
michael@0 1411 nscoord cwidth;
michael@0 1412 nscoord twidth = 0;
michael@0 1413 int32_t length = aText.Length();
michael@0 1414 int32_t i;
michael@0 1415 for (i=length-1; i >= 0; --i) {
michael@0 1416 char16_t ch = aText[i];
michael@0 1417 cwidth = aRenderingContext.GetWidth(ch);
michael@0 1418 if (twidth + cwidth > width)
michael@0 1419 break;
michael@0 1420 twidth += cwidth;
michael@0 1421 }
michael@0 1422
michael@0 1423 nsAutoString copy;
michael@0 1424 aText.Right(copy, length-1-i);
michael@0 1425 aText.Assign(kEllipsis);
michael@0 1426 aText += copy;
michael@0 1427 }
michael@0 1428 break;
michael@0 1429
michael@0 1430 case 1:
michael@0 1431 {
michael@0 1432 // Crop center.
michael@0 1433 nsAutoString leftStr, rightStr;
michael@0 1434 nscoord cwidth, twidth = 0;
michael@0 1435 int32_t length = aText.Length();
michael@0 1436 int32_t rightPos = length - 1;
michael@0 1437 for (int32_t leftPos = 0; leftPos < rightPos; ++leftPos) {
michael@0 1438 char16_t ch = aText[leftPos];
michael@0 1439 cwidth = aRenderingContext.GetWidth(ch);
michael@0 1440 twidth += cwidth;
michael@0 1441 if (twidth > width)
michael@0 1442 break;
michael@0 1443 leftStr.Append(ch);
michael@0 1444
michael@0 1445 ch = aText[rightPos];
michael@0 1446 cwidth = aRenderingContext.GetWidth(ch);
michael@0 1447 twidth += cwidth;
michael@0 1448 if (twidth > width)
michael@0 1449 break;
michael@0 1450 rightStr.Insert(ch, 0);
michael@0 1451 --rightPos;
michael@0 1452 }
michael@0 1453 aText = leftStr;
michael@0 1454 aText.Append(kEllipsis);
michael@0 1455 aText += rightStr;
michael@0 1456 }
michael@0 1457 break;
michael@0 1458 }
michael@0 1459 }
michael@0 1460
michael@0 1461 width = nsLayoutUtils::GetStringWidth(this, &aRenderingContext, aText.get(), aText.Length());
michael@0 1462 }
michael@0 1463
michael@0 1464 switch (aColumn->GetTextAlignment()) {
michael@0 1465 case NS_STYLE_TEXT_ALIGN_RIGHT: {
michael@0 1466 aTextRect.x += aTextRect.width - width;
michael@0 1467 }
michael@0 1468 break;
michael@0 1469 case NS_STYLE_TEXT_ALIGN_CENTER: {
michael@0 1470 aTextRect.x += (aTextRect.width - width) / 2;
michael@0 1471 }
michael@0 1472 break;
michael@0 1473 }
michael@0 1474
michael@0 1475 aTextRect.width = width;
michael@0 1476 }
michael@0 1477
michael@0 1478 nsIAtom*
michael@0 1479 nsTreeBodyFrame::GetItemWithinCellAt(nscoord aX, const nsRect& aCellRect,
michael@0 1480 int32_t aRowIndex,
michael@0 1481 nsTreeColumn* aColumn)
michael@0 1482 {
michael@0 1483 NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
michael@0 1484
michael@0 1485 // Obtain the properties for our cell.
michael@0 1486 PrefillPropertyArray(aRowIndex, aColumn);
michael@0 1487 nsAutoString properties;
michael@0 1488 mView->GetCellProperties(aRowIndex, aColumn, properties);
michael@0 1489 nsTreeUtils::TokenizeProperties(properties, mScratchArray);
michael@0 1490
michael@0 1491 // Resolve style for the cell.
michael@0 1492 nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell);
michael@0 1493
michael@0 1494 // Obtain the margins for the cell and then deflate our rect by that
michael@0 1495 // amount. The cell is assumed to be contained within the deflated rect.
michael@0 1496 nsRect cellRect(aCellRect);
michael@0 1497 nsMargin cellMargin;
michael@0 1498 cellContext->StyleMargin()->GetMargin(cellMargin);
michael@0 1499 cellRect.Deflate(cellMargin);
michael@0 1500
michael@0 1501 // Adjust the rect for its border and padding.
michael@0 1502 AdjustForBorderPadding(cellContext, cellRect);
michael@0 1503
michael@0 1504 if (aX < cellRect.x || aX >= cellRect.x + cellRect.width) {
michael@0 1505 // The user clicked within the cell's margins/borders/padding. This constitutes a click on the cell.
michael@0 1506 return nsCSSAnonBoxes::moztreecell;
michael@0 1507 }
michael@0 1508
michael@0 1509 nscoord currX = cellRect.x;
michael@0 1510 nscoord remainingWidth = cellRect.width;
michael@0 1511
michael@0 1512 // Handle right alignment hit testing.
michael@0 1513 bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
michael@0 1514
michael@0 1515 nsPresContext* presContext = PresContext();
michael@0 1516 nsRefPtr<nsRenderingContext> rc =
michael@0 1517 presContext->PresShell()->CreateReferenceRenderingContext();
michael@0 1518
michael@0 1519 if (aColumn->IsPrimary()) {
michael@0 1520 // If we're the primary column, we have indentation and a twisty.
michael@0 1521 int32_t level;
michael@0 1522 mView->GetLevel(aRowIndex, &level);
michael@0 1523
michael@0 1524 if (!isRTL)
michael@0 1525 currX += mIndentation*level;
michael@0 1526 remainingWidth -= mIndentation*level;
michael@0 1527
michael@0 1528 if ((isRTL && aX > currX + remainingWidth) ||
michael@0 1529 (!isRTL && aX < currX)) {
michael@0 1530 // The user clicked within the indentation.
michael@0 1531 return nsCSSAnonBoxes::moztreecell;
michael@0 1532 }
michael@0 1533
michael@0 1534 // Always leave space for the twisty.
michael@0 1535 nsRect twistyRect(currX, cellRect.y, remainingWidth, cellRect.height);
michael@0 1536 bool hasTwisty = false;
michael@0 1537 bool isContainer = false;
michael@0 1538 mView->IsContainer(aRowIndex, &isContainer);
michael@0 1539 if (isContainer) {
michael@0 1540 bool isContainerEmpty = false;
michael@0 1541 mView->IsContainerEmpty(aRowIndex, &isContainerEmpty);
michael@0 1542 if (!isContainerEmpty)
michael@0 1543 hasTwisty = true;
michael@0 1544 }
michael@0 1545
michael@0 1546 // Resolve style for the twisty.
michael@0 1547 nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty);
michael@0 1548
michael@0 1549 nsRect imageSize;
michael@0 1550 GetTwistyRect(aRowIndex, aColumn, imageSize, twistyRect, presContext,
michael@0 1551 *rc, twistyContext);
michael@0 1552
michael@0 1553 // We will treat a click as hitting the twisty if it happens on the margins, borders, padding,
michael@0 1554 // or content of the twisty object. By allowing a "slop" into the margin, we make it a little
michael@0 1555 // bit easier for a user to hit the twisty. (We don't want to be too picky here.)
michael@0 1556 nsMargin twistyMargin;
michael@0 1557 twistyContext->StyleMargin()->GetMargin(twistyMargin);
michael@0 1558 twistyRect.Inflate(twistyMargin);
michael@0 1559 if (isRTL)
michael@0 1560 twistyRect.x = currX + remainingWidth - twistyRect.width;
michael@0 1561
michael@0 1562 // Now we test to see if aX is actually within the twistyRect. If it is, and if the item should
michael@0 1563 // have a twisty, then we return "twisty". If it is within the rect but we shouldn't have a twisty,
michael@0 1564 // then we return "cell".
michael@0 1565 if (aX >= twistyRect.x && aX < twistyRect.x + twistyRect.width) {
michael@0 1566 if (hasTwisty)
michael@0 1567 return nsCSSAnonBoxes::moztreetwisty;
michael@0 1568 else
michael@0 1569 return nsCSSAnonBoxes::moztreecell;
michael@0 1570 }
michael@0 1571
michael@0 1572 if (!isRTL)
michael@0 1573 currX += twistyRect.width;
michael@0 1574 remainingWidth -= twistyRect.width;
michael@0 1575 }
michael@0 1576
michael@0 1577 // Now test to see if the user hit the icon for the cell.
michael@0 1578 nsRect iconRect(currX, cellRect.y, remainingWidth, cellRect.height);
michael@0 1579
michael@0 1580 // Resolve style for the image.
michael@0 1581 nsStyleContext* imageContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeimage);
michael@0 1582
michael@0 1583 nsRect iconSize = GetImageSize(aRowIndex, aColumn, false, imageContext);
michael@0 1584 nsMargin imageMargin;
michael@0 1585 imageContext->StyleMargin()->GetMargin(imageMargin);
michael@0 1586 iconSize.Inflate(imageMargin);
michael@0 1587 iconRect.width = iconSize.width;
michael@0 1588 if (isRTL)
michael@0 1589 iconRect.x = currX + remainingWidth - iconRect.width;
michael@0 1590
michael@0 1591 if (aX >= iconRect.x && aX < iconRect.x + iconRect.width) {
michael@0 1592 // The user clicked on the image.
michael@0 1593 return nsCSSAnonBoxes::moztreeimage;
michael@0 1594 }
michael@0 1595
michael@0 1596 if (!isRTL)
michael@0 1597 currX += iconRect.width;
michael@0 1598 remainingWidth -= iconRect.width;
michael@0 1599
michael@0 1600 nsAutoString cellText;
michael@0 1601 mView->GetCellText(aRowIndex, aColumn, cellText);
michael@0 1602 // We're going to measure this text so we need to ensure bidi is enabled if
michael@0 1603 // necessary
michael@0 1604 CheckTextForBidi(cellText);
michael@0 1605
michael@0 1606 nsRect textRect(currX, cellRect.y, remainingWidth, cellRect.height);
michael@0 1607
michael@0 1608 nsStyleContext* textContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecelltext);
michael@0 1609
michael@0 1610 nsMargin textMargin;
michael@0 1611 textContext->StyleMargin()->GetMargin(textMargin);
michael@0 1612 textRect.Deflate(textMargin);
michael@0 1613
michael@0 1614 AdjustForBorderPadding(textContext, textRect);
michael@0 1615
michael@0 1616 nsRefPtr<nsFontMetrics> fm;
michael@0 1617 nsLayoutUtils::GetFontMetricsForStyleContext(textContext,
michael@0 1618 getter_AddRefs(fm));
michael@0 1619 rc->SetFont(fm);
michael@0 1620
michael@0 1621 AdjustForCellText(cellText, aRowIndex, aColumn, *rc, textRect);
michael@0 1622
michael@0 1623 if (aX >= textRect.x && aX < textRect.x + textRect.width)
michael@0 1624 return nsCSSAnonBoxes::moztreecelltext;
michael@0 1625 else
michael@0 1626 return nsCSSAnonBoxes::moztreecell;
michael@0 1627 }
michael@0 1628
michael@0 1629 void
michael@0 1630 nsTreeBodyFrame::GetCellAt(nscoord aX, nscoord aY, int32_t* aRow,
michael@0 1631 nsTreeColumn** aCol, nsIAtom** aChildElt)
michael@0 1632 {
michael@0 1633 *aCol = nullptr;
michael@0 1634 *aChildElt = nullptr;
michael@0 1635
michael@0 1636 *aRow = GetRowAt(aX, aY);
michael@0 1637 if (*aRow < 0)
michael@0 1638 return;
michael@0 1639
michael@0 1640 // Determine the column hit.
michael@0 1641 for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol;
michael@0 1642 currCol = currCol->GetNext()) {
michael@0 1643 nsRect cellRect;
michael@0 1644 nsresult rv = currCol->GetRect(this,
michael@0 1645 mInnerBox.y +
michael@0 1646 mRowHeight * (*aRow - mTopRowIndex),
michael@0 1647 mRowHeight,
michael@0 1648 &cellRect);
michael@0 1649 if (NS_FAILED(rv)) {
michael@0 1650 NS_NOTREACHED("column has no frame");
michael@0 1651 continue;
michael@0 1652 }
michael@0 1653
michael@0 1654 if (!OffsetForHorzScroll(cellRect, false))
michael@0 1655 continue;
michael@0 1656
michael@0 1657 if (aX >= cellRect.x && aX < cellRect.x + cellRect.width) {
michael@0 1658 // We know the column hit now.
michael@0 1659 *aCol = currCol;
michael@0 1660
michael@0 1661 if (currCol->IsCycler())
michael@0 1662 // Cyclers contain only images. Fill this in immediately and return.
michael@0 1663 *aChildElt = nsCSSAnonBoxes::moztreeimage;
michael@0 1664 else
michael@0 1665 *aChildElt = GetItemWithinCellAt(aX, cellRect, *aRow, currCol);
michael@0 1666 break;
michael@0 1667 }
michael@0 1668 }
michael@0 1669 }
michael@0 1670
michael@0 1671 nsresult
michael@0 1672 nsTreeBodyFrame::GetCellWidth(int32_t aRow, nsTreeColumn* aCol,
michael@0 1673 nsRenderingContext* aRenderingContext,
michael@0 1674 nscoord& aDesiredSize, nscoord& aCurrentSize)
michael@0 1675 {
michael@0 1676 NS_PRECONDITION(aCol, "aCol must not be null");
michael@0 1677 NS_PRECONDITION(aRenderingContext, "aRenderingContext must not be null");
michael@0 1678
michael@0 1679 // The rect for the current cell.
michael@0 1680 nscoord colWidth;
michael@0 1681 nsresult rv = aCol->GetWidthInTwips(this, &colWidth);
michael@0 1682 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1683
michael@0 1684 nsRect cellRect(0, 0, colWidth, mRowHeight);
michael@0 1685
michael@0 1686 int32_t overflow = cellRect.x+cellRect.width-(mInnerBox.x+mInnerBox.width);
michael@0 1687 if (overflow > 0)
michael@0 1688 cellRect.width -= overflow;
michael@0 1689
michael@0 1690 // Adjust borders and padding for the cell.
michael@0 1691 nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell);
michael@0 1692 nsMargin bp(0,0,0,0);
michael@0 1693 GetBorderPadding(cellContext, bp);
michael@0 1694
michael@0 1695 aCurrentSize = cellRect.width;
michael@0 1696 aDesiredSize = bp.left + bp.right;
michael@0 1697
michael@0 1698 if (aCol->IsPrimary()) {
michael@0 1699 // If the current Column is a Primary, then we need to take into account
michael@0 1700 // the indentation and possibly a twisty.
michael@0 1701
michael@0 1702 // The amount of indentation is the indentation width (|mIndentation|) by the level.
michael@0 1703 int32_t level;
michael@0 1704 mView->GetLevel(aRow, &level);
michael@0 1705 aDesiredSize += mIndentation * level;
michael@0 1706
michael@0 1707 // Find the twisty rect by computing its size.
michael@0 1708 nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty);
michael@0 1709
michael@0 1710 nsRect imageSize;
michael@0 1711 nsRect twistyRect(cellRect);
michael@0 1712 GetTwistyRect(aRow, aCol, imageSize, twistyRect, PresContext(),
michael@0 1713 *aRenderingContext, twistyContext);
michael@0 1714
michael@0 1715 // Add in the margins of the twisty element.
michael@0 1716 nsMargin twistyMargin;
michael@0 1717 twistyContext->StyleMargin()->GetMargin(twistyMargin);
michael@0 1718 twistyRect.Inflate(twistyMargin);
michael@0 1719
michael@0 1720 aDesiredSize += twistyRect.width;
michael@0 1721 }
michael@0 1722
michael@0 1723 nsStyleContext* imageContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeimage);
michael@0 1724
michael@0 1725 // Account for the width of the cell image.
michael@0 1726 nsRect imageSize = GetImageSize(aRow, aCol, false, imageContext);
michael@0 1727 // Add in the margins of the cell image.
michael@0 1728 nsMargin imageMargin;
michael@0 1729 imageContext->StyleMargin()->GetMargin(imageMargin);
michael@0 1730 imageSize.Inflate(imageMargin);
michael@0 1731
michael@0 1732 aDesiredSize += imageSize.width;
michael@0 1733
michael@0 1734 // Get the cell text.
michael@0 1735 nsAutoString cellText;
michael@0 1736 mView->GetCellText(aRow, aCol, cellText);
michael@0 1737 // We're going to measure this text so we need to ensure bidi is enabled if
michael@0 1738 // necessary
michael@0 1739 CheckTextForBidi(cellText);
michael@0 1740
michael@0 1741 nsStyleContext* textContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecelltext);
michael@0 1742
michael@0 1743 // Get the borders and padding for the text.
michael@0 1744 GetBorderPadding(textContext, bp);
michael@0 1745
michael@0 1746 nsRefPtr<nsFontMetrics> fm;
michael@0 1747 nsLayoutUtils::GetFontMetricsForStyleContext(textContext,
michael@0 1748 getter_AddRefs(fm));
michael@0 1749 aRenderingContext->SetFont(fm);
michael@0 1750
michael@0 1751 // Get the width of the text itself
michael@0 1752 nscoord width =
michael@0 1753 nsLayoutUtils::GetStringWidth(this, aRenderingContext, cellText.get(), cellText.Length());
michael@0 1754 nscoord totalTextWidth = width + bp.left + bp.right;
michael@0 1755 aDesiredSize += totalTextWidth;
michael@0 1756 return NS_OK;
michael@0 1757 }
michael@0 1758
michael@0 1759 nsresult
michael@0 1760 nsTreeBodyFrame::IsCellCropped(int32_t aRow, nsITreeColumn* aCol, bool *_retval)
michael@0 1761 {
michael@0 1762 nscoord currentSize, desiredSize;
michael@0 1763 nsresult rv;
michael@0 1764
michael@0 1765 nsRefPtr<nsTreeColumn> col = GetColumnImpl(aCol);
michael@0 1766 if (!col)
michael@0 1767 return NS_ERROR_INVALID_ARG;
michael@0 1768
michael@0 1769 nsRefPtr<nsRenderingContext> rc =
michael@0 1770 PresContext()->PresShell()->CreateReferenceRenderingContext();
michael@0 1771
michael@0 1772 rv = GetCellWidth(aRow, col, rc, desiredSize, currentSize);
michael@0 1773 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1774
michael@0 1775 *_retval = desiredSize > currentSize;
michael@0 1776
michael@0 1777 return NS_OK;
michael@0 1778 }
michael@0 1779
michael@0 1780 void
michael@0 1781 nsTreeBodyFrame::MarkDirtyIfSelect()
michael@0 1782 {
michael@0 1783 nsIContent* baseElement = GetBaseElement();
michael@0 1784
michael@0 1785 if (baseElement && baseElement->Tag() == nsGkAtoms::select &&
michael@0 1786 baseElement->IsHTML()) {
michael@0 1787 // If we are an intrinsically sized select widget, we may need to
michael@0 1788 // resize, if the widest item was removed or a new item was added.
michael@0 1789 // XXX optimize this more
michael@0 1790
michael@0 1791 mStringWidth = -1;
michael@0 1792 PresContext()->PresShell()->FrameNeedsReflow(this,
michael@0 1793 nsIPresShell::eTreeChange,
michael@0 1794 NS_FRAME_IS_DIRTY);
michael@0 1795 }
michael@0 1796 }
michael@0 1797
michael@0 1798 nsresult
michael@0 1799 nsTreeBodyFrame::CreateTimer(const LookAndFeel::IntID aID,
michael@0 1800 nsTimerCallbackFunc aFunc, int32_t aType,
michael@0 1801 nsITimer** aTimer)
michael@0 1802 {
michael@0 1803 // Get the delay from the look and feel service.
michael@0 1804 int32_t delay = LookAndFeel::GetInt(aID, 0);
michael@0 1805
michael@0 1806 nsCOMPtr<nsITimer> timer;
michael@0 1807
michael@0 1808 // Create a new timer only if the delay is greater than zero.
michael@0 1809 // Zero value means that this feature is completely disabled.
michael@0 1810 if (delay > 0) {
michael@0 1811 timer = do_CreateInstance("@mozilla.org/timer;1");
michael@0 1812 if (timer)
michael@0 1813 timer->InitWithFuncCallback(aFunc, this, delay, aType);
michael@0 1814 }
michael@0 1815
michael@0 1816 NS_IF_ADDREF(*aTimer = timer);
michael@0 1817
michael@0 1818 return NS_OK;
michael@0 1819 }
michael@0 1820
michael@0 1821 nsresult
michael@0 1822 nsTreeBodyFrame::RowCountChanged(int32_t aIndex, int32_t aCount)
michael@0 1823 {
michael@0 1824 if (aCount == 0 || !mView)
michael@0 1825 return NS_OK; // Nothing to do.
michael@0 1826
michael@0 1827 #ifdef ACCESSIBILITY
michael@0 1828 if (nsIPresShell::IsAccessibilityActive())
michael@0 1829 FireRowCountChangedEvent(aIndex, aCount);
michael@0 1830 #endif
michael@0 1831
michael@0 1832 // Adjust our selection.
michael@0 1833 nsCOMPtr<nsITreeSelection> sel;
michael@0 1834 mView->GetSelection(getter_AddRefs(sel));
michael@0 1835 if (sel)
michael@0 1836 sel->AdjustSelection(aIndex, aCount);
michael@0 1837
michael@0 1838 if (mUpdateBatchNest)
michael@0 1839 return NS_OK;
michael@0 1840
michael@0 1841 mRowCount += aCount;
michael@0 1842 #ifdef DEBUG
michael@0 1843 int32_t rowCount = mRowCount;
michael@0 1844 mView->GetRowCount(&rowCount);
michael@0 1845 NS_ASSERTION(rowCount == mRowCount, "row count did not change by the amount suggested, check caller");
michael@0 1846 #endif
michael@0 1847
michael@0 1848 int32_t count = Abs(aCount);
michael@0 1849 int32_t last = LastVisibleRow();
michael@0 1850 if (aIndex >= mTopRowIndex && aIndex <= last)
michael@0 1851 InvalidateRange(aIndex, last);
michael@0 1852
michael@0 1853 ScrollParts parts = GetScrollParts();
michael@0 1854
michael@0 1855 if (mTopRowIndex == 0) {
michael@0 1856 // Just update the scrollbar and return.
michael@0 1857 if (FullScrollbarsUpdate(false)) {
michael@0 1858 MarkDirtyIfSelect();
michael@0 1859 }
michael@0 1860 return NS_OK;
michael@0 1861 }
michael@0 1862
michael@0 1863 bool needsInvalidation = false;
michael@0 1864 // Adjust our top row index.
michael@0 1865 if (aCount > 0) {
michael@0 1866 if (mTopRowIndex > aIndex) {
michael@0 1867 // Rows came in above us. Augment the top row index.
michael@0 1868 mTopRowIndex += aCount;
michael@0 1869 }
michael@0 1870 }
michael@0 1871 else if (aCount < 0) {
michael@0 1872 if (mTopRowIndex > aIndex+count-1) {
michael@0 1873 // No need to invalidate. The remove happened
michael@0 1874 // completely above us (offscreen).
michael@0 1875 mTopRowIndex -= count;
michael@0 1876 }
michael@0 1877 else if (mTopRowIndex >= aIndex) {
michael@0 1878 // This is a full-blown invalidate.
michael@0 1879 if (mTopRowIndex + mPageLength > mRowCount - 1) {
michael@0 1880 mTopRowIndex = std::max(0, mRowCount - 1 - mPageLength);
michael@0 1881 }
michael@0 1882 needsInvalidation = true;
michael@0 1883 }
michael@0 1884 }
michael@0 1885
michael@0 1886 if (FullScrollbarsUpdate(needsInvalidation)) {
michael@0 1887 MarkDirtyIfSelect();
michael@0 1888 }
michael@0 1889 return NS_OK;
michael@0 1890 }
michael@0 1891
michael@0 1892 nsresult
michael@0 1893 nsTreeBodyFrame::BeginUpdateBatch()
michael@0 1894 {
michael@0 1895 ++mUpdateBatchNest;
michael@0 1896
michael@0 1897 return NS_OK;
michael@0 1898 }
michael@0 1899
michael@0 1900 nsresult
michael@0 1901 nsTreeBodyFrame::EndUpdateBatch()
michael@0 1902 {
michael@0 1903 NS_ASSERTION(mUpdateBatchNest > 0, "badly nested update batch");
michael@0 1904
michael@0 1905 if (--mUpdateBatchNest == 0) {
michael@0 1906 if (mView) {
michael@0 1907 Invalidate();
michael@0 1908 int32_t countBeforeUpdate = mRowCount;
michael@0 1909 mView->GetRowCount(&mRowCount);
michael@0 1910 if (countBeforeUpdate != mRowCount) {
michael@0 1911 if (mTopRowIndex + mPageLength > mRowCount - 1) {
michael@0 1912 mTopRowIndex = std::max(0, mRowCount - 1 - mPageLength);
michael@0 1913 }
michael@0 1914 FullScrollbarsUpdate(false);
michael@0 1915 }
michael@0 1916 }
michael@0 1917 }
michael@0 1918
michael@0 1919 return NS_OK;
michael@0 1920 }
michael@0 1921
michael@0 1922 void
michael@0 1923 nsTreeBodyFrame::PrefillPropertyArray(int32_t aRowIndex, nsTreeColumn* aCol)
michael@0 1924 {
michael@0 1925 NS_PRECONDITION(!aCol || aCol->GetFrame(), "invalid column passed");
michael@0 1926 mScratchArray.Clear();
michael@0 1927
michael@0 1928 // focus
michael@0 1929 if (mFocused)
michael@0 1930 mScratchArray.AppendElement(nsGkAtoms::focus);
michael@0 1931
michael@0 1932 // sort
michael@0 1933 bool sorted = false;
michael@0 1934 mView->IsSorted(&sorted);
michael@0 1935 if (sorted)
michael@0 1936 mScratchArray.AppendElement(nsGkAtoms::sorted);
michael@0 1937
michael@0 1938 // drag session
michael@0 1939 if (mSlots && mSlots->mIsDragging)
michael@0 1940 mScratchArray.AppendElement(nsGkAtoms::dragSession);
michael@0 1941
michael@0 1942 if (aRowIndex != -1) {
michael@0 1943 if (aRowIndex == mMouseOverRow)
michael@0 1944 mScratchArray.AppendElement(nsGkAtoms::hover);
michael@0 1945
michael@0 1946 nsCOMPtr<nsITreeSelection> selection;
michael@0 1947 mView->GetSelection(getter_AddRefs(selection));
michael@0 1948
michael@0 1949 if (selection) {
michael@0 1950 // selected
michael@0 1951 bool isSelected;
michael@0 1952 selection->IsSelected(aRowIndex, &isSelected);
michael@0 1953 if (isSelected)
michael@0 1954 mScratchArray.AppendElement(nsGkAtoms::selected);
michael@0 1955
michael@0 1956 // current
michael@0 1957 int32_t currentIndex;
michael@0 1958 selection->GetCurrentIndex(&currentIndex);
michael@0 1959 if (aRowIndex == currentIndex)
michael@0 1960 mScratchArray.AppendElement(nsGkAtoms::current);
michael@0 1961
michael@0 1962 // active
michael@0 1963 if (aCol) {
michael@0 1964 nsCOMPtr<nsITreeColumn> currentColumn;
michael@0 1965 selection->GetCurrentColumn(getter_AddRefs(currentColumn));
michael@0 1966 if (aCol == currentColumn)
michael@0 1967 mScratchArray.AppendElement(nsGkAtoms::active);
michael@0 1968 }
michael@0 1969 }
michael@0 1970
michael@0 1971 // container or leaf
michael@0 1972 bool isContainer = false;
michael@0 1973 mView->IsContainer(aRowIndex, &isContainer);
michael@0 1974 if (isContainer) {
michael@0 1975 mScratchArray.AppendElement(nsGkAtoms::container);
michael@0 1976
michael@0 1977 // open or closed
michael@0 1978 bool isOpen = false;
michael@0 1979 mView->IsContainerOpen(aRowIndex, &isOpen);
michael@0 1980 if (isOpen)
michael@0 1981 mScratchArray.AppendElement(nsGkAtoms::open);
michael@0 1982 else
michael@0 1983 mScratchArray.AppendElement(nsGkAtoms::closed);
michael@0 1984 }
michael@0 1985 else {
michael@0 1986 mScratchArray.AppendElement(nsGkAtoms::leaf);
michael@0 1987 }
michael@0 1988
michael@0 1989 // drop orientation
michael@0 1990 if (mSlots && mSlots->mDropAllowed && mSlots->mDropRow == aRowIndex) {
michael@0 1991 if (mSlots->mDropOrient == nsITreeView::DROP_BEFORE)
michael@0 1992 mScratchArray.AppendElement(nsGkAtoms::dropBefore);
michael@0 1993 else if (mSlots->mDropOrient == nsITreeView::DROP_ON)
michael@0 1994 mScratchArray.AppendElement(nsGkAtoms::dropOn);
michael@0 1995 else if (mSlots->mDropOrient == nsITreeView::DROP_AFTER)
michael@0 1996 mScratchArray.AppendElement(nsGkAtoms::dropAfter);
michael@0 1997 }
michael@0 1998
michael@0 1999 // odd or even
michael@0 2000 if (aRowIndex % 2)
michael@0 2001 mScratchArray.AppendElement(nsGkAtoms::odd);
michael@0 2002 else
michael@0 2003 mScratchArray.AppendElement(nsGkAtoms::even);
michael@0 2004
michael@0 2005 nsIContent* baseContent = GetBaseElement();
michael@0 2006 if (baseContent && baseContent->HasAttr(kNameSpaceID_None, nsGkAtoms::editing))
michael@0 2007 mScratchArray.AppendElement(nsGkAtoms::editing);
michael@0 2008
michael@0 2009 // multiple columns
michael@0 2010 if (mColumns->GetColumnAt(1))
michael@0 2011 mScratchArray.AppendElement(nsGkAtoms::multicol);
michael@0 2012 }
michael@0 2013
michael@0 2014 if (aCol) {
michael@0 2015 mScratchArray.AppendElement(aCol->GetAtom());
michael@0 2016
michael@0 2017 if (aCol->IsPrimary())
michael@0 2018 mScratchArray.AppendElement(nsGkAtoms::primary);
michael@0 2019
michael@0 2020 if (aCol->GetType() == nsITreeColumn::TYPE_CHECKBOX) {
michael@0 2021 mScratchArray.AppendElement(nsGkAtoms::checkbox);
michael@0 2022
michael@0 2023 if (aRowIndex != -1) {
michael@0 2024 nsAutoString value;
michael@0 2025 mView->GetCellValue(aRowIndex, aCol, value);
michael@0 2026 if (value.EqualsLiteral("true"))
michael@0 2027 mScratchArray.AppendElement(nsGkAtoms::checked);
michael@0 2028 }
michael@0 2029 }
michael@0 2030 else if (aCol->GetType() == nsITreeColumn::TYPE_PROGRESSMETER) {
michael@0 2031 mScratchArray.AppendElement(nsGkAtoms::progressmeter);
michael@0 2032
michael@0 2033 if (aRowIndex != -1) {
michael@0 2034 int32_t state;
michael@0 2035 mView->GetProgressMode(aRowIndex, aCol, &state);
michael@0 2036 if (state == nsITreeView::PROGRESS_NORMAL)
michael@0 2037 mScratchArray.AppendElement(nsGkAtoms::progressNormal);
michael@0 2038 else if (state == nsITreeView::PROGRESS_UNDETERMINED)
michael@0 2039 mScratchArray.AppendElement(nsGkAtoms::progressUndetermined);
michael@0 2040 }
michael@0 2041 }
michael@0 2042
michael@0 2043 // Read special properties from attributes on the column content node
michael@0 2044 if (aCol->mContent->AttrValueIs(kNameSpaceID_None,
michael@0 2045 nsGkAtoms::insertbefore,
michael@0 2046 nsGkAtoms::_true, eCaseMatters))
michael@0 2047 mScratchArray.AppendElement(nsGkAtoms::insertbefore);
michael@0 2048 if (aCol->mContent->AttrValueIs(kNameSpaceID_None,
michael@0 2049 nsGkAtoms::insertafter,
michael@0 2050 nsGkAtoms::_true, eCaseMatters))
michael@0 2051 mScratchArray.AppendElement(nsGkAtoms::insertafter);
michael@0 2052 }
michael@0 2053 }
michael@0 2054
michael@0 2055 nsITheme*
michael@0 2056 nsTreeBodyFrame::GetTwistyRect(int32_t aRowIndex,
michael@0 2057 nsTreeColumn* aColumn,
michael@0 2058 nsRect& aImageRect,
michael@0 2059 nsRect& aTwistyRect,
michael@0 2060 nsPresContext* aPresContext,
michael@0 2061 nsRenderingContext& aRenderingContext,
michael@0 2062 nsStyleContext* aTwistyContext)
michael@0 2063 {
michael@0 2064 // The twisty rect extends all the way to the end of the cell. This is incorrect. We need to
michael@0 2065 // determine the twisty rect's true width. This is done by examining the style context for
michael@0 2066 // a width first. If it has one, we use that. If it doesn't, we use the image's natural width.
michael@0 2067 // If the image hasn't loaded and if no width is specified, then we just bail. If there is
michael@0 2068 // a -moz-appearance involved, adjust the rect by the minimum widget size provided by
michael@0 2069 // the theme implementation.
michael@0 2070 aImageRect = GetImageSize(aRowIndex, aColumn, true, aTwistyContext);
michael@0 2071 if (aImageRect.height > aTwistyRect.height)
michael@0 2072 aImageRect.height = aTwistyRect.height;
michael@0 2073 if (aImageRect.width > aTwistyRect.width)
michael@0 2074 aImageRect.width = aTwistyRect.width;
michael@0 2075 else
michael@0 2076 aTwistyRect.width = aImageRect.width;
michael@0 2077
michael@0 2078 bool useTheme = false;
michael@0 2079 nsITheme *theme = nullptr;
michael@0 2080 const nsStyleDisplay* twistyDisplayData = aTwistyContext->StyleDisplay();
michael@0 2081 if (twistyDisplayData->mAppearance) {
michael@0 2082 theme = aPresContext->GetTheme();
michael@0 2083 if (theme && theme->ThemeSupportsWidget(aPresContext, nullptr, twistyDisplayData->mAppearance))
michael@0 2084 useTheme = true;
michael@0 2085 }
michael@0 2086
michael@0 2087 if (useTheme) {
michael@0 2088 nsIntSize minTwistySizePx(0,0);
michael@0 2089 bool canOverride = true;
michael@0 2090 theme->GetMinimumWidgetSize(&aRenderingContext, this, twistyDisplayData->mAppearance,
michael@0 2091 &minTwistySizePx, &canOverride);
michael@0 2092
michael@0 2093 // GMWS() returns size in pixels, we need to convert it back to app units
michael@0 2094 nsSize minTwistySize;
michael@0 2095 minTwistySize.width = aPresContext->DevPixelsToAppUnits(minTwistySizePx.width);
michael@0 2096 minTwistySize.height = aPresContext->DevPixelsToAppUnits(minTwistySizePx.height);
michael@0 2097
michael@0 2098 if (aTwistyRect.width < minTwistySize.width || !canOverride)
michael@0 2099 aTwistyRect.width = minTwistySize.width;
michael@0 2100 }
michael@0 2101
michael@0 2102 return useTheme ? theme : nullptr;
michael@0 2103 }
michael@0 2104
michael@0 2105 nsresult
michael@0 2106 nsTreeBodyFrame::GetImage(int32_t aRowIndex, nsTreeColumn* aCol, bool aUseContext,
michael@0 2107 nsStyleContext* aStyleContext, bool& aAllowImageRegions, imgIContainer** aResult)
michael@0 2108 {
michael@0 2109 *aResult = nullptr;
michael@0 2110
michael@0 2111 nsAutoString imageSrc;
michael@0 2112 mView->GetImageSrc(aRowIndex, aCol, imageSrc);
michael@0 2113 nsRefPtr<imgRequestProxy> styleRequest;
michael@0 2114 if (!aUseContext && !imageSrc.IsEmpty()) {
michael@0 2115 aAllowImageRegions = false;
michael@0 2116 }
michael@0 2117 else {
michael@0 2118 // Obtain the URL from the style context.
michael@0 2119 aAllowImageRegions = true;
michael@0 2120 styleRequest = aStyleContext->StyleList()->GetListStyleImage();
michael@0 2121 if (!styleRequest)
michael@0 2122 return NS_OK;
michael@0 2123 nsCOMPtr<nsIURI> uri;
michael@0 2124 styleRequest->GetURI(getter_AddRefs(uri));
michael@0 2125 nsAutoCString spec;
michael@0 2126 uri->GetSpec(spec);
michael@0 2127 CopyUTF8toUTF16(spec, imageSrc);
michael@0 2128 }
michael@0 2129
michael@0 2130 // Look the image up in our cache.
michael@0 2131 nsTreeImageCacheEntry entry;
michael@0 2132 if (mImageCache.Get(imageSrc, &entry)) {
michael@0 2133 // Find out if the image has loaded.
michael@0 2134 uint32_t status;
michael@0 2135 imgIRequest *imgReq = entry.request;
michael@0 2136 imgReq->GetImageStatus(&status);
michael@0 2137 imgReq->GetImage(aResult); // We hand back the image here. The GetImage call addrefs *aResult.
michael@0 2138 bool animated = true; // Assuming animated is the safe option
michael@0 2139
michael@0 2140 // We can only call GetAnimated if we're decoded
michael@0 2141 if (*aResult && (status & imgIRequest::STATUS_DECODE_COMPLETE))
michael@0 2142 (*aResult)->GetAnimated(&animated);
michael@0 2143
michael@0 2144 if ((!(status & imgIRequest::STATUS_LOAD_COMPLETE)) || animated) {
michael@0 2145 // We either aren't done loading, or we're animating. Add our row as a listener for invalidations.
michael@0 2146 nsCOMPtr<imgINotificationObserver> obs;
michael@0 2147 imgReq->GetNotificationObserver(getter_AddRefs(obs));
michael@0 2148
michael@0 2149 if (obs) {
michael@0 2150 static_cast<nsTreeImageListener*> (obs.get())->AddCell(aRowIndex, aCol);
michael@0 2151 }
michael@0 2152
michael@0 2153 return NS_OK;
michael@0 2154 }
michael@0 2155 }
michael@0 2156
michael@0 2157 if (!*aResult) {
michael@0 2158 // Create a new nsTreeImageListener object and pass it our row and column
michael@0 2159 // information.
michael@0 2160 nsTreeImageListener* listener = new nsTreeImageListener(this);
michael@0 2161 if (!listener)
michael@0 2162 return NS_ERROR_OUT_OF_MEMORY;
michael@0 2163
michael@0 2164 if (!mCreatedListeners.PutEntry(listener)) {
michael@0 2165 return NS_ERROR_FAILURE;
michael@0 2166 }
michael@0 2167
michael@0 2168 listener->AddCell(aRowIndex, aCol);
michael@0 2169 nsCOMPtr<imgINotificationObserver> imgNotificationObserver = listener;
michael@0 2170
michael@0 2171 nsRefPtr<imgRequestProxy> imageRequest;
michael@0 2172 if (styleRequest) {
michael@0 2173 styleRequest->Clone(imgNotificationObserver, getter_AddRefs(imageRequest));
michael@0 2174 } else {
michael@0 2175 nsIDocument* doc = mContent->GetDocument();
michael@0 2176 if (!doc)
michael@0 2177 // The page is currently being torn down. Why bother.
michael@0 2178 return NS_ERROR_FAILURE;
michael@0 2179
michael@0 2180 nsCOMPtr<nsIURI> baseURI = mContent->GetBaseURI();
michael@0 2181
michael@0 2182 nsCOMPtr<nsIURI> srcURI;
michael@0 2183 nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(srcURI),
michael@0 2184 imageSrc,
michael@0 2185 doc,
michael@0 2186 baseURI);
michael@0 2187 if (!srcURI)
michael@0 2188 return NS_ERROR_FAILURE;
michael@0 2189
michael@0 2190 // XXXbz what's the origin principal for this stuff that comes from our
michael@0 2191 // view? I guess we should assume that it's the node's principal...
michael@0 2192 if (nsContentUtils::CanLoadImage(srcURI, mContent, doc,
michael@0 2193 mContent->NodePrincipal())) {
michael@0 2194 nsresult rv = nsContentUtils::LoadImage(srcURI,
michael@0 2195 doc,
michael@0 2196 mContent->NodePrincipal(),
michael@0 2197 doc->GetDocumentURI(),
michael@0 2198 imgNotificationObserver,
michael@0 2199 nsIRequest::LOAD_NORMAL,
michael@0 2200 EmptyString(),
michael@0 2201 getter_AddRefs(imageRequest));
michael@0 2202 NS_ENSURE_SUCCESS(rv, rv);
michael@0 2203
michael@0 2204 }
michael@0 2205 }
michael@0 2206 listener->UnsuppressInvalidation();
michael@0 2207
michael@0 2208 if (!imageRequest)
michael@0 2209 return NS_ERROR_FAILURE;
michael@0 2210
michael@0 2211 // We don't want discarding/decode-on-draw for xul images
michael@0 2212 imageRequest->StartDecoding();
michael@0 2213 imageRequest->LockImage();
michael@0 2214
michael@0 2215 // In a case it was already cached.
michael@0 2216 imageRequest->GetImage(aResult);
michael@0 2217 nsTreeImageCacheEntry cacheEntry(imageRequest, imgNotificationObserver);
michael@0 2218 mImageCache.Put(imageSrc, cacheEntry);
michael@0 2219 }
michael@0 2220 return NS_OK;
michael@0 2221 }
michael@0 2222
michael@0 2223 nsRect nsTreeBodyFrame::GetImageSize(int32_t aRowIndex, nsTreeColumn* aCol, bool aUseContext,
michael@0 2224 nsStyleContext* aStyleContext)
michael@0 2225 {
michael@0 2226 // XXX We should respond to visibility rules for collapsed vs. hidden.
michael@0 2227
michael@0 2228 // This method returns the width of the twisty INCLUDING borders and padding.
michael@0 2229 // It first checks the style context for a width. If none is found, it tries to
michael@0 2230 // use the default image width for the twisty. If no image is found, it defaults
michael@0 2231 // to border+padding.
michael@0 2232 nsRect r(0,0,0,0);
michael@0 2233 nsMargin bp(0,0,0,0);
michael@0 2234 GetBorderPadding(aStyleContext, bp);
michael@0 2235 r.Inflate(bp);
michael@0 2236
michael@0 2237 // Now r contains our border+padding info. We now need to get our width and
michael@0 2238 // height.
michael@0 2239 bool needWidth = false;
michael@0 2240 bool needHeight = false;
michael@0 2241
michael@0 2242 // We have to load image even though we already have a size.
michael@0 2243 // Don't change this, otherwise things start to go crazy.
michael@0 2244 bool useImageRegion = true;
michael@0 2245 nsCOMPtr<imgIContainer> image;
michael@0 2246 GetImage(aRowIndex, aCol, aUseContext, aStyleContext, useImageRegion, getter_AddRefs(image));
michael@0 2247
michael@0 2248 const nsStylePosition* myPosition = aStyleContext->StylePosition();
michael@0 2249 const nsStyleList* myList = aStyleContext->StyleList();
michael@0 2250
michael@0 2251 if (useImageRegion) {
michael@0 2252 r.x += myList->mImageRegion.x;
michael@0 2253 r.y += myList->mImageRegion.y;
michael@0 2254 }
michael@0 2255
michael@0 2256 if (myPosition->mWidth.GetUnit() == eStyleUnit_Coord) {
michael@0 2257 int32_t val = myPosition->mWidth.GetCoordValue();
michael@0 2258 r.width += val;
michael@0 2259 }
michael@0 2260 else if (useImageRegion && myList->mImageRegion.width > 0)
michael@0 2261 r.width += myList->mImageRegion.width;
michael@0 2262 else
michael@0 2263 needWidth = true;
michael@0 2264
michael@0 2265 if (myPosition->mHeight.GetUnit() == eStyleUnit_Coord) {
michael@0 2266 int32_t val = myPosition->mHeight.GetCoordValue();
michael@0 2267 r.height += val;
michael@0 2268 }
michael@0 2269 else if (useImageRegion && myList->mImageRegion.height > 0)
michael@0 2270 r.height += myList->mImageRegion.height;
michael@0 2271 else
michael@0 2272 needHeight = true;
michael@0 2273
michael@0 2274 if (image) {
michael@0 2275 if (needWidth || needHeight) {
michael@0 2276 // Get the natural image size.
michael@0 2277
michael@0 2278 if (needWidth) {
michael@0 2279 // Get the size from the image.
michael@0 2280 nscoord width;
michael@0 2281 image->GetWidth(&width);
michael@0 2282 r.width += nsPresContext::CSSPixelsToAppUnits(width);
michael@0 2283 }
michael@0 2284
michael@0 2285 if (needHeight) {
michael@0 2286 nscoord height;
michael@0 2287 image->GetHeight(&height);
michael@0 2288 r.height += nsPresContext::CSSPixelsToAppUnits(height);
michael@0 2289 }
michael@0 2290 }
michael@0 2291 }
michael@0 2292
michael@0 2293 return r;
michael@0 2294 }
michael@0 2295
michael@0 2296 // GetImageDestSize returns the destination size of the image.
michael@0 2297 // The width and height do not include borders and padding.
michael@0 2298 // The width and height have not been adjusted to fit in the row height
michael@0 2299 // or cell width.
michael@0 2300 // The width and height reflect the destination size specified in CSS,
michael@0 2301 // or the image region specified in CSS, or the natural size of the
michael@0 2302 // image.
michael@0 2303 // If only the destination width has been specified in CSS, the height is
michael@0 2304 // calculated to maintain the aspect ratio of the image.
michael@0 2305 // If only the destination height has been specified in CSS, the width is
michael@0 2306 // calculated to maintain the aspect ratio of the image.
michael@0 2307 nsSize
michael@0 2308 nsTreeBodyFrame::GetImageDestSize(nsStyleContext* aStyleContext,
michael@0 2309 bool useImageRegion,
michael@0 2310 imgIContainer* image)
michael@0 2311 {
michael@0 2312 nsSize size(0,0);
michael@0 2313
michael@0 2314 // We need to get the width and height.
michael@0 2315 bool needWidth = false;
michael@0 2316 bool needHeight = false;
michael@0 2317
michael@0 2318 // Get the style position to see if the CSS has specified the
michael@0 2319 // destination width/height.
michael@0 2320 const nsStylePosition* myPosition = aStyleContext->StylePosition();
michael@0 2321
michael@0 2322 if (myPosition->mWidth.GetUnit() == eStyleUnit_Coord) {
michael@0 2323 // CSS has specified the destination width.
michael@0 2324 size.width = myPosition->mWidth.GetCoordValue();
michael@0 2325 }
michael@0 2326 else {
michael@0 2327 // We'll need to get the width of the image/region.
michael@0 2328 needWidth = true;
michael@0 2329 }
michael@0 2330
michael@0 2331 if (myPosition->mHeight.GetUnit() == eStyleUnit_Coord) {
michael@0 2332 // CSS has specified the destination height.
michael@0 2333 size.height = myPosition->mHeight.GetCoordValue();
michael@0 2334 }
michael@0 2335 else {
michael@0 2336 // We'll need to get the height of the image/region.
michael@0 2337 needHeight = true;
michael@0 2338 }
michael@0 2339
michael@0 2340 if (needWidth || needHeight) {
michael@0 2341 // We need to get the size of the image/region.
michael@0 2342 nsSize imageSize(0,0);
michael@0 2343
michael@0 2344 const nsStyleList* myList = aStyleContext->StyleList();
michael@0 2345
michael@0 2346 if (useImageRegion && myList->mImageRegion.width > 0) {
michael@0 2347 // CSS has specified an image region.
michael@0 2348 // Use the width of the image region.
michael@0 2349 imageSize.width = myList->mImageRegion.width;
michael@0 2350 }
michael@0 2351 else if (image) {
michael@0 2352 nscoord width;
michael@0 2353 image->GetWidth(&width);
michael@0 2354 imageSize.width = nsPresContext::CSSPixelsToAppUnits(width);
michael@0 2355 }
michael@0 2356
michael@0 2357 if (useImageRegion && myList->mImageRegion.height > 0) {
michael@0 2358 // CSS has specified an image region.
michael@0 2359 // Use the height of the image region.
michael@0 2360 imageSize.height = myList->mImageRegion.height;
michael@0 2361 }
michael@0 2362 else if (image) {
michael@0 2363 nscoord height;
michael@0 2364 image->GetHeight(&height);
michael@0 2365 imageSize.height = nsPresContext::CSSPixelsToAppUnits(height);
michael@0 2366 }
michael@0 2367
michael@0 2368 if (needWidth) {
michael@0 2369 if (!needHeight && imageSize.height != 0) {
michael@0 2370 // The CSS specified the destination height, but not the destination
michael@0 2371 // width. We need to calculate the width so that we maintain the
michael@0 2372 // image's aspect ratio.
michael@0 2373 size.width = imageSize.width * size.height / imageSize.height;
michael@0 2374 }
michael@0 2375 else {
michael@0 2376 size.width = imageSize.width;
michael@0 2377 }
michael@0 2378 }
michael@0 2379
michael@0 2380 if (needHeight) {
michael@0 2381 if (!needWidth && imageSize.width != 0) {
michael@0 2382 // The CSS specified the destination width, but not the destination
michael@0 2383 // height. We need to calculate the height so that we maintain the
michael@0 2384 // image's aspect ratio.
michael@0 2385 size.height = imageSize.height * size.width / imageSize.width;
michael@0 2386 }
michael@0 2387 else {
michael@0 2388 size.height = imageSize.height;
michael@0 2389 }
michael@0 2390 }
michael@0 2391 }
michael@0 2392
michael@0 2393 return size;
michael@0 2394 }
michael@0 2395
michael@0 2396 // GetImageSourceRect returns the source rectangle of the image to be
michael@0 2397 // displayed.
michael@0 2398 // The width and height reflect the image region specified in CSS, or
michael@0 2399 // the natural size of the image.
michael@0 2400 // The width and height do not include borders and padding.
michael@0 2401 // The width and height do not reflect the destination size specified
michael@0 2402 // in CSS.
michael@0 2403 nsRect
michael@0 2404 nsTreeBodyFrame::GetImageSourceRect(nsStyleContext* aStyleContext,
michael@0 2405 bool useImageRegion,
michael@0 2406 imgIContainer* image)
michael@0 2407 {
michael@0 2408 nsRect r(0,0,0,0);
michael@0 2409
michael@0 2410 const nsStyleList* myList = aStyleContext->StyleList();
michael@0 2411
michael@0 2412 if (useImageRegion &&
michael@0 2413 (myList->mImageRegion.width > 0 || myList->mImageRegion.height > 0)) {
michael@0 2414 // CSS has specified an image region.
michael@0 2415 r = myList->mImageRegion;
michael@0 2416 }
michael@0 2417 else if (image) {
michael@0 2418 // Use the actual image size.
michael@0 2419 nscoord coord;
michael@0 2420 image->GetWidth(&coord);
michael@0 2421 r.width = nsPresContext::CSSPixelsToAppUnits(coord);
michael@0 2422 image->GetHeight(&coord);
michael@0 2423 r.height = nsPresContext::CSSPixelsToAppUnits(coord);
michael@0 2424 }
michael@0 2425
michael@0 2426 return r;
michael@0 2427 }
michael@0 2428
michael@0 2429 int32_t nsTreeBodyFrame::GetRowHeight()
michael@0 2430 {
michael@0 2431 // Look up the correct height. It is equal to the specified height
michael@0 2432 // + the specified margins.
michael@0 2433 mScratchArray.Clear();
michael@0 2434 nsStyleContext* rowContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreerow);
michael@0 2435 if (rowContext) {
michael@0 2436 const nsStylePosition* myPosition = rowContext->StylePosition();
michael@0 2437
michael@0 2438 nscoord minHeight = 0;
michael@0 2439 if (myPosition->mMinHeight.GetUnit() == eStyleUnit_Coord)
michael@0 2440 minHeight = myPosition->mMinHeight.GetCoordValue();
michael@0 2441
michael@0 2442 nscoord height = 0;
michael@0 2443 if (myPosition->mHeight.GetUnit() == eStyleUnit_Coord)
michael@0 2444 height = myPosition->mHeight.GetCoordValue();
michael@0 2445
michael@0 2446 if (height < minHeight)
michael@0 2447 height = minHeight;
michael@0 2448
michael@0 2449 if (height > 0) {
michael@0 2450 height = nsPresContext::AppUnitsToIntCSSPixels(height);
michael@0 2451 height += height % 2;
michael@0 2452 height = nsPresContext::CSSPixelsToAppUnits(height);
michael@0 2453
michael@0 2454 // XXX Check box-sizing to determine if border/padding should augment the height
michael@0 2455 // Inflate the height by our margins.
michael@0 2456 nsRect rowRect(0,0,0,height);
michael@0 2457 nsMargin rowMargin;
michael@0 2458 rowContext->StyleMargin()->GetMargin(rowMargin);
michael@0 2459 rowRect.Inflate(rowMargin);
michael@0 2460 height = rowRect.height;
michael@0 2461 return height;
michael@0 2462 }
michael@0 2463 }
michael@0 2464
michael@0 2465 return nsPresContext::CSSPixelsToAppUnits(18); // As good a default as any.
michael@0 2466 }
michael@0 2467
michael@0 2468 int32_t nsTreeBodyFrame::GetIndentation()
michael@0 2469 {
michael@0 2470 // Look up the correct indentation. It is equal to the specified indentation width.
michael@0 2471 mScratchArray.Clear();
michael@0 2472 nsStyleContext* indentContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeindentation);
michael@0 2473 if (indentContext) {
michael@0 2474 const nsStylePosition* myPosition = indentContext->StylePosition();
michael@0 2475 if (myPosition->mWidth.GetUnit() == eStyleUnit_Coord) {
michael@0 2476 nscoord val = myPosition->mWidth.GetCoordValue();
michael@0 2477 return val;
michael@0 2478 }
michael@0 2479 }
michael@0 2480
michael@0 2481 return nsPresContext::CSSPixelsToAppUnits(16); // As good a default as any.
michael@0 2482 }
michael@0 2483
michael@0 2484 void nsTreeBodyFrame::CalcInnerBox()
michael@0 2485 {
michael@0 2486 mInnerBox.SetRect(0, 0, mRect.width, mRect.height);
michael@0 2487 AdjustForBorderPadding(mStyleContext, mInnerBox);
michael@0 2488 }
michael@0 2489
michael@0 2490 nscoord
michael@0 2491 nsTreeBodyFrame::CalcHorzWidth(const ScrollParts& aParts)
michael@0 2492 {
michael@0 2493 // Compute the adjustment to the last column. This varies depending on the
michael@0 2494 // visibility of the columnpicker and the scrollbar.
michael@0 2495 if (aParts.mColumnsFrame)
michael@0 2496 mAdjustWidth = mRect.width - aParts.mColumnsFrame->GetRect().width;
michael@0 2497 else
michael@0 2498 mAdjustWidth = 0;
michael@0 2499
michael@0 2500 nscoord width = 0;
michael@0 2501
michael@0 2502 // We calculate this from the scrollable frame, so that it
michael@0 2503 // properly covers all contingencies of what could be
michael@0 2504 // scrollable (columns, body, etc...)
michael@0 2505
michael@0 2506 if (aParts.mColumnsScrollFrame) {
michael@0 2507 width = aParts.mColumnsScrollFrame->GetScrollRange().width +
michael@0 2508 aParts.mColumnsScrollFrame->GetScrollPortRect().width;
michael@0 2509 }
michael@0 2510
michael@0 2511 // If no horz scrolling periphery is present, then just return our width
michael@0 2512 if (width == 0)
michael@0 2513 width = mRect.width;
michael@0 2514
michael@0 2515 return width;
michael@0 2516 }
michael@0 2517
michael@0 2518 nsresult
michael@0 2519 nsTreeBodyFrame::GetCursor(const nsPoint& aPoint,
michael@0 2520 nsIFrame::Cursor& aCursor)
michael@0 2521 {
michael@0 2522 // Check the GetScriptHandlingObject so we don't end up running code when
michael@0 2523 // the document is a zombie.
michael@0 2524 bool dummy;
michael@0 2525 if (mView && GetContent()->GetCurrentDoc()->GetScriptHandlingObject(dummy)) {
michael@0 2526 int32_t row;
michael@0 2527 nsTreeColumn* col;
michael@0 2528 nsIAtom* child;
michael@0 2529 GetCellAt(aPoint.x, aPoint.y, &row, &col, &child);
michael@0 2530
michael@0 2531 if (child) {
michael@0 2532 // Our scratch array is already prefilled.
michael@0 2533 nsStyleContext* childContext = GetPseudoStyleContext(child);
michael@0 2534
michael@0 2535 FillCursorInformationFromStyle(childContext->StyleUserInterface(),
michael@0 2536 aCursor);
michael@0 2537 if (aCursor.mCursor == NS_STYLE_CURSOR_AUTO)
michael@0 2538 aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT;
michael@0 2539
michael@0 2540 return NS_OK;
michael@0 2541 }
michael@0 2542 }
michael@0 2543
michael@0 2544 return nsLeafBoxFrame::GetCursor(aPoint, aCursor);
michael@0 2545 }
michael@0 2546
michael@0 2547 static uint32_t GetDropEffect(WidgetGUIEvent* aEvent)
michael@0 2548 {
michael@0 2549 NS_ASSERTION(aEvent->eventStructType == NS_DRAG_EVENT, "wrong event type");
michael@0 2550 WidgetDragEvent* dragEvent = aEvent->AsDragEvent();
michael@0 2551 nsContentUtils::SetDataTransferInEvent(dragEvent);
michael@0 2552
michael@0 2553 uint32_t action = 0;
michael@0 2554 if (dragEvent->dataTransfer)
michael@0 2555 dragEvent->dataTransfer->GetDropEffectInt(&action);
michael@0 2556 return action;
michael@0 2557 }
michael@0 2558
michael@0 2559 nsresult
michael@0 2560 nsTreeBodyFrame::HandleEvent(nsPresContext* aPresContext,
michael@0 2561 WidgetGUIEvent* aEvent,
michael@0 2562 nsEventStatus* aEventStatus)
michael@0 2563 {
michael@0 2564 if (aEvent->message == NS_MOUSE_ENTER_SYNTH || aEvent->message == NS_MOUSE_MOVE) {
michael@0 2565 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this);
michael@0 2566 int32_t xTwips = pt.x - mInnerBox.x;
michael@0 2567 int32_t yTwips = pt.y - mInnerBox.y;
michael@0 2568 int32_t newrow = GetRowAt(xTwips, yTwips);
michael@0 2569 if (mMouseOverRow != newrow) {
michael@0 2570 // redraw the old and the new row
michael@0 2571 if (mMouseOverRow != -1)
michael@0 2572 InvalidateRow(mMouseOverRow);
michael@0 2573 mMouseOverRow = newrow;
michael@0 2574 if (mMouseOverRow != -1)
michael@0 2575 InvalidateRow(mMouseOverRow);
michael@0 2576 }
michael@0 2577 }
michael@0 2578 else if (aEvent->message == NS_MOUSE_EXIT_SYNTH) {
michael@0 2579 if (mMouseOverRow != -1) {
michael@0 2580 InvalidateRow(mMouseOverRow);
michael@0 2581 mMouseOverRow = -1;
michael@0 2582 }
michael@0 2583 }
michael@0 2584 else if (aEvent->message == NS_DRAGDROP_ENTER) {
michael@0 2585 if (!mSlots)
michael@0 2586 mSlots = new Slots();
michael@0 2587
michael@0 2588 // Cache several things we'll need throughout the course of our work. These
michael@0 2589 // will all get released on a drag exit.
michael@0 2590
michael@0 2591 if (mSlots->mTimer) {
michael@0 2592 mSlots->mTimer->Cancel();
michael@0 2593 mSlots->mTimer = nullptr;
michael@0 2594 }
michael@0 2595
michael@0 2596 // Cache the drag session.
michael@0 2597 mSlots->mIsDragging = true;
michael@0 2598 mSlots->mDropRow = -1;
michael@0 2599 mSlots->mDropOrient = -1;
michael@0 2600 mSlots->mDragAction = GetDropEffect(aEvent);
michael@0 2601 }
michael@0 2602 else if (aEvent->message == NS_DRAGDROP_OVER) {
michael@0 2603 // The mouse is hovering over this tree. If we determine things are
michael@0 2604 // different from the last time, invalidate the drop feedback at the old
michael@0 2605 // position, query the view to see if the current location is droppable,
michael@0 2606 // and then invalidate the drop feedback at the new location if it is.
michael@0 2607 // The mouse may or may not have changed position from the last time
michael@0 2608 // we were called, so optimize out a lot of the extra notifications by
michael@0 2609 // checking if anything changed first. For drop feedback we use drop,
michael@0 2610 // dropBefore and dropAfter property.
michael@0 2611
michael@0 2612 if (!mView || !mSlots)
michael@0 2613 return NS_OK;
michael@0 2614
michael@0 2615 // Save last values, we will need them.
michael@0 2616 int32_t lastDropRow = mSlots->mDropRow;
michael@0 2617 int16_t lastDropOrient = mSlots->mDropOrient;
michael@0 2618 #ifndef XP_MACOSX
michael@0 2619 int16_t lastScrollLines = mSlots->mScrollLines;
michael@0 2620 #endif
michael@0 2621
michael@0 2622 // Find out the current drag action
michael@0 2623 uint32_t lastDragAction = mSlots->mDragAction;
michael@0 2624 mSlots->mDragAction = GetDropEffect(aEvent);
michael@0 2625
michael@0 2626 // Compute the row mouse is over and the above/below/on state.
michael@0 2627 // Below we'll use this to see if anything changed.
michael@0 2628 // Also check if we want to auto-scroll.
michael@0 2629 ComputeDropPosition(aEvent, &mSlots->mDropRow, &mSlots->mDropOrient, &mSlots->mScrollLines);
michael@0 2630
michael@0 2631 // While we're here, handle tracking of scrolling during a drag.
michael@0 2632 if (mSlots->mScrollLines) {
michael@0 2633 if (mSlots->mDropAllowed) {
michael@0 2634 // Invalidate primary cell at old location.
michael@0 2635 mSlots->mDropAllowed = false;
michael@0 2636 InvalidateDropFeedback(lastDropRow, lastDropOrient);
michael@0 2637 }
michael@0 2638 #ifdef XP_MACOSX
michael@0 2639 ScrollByLines(mSlots->mScrollLines);
michael@0 2640 #else
michael@0 2641 if (!lastScrollLines) {
michael@0 2642 // Cancel any previously initialized timer.
michael@0 2643 if (mSlots->mTimer) {
michael@0 2644 mSlots->mTimer->Cancel();
michael@0 2645 mSlots->mTimer = nullptr;
michael@0 2646 }
michael@0 2647
michael@0 2648 // Set a timer to trigger the tree scrolling.
michael@0 2649 CreateTimer(LookAndFeel::eIntID_TreeLazyScrollDelay,
michael@0 2650 LazyScrollCallback, nsITimer::TYPE_ONE_SHOT,
michael@0 2651 getter_AddRefs(mSlots->mTimer));
michael@0 2652 }
michael@0 2653 #endif
michael@0 2654 // Bail out to prevent spring loaded timer and feedback line settings.
michael@0 2655 return NS_OK;
michael@0 2656 }
michael@0 2657
michael@0 2658 // If changed from last time, invalidate primary cell at the old location and if allowed,
michael@0 2659 // invalidate primary cell at the new location. If nothing changed, just bail.
michael@0 2660 if (mSlots->mDropRow != lastDropRow ||
michael@0 2661 mSlots->mDropOrient != lastDropOrient ||
michael@0 2662 mSlots->mDragAction != lastDragAction) {
michael@0 2663
michael@0 2664 // Invalidate row at the old location.
michael@0 2665 if (mSlots->mDropAllowed) {
michael@0 2666 mSlots->mDropAllowed = false;
michael@0 2667 InvalidateDropFeedback(lastDropRow, lastDropOrient);
michael@0 2668 }
michael@0 2669
michael@0 2670 if (mSlots->mTimer) {
michael@0 2671 // Timer is active but for a different row than the current one, kill it.
michael@0 2672 mSlots->mTimer->Cancel();
michael@0 2673 mSlots->mTimer = nullptr;
michael@0 2674 }
michael@0 2675
michael@0 2676 if (mSlots->mDropRow >= 0) {
michael@0 2677 if (!mSlots->mTimer && mSlots->mDropOrient == nsITreeView::DROP_ON) {
michael@0 2678 // Either there wasn't a timer running or it was just killed above.
michael@0 2679 // If over a folder, start up a timer to open the folder.
michael@0 2680 bool isContainer = false;
michael@0 2681 mView->IsContainer(mSlots->mDropRow, &isContainer);
michael@0 2682 if (isContainer) {
michael@0 2683 bool isOpen = false;
michael@0 2684 mView->IsContainerOpen(mSlots->mDropRow, &isOpen);
michael@0 2685 if (!isOpen) {
michael@0 2686 // This node isn't expanded, set a timer to expand it.
michael@0 2687 CreateTimer(LookAndFeel::eIntID_TreeOpenDelay,
michael@0 2688 OpenCallback, nsITimer::TYPE_ONE_SHOT,
michael@0 2689 getter_AddRefs(mSlots->mTimer));
michael@0 2690 }
michael@0 2691 }
michael@0 2692 }
michael@0 2693
michael@0 2694 // The dataTransfer was initialized by the call to GetDropEffect above.
michael@0 2695 bool canDropAtNewLocation = false;
michael@0 2696 mView->CanDrop(mSlots->mDropRow, mSlots->mDropOrient,
michael@0 2697 aEvent->AsDragEvent()->dataTransfer,
michael@0 2698 &canDropAtNewLocation);
michael@0 2699
michael@0 2700 if (canDropAtNewLocation) {
michael@0 2701 // Invalidate row at the new location.
michael@0 2702 mSlots->mDropAllowed = canDropAtNewLocation;
michael@0 2703 InvalidateDropFeedback(mSlots->mDropRow, mSlots->mDropOrient);
michael@0 2704 }
michael@0 2705 }
michael@0 2706 }
michael@0 2707
michael@0 2708 // Indicate that the drop is allowed by preventing the default behaviour.
michael@0 2709 if (mSlots->mDropAllowed)
michael@0 2710 *aEventStatus = nsEventStatus_eConsumeNoDefault;
michael@0 2711 }
michael@0 2712 else if (aEvent->message == NS_DRAGDROP_DROP) {
michael@0 2713 // this event was meant for another frame, so ignore it
michael@0 2714 if (!mSlots)
michael@0 2715 return NS_OK;
michael@0 2716
michael@0 2717 // Tell the view where the drop happened.
michael@0 2718
michael@0 2719 // Remove the drop folder and all its parents from the array.
michael@0 2720 int32_t parentIndex;
michael@0 2721 nsresult rv = mView->GetParentIndex(mSlots->mDropRow, &parentIndex);
michael@0 2722 while (NS_SUCCEEDED(rv) && parentIndex >= 0) {
michael@0 2723 mSlots->mArray.RemoveElement(parentIndex);
michael@0 2724 rv = mView->GetParentIndex(parentIndex, &parentIndex);
michael@0 2725 }
michael@0 2726
michael@0 2727 NS_ASSERTION(aEvent->eventStructType == NS_DRAG_EVENT, "wrong event type");
michael@0 2728 WidgetDragEvent* dragEvent = aEvent->AsDragEvent();
michael@0 2729 nsContentUtils::SetDataTransferInEvent(dragEvent);
michael@0 2730
michael@0 2731 mView->Drop(mSlots->mDropRow, mSlots->mDropOrient, dragEvent->dataTransfer);
michael@0 2732 mSlots->mDropRow = -1;
michael@0 2733 mSlots->mDropOrient = -1;
michael@0 2734 mSlots->mIsDragging = false;
michael@0 2735 *aEventStatus = nsEventStatus_eConsumeNoDefault; // already handled the drop
michael@0 2736 }
michael@0 2737 else if (aEvent->message == NS_DRAGDROP_EXIT) {
michael@0 2738 // this event was meant for another frame, so ignore it
michael@0 2739 if (!mSlots)
michael@0 2740 return NS_OK;
michael@0 2741
michael@0 2742 // Clear out all our tracking vars.
michael@0 2743
michael@0 2744 if (mSlots->mDropAllowed) {
michael@0 2745 mSlots->mDropAllowed = false;
michael@0 2746 InvalidateDropFeedback(mSlots->mDropRow, mSlots->mDropOrient);
michael@0 2747 }
michael@0 2748 else
michael@0 2749 mSlots->mDropAllowed = false;
michael@0 2750 mSlots->mIsDragging = false;
michael@0 2751 mSlots->mScrollLines = 0;
michael@0 2752 // If a drop is occuring, the exit event will fire just before the drop
michael@0 2753 // event, so don't reset mDropRow or mDropOrient as these fields are used
michael@0 2754 // by the drop event.
michael@0 2755 if (mSlots->mTimer) {
michael@0 2756 mSlots->mTimer->Cancel();
michael@0 2757 mSlots->mTimer = nullptr;
michael@0 2758 }
michael@0 2759
michael@0 2760 if (!mSlots->mArray.IsEmpty()) {
michael@0 2761 // Close all spring loaded folders except the drop folder.
michael@0 2762 CreateTimer(LookAndFeel::eIntID_TreeCloseDelay,
michael@0 2763 CloseCallback, nsITimer::TYPE_ONE_SHOT,
michael@0 2764 getter_AddRefs(mSlots->mTimer));
michael@0 2765 }
michael@0 2766 }
michael@0 2767
michael@0 2768 return NS_OK;
michael@0 2769 }
michael@0 2770
michael@0 2771 static nsLineStyle
michael@0 2772 ConvertBorderStyleToLineStyle(uint8_t aBorderStyle)
michael@0 2773 {
michael@0 2774 switch (aBorderStyle) {
michael@0 2775 case NS_STYLE_BORDER_STYLE_DOTTED:
michael@0 2776 return nsLineStyle_kDotted;
michael@0 2777 case NS_STYLE_BORDER_STYLE_DASHED:
michael@0 2778 return nsLineStyle_kDashed;
michael@0 2779 default:
michael@0 2780 return nsLineStyle_kSolid;
michael@0 2781 }
michael@0 2782 }
michael@0 2783
michael@0 2784 static void
michael@0 2785 PaintTreeBody(nsIFrame* aFrame, nsRenderingContext* aCtx,
michael@0 2786 const nsRect& aDirtyRect, nsPoint aPt)
michael@0 2787 {
michael@0 2788 static_cast<nsTreeBodyFrame*>(aFrame)->PaintTreeBody(*aCtx, aDirtyRect, aPt);
michael@0 2789 }
michael@0 2790
michael@0 2791 // Painting routines
michael@0 2792 void
michael@0 2793 nsTreeBodyFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
michael@0 2794 const nsRect& aDirtyRect,
michael@0 2795 const nsDisplayListSet& aLists)
michael@0 2796 {
michael@0 2797 // REVIEW: why did we paint if we were collapsed? that makes no sense!
michael@0 2798 if (!IsVisibleForPainting(aBuilder))
michael@0 2799 return; // We're invisible. Don't paint.
michael@0 2800
michael@0 2801 // Handles painting our background, border, and outline.
michael@0 2802 nsLeafBoxFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
michael@0 2803
michael@0 2804 // Bail out now if there's no view or we can't run script because the
michael@0 2805 // document is a zombie
michael@0 2806 if (!mView || !GetContent()->GetCurrentDoc()->GetWindow())
michael@0 2807 return;
michael@0 2808
michael@0 2809 aLists.Content()->AppendNewToTop(new (aBuilder)
michael@0 2810 nsDisplayGeneric(aBuilder, this, ::PaintTreeBody, "XULTreeBody",
michael@0 2811 nsDisplayItem::TYPE_XUL_TREE_BODY));
michael@0 2812 }
michael@0 2813
michael@0 2814 void
michael@0 2815 nsTreeBodyFrame::PaintTreeBody(nsRenderingContext& aRenderingContext,
michael@0 2816 const nsRect& aDirtyRect, nsPoint aPt)
michael@0 2817 {
michael@0 2818 // Update our available height and our page count.
michael@0 2819 CalcInnerBox();
michael@0 2820 aRenderingContext.PushState();
michael@0 2821 aRenderingContext.IntersectClip(mInnerBox + aPt);
michael@0 2822 int32_t oldPageCount = mPageLength;
michael@0 2823 if (!mHasFixedRowCount)
michael@0 2824 mPageLength = mInnerBox.height/mRowHeight;
michael@0 2825
michael@0 2826 if (oldPageCount != mPageLength || mHorzWidth != CalcHorzWidth(GetScrollParts())) {
michael@0 2827 // Schedule a ResizeReflow that will update our info properly.
michael@0 2828 PresContext()->PresShell()->
michael@0 2829 FrameNeedsReflow(this, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
michael@0 2830 }
michael@0 2831 #ifdef DEBUG
michael@0 2832 int32_t rowCount = mRowCount;
michael@0 2833 mView->GetRowCount(&rowCount);
michael@0 2834 NS_WARN_IF_FALSE(mRowCount == rowCount, "row count changed unexpectedly");
michael@0 2835 #endif
michael@0 2836
michael@0 2837 // Loop through our columns and paint them (e.g., for sorting). This is only
michael@0 2838 // relevant when painting backgrounds, since columns contain no content. Content
michael@0 2839 // is contained in the rows.
michael@0 2840 for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol;
michael@0 2841 currCol = currCol->GetNext()) {
michael@0 2842 nsRect colRect;
michael@0 2843 nsresult rv = currCol->GetRect(this, mInnerBox.y, mInnerBox.height,
michael@0 2844 &colRect);
michael@0 2845 // Don't paint hidden columns.
michael@0 2846 if (NS_FAILED(rv) || colRect.width == 0) continue;
michael@0 2847
michael@0 2848 if (OffsetForHorzScroll(colRect, false)) {
michael@0 2849 nsRect dirtyRect;
michael@0 2850 colRect += aPt;
michael@0 2851 if (dirtyRect.IntersectRect(aDirtyRect, colRect)) {
michael@0 2852 PaintColumn(currCol, colRect, PresContext(), aRenderingContext, aDirtyRect);
michael@0 2853 }
michael@0 2854 }
michael@0 2855 }
michael@0 2856 // Loop through our on-screen rows.
michael@0 2857 for (int32_t i = mTopRowIndex; i < mRowCount && i <= mTopRowIndex+mPageLength; i++) {
michael@0 2858 nsRect rowRect(mInnerBox.x, mInnerBox.y+mRowHeight*(i-mTopRowIndex), mInnerBox.width, mRowHeight);
michael@0 2859 nsRect dirtyRect;
michael@0 2860 if (dirtyRect.IntersectRect(aDirtyRect, rowRect + aPt) &&
michael@0 2861 rowRect.y < (mInnerBox.y+mInnerBox.height)) {
michael@0 2862 PaintRow(i, rowRect + aPt, PresContext(), aRenderingContext, aDirtyRect, aPt);
michael@0 2863 }
michael@0 2864 }
michael@0 2865
michael@0 2866 if (mSlots && mSlots->mDropAllowed && (mSlots->mDropOrient == nsITreeView::DROP_BEFORE ||
michael@0 2867 mSlots->mDropOrient == nsITreeView::DROP_AFTER)) {
michael@0 2868 nscoord yPos = mInnerBox.y + mRowHeight * (mSlots->mDropRow - mTopRowIndex) - mRowHeight / 2;
michael@0 2869 nsRect feedbackRect(mInnerBox.x, yPos, mInnerBox.width, mRowHeight);
michael@0 2870 if (mSlots->mDropOrient == nsITreeView::DROP_AFTER)
michael@0 2871 feedbackRect.y += mRowHeight;
michael@0 2872
michael@0 2873 nsRect dirtyRect;
michael@0 2874 feedbackRect += aPt;
michael@0 2875 if (dirtyRect.IntersectRect(aDirtyRect, feedbackRect)) {
michael@0 2876 PaintDropFeedback(feedbackRect, PresContext(), aRenderingContext, aDirtyRect, aPt);
michael@0 2877 }
michael@0 2878 }
michael@0 2879 aRenderingContext.PopState();
michael@0 2880 }
michael@0 2881
michael@0 2882
michael@0 2883
michael@0 2884 void
michael@0 2885 nsTreeBodyFrame::PaintColumn(nsTreeColumn* aColumn,
michael@0 2886 const nsRect& aColumnRect,
michael@0 2887 nsPresContext* aPresContext,
michael@0 2888 nsRenderingContext& aRenderingContext,
michael@0 2889 const nsRect& aDirtyRect)
michael@0 2890 {
michael@0 2891 NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
michael@0 2892
michael@0 2893 // Now obtain the properties for our cell.
michael@0 2894 PrefillPropertyArray(-1, aColumn);
michael@0 2895 nsAutoString properties;
michael@0 2896 mView->GetColumnProperties(aColumn, properties);
michael@0 2897 nsTreeUtils::TokenizeProperties(properties, mScratchArray);
michael@0 2898
michael@0 2899 // Resolve style for the column. It contains all the info we need to lay ourselves
michael@0 2900 // out and to paint.
michael@0 2901 nsStyleContext* colContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecolumn);
michael@0 2902
michael@0 2903 // Obtain the margins for the cell and then deflate our rect by that
michael@0 2904 // amount. The cell is assumed to be contained within the deflated rect.
michael@0 2905 nsRect colRect(aColumnRect);
michael@0 2906 nsMargin colMargin;
michael@0 2907 colContext->StyleMargin()->GetMargin(colMargin);
michael@0 2908 colRect.Deflate(colMargin);
michael@0 2909
michael@0 2910 PaintBackgroundLayer(colContext, aPresContext, aRenderingContext, colRect, aDirtyRect);
michael@0 2911 }
michael@0 2912
michael@0 2913 void
michael@0 2914 nsTreeBodyFrame::PaintRow(int32_t aRowIndex,
michael@0 2915 const nsRect& aRowRect,
michael@0 2916 nsPresContext* aPresContext,
michael@0 2917 nsRenderingContext& aRenderingContext,
michael@0 2918 const nsRect& aDirtyRect,
michael@0 2919 nsPoint aPt)
michael@0 2920 {
michael@0 2921 // We have been given a rect for our row. We treat this row like a full-blown
michael@0 2922 // frame, meaning that it can have borders, margins, padding, and a background.
michael@0 2923
michael@0 2924 // Without a view, we have no data. Check for this up front.
michael@0 2925 if (!mView)
michael@0 2926 return;
michael@0 2927
michael@0 2928 nsresult rv;
michael@0 2929
michael@0 2930 // Now obtain the properties for our row.
michael@0 2931 // XXX Automatically fill in the following props: open, closed, container, leaf, selected, focused
michael@0 2932 PrefillPropertyArray(aRowIndex, nullptr);
michael@0 2933
michael@0 2934 nsAutoString properties;
michael@0 2935 mView->GetRowProperties(aRowIndex, properties);
michael@0 2936 nsTreeUtils::TokenizeProperties(properties, mScratchArray);
michael@0 2937
michael@0 2938 // Resolve style for the row. It contains all the info we need to lay ourselves
michael@0 2939 // out and to paint.
michael@0 2940 nsStyleContext* rowContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreerow);
michael@0 2941
michael@0 2942 // Obtain the margins for the row and then deflate our rect by that
michael@0 2943 // amount. The row is assumed to be contained within the deflated rect.
michael@0 2944 nsRect rowRect(aRowRect);
michael@0 2945 nsMargin rowMargin;
michael@0 2946 rowContext->StyleMargin()->GetMargin(rowMargin);
michael@0 2947 rowRect.Deflate(rowMargin);
michael@0 2948
michael@0 2949 // Paint our borders and background for our row rect.
michael@0 2950 // If a -moz-appearance is provided, use theme drawing only if the current row
michael@0 2951 // is not selected (since we draw the selection as part of drawing the background).
michael@0 2952 bool useTheme = false;
michael@0 2953 nsITheme *theme = nullptr;
michael@0 2954 const nsStyleDisplay* displayData = rowContext->StyleDisplay();
michael@0 2955 if (displayData->mAppearance) {
michael@0 2956 theme = aPresContext->GetTheme();
michael@0 2957 if (theme && theme->ThemeSupportsWidget(aPresContext, nullptr, displayData->mAppearance))
michael@0 2958 useTheme = true;
michael@0 2959 }
michael@0 2960 bool isSelected = false;
michael@0 2961 nsCOMPtr<nsITreeSelection> selection;
michael@0 2962 mView->GetSelection(getter_AddRefs(selection));
michael@0 2963 if (selection)
michael@0 2964 selection->IsSelected(aRowIndex, &isSelected);
michael@0 2965 if (useTheme && !isSelected) {
michael@0 2966 nsRect dirty;
michael@0 2967 dirty.IntersectRect(rowRect, aDirtyRect);
michael@0 2968 theme->DrawWidgetBackground(&aRenderingContext, this,
michael@0 2969 displayData->mAppearance, rowRect, dirty);
michael@0 2970 } else {
michael@0 2971 PaintBackgroundLayer(rowContext, aPresContext, aRenderingContext, rowRect, aDirtyRect);
michael@0 2972 }
michael@0 2973
michael@0 2974 // Adjust the rect for its border and padding.
michael@0 2975 nsRect originalRowRect = rowRect;
michael@0 2976 AdjustForBorderPadding(rowContext, rowRect);
michael@0 2977
michael@0 2978 bool isSeparator = false;
michael@0 2979 mView->IsSeparator(aRowIndex, &isSeparator);
michael@0 2980 if (isSeparator) {
michael@0 2981 // The row is a separator.
michael@0 2982
michael@0 2983 nscoord primaryX = rowRect.x;
michael@0 2984 nsTreeColumn* primaryCol = mColumns->GetPrimaryColumn();
michael@0 2985 if (primaryCol) {
michael@0 2986 // Paint the primary cell.
michael@0 2987 nsRect cellRect;
michael@0 2988 rv = primaryCol->GetRect(this, rowRect.y, rowRect.height, &cellRect);
michael@0 2989 if (NS_FAILED(rv)) {
michael@0 2990 NS_NOTREACHED("primary column is invalid");
michael@0 2991 return;
michael@0 2992 }
michael@0 2993
michael@0 2994 if (OffsetForHorzScroll(cellRect, false)) {
michael@0 2995 cellRect.x += aPt.x;
michael@0 2996 nsRect dirtyRect;
michael@0 2997 nsRect checkRect(cellRect.x, originalRowRect.y,
michael@0 2998 cellRect.width, originalRowRect.height);
michael@0 2999 if (dirtyRect.IntersectRect(aDirtyRect, checkRect))
michael@0 3000 PaintCell(aRowIndex, primaryCol, cellRect, aPresContext,
michael@0 3001 aRenderingContext, aDirtyRect, primaryX, aPt);
michael@0 3002 }
michael@0 3003
michael@0 3004 // Paint the left side of the separator.
michael@0 3005 nscoord currX;
michael@0 3006 nsTreeColumn* previousCol = primaryCol->GetPrevious();
michael@0 3007 if (previousCol) {
michael@0 3008 nsRect prevColRect;
michael@0 3009 rv = previousCol->GetRect(this, 0, 0, &prevColRect);
michael@0 3010 if (NS_SUCCEEDED(rv)) {
michael@0 3011 currX = (prevColRect.x - mHorzPosition) + prevColRect.width + aPt.x;
michael@0 3012 } else {
michael@0 3013 NS_NOTREACHED("The column before the primary column is invalid");
michael@0 3014 currX = rowRect.x;
michael@0 3015 }
michael@0 3016 } else {
michael@0 3017 currX = rowRect.x;
michael@0 3018 }
michael@0 3019
michael@0 3020 int32_t level;
michael@0 3021 mView->GetLevel(aRowIndex, &level);
michael@0 3022 if (level == 0)
michael@0 3023 currX += mIndentation;
michael@0 3024
michael@0 3025 if (currX > rowRect.x) {
michael@0 3026 nsRect separatorRect(rowRect);
michael@0 3027 separatorRect.width -= rowRect.x + rowRect.width - currX;
michael@0 3028 PaintSeparator(aRowIndex, separatorRect, aPresContext, aRenderingContext, aDirtyRect);
michael@0 3029 }
michael@0 3030 }
michael@0 3031
michael@0 3032 // Paint the right side (whole) separator.
michael@0 3033 nsRect separatorRect(rowRect);
michael@0 3034 if (primaryX > rowRect.x) {
michael@0 3035 separatorRect.width -= primaryX - rowRect.x;
michael@0 3036 separatorRect.x += primaryX - rowRect.x;
michael@0 3037 }
michael@0 3038 PaintSeparator(aRowIndex, separatorRect, aPresContext, aRenderingContext, aDirtyRect);
michael@0 3039 }
michael@0 3040 else {
michael@0 3041 // Now loop over our cells. Only paint a cell if it intersects with our dirty rect.
michael@0 3042 for (nsTreeColumn* currCol = mColumns->GetFirstColumn(); currCol;
michael@0 3043 currCol = currCol->GetNext()) {
michael@0 3044 nsRect cellRect;
michael@0 3045 rv = currCol->GetRect(this, rowRect.y, rowRect.height, &cellRect);
michael@0 3046 // Don't paint cells in hidden columns.
michael@0 3047 if (NS_FAILED(rv) || cellRect.width == 0)
michael@0 3048 continue;
michael@0 3049
michael@0 3050 if (OffsetForHorzScroll(cellRect, false)) {
michael@0 3051 cellRect.x += aPt.x;
michael@0 3052
michael@0 3053 // for primary columns, use the row's vertical size so that the
michael@0 3054 // lines get drawn properly
michael@0 3055 nsRect checkRect = cellRect;
michael@0 3056 if (currCol->IsPrimary())
michael@0 3057 checkRect = nsRect(cellRect.x, originalRowRect.y,
michael@0 3058 cellRect.width, originalRowRect.height);
michael@0 3059
michael@0 3060 nsRect dirtyRect;
michael@0 3061 nscoord dummy;
michael@0 3062 if (dirtyRect.IntersectRect(aDirtyRect, checkRect))
michael@0 3063 PaintCell(aRowIndex, currCol, cellRect, aPresContext,
michael@0 3064 aRenderingContext, aDirtyRect, dummy, aPt);
michael@0 3065 }
michael@0 3066 }
michael@0 3067 }
michael@0 3068 }
michael@0 3069
michael@0 3070 void
michael@0 3071 nsTreeBodyFrame::PaintSeparator(int32_t aRowIndex,
michael@0 3072 const nsRect& aSeparatorRect,
michael@0 3073 nsPresContext* aPresContext,
michael@0 3074 nsRenderingContext& aRenderingContext,
michael@0 3075 const nsRect& aDirtyRect)
michael@0 3076 {
michael@0 3077 // Resolve style for the separator.
michael@0 3078 nsStyleContext* separatorContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeseparator);
michael@0 3079 bool useTheme = false;
michael@0 3080 nsITheme *theme = nullptr;
michael@0 3081 const nsStyleDisplay* displayData = separatorContext->StyleDisplay();
michael@0 3082 if ( displayData->mAppearance ) {
michael@0 3083 theme = aPresContext->GetTheme();
michael@0 3084 if (theme && theme->ThemeSupportsWidget(aPresContext, nullptr, displayData->mAppearance))
michael@0 3085 useTheme = true;
michael@0 3086 }
michael@0 3087
michael@0 3088 // use -moz-appearance if provided.
michael@0 3089 if (useTheme) {
michael@0 3090 nsRect dirty;
michael@0 3091 dirty.IntersectRect(aSeparatorRect, aDirtyRect);
michael@0 3092 theme->DrawWidgetBackground(&aRenderingContext, this,
michael@0 3093 displayData->mAppearance, aSeparatorRect, dirty);
michael@0 3094 }
michael@0 3095 else {
michael@0 3096 const nsStylePosition* stylePosition = separatorContext->StylePosition();
michael@0 3097
michael@0 3098 // Obtain the height for the separator or use the default value.
michael@0 3099 nscoord height;
michael@0 3100 if (stylePosition->mHeight.GetUnit() == eStyleUnit_Coord)
michael@0 3101 height = stylePosition->mHeight.GetCoordValue();
michael@0 3102 else {
michael@0 3103 // Use default height 2px.
michael@0 3104 height = nsPresContext::CSSPixelsToAppUnits(2);
michael@0 3105 }
michael@0 3106
michael@0 3107 // Obtain the margins for the separator and then deflate our rect by that
michael@0 3108 // amount. The separator is assumed to be contained within the deflated rect.
michael@0 3109 nsRect separatorRect(aSeparatorRect.x, aSeparatorRect.y, aSeparatorRect.width, height);
michael@0 3110 nsMargin separatorMargin;
michael@0 3111 separatorContext->StyleMargin()->GetMargin(separatorMargin);
michael@0 3112 separatorRect.Deflate(separatorMargin);
michael@0 3113
michael@0 3114 // Center the separator.
michael@0 3115 separatorRect.y += (aSeparatorRect.height - height) / 2;
michael@0 3116
michael@0 3117 PaintBackgroundLayer(separatorContext, aPresContext, aRenderingContext, separatorRect, aDirtyRect);
michael@0 3118 }
michael@0 3119 }
michael@0 3120
michael@0 3121 void
michael@0 3122 nsTreeBodyFrame::PaintCell(int32_t aRowIndex,
michael@0 3123 nsTreeColumn* aColumn,
michael@0 3124 const nsRect& aCellRect,
michael@0 3125 nsPresContext* aPresContext,
michael@0 3126 nsRenderingContext& aRenderingContext,
michael@0 3127 const nsRect& aDirtyRect,
michael@0 3128 nscoord& aCurrX,
michael@0 3129 nsPoint aPt)
michael@0 3130 {
michael@0 3131 NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
michael@0 3132
michael@0 3133 // Now obtain the properties for our cell.
michael@0 3134 // XXX Automatically fill in the following props: open, closed, container, leaf, selected, focused, and the col ID.
michael@0 3135 PrefillPropertyArray(aRowIndex, aColumn);
michael@0 3136 nsAutoString properties;
michael@0 3137 mView->GetCellProperties(aRowIndex, aColumn, properties);
michael@0 3138 nsTreeUtils::TokenizeProperties(properties, mScratchArray);
michael@0 3139
michael@0 3140 // Resolve style for the cell. It contains all the info we need to lay ourselves
michael@0 3141 // out and to paint.
michael@0 3142 nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell);
michael@0 3143
michael@0 3144 bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
michael@0 3145
michael@0 3146 // Obtain the margins for the cell and then deflate our rect by that
michael@0 3147 // amount. The cell is assumed to be contained within the deflated rect.
michael@0 3148 nsRect cellRect(aCellRect);
michael@0 3149 nsMargin cellMargin;
michael@0 3150 cellContext->StyleMargin()->GetMargin(cellMargin);
michael@0 3151 cellRect.Deflate(cellMargin);
michael@0 3152
michael@0 3153 // Paint our borders and background for our row rect.
michael@0 3154 PaintBackgroundLayer(cellContext, aPresContext, aRenderingContext, cellRect, aDirtyRect);
michael@0 3155
michael@0 3156 // Adjust the rect for its border and padding.
michael@0 3157 AdjustForBorderPadding(cellContext, cellRect);
michael@0 3158
michael@0 3159 nscoord currX = cellRect.x;
michael@0 3160 nscoord remainingWidth = cellRect.width;
michael@0 3161
michael@0 3162 // Now we paint the contents of the cells.
michael@0 3163 // Directionality of the tree determines the order in which we paint.
michael@0 3164 // NS_STYLE_DIRECTION_LTR means paint from left to right.
michael@0 3165 // NS_STYLE_DIRECTION_RTL means paint from right to left.
michael@0 3166
michael@0 3167 if (aColumn->IsPrimary()) {
michael@0 3168 // If we're the primary column, we need to indent and paint the twisty and any connecting lines
michael@0 3169 // between siblings.
michael@0 3170
michael@0 3171 int32_t level;
michael@0 3172 mView->GetLevel(aRowIndex, &level);
michael@0 3173
michael@0 3174 if (!isRTL)
michael@0 3175 currX += mIndentation * level;
michael@0 3176 remainingWidth -= mIndentation * level;
michael@0 3177
michael@0 3178 // Resolve the style to use for the connecting lines.
michael@0 3179 nsStyleContext* lineContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeline);
michael@0 3180
michael@0 3181 if (mIndentation && level &&
michael@0 3182 lineContext->StyleVisibility()->IsVisibleOrCollapsed()) {
michael@0 3183 // Paint the thread lines.
michael@0 3184
michael@0 3185 // Get the size of the twisty. We don't want to paint the twisty
michael@0 3186 // before painting of connecting lines since it would paint lines over
michael@0 3187 // the twisty. But we need to leave a place for it.
michael@0 3188 nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty);
michael@0 3189
michael@0 3190 nsRect imageSize;
michael@0 3191 nsRect twistyRect(aCellRect);
michael@0 3192 GetTwistyRect(aRowIndex, aColumn, imageSize, twistyRect, aPresContext,
michael@0 3193 aRenderingContext, twistyContext);
michael@0 3194
michael@0 3195 nsMargin twistyMargin;
michael@0 3196 twistyContext->StyleMargin()->GetMargin(twistyMargin);
michael@0 3197 twistyRect.Inflate(twistyMargin);
michael@0 3198
michael@0 3199 aRenderingContext.PushState();
michael@0 3200
michael@0 3201 const nsStyleBorder* borderStyle = lineContext->StyleBorder();
michael@0 3202 nscolor color;
michael@0 3203 bool foreground;
michael@0 3204 borderStyle->GetBorderColor(NS_SIDE_LEFT, color, foreground);
michael@0 3205 if (foreground) {
michael@0 3206 // GetBorderColor didn't touch color, thus grab it from the treeline context
michael@0 3207 color = lineContext->StyleColor()->mColor;
michael@0 3208 }
michael@0 3209 aRenderingContext.SetColor(color);
michael@0 3210 uint8_t style;
michael@0 3211 style = borderStyle->GetBorderStyle(NS_SIDE_LEFT);
michael@0 3212 aRenderingContext.SetLineStyle(ConvertBorderStyleToLineStyle(style));
michael@0 3213
michael@0 3214 nscoord srcX = currX + twistyRect.width - mIndentation / 2;
michael@0 3215 nscoord lineY = (aRowIndex - mTopRowIndex) * mRowHeight + aPt.y;
michael@0 3216
michael@0 3217 // Don't paint off our cell.
michael@0 3218 if (srcX <= cellRect.x + cellRect.width) {
michael@0 3219 nscoord destX = currX + twistyRect.width;
michael@0 3220 if (destX > cellRect.x + cellRect.width)
michael@0 3221 destX = cellRect.x + cellRect.width;
michael@0 3222 if (isRTL) {
michael@0 3223 srcX = currX + remainingWidth - (srcX - cellRect.x);
michael@0 3224 destX = currX + remainingWidth - (destX - cellRect.x);
michael@0 3225 }
michael@0 3226 aRenderingContext.DrawLine(srcX, lineY + mRowHeight / 2, destX, lineY + mRowHeight / 2);
michael@0 3227 }
michael@0 3228
michael@0 3229 int32_t currentParent = aRowIndex;
michael@0 3230 for (int32_t i = level; i > 0; i--) {
michael@0 3231 if (srcX <= cellRect.x + cellRect.width) {
michael@0 3232 // Paint full vertical line only if we have next sibling.
michael@0 3233 bool hasNextSibling;
michael@0 3234 mView->HasNextSibling(currentParent, aRowIndex, &hasNextSibling);
michael@0 3235 if (hasNextSibling)
michael@0 3236 aRenderingContext.DrawLine(srcX, lineY, srcX, lineY + mRowHeight);
michael@0 3237 else if (i == level)
michael@0 3238 aRenderingContext.DrawLine(srcX, lineY, srcX, lineY + mRowHeight / 2);
michael@0 3239 }
michael@0 3240
michael@0 3241 int32_t parent;
michael@0 3242 if (NS_FAILED(mView->GetParentIndex(currentParent, &parent)) || parent < 0)
michael@0 3243 break;
michael@0 3244 currentParent = parent;
michael@0 3245 srcX -= mIndentation;
michael@0 3246 }
michael@0 3247
michael@0 3248 aRenderingContext.PopState();
michael@0 3249 }
michael@0 3250
michael@0 3251 // Always leave space for the twisty.
michael@0 3252 nsRect twistyRect(currX, cellRect.y, remainingWidth, cellRect.height);
michael@0 3253 PaintTwisty(aRowIndex, aColumn, twistyRect, aPresContext, aRenderingContext, aDirtyRect,
michael@0 3254 remainingWidth, currX);
michael@0 3255 }
michael@0 3256
michael@0 3257 // Now paint the icon for our cell.
michael@0 3258 nsRect iconRect(currX, cellRect.y, remainingWidth, cellRect.height);
michael@0 3259 nsRect dirtyRect;
michael@0 3260 if (dirtyRect.IntersectRect(aDirtyRect, iconRect))
michael@0 3261 PaintImage(aRowIndex, aColumn, iconRect, aPresContext, aRenderingContext, aDirtyRect,
michael@0 3262 remainingWidth, currX);
michael@0 3263
michael@0 3264 // Now paint our element, but only if we aren't a cycler column.
michael@0 3265 // XXX until we have the ability to load images, allow the view to
michael@0 3266 // insert text into cycler columns...
michael@0 3267 if (!aColumn->IsCycler()) {
michael@0 3268 nsRect elementRect(currX, cellRect.y, remainingWidth, cellRect.height);
michael@0 3269 nsRect dirtyRect;
michael@0 3270 if (dirtyRect.IntersectRect(aDirtyRect, elementRect)) {
michael@0 3271 switch (aColumn->GetType()) {
michael@0 3272 case nsITreeColumn::TYPE_TEXT:
michael@0 3273 PaintText(aRowIndex, aColumn, elementRect, aPresContext, aRenderingContext, aDirtyRect, currX);
michael@0 3274 break;
michael@0 3275 case nsITreeColumn::TYPE_CHECKBOX:
michael@0 3276 PaintCheckbox(aRowIndex, aColumn, elementRect, aPresContext, aRenderingContext, aDirtyRect);
michael@0 3277 break;
michael@0 3278 case nsITreeColumn::TYPE_PROGRESSMETER:
michael@0 3279 int32_t state;
michael@0 3280 mView->GetProgressMode(aRowIndex, aColumn, &state);
michael@0 3281 switch (state) {
michael@0 3282 case nsITreeView::PROGRESS_NORMAL:
michael@0 3283 case nsITreeView::PROGRESS_UNDETERMINED:
michael@0 3284 PaintProgressMeter(aRowIndex, aColumn, elementRect, aPresContext, aRenderingContext, aDirtyRect);
michael@0 3285 break;
michael@0 3286 case nsITreeView::PROGRESS_NONE:
michael@0 3287 default:
michael@0 3288 PaintText(aRowIndex, aColumn, elementRect, aPresContext, aRenderingContext, aDirtyRect, currX);
michael@0 3289 break;
michael@0 3290 }
michael@0 3291 break;
michael@0 3292 }
michael@0 3293 }
michael@0 3294 }
michael@0 3295
michael@0 3296 aCurrX = currX;
michael@0 3297 }
michael@0 3298
michael@0 3299 void
michael@0 3300 nsTreeBodyFrame::PaintTwisty(int32_t aRowIndex,
michael@0 3301 nsTreeColumn* aColumn,
michael@0 3302 const nsRect& aTwistyRect,
michael@0 3303 nsPresContext* aPresContext,
michael@0 3304 nsRenderingContext& aRenderingContext,
michael@0 3305 const nsRect& aDirtyRect,
michael@0 3306 nscoord& aRemainingWidth,
michael@0 3307 nscoord& aCurrX)
michael@0 3308 {
michael@0 3309 NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
michael@0 3310
michael@0 3311 bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
michael@0 3312 nscoord rightEdge = aCurrX + aRemainingWidth;
michael@0 3313 // Paint the twisty, but only if we are a non-empty container.
michael@0 3314 bool shouldPaint = false;
michael@0 3315 bool isContainer = false;
michael@0 3316 mView->IsContainer(aRowIndex, &isContainer);
michael@0 3317 if (isContainer) {
michael@0 3318 bool isContainerEmpty = false;
michael@0 3319 mView->IsContainerEmpty(aRowIndex, &isContainerEmpty);
michael@0 3320 if (!isContainerEmpty)
michael@0 3321 shouldPaint = true;
michael@0 3322 }
michael@0 3323
michael@0 3324 // Resolve style for the twisty.
michael@0 3325 nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty);
michael@0 3326
michael@0 3327 // Obtain the margins for the twisty and then deflate our rect by that
michael@0 3328 // amount. The twisty is assumed to be contained within the deflated rect.
michael@0 3329 nsRect twistyRect(aTwistyRect);
michael@0 3330 nsMargin twistyMargin;
michael@0 3331 twistyContext->StyleMargin()->GetMargin(twistyMargin);
michael@0 3332 twistyRect.Deflate(twistyMargin);
michael@0 3333
michael@0 3334 nsRect imageSize;
michael@0 3335 nsITheme* theme = GetTwistyRect(aRowIndex, aColumn, imageSize, twistyRect,
michael@0 3336 aPresContext, aRenderingContext, twistyContext);
michael@0 3337
michael@0 3338 // Subtract out the remaining width. This is done even when we don't actually paint a twisty in
michael@0 3339 // this cell, so that cells in different rows still line up.
michael@0 3340 nsRect copyRect(twistyRect);
michael@0 3341 copyRect.Inflate(twistyMargin);
michael@0 3342 aRemainingWidth -= copyRect.width;
michael@0 3343 if (!isRTL)
michael@0 3344 aCurrX += copyRect.width;
michael@0 3345
michael@0 3346 if (shouldPaint) {
michael@0 3347 // Paint our borders and background for our image rect.
michael@0 3348 PaintBackgroundLayer(twistyContext, aPresContext, aRenderingContext, twistyRect, aDirtyRect);
michael@0 3349
michael@0 3350 if (theme) {
michael@0 3351 if (isRTL)
michael@0 3352 twistyRect.x = rightEdge - twistyRect.width;
michael@0 3353 // yeah, I know it says we're drawing a background, but a twisty is really a fg
michael@0 3354 // object since it doesn't have anything that gecko would want to draw over it. Besides,
michael@0 3355 // we have to prevent imagelib from drawing it.
michael@0 3356 nsRect dirty;
michael@0 3357 dirty.IntersectRect(twistyRect, aDirtyRect);
michael@0 3358 theme->DrawWidgetBackground(&aRenderingContext, this,
michael@0 3359 twistyContext->StyleDisplay()->mAppearance, twistyRect, dirty);
michael@0 3360 }
michael@0 3361 else {
michael@0 3362 // Time to paint the twisty.
michael@0 3363 // Adjust the rect for its border and padding.
michael@0 3364 nsMargin bp(0,0,0,0);
michael@0 3365 GetBorderPadding(twistyContext, bp);
michael@0 3366 twistyRect.Deflate(bp);
michael@0 3367 if (isRTL)
michael@0 3368 twistyRect.x = rightEdge - twistyRect.width;
michael@0 3369 imageSize.Deflate(bp);
michael@0 3370
michael@0 3371 // Get the image for drawing.
michael@0 3372 nsCOMPtr<imgIContainer> image;
michael@0 3373 bool useImageRegion = true;
michael@0 3374 GetImage(aRowIndex, aColumn, true, twistyContext, useImageRegion, getter_AddRefs(image));
michael@0 3375 if (image) {
michael@0 3376 nsPoint pt = twistyRect.TopLeft();
michael@0 3377
michael@0 3378 // Center the image. XXX Obey vertical-align style prop?
michael@0 3379 if (imageSize.height < twistyRect.height) {
michael@0 3380 pt.y += (twistyRect.height - imageSize.height)/2;
michael@0 3381 }
michael@0 3382
michael@0 3383 // Paint the image.
michael@0 3384 nsLayoutUtils::DrawSingleUnscaledImage(&aRenderingContext, image,
michael@0 3385 GraphicsFilter::FILTER_NEAREST, pt, &aDirtyRect,
michael@0 3386 imgIContainer::FLAG_NONE, &imageSize);
michael@0 3387 }
michael@0 3388 }
michael@0 3389 }
michael@0 3390 }
michael@0 3391
michael@0 3392 void
michael@0 3393 nsTreeBodyFrame::PaintImage(int32_t aRowIndex,
michael@0 3394 nsTreeColumn* aColumn,
michael@0 3395 const nsRect& aImageRect,
michael@0 3396 nsPresContext* aPresContext,
michael@0 3397 nsRenderingContext& aRenderingContext,
michael@0 3398 const nsRect& aDirtyRect,
michael@0 3399 nscoord& aRemainingWidth,
michael@0 3400 nscoord& aCurrX)
michael@0 3401 {
michael@0 3402 NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
michael@0 3403
michael@0 3404 bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
michael@0 3405 nscoord rightEdge = aCurrX + aRemainingWidth;
michael@0 3406 // Resolve style for the image.
michael@0 3407 nsStyleContext* imageContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeimage);
michael@0 3408
michael@0 3409 // Obtain opacity value for the image.
michael@0 3410 float opacity = imageContext->StyleDisplay()->mOpacity;
michael@0 3411
michael@0 3412 // Obtain the margins for the image and then deflate our rect by that
michael@0 3413 // amount. The image is assumed to be contained within the deflated rect.
michael@0 3414 nsRect imageRect(aImageRect);
michael@0 3415 nsMargin imageMargin;
michael@0 3416 imageContext->StyleMargin()->GetMargin(imageMargin);
michael@0 3417 imageRect.Deflate(imageMargin);
michael@0 3418
michael@0 3419 // Get the image.
michael@0 3420 bool useImageRegion = true;
michael@0 3421 nsCOMPtr<imgIContainer> image;
michael@0 3422 GetImage(aRowIndex, aColumn, false, imageContext, useImageRegion, getter_AddRefs(image));
michael@0 3423
michael@0 3424 // Get the image destination size.
michael@0 3425 nsSize imageDestSize = GetImageDestSize(imageContext, useImageRegion, image);
michael@0 3426 if (!imageDestSize.width || !imageDestSize.height)
michael@0 3427 return;
michael@0 3428
michael@0 3429 // Get the borders and padding.
michael@0 3430 nsMargin bp(0,0,0,0);
michael@0 3431 GetBorderPadding(imageContext, bp);
michael@0 3432
michael@0 3433 // destRect will be passed as the aDestRect argument in the DrawImage method.
michael@0 3434 // Start with the imageDestSize width and height.
michael@0 3435 nsRect destRect(0, 0, imageDestSize.width, imageDestSize.height);
michael@0 3436 // Inflate destRect for borders and padding so that we can compare/adjust
michael@0 3437 // with respect to imageRect.
michael@0 3438 destRect.Inflate(bp);
michael@0 3439
michael@0 3440 // The destRect width and height have not been adjusted to fit within the
michael@0 3441 // cell width and height.
michael@0 3442 // We must adjust the width even if image is null, because the width is used
michael@0 3443 // to update the aRemainingWidth and aCurrX values.
michael@0 3444 // Since the height isn't used unless the image is not null, we will adjust
michael@0 3445 // the height inside the if (image) block below.
michael@0 3446
michael@0 3447 if (destRect.width > imageRect.width) {
michael@0 3448 // The destRect is too wide to fit within the cell width.
michael@0 3449 // Adjust destRect width to fit within the cell width.
michael@0 3450 destRect.width = imageRect.width;
michael@0 3451 }
michael@0 3452 else {
michael@0 3453 // The cell is wider than the destRect.
michael@0 3454 // In a cycler column, the image is centered horizontally.
michael@0 3455 if (!aColumn->IsCycler()) {
michael@0 3456 // If this column is not a cycler, we won't center the image horizontally.
michael@0 3457 // We adjust the imageRect width so that the image is placed at the start
michael@0 3458 // of the cell.
michael@0 3459 imageRect.width = destRect.width;
michael@0 3460 }
michael@0 3461 }
michael@0 3462
michael@0 3463 if (image) {
michael@0 3464 if (isRTL)
michael@0 3465 imageRect.x = rightEdge - imageRect.width;
michael@0 3466 // Paint our borders and background for our image rect
michael@0 3467 PaintBackgroundLayer(imageContext, aPresContext, aRenderingContext, imageRect, aDirtyRect);
michael@0 3468
michael@0 3469 // The destRect x and y have not been set yet. Let's do that now.
michael@0 3470 // Initially, we use the imageRect x and y.
michael@0 3471 destRect.x = imageRect.x;
michael@0 3472 destRect.y = imageRect.y;
michael@0 3473
michael@0 3474 if (destRect.width < imageRect.width) {
michael@0 3475 // The destRect width is smaller than the cell width.
michael@0 3476 // Center the image horizontally in the cell.
michael@0 3477 // Adjust the destRect x accordingly.
michael@0 3478 destRect.x += (imageRect.width - destRect.width)/2;
michael@0 3479 }
michael@0 3480
michael@0 3481 // Now it's time to adjust the destRect height to fit within the cell height.
michael@0 3482 if (destRect.height > imageRect.height) {
michael@0 3483 // The destRect height is larger than the cell height.
michael@0 3484 // Adjust destRect height to fit within the cell height.
michael@0 3485 destRect.height = imageRect.height;
michael@0 3486 }
michael@0 3487 else if (destRect.height < imageRect.height) {
michael@0 3488 // The destRect height is smaller than the cell height.
michael@0 3489 // Center the image vertically in the cell.
michael@0 3490 // Adjust the destRect y accordingly.
michael@0 3491 destRect.y += (imageRect.height - destRect.height)/2;
michael@0 3492 }
michael@0 3493
michael@0 3494 // It's almost time to paint the image.
michael@0 3495 // Deflate destRect for the border and padding.
michael@0 3496 destRect.Deflate(bp);
michael@0 3497
michael@0 3498 // Get the image source rectangle - the rectangle containing the part of
michael@0 3499 // the image that we are going to display.
michael@0 3500 // sourceRect will be passed as the aSrcRect argument in the DrawImage method.
michael@0 3501 nsRect sourceRect = GetImageSourceRect(imageContext, useImageRegion, image);
michael@0 3502
michael@0 3503 // Let's say that the image is 100 pixels tall and
michael@0 3504 // that the CSS has specified that the destination height should be 50
michael@0 3505 // pixels tall. Let's say that the cell height is only 20 pixels. So, in
michael@0 3506 // those 20 visible pixels, we want to see the top 20/50ths of the image.
michael@0 3507 // So, the sourceRect.height should be 100 * 20 / 50, which is 40 pixels.
michael@0 3508 // Essentially, we are scaling the image as dictated by the CSS destination
michael@0 3509 // height and width, and we are then clipping the scaled image by the cell
michael@0 3510 // width and height.
michael@0 3511 nsIntSize rawImageSize;
michael@0 3512 image->GetWidth(&rawImageSize.width);
michael@0 3513 image->GetHeight(&rawImageSize.height);
michael@0 3514 nsRect wholeImageDest =
michael@0 3515 nsLayoutUtils::GetWholeImageDestination(rawImageSize, sourceRect,
michael@0 3516 nsRect(destRect.TopLeft(), imageDestSize));
michael@0 3517
michael@0 3518 gfxContext* ctx = aRenderingContext.ThebesContext();
michael@0 3519 if (opacity != 1.0f) {
michael@0 3520 ctx->PushGroup(gfxContentType::COLOR_ALPHA);
michael@0 3521 }
michael@0 3522
michael@0 3523 nsLayoutUtils::DrawImage(&aRenderingContext, image,
michael@0 3524 nsLayoutUtils::GetGraphicsFilterForFrame(this),
michael@0 3525 wholeImageDest, destRect, destRect.TopLeft(), aDirtyRect,
michael@0 3526 imgIContainer::FLAG_NONE);
michael@0 3527
michael@0 3528 if (opacity != 1.0f) {
michael@0 3529 ctx->PopGroupToSource();
michael@0 3530 ctx->Paint(opacity);
michael@0 3531 }
michael@0 3532 }
michael@0 3533
michael@0 3534 // Update the aRemainingWidth and aCurrX values.
michael@0 3535 imageRect.Inflate(imageMargin);
michael@0 3536 aRemainingWidth -= imageRect.width;
michael@0 3537 if (!isRTL)
michael@0 3538 aCurrX += imageRect.width;
michael@0 3539 }
michael@0 3540
michael@0 3541 void
michael@0 3542 nsTreeBodyFrame::PaintText(int32_t aRowIndex,
michael@0 3543 nsTreeColumn* aColumn,
michael@0 3544 const nsRect& aTextRect,
michael@0 3545 nsPresContext* aPresContext,
michael@0 3546 nsRenderingContext& aRenderingContext,
michael@0 3547 const nsRect& aDirtyRect,
michael@0 3548 nscoord& aCurrX)
michael@0 3549 {
michael@0 3550 NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
michael@0 3551
michael@0 3552 bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
michael@0 3553
michael@0 3554 // Now obtain the text for our cell.
michael@0 3555 nsAutoString text;
michael@0 3556 mView->GetCellText(aRowIndex, aColumn, text);
michael@0 3557 // We're going to paint this text so we need to ensure bidi is enabled if
michael@0 3558 // necessary
michael@0 3559 CheckTextForBidi(text);
michael@0 3560
michael@0 3561 if (text.Length() == 0)
michael@0 3562 return; // Don't paint an empty string. XXX What about background/borders? Still paint?
michael@0 3563
michael@0 3564 // Resolve style for the text. It contains all the info we need to lay ourselves
michael@0 3565 // out and to paint.
michael@0 3566 nsStyleContext* textContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecelltext);
michael@0 3567
michael@0 3568 // Obtain opacity value for the image.
michael@0 3569 float opacity = textContext->StyleDisplay()->mOpacity;
michael@0 3570
michael@0 3571 // Obtain the margins for the text and then deflate our rect by that
michael@0 3572 // amount. The text is assumed to be contained within the deflated rect.
michael@0 3573 nsRect textRect(aTextRect);
michael@0 3574 nsMargin textMargin;
michael@0 3575 textContext->StyleMargin()->GetMargin(textMargin);
michael@0 3576 textRect.Deflate(textMargin);
michael@0 3577
michael@0 3578 // Adjust the rect for its border and padding.
michael@0 3579 nsMargin bp(0,0,0,0);
michael@0 3580 GetBorderPadding(textContext, bp);
michael@0 3581 textRect.Deflate(bp);
michael@0 3582
michael@0 3583 // Compute our text size.
michael@0 3584 nsRefPtr<nsFontMetrics> fontMet;
michael@0 3585 nsLayoutUtils::GetFontMetricsForStyleContext(textContext,
michael@0 3586 getter_AddRefs(fontMet));
michael@0 3587
michael@0 3588 nscoord height = fontMet->MaxHeight();
michael@0 3589 nscoord baseline = fontMet->MaxAscent();
michael@0 3590
michael@0 3591 // Center the text. XXX Obey vertical-align style prop?
michael@0 3592 if (height < textRect.height) {
michael@0 3593 textRect.y += (textRect.height - height)/2;
michael@0 3594 textRect.height = height;
michael@0 3595 }
michael@0 3596
michael@0 3597 // Set our font.
michael@0 3598 aRenderingContext.SetFont(fontMet);
michael@0 3599
michael@0 3600 AdjustForCellText(text, aRowIndex, aColumn, aRenderingContext, textRect);
michael@0 3601 textRect.Inflate(bp);
michael@0 3602
michael@0 3603 // Subtract out the remaining width.
michael@0 3604 if (!isRTL)
michael@0 3605 aCurrX += textRect.width + textMargin.LeftRight();
michael@0 3606
michael@0 3607 PaintBackgroundLayer(textContext, aPresContext, aRenderingContext, textRect, aDirtyRect);
michael@0 3608
michael@0 3609 // Time to paint our text.
michael@0 3610 textRect.Deflate(bp);
michael@0 3611
michael@0 3612 // Set our color.
michael@0 3613 aRenderingContext.SetColor(textContext->StyleColor()->mColor);
michael@0 3614
michael@0 3615 // Draw decorations.
michael@0 3616 uint8_t decorations = textContext->StyleTextReset()->mTextDecorationLine;
michael@0 3617
michael@0 3618 nscoord offset;
michael@0 3619 nscoord size;
michael@0 3620 if (decorations & (NS_FONT_DECORATION_OVERLINE | NS_FONT_DECORATION_UNDERLINE)) {
michael@0 3621 fontMet->GetUnderline(offset, size);
michael@0 3622 if (decorations & NS_FONT_DECORATION_OVERLINE)
michael@0 3623 aRenderingContext.FillRect(textRect.x, textRect.y, textRect.width, size);
michael@0 3624 if (decorations & NS_FONT_DECORATION_UNDERLINE)
michael@0 3625 aRenderingContext.FillRect(textRect.x, textRect.y + baseline - offset, textRect.width, size);
michael@0 3626 }
michael@0 3627 if (decorations & NS_FONT_DECORATION_LINE_THROUGH) {
michael@0 3628 fontMet->GetStrikeout(offset, size);
michael@0 3629 aRenderingContext.FillRect(textRect.x, textRect.y + baseline - offset, textRect.width, size);
michael@0 3630 }
michael@0 3631 nsStyleContext* cellContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecell);
michael@0 3632
michael@0 3633 gfxContext* ctx = aRenderingContext.ThebesContext();
michael@0 3634 if (opacity != 1.0f) {
michael@0 3635 ctx->PushGroup(gfxContentType::COLOR_ALPHA);
michael@0 3636 }
michael@0 3637
michael@0 3638 nsLayoutUtils::DrawString(this, &aRenderingContext, text.get(), text.Length(),
michael@0 3639 textRect.TopLeft() + nsPoint(0, baseline), cellContext);
michael@0 3640
michael@0 3641 if (opacity != 1.0f) {
michael@0 3642 ctx->PopGroupToSource();
michael@0 3643 ctx->Paint(opacity);
michael@0 3644 }
michael@0 3645
michael@0 3646 }
michael@0 3647
michael@0 3648 void
michael@0 3649 nsTreeBodyFrame::PaintCheckbox(int32_t aRowIndex,
michael@0 3650 nsTreeColumn* aColumn,
michael@0 3651 const nsRect& aCheckboxRect,
michael@0 3652 nsPresContext* aPresContext,
michael@0 3653 nsRenderingContext& aRenderingContext,
michael@0 3654 const nsRect& aDirtyRect)
michael@0 3655 {
michael@0 3656 NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
michael@0 3657
michael@0 3658 // Resolve style for the checkbox.
michael@0 3659 nsStyleContext* checkboxContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreecheckbox);
michael@0 3660
michael@0 3661 nscoord rightEdge = aCheckboxRect.XMost();
michael@0 3662
michael@0 3663 // Obtain the margins for the checkbox and then deflate our rect by that
michael@0 3664 // amount. The checkbox is assumed to be contained within the deflated rect.
michael@0 3665 nsRect checkboxRect(aCheckboxRect);
michael@0 3666 nsMargin checkboxMargin;
michael@0 3667 checkboxContext->StyleMargin()->GetMargin(checkboxMargin);
michael@0 3668 checkboxRect.Deflate(checkboxMargin);
michael@0 3669
michael@0 3670 nsRect imageSize = GetImageSize(aRowIndex, aColumn, true, checkboxContext);
michael@0 3671
michael@0 3672 if (imageSize.height > checkboxRect.height)
michael@0 3673 imageSize.height = checkboxRect.height;
michael@0 3674 if (imageSize.width > checkboxRect.width)
michael@0 3675 imageSize.width = checkboxRect.width;
michael@0 3676
michael@0 3677 if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL)
michael@0 3678 checkboxRect.x = rightEdge - checkboxRect.width;
michael@0 3679
michael@0 3680 // Paint our borders and background for our image rect.
michael@0 3681 PaintBackgroundLayer(checkboxContext, aPresContext, aRenderingContext, checkboxRect, aDirtyRect);
michael@0 3682
michael@0 3683 // Time to paint the checkbox.
michael@0 3684 // Adjust the rect for its border and padding.
michael@0 3685 nsMargin bp(0,0,0,0);
michael@0 3686 GetBorderPadding(checkboxContext, bp);
michael@0 3687 checkboxRect.Deflate(bp);
michael@0 3688
michael@0 3689 // Get the image for drawing.
michael@0 3690 nsCOMPtr<imgIContainer> image;
michael@0 3691 bool useImageRegion = true;
michael@0 3692 GetImage(aRowIndex, aColumn, true, checkboxContext, useImageRegion, getter_AddRefs(image));
michael@0 3693 if (image) {
michael@0 3694 nsPoint pt = checkboxRect.TopLeft();
michael@0 3695
michael@0 3696 if (imageSize.height < checkboxRect.height) {
michael@0 3697 pt.y += (checkboxRect.height - imageSize.height)/2;
michael@0 3698 }
michael@0 3699
michael@0 3700 if (imageSize.width < checkboxRect.width) {
michael@0 3701 pt.x += (checkboxRect.width - imageSize.width)/2;
michael@0 3702 }
michael@0 3703
michael@0 3704 // Paint the image.
michael@0 3705 nsLayoutUtils::DrawSingleUnscaledImage(&aRenderingContext, image,
michael@0 3706 GraphicsFilter::FILTER_NEAREST, pt, &aDirtyRect,
michael@0 3707 imgIContainer::FLAG_NONE, &imageSize);
michael@0 3708 }
michael@0 3709 }
michael@0 3710
michael@0 3711 void
michael@0 3712 nsTreeBodyFrame::PaintProgressMeter(int32_t aRowIndex,
michael@0 3713 nsTreeColumn* aColumn,
michael@0 3714 const nsRect& aProgressMeterRect,
michael@0 3715 nsPresContext* aPresContext,
michael@0 3716 nsRenderingContext& aRenderingContext,
michael@0 3717 const nsRect& aDirtyRect)
michael@0 3718 {
michael@0 3719 NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
michael@0 3720
michael@0 3721 // Resolve style for the progress meter. It contains all the info we need
michael@0 3722 // to lay ourselves out and to paint.
michael@0 3723 nsStyleContext* meterContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreeprogressmeter);
michael@0 3724
michael@0 3725 // Obtain the margins for the progress meter and then deflate our rect by that
michael@0 3726 // amount. The progress meter is assumed to be contained within the deflated
michael@0 3727 // rect.
michael@0 3728 nsRect meterRect(aProgressMeterRect);
michael@0 3729 nsMargin meterMargin;
michael@0 3730 meterContext->StyleMargin()->GetMargin(meterMargin);
michael@0 3731 meterRect.Deflate(meterMargin);
michael@0 3732
michael@0 3733 // Paint our borders and background for our progress meter rect.
michael@0 3734 PaintBackgroundLayer(meterContext, aPresContext, aRenderingContext, meterRect, aDirtyRect);
michael@0 3735
michael@0 3736 // Time to paint our progress.
michael@0 3737 int32_t state;
michael@0 3738 mView->GetProgressMode(aRowIndex, aColumn, &state);
michael@0 3739 if (state == nsITreeView::PROGRESS_NORMAL) {
michael@0 3740 // Adjust the rect for its border and padding.
michael@0 3741 AdjustForBorderPadding(meterContext, meterRect);
michael@0 3742
michael@0 3743 // Set our color.
michael@0 3744 aRenderingContext.SetColor(meterContext->StyleColor()->mColor);
michael@0 3745
michael@0 3746 // Now obtain the value for our cell.
michael@0 3747 nsAutoString value;
michael@0 3748 mView->GetCellValue(aRowIndex, aColumn, value);
michael@0 3749
michael@0 3750 nsresult rv;
michael@0 3751 int32_t intValue = value.ToInteger(&rv);
michael@0 3752 if (intValue < 0)
michael@0 3753 intValue = 0;
michael@0 3754 else if (intValue > 100)
michael@0 3755 intValue = 100;
michael@0 3756
michael@0 3757 nscoord meterWidth = NSToCoordRound((float)intValue / 100 * meterRect.width);
michael@0 3758 if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL)
michael@0 3759 meterRect.x += meterRect.width - meterWidth; // right align
michael@0 3760 meterRect.width = meterWidth;
michael@0 3761 bool useImageRegion = true;
michael@0 3762 nsCOMPtr<imgIContainer> image;
michael@0 3763 GetImage(aRowIndex, aColumn, true, meterContext, useImageRegion, getter_AddRefs(image));
michael@0 3764 if (image) {
michael@0 3765 int32_t width, height;
michael@0 3766 image->GetWidth(&width);
michael@0 3767 image->GetHeight(&height);
michael@0 3768 nsSize size(width*nsDeviceContext::AppUnitsPerCSSPixel(),
michael@0 3769 height*nsDeviceContext::AppUnitsPerCSSPixel());
michael@0 3770 nsLayoutUtils::DrawImage(&aRenderingContext, image,
michael@0 3771 nsLayoutUtils::GetGraphicsFilterForFrame(this),
michael@0 3772 nsRect(meterRect.TopLeft(), size), meterRect, meterRect.TopLeft(),
michael@0 3773 aDirtyRect, imgIContainer::FLAG_NONE);
michael@0 3774 } else {
michael@0 3775 aRenderingContext.FillRect(meterRect);
michael@0 3776 }
michael@0 3777 }
michael@0 3778 else if (state == nsITreeView::PROGRESS_UNDETERMINED) {
michael@0 3779 // Adjust the rect for its border and padding.
michael@0 3780 AdjustForBorderPadding(meterContext, meterRect);
michael@0 3781
michael@0 3782 bool useImageRegion = true;
michael@0 3783 nsCOMPtr<imgIContainer> image;
michael@0 3784 GetImage(aRowIndex, aColumn, true, meterContext, useImageRegion, getter_AddRefs(image));
michael@0 3785 if (image) {
michael@0 3786 int32_t width, height;
michael@0 3787 image->GetWidth(&width);
michael@0 3788 image->GetHeight(&height);
michael@0 3789 nsSize size(width*nsDeviceContext::AppUnitsPerCSSPixel(),
michael@0 3790 height*nsDeviceContext::AppUnitsPerCSSPixel());
michael@0 3791 nsLayoutUtils::DrawImage(&aRenderingContext, image,
michael@0 3792 nsLayoutUtils::GetGraphicsFilterForFrame(this),
michael@0 3793 nsRect(meterRect.TopLeft(), size), meterRect, meterRect.TopLeft(),
michael@0 3794 aDirtyRect, imgIContainer::FLAG_NONE);
michael@0 3795 }
michael@0 3796 }
michael@0 3797 }
michael@0 3798
michael@0 3799
michael@0 3800 void
michael@0 3801 nsTreeBodyFrame::PaintDropFeedback(const nsRect& aDropFeedbackRect,
michael@0 3802 nsPresContext* aPresContext,
michael@0 3803 nsRenderingContext& aRenderingContext,
michael@0 3804 const nsRect& aDirtyRect,
michael@0 3805 nsPoint aPt)
michael@0 3806 {
michael@0 3807 // Paint the drop feedback in between rows.
michael@0 3808
michael@0 3809 nscoord currX;
michael@0 3810
michael@0 3811 // Adjust for the primary cell.
michael@0 3812 nsTreeColumn* primaryCol = mColumns->GetPrimaryColumn();
michael@0 3813
michael@0 3814 if (primaryCol) {
michael@0 3815 #ifdef DEBUG
michael@0 3816 nsresult rv =
michael@0 3817 #endif
michael@0 3818 primaryCol->GetXInTwips(this, &currX);
michael@0 3819 NS_ASSERTION(NS_SUCCEEDED(rv), "primary column is invalid?");
michael@0 3820
michael@0 3821 currX += aPt.x - mHorzPosition;
michael@0 3822 } else {
michael@0 3823 currX = aDropFeedbackRect.x;
michael@0 3824 }
michael@0 3825
michael@0 3826 PrefillPropertyArray(mSlots->mDropRow, primaryCol);
michael@0 3827
michael@0 3828 // Resolve the style to use for the drop feedback.
michael@0 3829 nsStyleContext* feedbackContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreedropfeedback);
michael@0 3830
michael@0 3831 // Paint only if it is visible.
michael@0 3832 if (feedbackContext->StyleVisibility()->IsVisibleOrCollapsed()) {
michael@0 3833 int32_t level;
michael@0 3834 mView->GetLevel(mSlots->mDropRow, &level);
michael@0 3835
michael@0 3836 // If our previous or next row has greater level use that for
michael@0 3837 // correct visual indentation.
michael@0 3838 if (mSlots->mDropOrient == nsITreeView::DROP_BEFORE) {
michael@0 3839 if (mSlots->mDropRow > 0) {
michael@0 3840 int32_t previousLevel;
michael@0 3841 mView->GetLevel(mSlots->mDropRow - 1, &previousLevel);
michael@0 3842 if (previousLevel > level)
michael@0 3843 level = previousLevel;
michael@0 3844 }
michael@0 3845 }
michael@0 3846 else {
michael@0 3847 if (mSlots->mDropRow < mRowCount - 1) {
michael@0 3848 int32_t nextLevel;
michael@0 3849 mView->GetLevel(mSlots->mDropRow + 1, &nextLevel);
michael@0 3850 if (nextLevel > level)
michael@0 3851 level = nextLevel;
michael@0 3852 }
michael@0 3853 }
michael@0 3854
michael@0 3855 currX += mIndentation * level;
michael@0 3856
michael@0 3857 if (primaryCol){
michael@0 3858 nsStyleContext* twistyContext = GetPseudoStyleContext(nsCSSAnonBoxes::moztreetwisty);
michael@0 3859 nsRect imageSize;
michael@0 3860 nsRect twistyRect;
michael@0 3861 GetTwistyRect(mSlots->mDropRow, primaryCol, imageSize, twistyRect, aPresContext,
michael@0 3862 aRenderingContext, twistyContext);
michael@0 3863 nsMargin twistyMargin;
michael@0 3864 twistyContext->StyleMargin()->GetMargin(twistyMargin);
michael@0 3865 twistyRect.Inflate(twistyMargin);
michael@0 3866 currX += twistyRect.width;
michael@0 3867 }
michael@0 3868
michael@0 3869 const nsStylePosition* stylePosition = feedbackContext->StylePosition();
michael@0 3870
michael@0 3871 // Obtain the width for the drop feedback or use default value.
michael@0 3872 nscoord width;
michael@0 3873 if (stylePosition->mWidth.GetUnit() == eStyleUnit_Coord)
michael@0 3874 width = stylePosition->mWidth.GetCoordValue();
michael@0 3875 else {
michael@0 3876 // Use default width 50px.
michael@0 3877 width = nsPresContext::CSSPixelsToAppUnits(50);
michael@0 3878 }
michael@0 3879
michael@0 3880 // Obtain the height for the drop feedback or use default value.
michael@0 3881 nscoord height;
michael@0 3882 if (stylePosition->mHeight.GetUnit() == eStyleUnit_Coord)
michael@0 3883 height = stylePosition->mHeight.GetCoordValue();
michael@0 3884 else {
michael@0 3885 // Use default height 2px.
michael@0 3886 height = nsPresContext::CSSPixelsToAppUnits(2);
michael@0 3887 }
michael@0 3888
michael@0 3889 // Obtain the margins for the drop feedback and then deflate our rect
michael@0 3890 // by that amount.
michael@0 3891 nsRect feedbackRect(currX, aDropFeedbackRect.y, width, height);
michael@0 3892 nsMargin margin;
michael@0 3893 feedbackContext->StyleMargin()->GetMargin(margin);
michael@0 3894 feedbackRect.Deflate(margin);
michael@0 3895
michael@0 3896 feedbackRect.y += (aDropFeedbackRect.height - height) / 2;
michael@0 3897
michael@0 3898 // Finally paint the drop feedback.
michael@0 3899 PaintBackgroundLayer(feedbackContext, aPresContext, aRenderingContext, feedbackRect, aDirtyRect);
michael@0 3900 }
michael@0 3901 }
michael@0 3902
michael@0 3903 void
michael@0 3904 nsTreeBodyFrame::PaintBackgroundLayer(nsStyleContext* aStyleContext,
michael@0 3905 nsPresContext* aPresContext,
michael@0 3906 nsRenderingContext& aRenderingContext,
michael@0 3907 const nsRect& aRect,
michael@0 3908 const nsRect& aDirtyRect)
michael@0 3909 {
michael@0 3910 const nsStyleBorder* myBorder = aStyleContext->StyleBorder();
michael@0 3911
michael@0 3912 nsCSSRendering::PaintBackgroundWithSC(aPresContext, aRenderingContext,
michael@0 3913 this, aDirtyRect, aRect,
michael@0 3914 aStyleContext, *myBorder,
michael@0 3915 nsCSSRendering::PAINTBG_SYNC_DECODE_IMAGES);
michael@0 3916
michael@0 3917 nsCSSRendering::PaintBorderWithStyleBorder(aPresContext, aRenderingContext,
michael@0 3918 this, aDirtyRect, aRect,
michael@0 3919 *myBorder, mStyleContext);
michael@0 3920
michael@0 3921 nsCSSRendering::PaintOutline(aPresContext, aRenderingContext, this,
michael@0 3922 aDirtyRect, aRect, aStyleContext);
michael@0 3923 }
michael@0 3924
michael@0 3925 // Scrolling
michael@0 3926 nsresult
michael@0 3927 nsTreeBodyFrame::EnsureRowIsVisible(int32_t aRow)
michael@0 3928 {
michael@0 3929 ScrollParts parts = GetScrollParts();
michael@0 3930 nsresult rv = EnsureRowIsVisibleInternal(parts, aRow);
michael@0 3931 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3932 UpdateScrollbars(parts);
michael@0 3933 return rv;
michael@0 3934 }
michael@0 3935
michael@0 3936 nsresult nsTreeBodyFrame::EnsureRowIsVisibleInternal(const ScrollParts& aParts, int32_t aRow)
michael@0 3937 {
michael@0 3938 if (!mView || !mPageLength)
michael@0 3939 return NS_OK;
michael@0 3940
michael@0 3941 if (mTopRowIndex <= aRow && mTopRowIndex+mPageLength > aRow)
michael@0 3942 return NS_OK;
michael@0 3943
michael@0 3944 if (aRow < mTopRowIndex)
michael@0 3945 ScrollToRowInternal(aParts, aRow);
michael@0 3946 else {
michael@0 3947 // Bring it just on-screen.
michael@0 3948 int32_t distance = aRow - (mTopRowIndex+mPageLength)+1;
michael@0 3949 ScrollToRowInternal(aParts, mTopRowIndex+distance);
michael@0 3950 }
michael@0 3951
michael@0 3952 return NS_OK;
michael@0 3953 }
michael@0 3954
michael@0 3955 nsresult
michael@0 3956 nsTreeBodyFrame::EnsureCellIsVisible(int32_t aRow, nsITreeColumn* aCol)
michael@0 3957 {
michael@0 3958 nsRefPtr<nsTreeColumn> col = GetColumnImpl(aCol);
michael@0 3959 if (!col)
michael@0 3960 return NS_ERROR_INVALID_ARG;
michael@0 3961
michael@0 3962 ScrollParts parts = GetScrollParts();
michael@0 3963
michael@0 3964 nscoord result = -1;
michael@0 3965 nsresult rv;
michael@0 3966
michael@0 3967 nscoord columnPos;
michael@0 3968 rv = col->GetXInTwips(this, &columnPos);
michael@0 3969 if(NS_FAILED(rv)) return rv;
michael@0 3970
michael@0 3971 nscoord columnWidth;
michael@0 3972 rv = col->GetWidthInTwips(this, &columnWidth);
michael@0 3973 if(NS_FAILED(rv)) return rv;
michael@0 3974
michael@0 3975 // If the start of the column is before the
michael@0 3976 // start of the horizontal view, then scroll
michael@0 3977 if (columnPos < mHorzPosition)
michael@0 3978 result = columnPos;
michael@0 3979 // If the end of the column is past the end of
michael@0 3980 // the horizontal view, then scroll
michael@0 3981 else if ((columnPos + columnWidth) > (mHorzPosition + mInnerBox.width))
michael@0 3982 result = ((columnPos + columnWidth) - (mHorzPosition + mInnerBox.width)) + mHorzPosition;
michael@0 3983
michael@0 3984 if (result != -1) {
michael@0 3985 rv = ScrollHorzInternal(parts, result);
michael@0 3986 if(NS_FAILED(rv)) return rv;
michael@0 3987 }
michael@0 3988
michael@0 3989 rv = EnsureRowIsVisibleInternal(parts, aRow);
michael@0 3990 NS_ENSURE_SUCCESS(rv, rv);
michael@0 3991 UpdateScrollbars(parts);
michael@0 3992 return rv;
michael@0 3993 }
michael@0 3994
michael@0 3995 nsresult
michael@0 3996 nsTreeBodyFrame::ScrollToCell(int32_t aRow, nsITreeColumn* aCol)
michael@0 3997 {
michael@0 3998 ScrollParts parts = GetScrollParts();
michael@0 3999 nsresult rv = ScrollToRowInternal(parts, aRow);
michael@0 4000 NS_ENSURE_SUCCESS(rv, rv);
michael@0 4001
michael@0 4002 rv = ScrollToColumnInternal(parts, aCol);
michael@0 4003 NS_ENSURE_SUCCESS(rv, rv);
michael@0 4004
michael@0 4005 UpdateScrollbars(parts);
michael@0 4006 return rv;
michael@0 4007 }
michael@0 4008
michael@0 4009 nsresult
michael@0 4010 nsTreeBodyFrame::ScrollToColumn(nsITreeColumn* aCol)
michael@0 4011 {
michael@0 4012 ScrollParts parts = GetScrollParts();
michael@0 4013 nsresult rv = ScrollToColumnInternal(parts, aCol);
michael@0 4014 NS_ENSURE_SUCCESS(rv, rv);
michael@0 4015 UpdateScrollbars(parts);
michael@0 4016 return rv;
michael@0 4017 }
michael@0 4018
michael@0 4019 nsresult nsTreeBodyFrame::ScrollToColumnInternal(const ScrollParts& aParts,
michael@0 4020 nsITreeColumn* aCol)
michael@0 4021 {
michael@0 4022 nsRefPtr<nsTreeColumn> col = GetColumnImpl(aCol);
michael@0 4023 if (!col)
michael@0 4024 return NS_ERROR_INVALID_ARG;
michael@0 4025
michael@0 4026 nscoord x;
michael@0 4027 nsresult rv = col->GetXInTwips(this, &x);
michael@0 4028 if (NS_FAILED(rv))
michael@0 4029 return rv;
michael@0 4030
michael@0 4031 return ScrollHorzInternal(aParts, x);
michael@0 4032 }
michael@0 4033
michael@0 4034 nsresult
michael@0 4035 nsTreeBodyFrame::ScrollToHorizontalPosition(int32_t aHorizontalPosition)
michael@0 4036 {
michael@0 4037 ScrollParts parts = GetScrollParts();
michael@0 4038 int32_t position = nsPresContext::CSSPixelsToAppUnits(aHorizontalPosition);
michael@0 4039 nsresult rv = ScrollHorzInternal(parts, position);
michael@0 4040 NS_ENSURE_SUCCESS(rv, rv);
michael@0 4041 UpdateScrollbars(parts);
michael@0 4042 return rv;
michael@0 4043 }
michael@0 4044
michael@0 4045 nsresult
michael@0 4046 nsTreeBodyFrame::ScrollToRow(int32_t aRow)
michael@0 4047 {
michael@0 4048 ScrollParts parts = GetScrollParts();
michael@0 4049 nsresult rv = ScrollToRowInternal(parts, aRow);
michael@0 4050 NS_ENSURE_SUCCESS(rv, rv);
michael@0 4051 UpdateScrollbars(parts);
michael@0 4052 return rv;
michael@0 4053 }
michael@0 4054
michael@0 4055 nsresult nsTreeBodyFrame::ScrollToRowInternal(const ScrollParts& aParts, int32_t aRow)
michael@0 4056 {
michael@0 4057 ScrollInternal(aParts, aRow);
michael@0 4058
michael@0 4059 return NS_OK;
michael@0 4060 }
michael@0 4061
michael@0 4062 nsresult
michael@0 4063 nsTreeBodyFrame::ScrollByLines(int32_t aNumLines)
michael@0 4064 {
michael@0 4065 if (!mView)
michael@0 4066 return NS_OK;
michael@0 4067
michael@0 4068 int32_t newIndex = mTopRowIndex + aNumLines;
michael@0 4069 if (newIndex < 0)
michael@0 4070 newIndex = 0;
michael@0 4071 else {
michael@0 4072 int32_t lastPageTopRow = mRowCount - mPageLength;
michael@0 4073 if (newIndex > lastPageTopRow)
michael@0 4074 newIndex = lastPageTopRow;
michael@0 4075 }
michael@0 4076 ScrollToRow(newIndex);
michael@0 4077
michael@0 4078 return NS_OK;
michael@0 4079 }
michael@0 4080
michael@0 4081 nsresult
michael@0 4082 nsTreeBodyFrame::ScrollByPages(int32_t aNumPages)
michael@0 4083 {
michael@0 4084 if (!mView)
michael@0 4085 return NS_OK;
michael@0 4086
michael@0 4087 int32_t newIndex = mTopRowIndex + aNumPages * mPageLength;
michael@0 4088 if (newIndex < 0)
michael@0 4089 newIndex = 0;
michael@0 4090 else {
michael@0 4091 int32_t lastPageTopRow = mRowCount - mPageLength;
michael@0 4092 if (newIndex > lastPageTopRow)
michael@0 4093 newIndex = lastPageTopRow;
michael@0 4094 }
michael@0 4095 ScrollToRow(newIndex);
michael@0 4096
michael@0 4097 return NS_OK;
michael@0 4098 }
michael@0 4099
michael@0 4100 nsresult
michael@0 4101 nsTreeBodyFrame::ScrollInternal(const ScrollParts& aParts, int32_t aRow)
michael@0 4102 {
michael@0 4103 if (!mView)
michael@0 4104 return NS_OK;
michael@0 4105
michael@0 4106 int32_t delta = aRow - mTopRowIndex;
michael@0 4107
michael@0 4108 if (delta > 0) {
michael@0 4109 if (mTopRowIndex == (mRowCount - mPageLength + 1))
michael@0 4110 return NS_OK;
michael@0 4111 }
michael@0 4112 else {
michael@0 4113 if (mTopRowIndex == 0)
michael@0 4114 return NS_OK;
michael@0 4115 }
michael@0 4116
michael@0 4117 mTopRowIndex += delta;
michael@0 4118
michael@0 4119 Invalidate();
michael@0 4120
michael@0 4121 PostScrollEvent();
michael@0 4122 return NS_OK;
michael@0 4123 }
michael@0 4124
michael@0 4125 nsresult
michael@0 4126 nsTreeBodyFrame::ScrollHorzInternal(const ScrollParts& aParts, int32_t aPosition)
michael@0 4127 {
michael@0 4128 if (!mView || !aParts.mColumnsScrollFrame || !aParts.mHScrollbar)
michael@0 4129 return NS_OK;
michael@0 4130
michael@0 4131 if (aPosition == mHorzPosition)
michael@0 4132 return NS_OK;
michael@0 4133
michael@0 4134 if (aPosition < 0 || aPosition > mHorzWidth)
michael@0 4135 return NS_OK;
michael@0 4136
michael@0 4137 nsRect bounds = aParts.mColumnsFrame->GetRect();
michael@0 4138 if (aPosition > (mHorzWidth - bounds.width))
michael@0 4139 aPosition = mHorzWidth - bounds.width;
michael@0 4140
michael@0 4141 mHorzPosition = aPosition;
michael@0 4142
michael@0 4143 Invalidate();
michael@0 4144
michael@0 4145 // Update the column scroll view
michael@0 4146 nsWeakFrame weakFrame(this);
michael@0 4147 aParts.mColumnsScrollFrame->ScrollTo(nsPoint(mHorzPosition, 0),
michael@0 4148 nsIScrollableFrame::INSTANT);
michael@0 4149 if (!weakFrame.IsAlive()) {
michael@0 4150 return NS_ERROR_FAILURE;
michael@0 4151 }
michael@0 4152 // And fire off an event about it all
michael@0 4153 PostScrollEvent();
michael@0 4154 return NS_OK;
michael@0 4155 }
michael@0 4156
michael@0 4157 NS_IMETHODIMP
michael@0 4158 nsTreeBodyFrame::ScrollbarButtonPressed(nsScrollbarFrame* aScrollbar, int32_t aOldIndex, int32_t aNewIndex)
michael@0 4159 {
michael@0 4160 ScrollParts parts = GetScrollParts();
michael@0 4161
michael@0 4162 if (aScrollbar == parts.mVScrollbar) {
michael@0 4163 if (aNewIndex > aOldIndex)
michael@0 4164 ScrollToRowInternal(parts, mTopRowIndex+1);
michael@0 4165 else if (aNewIndex < aOldIndex)
michael@0 4166 ScrollToRowInternal(parts, mTopRowIndex-1);
michael@0 4167 } else {
michael@0 4168 nsresult rv = ScrollHorzInternal(parts, aNewIndex);
michael@0 4169 if (NS_FAILED(rv)) return rv;
michael@0 4170 }
michael@0 4171
michael@0 4172 UpdateScrollbars(parts);
michael@0 4173
michael@0 4174 return NS_OK;
michael@0 4175 }
michael@0 4176
michael@0 4177 NS_IMETHODIMP
michael@0 4178 nsTreeBodyFrame::PositionChanged(nsScrollbarFrame* aScrollbar, int32_t aOldIndex, int32_t& aNewIndex)
michael@0 4179 {
michael@0 4180 ScrollParts parts = GetScrollParts();
michael@0 4181
michael@0 4182 if (aOldIndex == aNewIndex)
michael@0 4183 return NS_OK;
michael@0 4184
michael@0 4185 // Vertical Scrollbar
michael@0 4186 if (parts.mVScrollbar == aScrollbar) {
michael@0 4187 nscoord rh = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight);
michael@0 4188
michael@0 4189 nscoord newrow = aNewIndex/rh;
michael@0 4190 ScrollInternal(parts, newrow);
michael@0 4191 // Horizontal Scrollbar
michael@0 4192 } else if (parts.mHScrollbar == aScrollbar) {
michael@0 4193 nsresult rv = ScrollHorzInternal(parts, aNewIndex);
michael@0 4194 if (NS_FAILED(rv)) return rv;
michael@0 4195 }
michael@0 4196
michael@0 4197 UpdateScrollbars(parts);
michael@0 4198 return NS_OK;
michael@0 4199 }
michael@0 4200
michael@0 4201 // The style cache.
michael@0 4202 nsStyleContext*
michael@0 4203 nsTreeBodyFrame::GetPseudoStyleContext(nsIAtom* aPseudoElement)
michael@0 4204 {
michael@0 4205 return mStyleCache.GetStyleContext(this, PresContext(), mContent,
michael@0 4206 mStyleContext, aPseudoElement,
michael@0 4207 mScratchArray);
michael@0 4208 }
michael@0 4209
michael@0 4210 // Our comparator for resolving our complex pseudos
michael@0 4211 bool
michael@0 4212 nsTreeBodyFrame::PseudoMatches(nsCSSSelector* aSelector)
michael@0 4213 {
michael@0 4214 // Iterate the class list. For each item in the list, see if
michael@0 4215 // it is contained in our scratch array. If we have a miss, then
michael@0 4216 // we aren't a match. If all items in the class list are
michael@0 4217 // present in the scratch array, then we have a match.
michael@0 4218 nsAtomList* curr = aSelector->mClassList;
michael@0 4219 while (curr) {
michael@0 4220 if (!mScratchArray.Contains(curr->mAtom))
michael@0 4221 return false;
michael@0 4222 curr = curr->mNext;
michael@0 4223 }
michael@0 4224 return true;
michael@0 4225 }
michael@0 4226
michael@0 4227 nsIContent*
michael@0 4228 nsTreeBodyFrame::GetBaseElement()
michael@0 4229 {
michael@0 4230 nsIFrame* parent = GetParent();
michael@0 4231 while (parent) {
michael@0 4232 nsIContent* content = parent->GetContent();
michael@0 4233 if (content) {
michael@0 4234 nsINodeInfo* ni = content->NodeInfo();
michael@0 4235
michael@0 4236 if (ni->Equals(nsGkAtoms::tree, kNameSpaceID_XUL) ||
michael@0 4237 (ni->Equals(nsGkAtoms::select) &&
michael@0 4238 content->IsHTML()))
michael@0 4239 return content;
michael@0 4240 }
michael@0 4241
michael@0 4242 parent = parent->GetParent();
michael@0 4243 }
michael@0 4244
michael@0 4245 return nullptr;
michael@0 4246 }
michael@0 4247
michael@0 4248 nsresult
michael@0 4249 nsTreeBodyFrame::ClearStyleAndImageCaches()
michael@0 4250 {
michael@0 4251 mStyleCache.Clear();
michael@0 4252 mImageCache.EnumerateRead(CancelImageRequest, this);
michael@0 4253 mImageCache.Clear();
michael@0 4254 return NS_OK;
michael@0 4255 }
michael@0 4256
michael@0 4257 /* virtual */ void
michael@0 4258 nsTreeBodyFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
michael@0 4259 {
michael@0 4260 nsLeafBoxFrame::DidSetStyleContext(aOldStyleContext);
michael@0 4261
michael@0 4262 // Clear the style cache; the pointers are no longer even valid
michael@0 4263 mStyleCache.Clear();
michael@0 4264 // XXX The following is hacky, but it's not incorrect,
michael@0 4265 // and appears to fix a few bugs with style changes, like text zoom and
michael@0 4266 // dpi changes
michael@0 4267 mIndentation = GetIndentation();
michael@0 4268 mRowHeight = GetRowHeight();
michael@0 4269 mStringWidth = -1;
michael@0 4270 }
michael@0 4271
michael@0 4272 bool
michael@0 4273 nsTreeBodyFrame::OffsetForHorzScroll(nsRect& rect, bool clip)
michael@0 4274 {
michael@0 4275 rect.x -= mHorzPosition;
michael@0 4276
michael@0 4277 // Scrolled out before
michael@0 4278 if (rect.XMost() <= mInnerBox.x)
michael@0 4279 return false;
michael@0 4280
michael@0 4281 // Scrolled out after
michael@0 4282 if (rect.x > mInnerBox.XMost())
michael@0 4283 return false;
michael@0 4284
michael@0 4285 if (clip) {
michael@0 4286 nscoord leftEdge = std::max(rect.x, mInnerBox.x);
michael@0 4287 nscoord rightEdge = std::min(rect.XMost(), mInnerBox.XMost());
michael@0 4288 rect.x = leftEdge;
michael@0 4289 rect.width = rightEdge - leftEdge;
michael@0 4290
michael@0 4291 // Should have returned false above
michael@0 4292 NS_ASSERTION(rect.width >= 0, "horz scroll code out of sync");
michael@0 4293 }
michael@0 4294
michael@0 4295 return true;
michael@0 4296 }
michael@0 4297
michael@0 4298 bool
michael@0 4299 nsTreeBodyFrame::CanAutoScroll(int32_t aRowIndex)
michael@0 4300 {
michael@0 4301 // Check first for partially visible last row.
michael@0 4302 if (aRowIndex == mRowCount - 1) {
michael@0 4303 nscoord y = mInnerBox.y + (aRowIndex - mTopRowIndex) * mRowHeight;
michael@0 4304 if (y < mInnerBox.height && y + mRowHeight > mInnerBox.height)
michael@0 4305 return true;
michael@0 4306 }
michael@0 4307
michael@0 4308 if (aRowIndex > 0 && aRowIndex < mRowCount - 1)
michael@0 4309 return true;
michael@0 4310
michael@0 4311 return false;
michael@0 4312 }
michael@0 4313
michael@0 4314 // Given a dom event, figure out which row in the tree the mouse is over,
michael@0 4315 // if we should drop before/after/on that row or we should auto-scroll.
michael@0 4316 // Doesn't query the content about if the drag is allowable, that's done elsewhere.
michael@0 4317 //
michael@0 4318 // For containers, we break up the vertical space of the row as follows: if in
michael@0 4319 // the topmost 25%, the drop is _before_ the row the mouse is over; if in the
michael@0 4320 // last 25%, _after_; in the middle 50%, we consider it a drop _on_ the container.
michael@0 4321 //
michael@0 4322 // For non-containers, if the mouse is in the top 50% of the row, the drop is
michael@0 4323 // _before_ and the bottom 50% _after_
michael@0 4324 void
michael@0 4325 nsTreeBodyFrame::ComputeDropPosition(WidgetGUIEvent* aEvent,
michael@0 4326 int32_t* aRow,
michael@0 4327 int16_t* aOrient,
michael@0 4328 int16_t* aScrollLines)
michael@0 4329 {
michael@0 4330 *aOrient = -1;
michael@0 4331 *aScrollLines = 0;
michael@0 4332
michael@0 4333 // Convert the event's point to our coordinates. We want it in
michael@0 4334 // the coordinates of our inner box's coordinates.
michael@0 4335 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this);
michael@0 4336 int32_t xTwips = pt.x - mInnerBox.x;
michael@0 4337 int32_t yTwips = pt.y - mInnerBox.y;
michael@0 4338
michael@0 4339 *aRow = GetRowAt(xTwips, yTwips);
michael@0 4340 if (*aRow >=0) {
michael@0 4341 // Compute the top/bottom of the row in question.
michael@0 4342 int32_t yOffset = yTwips - mRowHeight * (*aRow - mTopRowIndex);
michael@0 4343
michael@0 4344 bool isContainer = false;
michael@0 4345 mView->IsContainer (*aRow, &isContainer);
michael@0 4346 if (isContainer) {
michael@0 4347 // for a container, use a 25%/50%/25% breakdown
michael@0 4348 if (yOffset < mRowHeight / 4)
michael@0 4349 *aOrient = nsITreeView::DROP_BEFORE;
michael@0 4350 else if (yOffset > mRowHeight - (mRowHeight / 4))
michael@0 4351 *aOrient = nsITreeView::DROP_AFTER;
michael@0 4352 else
michael@0 4353 *aOrient = nsITreeView::DROP_ON;
michael@0 4354 }
michael@0 4355 else {
michael@0 4356 // for a non-container use a 50%/50% breakdown
michael@0 4357 if (yOffset < mRowHeight / 2)
michael@0 4358 *aOrient = nsITreeView::DROP_BEFORE;
michael@0 4359 else
michael@0 4360 *aOrient = nsITreeView::DROP_AFTER;
michael@0 4361 }
michael@0 4362 }
michael@0 4363
michael@0 4364 if (CanAutoScroll(*aRow)) {
michael@0 4365 // Get the max value from the look and feel service.
michael@0 4366 int32_t scrollLinesMax =
michael@0 4367 LookAndFeel::GetInt(LookAndFeel::eIntID_TreeScrollLinesMax, 0);
michael@0 4368 scrollLinesMax--;
michael@0 4369 if (scrollLinesMax < 0)
michael@0 4370 scrollLinesMax = 0;
michael@0 4371
michael@0 4372 // Determine if we're w/in a margin of the top/bottom of the tree during a drag.
michael@0 4373 // This will ultimately cause us to scroll, but that's done elsewhere.
michael@0 4374 nscoord height = (3 * mRowHeight) / 4;
michael@0 4375 if (yTwips < height) {
michael@0 4376 // scroll up
michael@0 4377 *aScrollLines = NSToIntRound(-scrollLinesMax * (1 - (float)yTwips / height) - 1);
michael@0 4378 }
michael@0 4379 else if (yTwips > mRect.height - height) {
michael@0 4380 // scroll down
michael@0 4381 *aScrollLines = NSToIntRound(scrollLinesMax * (1 - (float)(mRect.height - yTwips) / height) + 1);
michael@0 4382 }
michael@0 4383 }
michael@0 4384 } // ComputeDropPosition
michael@0 4385
michael@0 4386 void
michael@0 4387 nsTreeBodyFrame::OpenCallback(nsITimer *aTimer, void *aClosure)
michael@0 4388 {
michael@0 4389 nsTreeBodyFrame* self = static_cast<nsTreeBodyFrame*>(aClosure);
michael@0 4390 if (self) {
michael@0 4391 aTimer->Cancel();
michael@0 4392 self->mSlots->mTimer = nullptr;
michael@0 4393
michael@0 4394 if (self->mSlots->mDropRow >= 0) {
michael@0 4395 self->mSlots->mArray.AppendElement(self->mSlots->mDropRow);
michael@0 4396 self->mView->ToggleOpenState(self->mSlots->mDropRow);
michael@0 4397 }
michael@0 4398 }
michael@0 4399 }
michael@0 4400
michael@0 4401 void
michael@0 4402 nsTreeBodyFrame::CloseCallback(nsITimer *aTimer, void *aClosure)
michael@0 4403 {
michael@0 4404 nsTreeBodyFrame* self = static_cast<nsTreeBodyFrame*>(aClosure);
michael@0 4405 if (self) {
michael@0 4406 aTimer->Cancel();
michael@0 4407 self->mSlots->mTimer = nullptr;
michael@0 4408
michael@0 4409 for (uint32_t i = self->mSlots->mArray.Length(); i--; ) {
michael@0 4410 if (self->mView)
michael@0 4411 self->mView->ToggleOpenState(self->mSlots->mArray[i]);
michael@0 4412 }
michael@0 4413 self->mSlots->mArray.Clear();
michael@0 4414 }
michael@0 4415 }
michael@0 4416
michael@0 4417 void
michael@0 4418 nsTreeBodyFrame::LazyScrollCallback(nsITimer *aTimer, void *aClosure)
michael@0 4419 {
michael@0 4420 nsTreeBodyFrame* self = static_cast<nsTreeBodyFrame*>(aClosure);
michael@0 4421 if (self) {
michael@0 4422 aTimer->Cancel();
michael@0 4423 self->mSlots->mTimer = nullptr;
michael@0 4424
michael@0 4425 if (self->mView) {
michael@0 4426 // Set a new timer to scroll the tree repeatedly.
michael@0 4427 self->CreateTimer(LookAndFeel::eIntID_TreeScrollDelay,
michael@0 4428 ScrollCallback, nsITimer::TYPE_REPEATING_SLACK,
michael@0 4429 getter_AddRefs(self->mSlots->mTimer));
michael@0 4430 self->ScrollByLines(self->mSlots->mScrollLines);
michael@0 4431 // ScrollByLines may have deleted |self|.
michael@0 4432 }
michael@0 4433 }
michael@0 4434 }
michael@0 4435
michael@0 4436 void
michael@0 4437 nsTreeBodyFrame::ScrollCallback(nsITimer *aTimer, void *aClosure)
michael@0 4438 {
michael@0 4439 nsTreeBodyFrame* self = static_cast<nsTreeBodyFrame*>(aClosure);
michael@0 4440 if (self) {
michael@0 4441 // Don't scroll if we are already at the top or bottom of the view.
michael@0 4442 if (self->mView && self->CanAutoScroll(self->mSlots->mDropRow)) {
michael@0 4443 self->ScrollByLines(self->mSlots->mScrollLines);
michael@0 4444 }
michael@0 4445 else {
michael@0 4446 aTimer->Cancel();
michael@0 4447 self->mSlots->mTimer = nullptr;
michael@0 4448 }
michael@0 4449 }
michael@0 4450 }
michael@0 4451
michael@0 4452 NS_IMETHODIMP
michael@0 4453 nsTreeBodyFrame::ScrollEvent::Run()
michael@0 4454 {
michael@0 4455 if (mInner) {
michael@0 4456 mInner->FireScrollEvent();
michael@0 4457 }
michael@0 4458 return NS_OK;
michael@0 4459 }
michael@0 4460
michael@0 4461
michael@0 4462 void
michael@0 4463 nsTreeBodyFrame::FireScrollEvent()
michael@0 4464 {
michael@0 4465 mScrollEvent.Forget();
michael@0 4466 WidgetGUIEvent event(true, NS_SCROLL_EVENT, nullptr);
michael@0 4467 // scroll events fired at elements don't bubble
michael@0 4468 event.mFlags.mBubbles = false;
michael@0 4469 EventDispatcher::Dispatch(GetContent(), PresContext(), &event);
michael@0 4470 }
michael@0 4471
michael@0 4472 void
michael@0 4473 nsTreeBodyFrame::PostScrollEvent()
michael@0 4474 {
michael@0 4475 if (mScrollEvent.IsPending())
michael@0 4476 return;
michael@0 4477
michael@0 4478 nsRefPtr<ScrollEvent> ev = new ScrollEvent(this);
michael@0 4479 if (NS_FAILED(NS_DispatchToCurrentThread(ev))) {
michael@0 4480 NS_WARNING("failed to dispatch ScrollEvent");
michael@0 4481 } else {
michael@0 4482 mScrollEvent = ev;
michael@0 4483 }
michael@0 4484 }
michael@0 4485
michael@0 4486 void
michael@0 4487 nsTreeBodyFrame::ScrollbarActivityStarted() const
michael@0 4488 {
michael@0 4489 if (mScrollbarActivity) {
michael@0 4490 mScrollbarActivity->ActivityStarted();
michael@0 4491 }
michael@0 4492 }
michael@0 4493
michael@0 4494 void
michael@0 4495 nsTreeBodyFrame::ScrollbarActivityStopped() const
michael@0 4496 {
michael@0 4497 if (mScrollbarActivity) {
michael@0 4498 mScrollbarActivity->ActivityStopped();
michael@0 4499 }
michael@0 4500 }
michael@0 4501
michael@0 4502 void
michael@0 4503 nsTreeBodyFrame::DetachImageListeners()
michael@0 4504 {
michael@0 4505 mCreatedListeners.Clear();
michael@0 4506 }
michael@0 4507
michael@0 4508 void
michael@0 4509 nsTreeBodyFrame::RemoveTreeImageListener(nsTreeImageListener* aListener)
michael@0 4510 {
michael@0 4511 if (aListener) {
michael@0 4512 mCreatedListeners.RemoveEntry(aListener);
michael@0 4513 }
michael@0 4514 }
michael@0 4515
michael@0 4516 #ifdef ACCESSIBILITY
michael@0 4517 void
michael@0 4518 nsTreeBodyFrame::FireRowCountChangedEvent(int32_t aIndex, int32_t aCount)
michael@0 4519 {
michael@0 4520 nsCOMPtr<nsIContent> content(GetBaseElement());
michael@0 4521 if (!content)
michael@0 4522 return;
michael@0 4523
michael@0 4524 nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(content->OwnerDoc());
michael@0 4525 if (!domDoc)
michael@0 4526 return;
michael@0 4527
michael@0 4528 nsCOMPtr<nsIDOMEvent> event;
michael@0 4529 domDoc->CreateEvent(NS_LITERAL_STRING("customevent"),
michael@0 4530 getter_AddRefs(event));
michael@0 4531
michael@0 4532 nsCOMPtr<nsIDOMCustomEvent> treeEvent(do_QueryInterface(event));
michael@0 4533 if (!treeEvent)
michael@0 4534 return;
michael@0 4535
michael@0 4536 nsCOMPtr<nsIWritablePropertyBag2> propBag(
michael@0 4537 do_CreateInstance("@mozilla.org/hash-property-bag;1"));
michael@0 4538 if (!propBag)
michael@0 4539 return;
michael@0 4540
michael@0 4541 // Set 'index' data - the row index rows are changed from.
michael@0 4542 propBag->SetPropertyAsInt32(NS_LITERAL_STRING("index"), aIndex);
michael@0 4543
michael@0 4544 // Set 'count' data - the number of changed rows.
michael@0 4545 propBag->SetPropertyAsInt32(NS_LITERAL_STRING("count"), aCount);
michael@0 4546
michael@0 4547 nsCOMPtr<nsIWritableVariant> detailVariant(
michael@0 4548 do_CreateInstance("@mozilla.org/variant;1"));
michael@0 4549 if (!detailVariant)
michael@0 4550 return;
michael@0 4551
michael@0 4552 detailVariant->SetAsISupports(propBag);
michael@0 4553 treeEvent->InitCustomEvent(NS_LITERAL_STRING("TreeRowCountChanged"),
michael@0 4554 true, false, detailVariant);
michael@0 4555
michael@0 4556 event->SetTrusted(true);
michael@0 4557
michael@0 4558 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
michael@0 4559 new AsyncEventDispatcher(content, event);
michael@0 4560 asyncDispatcher->PostDOMEvent();
michael@0 4561 }
michael@0 4562
michael@0 4563 void
michael@0 4564 nsTreeBodyFrame::FireInvalidateEvent(int32_t aStartRowIdx, int32_t aEndRowIdx,
michael@0 4565 nsITreeColumn *aStartCol,
michael@0 4566 nsITreeColumn *aEndCol)
michael@0 4567 {
michael@0 4568 nsCOMPtr<nsIContent> content(GetBaseElement());
michael@0 4569 if (!content)
michael@0 4570 return;
michael@0 4571
michael@0 4572 nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(content->OwnerDoc());
michael@0 4573 if (!domDoc)
michael@0 4574 return;
michael@0 4575
michael@0 4576 nsCOMPtr<nsIDOMEvent> event;
michael@0 4577 domDoc->CreateEvent(NS_LITERAL_STRING("customevent"),
michael@0 4578 getter_AddRefs(event));
michael@0 4579
michael@0 4580 nsCOMPtr<nsIDOMCustomEvent> treeEvent(do_QueryInterface(event));
michael@0 4581 if (!treeEvent)
michael@0 4582 return;
michael@0 4583
michael@0 4584 nsCOMPtr<nsIWritablePropertyBag2> propBag(
michael@0 4585 do_CreateInstance("@mozilla.org/hash-property-bag;1"));
michael@0 4586 if (!propBag)
michael@0 4587 return;
michael@0 4588
michael@0 4589 if (aStartRowIdx != -1 && aEndRowIdx != -1) {
michael@0 4590 // Set 'startrow' data - the start index of invalidated rows.
michael@0 4591 propBag->SetPropertyAsInt32(NS_LITERAL_STRING("startrow"),
michael@0 4592 aStartRowIdx);
michael@0 4593
michael@0 4594 // Set 'endrow' data - the end index of invalidated rows.
michael@0 4595 propBag->SetPropertyAsInt32(NS_LITERAL_STRING("endrow"),
michael@0 4596 aEndRowIdx);
michael@0 4597 }
michael@0 4598
michael@0 4599 if (aStartCol && aEndCol) {
michael@0 4600 // Set 'startcolumn' data - the start index of invalidated rows.
michael@0 4601 int32_t startColIdx = 0;
michael@0 4602 nsresult rv = aStartCol->GetIndex(&startColIdx);
michael@0 4603 if (NS_FAILED(rv))
michael@0 4604 return;
michael@0 4605
michael@0 4606 propBag->SetPropertyAsInt32(NS_LITERAL_STRING("startcolumn"),
michael@0 4607 startColIdx);
michael@0 4608
michael@0 4609 // Set 'endcolumn' data - the start index of invalidated rows.
michael@0 4610 int32_t endColIdx = 0;
michael@0 4611 rv = aEndCol->GetIndex(&endColIdx);
michael@0 4612 if (NS_FAILED(rv))
michael@0 4613 return;
michael@0 4614
michael@0 4615 propBag->SetPropertyAsInt32(NS_LITERAL_STRING("endcolumn"),
michael@0 4616 endColIdx);
michael@0 4617 }
michael@0 4618
michael@0 4619 nsCOMPtr<nsIWritableVariant> detailVariant(
michael@0 4620 do_CreateInstance("@mozilla.org/variant;1"));
michael@0 4621 if (!detailVariant)
michael@0 4622 return;
michael@0 4623
michael@0 4624 detailVariant->SetAsISupports(propBag);
michael@0 4625 treeEvent->InitCustomEvent(NS_LITERAL_STRING("TreeInvalidated"),
michael@0 4626 true, false, detailVariant);
michael@0 4627
michael@0 4628 event->SetTrusted(true);
michael@0 4629
michael@0 4630 nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
michael@0 4631 new AsyncEventDispatcher(content, event);
michael@0 4632 asyncDispatcher->PostDOMEvent();
michael@0 4633 }
michael@0 4634 #endif
michael@0 4635
michael@0 4636 class nsOverflowChecker : public nsRunnable
michael@0 4637 {
michael@0 4638 public:
michael@0 4639 nsOverflowChecker(nsTreeBodyFrame* aFrame) : mFrame(aFrame) {}
michael@0 4640 NS_IMETHOD Run() MOZ_OVERRIDE
michael@0 4641 {
michael@0 4642 if (mFrame.IsAlive()) {
michael@0 4643 nsTreeBodyFrame* tree = static_cast<nsTreeBodyFrame*>(mFrame.GetFrame());
michael@0 4644 nsTreeBodyFrame::ScrollParts parts = tree->GetScrollParts();
michael@0 4645 tree->CheckOverflow(parts);
michael@0 4646 }
michael@0 4647 return NS_OK;
michael@0 4648 }
michael@0 4649 private:
michael@0 4650 nsWeakFrame mFrame;
michael@0 4651 };
michael@0 4652
michael@0 4653 bool
michael@0 4654 nsTreeBodyFrame::FullScrollbarsUpdate(bool aNeedsFullInvalidation)
michael@0 4655 {
michael@0 4656 ScrollParts parts = GetScrollParts();
michael@0 4657 nsWeakFrame weakFrame(this);
michael@0 4658 nsWeakFrame weakColumnsFrame(parts.mColumnsFrame);
michael@0 4659 UpdateScrollbars(parts);
michael@0 4660 NS_ENSURE_TRUE(weakFrame.IsAlive(), false);
michael@0 4661 if (aNeedsFullInvalidation) {
michael@0 4662 Invalidate();
michael@0 4663 }
michael@0 4664 InvalidateScrollbars(parts, weakColumnsFrame);
michael@0 4665 NS_ENSURE_TRUE(weakFrame.IsAlive(), false);
michael@0 4666
michael@0 4667 // Overflow checking dispatches synchronous events, which can cause infinite
michael@0 4668 // recursion during reflow. Do the first overflow check synchronously, but
michael@0 4669 // force any nested checks to round-trip through the event loop. See bug
michael@0 4670 // 905909.
michael@0 4671 nsRefPtr<nsOverflowChecker> checker = new nsOverflowChecker(this);
michael@0 4672 if (!mCheckingOverflow) {
michael@0 4673 nsContentUtils::AddScriptRunner(checker);
michael@0 4674 } else {
michael@0 4675 NS_DispatchToCurrentThread(checker);
michael@0 4676 }
michael@0 4677 return weakFrame.IsAlive();
michael@0 4678 }
michael@0 4679
michael@0 4680 nsresult
michael@0 4681 nsTreeBodyFrame::OnImageIsAnimated(imgIRequest* aRequest)
michael@0 4682 {
michael@0 4683 nsLayoutUtils::RegisterImageRequest(PresContext(),
michael@0 4684 aRequest, nullptr);
michael@0 4685
michael@0 4686 return NS_OK;
michael@0 4687 }

mercurial