Back to index

lightning-sunbird  0.9+nobinonly
nsNativeScrollbarFrame.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
00006  * Version 1.1 (the "License"); you may not use this file except in
00007  * compliance with 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.org 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) 2002
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or 
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the NPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 
00039 #include "nsNativeScrollbarFrame.h"
00040 #include "nsXULAtoms.h"
00041 #include "nsBoxLayoutState.h"
00042 #include "nsComponentManagerUtils.h"
00043 #include "nsGUIEvent.h"
00044 #include "nsIDeviceContext.h"
00045 #include "nsIView.h"
00046 #include "nsINativeScrollbar.h"
00047 #include "nsIScrollbarFrame.h"
00048 #include "nsIScrollbarMediator.h"
00049 #include "nsWidgetsCID.h"
00050 #include "nsINameSpaceManager.h"
00051 
00052 //
00053 // NS_NewNativeScrollbarFrame
00054 //
00055 // Creates a new scrollbar frame and returns it in |aNewFrame|
00056 //
00057 nsresult
00058 NS_NewNativeScrollbarFrame ( nsIPresShell* aPresShell, nsIFrame** aNewFrame )
00059 {
00060   NS_PRECONDITION(aNewFrame, "null OUT ptr");
00061   if (nsnull == aNewFrame) {
00062     return NS_ERROR_NULL_POINTER;
00063   }
00064   nsNativeScrollbarFrame* it = new (aPresShell) nsNativeScrollbarFrame (aPresShell);
00065   if (nsnull == it)
00066     return NS_ERROR_OUT_OF_MEMORY;
00067 
00068   *aNewFrame = it;
00069   return NS_OK;
00070   
00071 } // NS_NewNativeScrollbarFrame
00072 
00073 
00074 //
00075 // QueryInterface
00076 //
00077 NS_IMETHODIMP
00078 nsNativeScrollbarFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr)
00079 {
00080   if (!aInstancePtr) {
00081     return NS_ERROR_NULL_POINTER;
00082   }
00083   if (aIID.Equals(NS_GET_IID(nsIScrollbarMediator))) {
00084     *aInstancePtr = (void*) ((nsIScrollbarMediator*) this);
00085     return NS_OK;
00086   }
00087   return nsBoxFrame::QueryInterface(aIID, aInstancePtr);
00088 } 
00089 
00090 nsNativeScrollbarFrame::nsNativeScrollbarFrame(nsIPresShell* aShell)
00091   : nsBoxFrame(aShell), mScrollbarNeedsContent(PR_TRUE)
00092 {
00093 
00094 }
00095 
00096 //
00097 // Init
00098 //
00099 // Pass along to our parent, but also create the native widget that we wrap. 
00100 //
00101 NS_IMETHODIMP
00102 nsNativeScrollbarFrame::Init(nsPresContext* aPresContext, nsIContent* aContent,
00103                                nsIFrame* aParent, nsStyleContext* aContext, nsIFrame* aPrevInFlow)
00104 {
00105   nsresult  rv = nsBoxFrame::Init(aPresContext, aContent, aParent, aContext, aPrevInFlow);
00106   NS_ENSURE_SUCCESS(rv, rv);
00107 
00108   // create a view for this frame and then associate the view with the native
00109   // scrollbar widget. The net result of this is that the view will automatically
00110   // be resized and moved for us when things reflow, and the widget will follow
00111   // suit. We don't have to lift a finger!
00112   static NS_DEFINE_IID(kScrollbarCID,  NS_NATIVESCROLLBAR_CID);
00113   if ( NS_SUCCEEDED(CreateViewForFrame(aPresContext, this, aContext, PR_TRUE)) ) {
00114     nsIView* myView = GetView();
00115     if (myView && !myView->HasWidget()) {
00116       nsWidgetInitData widgetData;
00117       if ( NS_SUCCEEDED(myView->CreateWidget(kScrollbarCID, &widgetData, nsnull)) ) {
00118         mScrollbar = myView->GetWidget();
00119         if (mScrollbar) {
00120           mScrollbar->Show(PR_TRUE);
00121           mScrollbar->Enable(PR_TRUE);
00122 
00123           // defer telling the scrollbar about the mediator and the content
00124           // node until its first reflow since not everything has been set
00125           // by this point.
00126           mScrollbarNeedsContent = PR_TRUE;
00127         } else {
00128           NS_WARNING("Couldn't create native scrollbar!");
00129           return NS_ERROR_FAILURE;
00130         }
00131       }
00132     }
00133   }
00134   
00135   return rv;
00136 }
00137 
00138 NS_IMETHODIMP
00139 nsNativeScrollbarFrame::Destroy(nsPresContext* aPresContext)
00140 {
00141   nsCOMPtr<nsINativeScrollbar> scrollbar(do_QueryInterface(mScrollbar));
00142   if (scrollbar) {
00143     // frame is going away, unhook the native scrollbar from
00144     // the content node just to be safe about lifetime issues
00145     scrollbar->SetContent(nsnull, nsnull, nsnull);
00146   }
00147   return nsBoxFrame::Destroy(aPresContext);
00148 }
00149 
00150 //
00151 // FindParts
00152 //
00153 // Walk up the parent frame tree and find the content node of the frame
00154 // with the tag "scrollbar". This is the content node that the GFX Scroll Frame
00155 // is watching for attribute changes. We return the associated frame and
00156 // any mediator.
00157 //
00158 nsNativeScrollbarFrame::Parts
00159 nsNativeScrollbarFrame::FindParts()
00160 {
00161   nsIFrame* f;
00162   for (f = GetParent(); f; f = f->GetParent()) {
00163     nsIContent* currContent = f->GetContent();
00164 
00165     if (currContent && currContent->Tag() == nsXULAtoms::scrollbar) {
00166       nsIScrollbarFrame* sb;
00167       CallQueryInterface(f, &sb);
00168       if (sb) {
00169         nsIScrollbarMediator* sbm;
00170         sb->GetScrollbarMediator(&sbm);
00171         return Parts(f, sb, sbm);
00172       }
00173     }
00174   }
00175 
00176   return Parts(nsnull, nsnull, nsnull);
00177 }
00178 
00179 NS_IMETHODIMP
00180 nsNativeScrollbarFrame::Reflow(nsPresContext*          aPresContext,
00181                                nsHTMLReflowMetrics&     aDesiredSize,
00182                                const nsHTMLReflowState& aReflowState,
00183                                nsReflowStatus&          aStatus)
00184 {
00185   nsresult rv = nsBoxFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus);
00186   NS_ENSURE_SUCCESS(rv, rv);
00187 
00188   // nsGfxScrollFrame may have told us to shrink to nothing. If so, make sure our
00189   // desired size agrees.
00190   if (aReflowState.availableWidth == 0) {
00191     aDesiredSize.width = 0;
00192   }
00193   if (aReflowState.availableHeight == 0) {
00194     aDesiredSize.height = 0;
00195   }
00196 
00197   return NS_OK;
00198 }
00199 
00200 //
00201 // AttributeChanged
00202 //
00203 // We inherit changes to certain attributes from the parent's content node. These
00204 // occur when gecko changes the values of the scrollbar or scrolls the content area
00205 // by some means other than our scrollbar (keyboard, scrollwheel, etc). Update
00206 // our native scrollbar with the correct values.
00207 //
00208 NS_IMETHODIMP
00209 nsNativeScrollbarFrame::AttributeChanged(nsIContent* aChild,
00210                                          PRInt32 aNameSpaceID,
00211                                          nsIAtom* aAttribute,
00212                                          PRInt32 aModType)
00213 {
00214   nsresult rv = nsBoxFrame::AttributeChanged(aChild, aNameSpaceID,
00215                                              aAttribute, aModType);
00216   
00217   if (  aAttribute == nsXULAtoms::curpos ||
00218         aAttribute == nsXULAtoms::maxpos || 
00219         aAttribute == nsXULAtoms::pageincrement ||
00220         aAttribute == nsXULAtoms::increment ) {
00221     nsAutoString valueStr;
00222     aChild->GetAttr(aNameSpaceID, aAttribute, valueStr);
00223     
00224     PRInt32 error;
00225     PRInt32 value = valueStr.ToInteger(&error);
00226     if (value < 0)
00227       value = 1;          // just be safe and sanity check, scrollbar expects unsigned
00228 
00229     nsCOMPtr<nsINativeScrollbar> scrollbar(do_QueryInterface(mScrollbar));
00230     if (scrollbar) {
00231       if (aAttribute == nsXULAtoms::maxpos) {
00232         // bounds check it
00233         PRUint32 maxValue = (PRUint32)value;
00234         PRUint32 current;
00235         scrollbar->GetPosition(&current);
00236         if (current > maxValue)
00237         {
00238           PRInt32 oldPosition = (PRInt32)current;
00239           PRInt32 curPosition = maxValue;
00240         
00241           Parts parts = FindParts();
00242           if (parts.mMediator) {
00243             parts.mMediator->PositionChanged(parts.mIScrollbarFrame, oldPosition, /* inout */ curPosition);
00244           }
00245 
00246           nsAutoString currentStr;
00247           currentStr.AppendInt(curPosition);
00248           parts.mScrollbarFrame->GetContent()->
00249             SetAttr(kNameSpaceID_None, nsXULAtoms::curpos, currentStr, PR_TRUE);
00250         }
00251       }
00252       
00253       if ( aAttribute == nsXULAtoms::curpos )
00254         scrollbar->SetPosition(value);
00255       else if ( aAttribute == nsXULAtoms::maxpos )
00256         scrollbar->SetMaxRange(value);
00257       else if ( aAttribute == nsXULAtoms::pageincrement )   // poorly named, actually the height of the visible view area
00258         scrollbar->SetViewSize(value);
00259      else if ( aAttribute == nsXULAtoms::increment )
00260         scrollbar->SetLineIncrement(value);
00261     }
00262   }
00263 
00264   return rv;
00265 }
00266 
00267 
00268 //
00269 // GetPrefSize
00270 //
00271 // Ask our native widget what dimensions it wants to be, convert them
00272 // back to twips, and tell gecko.
00273 //
00274 NS_IMETHODIMP
00275 nsNativeScrollbarFrame::GetPrefSize(nsBoxLayoutState& aState, nsSize& aSize)
00276 {
00277   float p2t = 0.0;
00278   p2t = aState.PresContext()->PixelsToTwips();
00279   
00280   PRInt32 narrowDimension = 0;
00281   nsCOMPtr<nsINativeScrollbar> native ( do_QueryInterface(mScrollbar) );
00282   if ( !native ) return NS_ERROR_FAILURE;  
00283   native->GetNarrowSize(&narrowDimension);
00284   
00285   if ( IsVertical() )
00286     aSize.width = nscoord(narrowDimension * p2t);
00287   else
00288     aSize.height = nscoord(narrowDimension * p2t);
00289   
00290   // By now, we have both the content node for the scrollbar and the associated
00291   // scrollbar mediator (for outliner, if applicable). Hook up the scrollbar to
00292   // gecko
00293   Hookup();
00294     
00295   return NS_OK;
00296 }
00297 
00298 
00299 //
00300 // Hookup
00301 //
00302 // Connect our widget to the content node and/or scrolling mediator. This needs
00303 // to be called late enough in the game where everything is ready. Calling it too
00304 // early can lead to situations where the mediator hasn't yet been hooked up to the
00305 // scrollbar frame
00306 //
00307 void
00308 nsNativeScrollbarFrame::Hookup()
00309 {
00310   if (!mScrollbarNeedsContent)
00311     return;
00312 
00313   nsCOMPtr<nsINativeScrollbar> scrollbar(do_QueryInterface(mScrollbar));
00314   if (!scrollbar) {
00315     NS_WARNING("Native scrollbar widget doesn't implement nsINativeScrollbar");
00316     return;
00317   }
00318 
00319   Parts parts = FindParts();
00320   if (!parts.mScrollbarFrame) {
00321     // Nothing to do here
00322     return;
00323   }
00324   
00325   // We can't just pass 'mediator' to the widget, because 'mediator' might go away.
00326   // So pass a pointer to us. When we go away, we can tell the widget.
00327   nsIContent* scrollbarContent = parts.mScrollbarFrame->GetContent();
00328   // Always configure ourselves as the mediator even if parts.mMediator is null;
00329   // the mediator might be changed.
00330   scrollbar->SetContent(scrollbarContent,
00331                         parts.mIScrollbarFrame, this);
00332   mScrollbarNeedsContent = PR_FALSE;
00333 
00334   if (!scrollbarContent)
00335     return;
00336 
00337   // Check to see if the curpos attribute is already present on the content
00338   // node. If so, notify the scrollbar.
00339 
00340   nsAutoString value;
00341   scrollbarContent->GetAttr(kNameSpaceID_None, nsXULAtoms::curpos, value);
00342 
00343   PRInt32 error;
00344   PRUint32 curpos = value.ToInteger(&error);
00345   if (!curpos || error)
00346     return;
00347 
00348   scrollbar->SetPosition(curpos);
00349 }
00350 
00351 NS_IMETHODIMP
00352 nsNativeScrollbarFrame::PositionChanged(nsISupports* aScrollbar, PRInt32 aOldIndex, PRInt32& aNewIndex)
00353 {
00354   Parts parts = FindParts();
00355   if (!parts.mMediator)
00356     return NS_OK;
00357   return parts.mMediator->PositionChanged(aScrollbar, aOldIndex, aNewIndex);
00358 }
00359 
00360 NS_IMETHODIMP
00361 nsNativeScrollbarFrame::ScrollbarButtonPressed(nsISupports* aScrollbar, PRInt32 aOldIndex, PRInt32 aNewIndex)
00362 {
00363   Parts parts = FindParts();
00364   if (!parts.mMediator)
00365     return NS_OK;
00366   return parts.mMediator->ScrollbarButtonPressed(aScrollbar, aOldIndex, aNewIndex);
00367 }
00368 
00369 NS_IMETHODIMP
00370 nsNativeScrollbarFrame::VisibilityChanged(nsISupports* aScrollbar, PRBool aVisible)
00371 {
00372   Parts parts = FindParts();
00373   if (!parts.mMediator)
00374     return NS_OK;
00375   return parts.mMediator->VisibilityChanged(aScrollbar, aVisible);
00376 }