michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsListBoxLayout.h" michael@0: michael@0: #include "nsListBoxBodyFrame.h" michael@0: #include "nsBox.h" michael@0: #include "nsBoxLayoutState.h" michael@0: #include "nsIScrollableFrame.h" michael@0: #include "nsIReflowCallback.h" michael@0: #include "nsNameSpaceManager.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsContentUtils.h" michael@0: michael@0: nsListBoxLayout::nsListBoxLayout() : nsGridRowGroupLayout() michael@0: { michael@0: } michael@0: michael@0: ////////// nsBoxLayout ////////////// michael@0: michael@0: nsSize michael@0: nsListBoxLayout::GetPrefSize(nsIFrame* aBox, nsBoxLayoutState& aBoxLayoutState) michael@0: { michael@0: nsSize pref = nsGridRowGroupLayout::GetPrefSize(aBox, aBoxLayoutState); michael@0: michael@0: nsListBoxBodyFrame* frame = static_cast(aBox); michael@0: if (frame) { michael@0: nscoord rowheight = frame->GetRowHeightAppUnits(); michael@0: pref.height = frame->GetRowCount() * rowheight; michael@0: // Pad the height. michael@0: nscoord y = frame->GetAvailableHeight(); michael@0: if (pref.height > y && y > 0 && rowheight > 0) { michael@0: nscoord m = (pref.height-y)%rowheight; michael@0: nscoord remainder = m == 0 ? 0 : rowheight - m; michael@0: pref.height += remainder; michael@0: } michael@0: if (nsContentUtils::HasNonEmptyAttr(frame->GetContent(), kNameSpaceID_None, michael@0: nsGkAtoms::sizemode)) { michael@0: nscoord width = frame->ComputeIntrinsicWidth(aBoxLayoutState); michael@0: if (width > pref.width) michael@0: pref.width = width; michael@0: } michael@0: } michael@0: return pref; michael@0: } michael@0: michael@0: nsSize michael@0: nsListBoxLayout::GetMinSize(nsIFrame* aBox, nsBoxLayoutState& aBoxLayoutState) michael@0: { michael@0: nsSize minSize = nsGridRowGroupLayout::GetMinSize(aBox, aBoxLayoutState); michael@0: michael@0: nsListBoxBodyFrame* frame = static_cast(aBox); michael@0: if (frame) { michael@0: nscoord rowheight = frame->GetRowHeightAppUnits(); michael@0: minSize.height = frame->GetRowCount() * rowheight; michael@0: // Pad the height. michael@0: nscoord y = frame->GetAvailableHeight(); michael@0: if (minSize.height > y && y > 0 && rowheight > 0) { michael@0: nscoord m = (minSize.height-y)%rowheight; michael@0: nscoord remainder = m == 0 ? 0 : rowheight - m; michael@0: minSize.height += remainder; michael@0: } michael@0: if (nsContentUtils::HasNonEmptyAttr(frame->GetContent(), kNameSpaceID_None, michael@0: nsGkAtoms::sizemode)) { michael@0: nscoord width = frame->ComputeIntrinsicWidth(aBoxLayoutState); michael@0: if (width > minSize.width) michael@0: minSize.width = width; michael@0: } michael@0: } michael@0: return minSize; michael@0: } michael@0: michael@0: nsSize michael@0: nsListBoxLayout::GetMaxSize(nsIFrame* aBox, nsBoxLayoutState& aBoxLayoutState) michael@0: { michael@0: nsSize maxSize = nsGridRowGroupLayout::GetMaxSize(aBox, aBoxLayoutState); michael@0: michael@0: nsListBoxBodyFrame* frame = static_cast(aBox); michael@0: if (frame) { michael@0: nscoord rowheight = frame->GetRowHeightAppUnits(); michael@0: maxSize.height = frame->GetRowCount() * rowheight; michael@0: // Pad the height. michael@0: nscoord y = frame->GetAvailableHeight(); michael@0: if (maxSize.height > y && y > 0 && rowheight > 0) { michael@0: nscoord m = (maxSize.height-y)%rowheight; michael@0: nscoord remainder = m == 0 ? 0 : rowheight - m; michael@0: maxSize.height += remainder; michael@0: } michael@0: } michael@0: return maxSize; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsListBoxLayout::Layout(nsIFrame* aBox, nsBoxLayoutState& aState) michael@0: { michael@0: return LayoutInternal(aBox, aState); michael@0: } michael@0: michael@0: michael@0: /////////// nsListBoxLayout ///////////////////////// michael@0: michael@0: /** michael@0: * Called to layout our our children. Does no frame construction michael@0: */ michael@0: NS_IMETHODIMP michael@0: nsListBoxLayout::LayoutInternal(nsIFrame* aBox, nsBoxLayoutState& aState) michael@0: { michael@0: int32_t redrawStart = -1; michael@0: michael@0: // Get the start y position. michael@0: nsListBoxBodyFrame* body = static_cast(aBox); michael@0: if (!body) { michael@0: NS_ERROR("Frame encountered that isn't a listboxbody!"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsMargin margin; michael@0: michael@0: // Get our client rect. michael@0: nsRect clientRect; michael@0: aBox->GetClientRect(clientRect); michael@0: michael@0: // Get the starting y position and the remaining available michael@0: // height. michael@0: nscoord availableHeight = body->GetAvailableHeight(); michael@0: nscoord yOffset = body->GetYPosition(); michael@0: michael@0: if (availableHeight <= 0) { michael@0: bool fixed = (body->GetFixedRowSize() != -1); michael@0: if (fixed) michael@0: availableHeight = 10; michael@0: else michael@0: return NS_OK; michael@0: } michael@0: michael@0: // run through all our currently created children michael@0: nsIFrame* box = body->GetChildBox(); michael@0: michael@0: // if the reason is resize or initial we must relayout. michael@0: nscoord rowHeight = body->GetRowHeightAppUnits(); michael@0: michael@0: while (box) { michael@0: // If this box is dirty or if it has dirty children, we michael@0: // call layout on it. michael@0: nsRect childRect(box->GetRect()); michael@0: box->GetMargin(margin); michael@0: michael@0: // relayout if we must or we are dirty or some of our children are dirty michael@0: // or the client area is wider than us michael@0: // XXXldb There should probably be a resize check here too! michael@0: if (NS_SUBTREE_DIRTY(box) || childRect.width < clientRect.width) { michael@0: childRect.x = 0; michael@0: childRect.y = yOffset; michael@0: childRect.width = clientRect.width; michael@0: michael@0: nsSize size = box->GetPrefSize(aState); michael@0: body->SetRowHeight(size.height); michael@0: michael@0: childRect.height = rowHeight; michael@0: michael@0: childRect.Deflate(margin); michael@0: box->SetBounds(aState, childRect); michael@0: box->Layout(aState); michael@0: } else { michael@0: // if the child did not need to be relayed out. Then its easy. michael@0: // Place the child by just grabbing its rect and adjusting the y. michael@0: int32_t newPos = yOffset+margin.top; michael@0: michael@0: // are we pushing down or pulling up any rows? michael@0: // Then we may have to redraw everything below the moved michael@0: // rows. michael@0: if (redrawStart == -1 && childRect.y != newPos) michael@0: redrawStart = newPos; michael@0: michael@0: childRect.y = newPos; michael@0: box->SetBounds(aState, childRect); michael@0: } michael@0: michael@0: // Ok now the available size gets smaller and we move the michael@0: // starting position of the next child down some. michael@0: nscoord size = childRect.height + margin.top + margin.bottom; michael@0: michael@0: yOffset += size; michael@0: availableHeight -= size; michael@0: michael@0: box = box->GetNextBox(); michael@0: } michael@0: michael@0: // We have enough available height left to add some more rows michael@0: // Since we can't do this during layout, we post a callback michael@0: // that will be processed after the reflow completes. michael@0: body->PostReflowCallback(); michael@0: michael@0: // if rows were pushed down or pulled up because some rows were added michael@0: // before them then redraw everything under the inserted rows. The inserted michael@0: // rows will automatically be redrawn because the were marked dirty on insertion. michael@0: if (redrawStart > -1) { michael@0: aBox->Redraw(aState); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Creation Routines /////////////////////////////////////////////////////////////////////// michael@0: michael@0: already_AddRefed NS_NewListBoxLayout() michael@0: { michael@0: nsRefPtr layout = new nsListBoxLayout(); michael@0: return layout.forget(); michael@0: }