Back to index

lightning-sunbird  0.9+nobinonly
nsListBoxLayout.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   David W. Hyatt (hyatt@netscape.com) (Original Author)
00024  *   Joe Hewitt (hewitt@netscape.com)
00025  *
00026  * Alternatively, the contents of this file may be used under the terms of
00027  * either of the GNU General Public License Version 2 or later (the "GPL"),
00028  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00029  * in which case the provisions of the GPL or the LGPL are applicable instead
00030  * of those above. If you wish to allow use of your version of this file only
00031  * under the terms of either the GPL or the LGPL, and not to allow others to
00032  * use your version of this file under the terms of the MPL, indicate your
00033  * decision by deleting the provisions above and replace them with the notice
00034  * and other provisions required by the GPL or the LGPL. If you do not delete
00035  * the provisions above, a recipient may use your version of this file under
00036  * the terms of any one of the MPL, the GPL or the LGPL.
00037  *
00038  * ***** END LICENSE BLOCK ***** */
00039 
00040 #include "nsListBoxLayout.h"
00041 
00042 #include "nsListBoxBodyFrame.h"
00043 #include "nsIFrame.h"
00044 #include "nsBox.h"
00045 #include "nsBoxLayoutState.h"
00046 #include "nsIScrollableFrame.h"
00047 #include "nsIReflowCallback.h"
00048 #include "nsINameSpaceManager.h"
00049 #include "nsXULAtoms.h"
00050 
00051 nsListBoxLayout::nsListBoxLayout(nsIPresShell* aPresShell)
00052   : nsGridRowGroupLayout(aPresShell)
00053 {
00054 }
00055 
00057 
00058 NS_IMETHODIMP
00059 nsListBoxLayout::GetPrefSize(nsIBox* aBox, nsBoxLayoutState& aBoxLayoutState, nsSize& aSize)
00060 {
00061   nsresult rv = nsGridRowGroupLayout::GetPrefSize(aBox, aBoxLayoutState, aSize);
00062 
00063   nsListBoxBodyFrame* frame = NS_STATIC_CAST(nsListBoxBodyFrame*, aBox);
00064   if (frame) {
00065     nscoord rowheight = frame->GetRowHeightTwips();
00066     aSize.height = frame->GetRowCount() * rowheight;
00067     // Pad the height.
00068     nscoord y = frame->GetAvailableHeight();
00069     if (aSize.height > y && y > 0 && rowheight > 0) {
00070       nscoord m = (aSize.height-y)%rowheight;
00071       nscoord remainder = m == 0 ? 0 : rowheight - m;
00072       aSize.height += remainder;
00073     }
00074     nsAutoString sizeMode;
00075     frame->GetContent()->GetAttr(kNameSpaceID_None, nsXULAtoms::sizemode, sizeMode);
00076     if (!sizeMode.IsEmpty()) {
00077       nscoord width = frame->ComputeIntrinsicWidth(aBoxLayoutState);
00078       if (width > aSize.width)
00079         aSize.width = width;
00080     }
00081   }
00082   return rv;
00083 }
00084 
00085 NS_IMETHODIMP
00086 nsListBoxLayout::GetMinSize(nsIBox* aBox, nsBoxLayoutState& aBoxLayoutState, nsSize& aSize)
00087 {
00088   nsresult rv = nsGridRowGroupLayout::GetMinSize(aBox, aBoxLayoutState, aSize);
00089 
00090   nsListBoxBodyFrame* frame = NS_STATIC_CAST(nsListBoxBodyFrame*, aBox);
00091   if (frame) {
00092     nscoord rowheight = frame->GetRowHeightTwips();
00093     aSize.height = frame->GetRowCount() * rowheight;
00094     // Pad the height.
00095     nscoord y = frame->GetAvailableHeight();
00096     if (aSize.height > y && y > 0 && rowheight > 0) {
00097       nscoord m = (aSize.height-y)%rowheight;
00098       nscoord remainder = m == 0 ? 0 : rowheight - m;
00099       aSize.height += remainder;
00100     }
00101     nsAutoString sizeMode;
00102     frame->GetContent()->GetAttr(kNameSpaceID_None, nsXULAtoms::sizemode, sizeMode);
00103     if (!sizeMode.IsEmpty()) {
00104       nscoord width = frame->ComputeIntrinsicWidth(aBoxLayoutState);
00105       if (width > aSize.width)
00106         aSize.width = width;
00107     }
00108   }
00109   return rv;
00110 }
00111 
00112 NS_IMETHODIMP
00113 nsListBoxLayout::GetMaxSize(nsIBox* aBox, nsBoxLayoutState& aBoxLayoutState, nsSize& aSize)
00114 {
00115   nsresult rv = nsGridRowGroupLayout::GetMaxSize(aBox, aBoxLayoutState, aSize);
00116 
00117   nsListBoxBodyFrame* frame = NS_STATIC_CAST(nsListBoxBodyFrame*, aBox);
00118   if (frame) {
00119     nscoord rowheight = frame->GetRowHeightTwips();
00120     aSize.height = frame->GetRowCount() * rowheight;
00121     // Pad the height.
00122     nscoord y = frame->GetAvailableHeight();
00123     if (aSize.height > y && y > 0 && rowheight > 0) {
00124       nscoord m = (aSize.height-y)%rowheight;
00125       nscoord remainder = m == 0 ? 0 : rowheight - m;
00126       aSize.height += remainder;
00127     }
00128   }
00129   return rv;
00130 }
00131 
00132 NS_IMETHODIMP
00133 nsListBoxLayout::Layout(nsIBox* aBox, nsBoxLayoutState& aState)
00134 {
00135   nsListBoxBodyFrame* frame = NS_STATIC_CAST(nsListBoxBodyFrame*, aBox);
00136 
00137   // Always ensure an accurate scrollview position
00138   // This is an edge case that was caused by the row height
00139   // changing after a scroll had occurred.  (Bug #51084)
00140   PRInt32 index;
00141   frame->GetIndexOfFirstVisibleRow(&index);
00142   if (index > 0) {
00143     nscoord pos = frame->GetYPosition();
00144     PRInt32 rowHeight = frame->GetRowHeightTwips();
00145     if (pos != (rowHeight*index)) {
00146       frame->VerticalScroll(rowHeight*index);
00147       frame->Redraw(aState, nsnull, PR_FALSE);
00148     }
00149   }
00150 
00151   nsresult rv = LayoutInternal(aBox, aState);
00152   if (NS_FAILED(rv)) return rv;
00153 
00154   return NS_OK;
00155 }
00156 
00157 
00159 
00163 NS_IMETHODIMP
00164 nsListBoxLayout::LayoutInternal(nsIBox* aBox, nsBoxLayoutState& aState)
00165 {
00166   PRInt32 redrawStart = -1;
00167 
00168   // Get the start y position.
00169   nsListBoxBodyFrame* body = NS_STATIC_CAST(nsListBoxBodyFrame*, aBox);
00170   if (!body) {
00171     NS_ERROR("Frame encountered that isn't a listboxbody!\n");
00172     return NS_ERROR_FAILURE;
00173   }
00174 
00175   nsMargin margin;
00176 
00177   // Get our client rect.
00178   nsRect clientRect;
00179   aBox->GetClientRect(clientRect);
00180 
00181   // Get the starting y position and the remaining available
00182   // height.
00183   nscoord availableHeight = body->GetAvailableHeight();
00184   nscoord yOffset = body->GetYPosition();
00185   
00186   if (availableHeight <= 0) {
00187     PRBool fixed = (body->GetFixedRowSize() != -1);
00188     if (fixed)
00189       availableHeight = 10;
00190     else
00191       return NS_OK;
00192   }
00193 
00194   // run through all our currently created children
00195   nsIBox* box = nsnull;
00196   body->GetChildBox(&box);
00197 
00198   // if the reason is resize or initial we must relayout.
00199   PRBool relayout = (aState.LayoutReason() == nsBoxLayoutState::Resize || aState.LayoutReason() == nsBoxLayoutState::Initial);
00200   nscoord rowHeight = body->GetRowHeightTwips();
00201 
00202   while (box) {
00203     // If this box is dirty or if it has dirty children, we
00204     // call layout on it.
00205     PRBool dirty = PR_FALSE;           
00206     PRBool dirtyChildren = PR_FALSE;           
00207     box->IsDirty(dirty);
00208     box->HasDirtyChildren(dirtyChildren);
00209        
00210     nsRect childRect(box->GetRect());
00211     box->GetMargin(margin);
00212     
00213     // relayout if we must or we are dirty or some of our children are dirty
00214     //   or the client area is wider than us
00215     if (relayout || dirty || dirtyChildren || childRect.width < clientRect.width) {
00216       childRect.x = 0;
00217       childRect.y = yOffset;
00218       childRect.width = clientRect.width;
00219       
00220       nsSize size;
00221       box->GetPrefSize(aState, size);
00222       body->SetRowHeight(size.height);
00223       
00224       childRect.height = rowHeight;
00225 
00226       childRect.Deflate(margin);
00227       box->SetBounds(aState, childRect);
00228       box->Layout(aState);
00229     } else {
00230       // if the child did not need to be relayed out. Then its easy.
00231       // Place the child by just grabbing its rect and adjusting the y.
00232       PRInt32 newPos = yOffset+margin.top;
00233 
00234       // are we pushing down or pulling up any rows?
00235       // Then we may have to redraw everything below the the moved 
00236       // rows.
00237       if (redrawStart == -1 && childRect.y != newPos)
00238         redrawStart = newPos;
00239 
00240       childRect.y = newPos;
00241       box->SetBounds(aState, childRect);
00242     }
00243 
00244     // Ok now the available size gets smaller and we move the
00245     // starting position of the next child down some.
00246     nscoord size = childRect.height + margin.top + margin.bottom;
00247 
00248     yOffset += size;
00249     availableHeight -= size;
00250     
00251     box->GetNextBox(&box);
00252   }
00253   
00254   // We have enough available height left to add some more rows
00255   // Since we can't do this during layout, we post a callback
00256   // that will be processed after the reflow completes.
00257   body->PostReflowCallback();
00258     
00259   // if rows were pushed down or pulled up because some rows were added
00260   // before them then redraw everything under the inserted rows. The inserted
00261   // rows will automatically be redrawn because the were marked dirty on insertion.
00262   if (redrawStart > -1) {
00263     nsRect bounds(aBox->GetRect());
00264     nsRect tempRect(0,redrawStart,bounds.width, bounds.height - redrawStart);
00265     aBox->Redraw(aState, &tempRect);
00266   }
00267 
00268   return NS_OK;
00269 }
00270 
00271 // Creation Routines ///////////////////////////////////////////////////////////////////////
00272 
00273 nsresult
00274 NS_NewListBoxLayout( nsIPresShell* aPresShell, nsCOMPtr<nsIBoxLayout>& aNewLayout)
00275 {
00276   aNewLayout = new nsListBoxLayout(aPresShell);
00277 
00278   return NS_OK;
00279 }