2007-03-22 10:30:00 -07:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is Mozilla Communicator client code.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Netscape Communications Corporation.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
* David W. Hyatt (hyatt@netscape.com) (Original Author)
|
|
|
|
* Joe Hewitt (hewitt@netscape.com)
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
|
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
|
|
#include "nsListBoxLayout.h"
|
|
|
|
|
|
|
|
#include "nsListBoxBodyFrame.h"
|
|
|
|
#include "nsIFrame.h"
|
|
|
|
#include "nsBox.h"
|
|
|
|
#include "nsBoxLayoutState.h"
|
|
|
|
#include "nsIScrollableFrame.h"
|
|
|
|
#include "nsIReflowCallback.h"
|
|
|
|
#include "nsINameSpaceManager.h"
|
|
|
|
#include "nsGkAtoms.h"
|
|
|
|
#include "nsContentUtils.h"
|
|
|
|
|
|
|
|
nsListBoxLayout::nsListBoxLayout(nsIPresShell* aPresShell)
|
|
|
|
: nsGridRowGroupLayout(aPresShell)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
////////// nsIBoxLayout //////////////
|
|
|
|
|
2008-01-08 23:28:39 -08:00
|
|
|
nsSize
|
|
|
|
nsListBoxLayout::GetPrefSize(nsIBox* aBox, nsBoxLayoutState& aBoxLayoutState)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-01-08 23:28:39 -08:00
|
|
|
nsSize pref = nsGridRowGroupLayout::GetPrefSize(aBox, aBoxLayoutState);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-07-08 00:08:04 -07:00
|
|
|
nsListBoxBodyFrame* frame = static_cast<nsListBoxBodyFrame*>(aBox);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (frame) {
|
|
|
|
nscoord rowheight = frame->GetRowHeightAppUnits();
|
2008-01-08 23:28:39 -08:00
|
|
|
pref.height = frame->GetRowCount() * rowheight;
|
2007-03-22 10:30:00 -07:00
|
|
|
// Pad the height.
|
|
|
|
nscoord y = frame->GetAvailableHeight();
|
2008-01-08 23:28:39 -08:00
|
|
|
if (pref.height > y && y > 0 && rowheight > 0) {
|
|
|
|
nscoord m = (pref.height-y)%rowheight;
|
2007-03-22 10:30:00 -07:00
|
|
|
nscoord remainder = m == 0 ? 0 : rowheight - m;
|
2008-01-08 23:28:39 -08:00
|
|
|
pref.height += remainder;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
if (nsContentUtils::HasNonEmptyAttr(frame->GetContent(), kNameSpaceID_None,
|
|
|
|
nsGkAtoms::sizemode)) {
|
|
|
|
nscoord width = frame->ComputeIntrinsicWidth(aBoxLayoutState);
|
2008-01-08 23:28:39 -08:00
|
|
|
if (width > pref.width)
|
|
|
|
pref.width = width;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
2008-01-08 23:28:39 -08:00
|
|
|
return pref;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2008-01-08 23:28:39 -08:00
|
|
|
nsSize
|
|
|
|
nsListBoxLayout::GetMinSize(nsIBox* aBox, nsBoxLayoutState& aBoxLayoutState)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-01-08 23:28:39 -08:00
|
|
|
nsSize minSize = nsGridRowGroupLayout::GetMinSize(aBox, aBoxLayoutState);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-07-08 00:08:04 -07:00
|
|
|
nsListBoxBodyFrame* frame = static_cast<nsListBoxBodyFrame*>(aBox);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (frame) {
|
|
|
|
nscoord rowheight = frame->GetRowHeightAppUnits();
|
2008-01-08 23:28:39 -08:00
|
|
|
minSize.height = frame->GetRowCount() * rowheight;
|
2007-03-22 10:30:00 -07:00
|
|
|
// Pad the height.
|
|
|
|
nscoord y = frame->GetAvailableHeight();
|
2008-01-08 23:28:39 -08:00
|
|
|
if (minSize.height > y && y > 0 && rowheight > 0) {
|
|
|
|
nscoord m = (minSize.height-y)%rowheight;
|
2007-03-22 10:30:00 -07:00
|
|
|
nscoord remainder = m == 0 ? 0 : rowheight - m;
|
2008-01-08 23:28:39 -08:00
|
|
|
minSize.height += remainder;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
if (nsContentUtils::HasNonEmptyAttr(frame->GetContent(), kNameSpaceID_None,
|
|
|
|
nsGkAtoms::sizemode)) {
|
|
|
|
nscoord width = frame->ComputeIntrinsicWidth(aBoxLayoutState);
|
2008-01-08 23:28:39 -08:00
|
|
|
if (width > minSize.width)
|
|
|
|
minSize.width = width;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
2008-01-08 23:28:39 -08:00
|
|
|
return minSize;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2008-01-08 23:28:39 -08:00
|
|
|
nsSize
|
|
|
|
nsListBoxLayout::GetMaxSize(nsIBox* aBox, nsBoxLayoutState& aBoxLayoutState)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-01-08 23:28:39 -08:00
|
|
|
nsSize maxSize = nsGridRowGroupLayout::GetMaxSize(aBox, aBoxLayoutState);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-07-08 00:08:04 -07:00
|
|
|
nsListBoxBodyFrame* frame = static_cast<nsListBoxBodyFrame*>(aBox);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (frame) {
|
|
|
|
nscoord rowheight = frame->GetRowHeightAppUnits();
|
2008-01-08 23:28:39 -08:00
|
|
|
maxSize.height = frame->GetRowCount() * rowheight;
|
2007-03-22 10:30:00 -07:00
|
|
|
// Pad the height.
|
|
|
|
nscoord y = frame->GetAvailableHeight();
|
2008-01-08 23:28:39 -08:00
|
|
|
if (maxSize.height > y && y > 0 && rowheight > 0) {
|
|
|
|
nscoord m = (maxSize.height-y)%rowheight;
|
2007-03-22 10:30:00 -07:00
|
|
|
nscoord remainder = m == 0 ? 0 : rowheight - m;
|
2008-01-08 23:28:39 -08:00
|
|
|
maxSize.height += remainder;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
2008-01-08 23:28:39 -08:00
|
|
|
return maxSize;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsListBoxLayout::Layout(nsIBox* aBox, nsBoxLayoutState& aState)
|
|
|
|
{
|
2007-07-08 00:08:04 -07:00
|
|
|
nsListBoxBodyFrame* frame = static_cast<nsListBoxBodyFrame*>(aBox);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// Always ensure an accurate scrollview position
|
|
|
|
// This is an edge case that was caused by the row height
|
|
|
|
// changing after a scroll had occurred. (Bug #51084)
|
|
|
|
PRInt32 index;
|
|
|
|
frame->GetIndexOfFirstVisibleRow(&index);
|
|
|
|
if (index > 0) {
|
|
|
|
nscoord pos = frame->GetYPosition();
|
|
|
|
PRInt32 rowHeight = frame->GetRowHeightAppUnits();
|
|
|
|
if (pos != (rowHeight*index)) {
|
|
|
|
frame->VerticalScroll(rowHeight*index);
|
|
|
|
frame->Redraw(aState, nsnull, PR_FALSE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult rv = LayoutInternal(aBox, aState);
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/////////// nsListBoxLayout /////////////////////////
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Called to layout our our children. Does no frame construction
|
|
|
|
*/
|
|
|
|
NS_IMETHODIMP
|
|
|
|
nsListBoxLayout::LayoutInternal(nsIBox* aBox, nsBoxLayoutState& aState)
|
|
|
|
{
|
|
|
|
PRInt32 redrawStart = -1;
|
|
|
|
|
|
|
|
// Get the start y position.
|
2007-07-08 00:08:04 -07:00
|
|
|
nsListBoxBodyFrame* body = static_cast<nsListBoxBodyFrame*>(aBox);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!body) {
|
|
|
|
NS_ERROR("Frame encountered that isn't a listboxbody!\n");
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsMargin margin;
|
|
|
|
|
|
|
|
// Get our client rect.
|
|
|
|
nsRect clientRect;
|
|
|
|
aBox->GetClientRect(clientRect);
|
|
|
|
|
|
|
|
// Get the starting y position and the remaining available
|
|
|
|
// height.
|
|
|
|
nscoord availableHeight = body->GetAvailableHeight();
|
|
|
|
nscoord yOffset = body->GetYPosition();
|
|
|
|
|
|
|
|
if (availableHeight <= 0) {
|
|
|
|
PRBool fixed = (body->GetFixedRowSize() != -1);
|
|
|
|
if (fixed)
|
|
|
|
availableHeight = 10;
|
|
|
|
else
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// run through all our currently created children
|
|
|
|
nsIBox* box = body->GetChildBox();
|
|
|
|
|
|
|
|
// if the reason is resize or initial we must relayout.
|
|
|
|
nscoord rowHeight = body->GetRowHeightAppUnits();
|
|
|
|
|
|
|
|
while (box) {
|
|
|
|
// If this box is dirty or if it has dirty children, we
|
|
|
|
// call layout on it.
|
|
|
|
nsRect childRect(box->GetRect());
|
|
|
|
box->GetMargin(margin);
|
|
|
|
|
|
|
|
// relayout if we must or we are dirty or some of our children are dirty
|
|
|
|
// or the client area is wider than us
|
|
|
|
// XXXldb There should probably be a resize check here too!
|
2007-05-06 12:16:51 -07:00
|
|
|
if (NS_SUBTREE_DIRTY(box) || childRect.width < clientRect.width) {
|
2007-03-22 10:30:00 -07:00
|
|
|
childRect.x = 0;
|
|
|
|
childRect.y = yOffset;
|
|
|
|
childRect.width = clientRect.width;
|
|
|
|
|
|
|
|
nsSize size = box->GetPrefSize(aState);
|
|
|
|
body->SetRowHeight(size.height);
|
|
|
|
|
|
|
|
childRect.height = rowHeight;
|
|
|
|
|
|
|
|
childRect.Deflate(margin);
|
|
|
|
box->SetBounds(aState, childRect);
|
|
|
|
box->Layout(aState);
|
|
|
|
} else {
|
|
|
|
// if the child did not need to be relayed out. Then its easy.
|
|
|
|
// Place the child by just grabbing its rect and adjusting the y.
|
|
|
|
PRInt32 newPos = yOffset+margin.top;
|
|
|
|
|
|
|
|
// are we pushing down or pulling up any rows?
|
|
|
|
// Then we may have to redraw everything below the moved
|
|
|
|
// rows.
|
|
|
|
if (redrawStart == -1 && childRect.y != newPos)
|
|
|
|
redrawStart = newPos;
|
|
|
|
|
|
|
|
childRect.y = newPos;
|
|
|
|
box->SetBounds(aState, childRect);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ok now the available size gets smaller and we move the
|
|
|
|
// starting position of the next child down some.
|
|
|
|
nscoord size = childRect.height + margin.top + margin.bottom;
|
|
|
|
|
|
|
|
yOffset += size;
|
|
|
|
availableHeight -= size;
|
|
|
|
|
|
|
|
box = box->GetNextBox();
|
|
|
|
}
|
|
|
|
|
|
|
|
// We have enough available height left to add some more rows
|
|
|
|
// Since we can't do this during layout, we post a callback
|
|
|
|
// that will be processed after the reflow completes.
|
|
|
|
body->PostReflowCallback();
|
|
|
|
|
|
|
|
// if rows were pushed down or pulled up because some rows were added
|
|
|
|
// before them then redraw everything under the inserted rows. The inserted
|
|
|
|
// rows will automatically be redrawn because the were marked dirty on insertion.
|
|
|
|
if (redrawStart > -1) {
|
|
|
|
nsRect bounds(aBox->GetRect());
|
|
|
|
nsRect tempRect(0,redrawStart,bounds.width, bounds.height - redrawStart);
|
|
|
|
aBox->Redraw(aState, &tempRect);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Creation Routines ///////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
NS_NewListBoxLayout( nsIPresShell* aPresShell, nsCOMPtr<nsIBoxLayout>& aNewLayout)
|
|
|
|
{
|
|
|
|
aNewLayout = new nsListBoxLayout(aPresShell);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|