Back to index

lightning-sunbird  0.9+nobinonly
nsSliderFrame.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  *   Dean Tessman <dean_tessman@hotmail.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 //
00040 // Eric Vaughan
00041 // Netscape Communications
00042 //
00043 // See documentation in associated header file
00044 //
00045 
00046 #include "nsSliderFrame.h"
00047 #include "nsStyleContext.h"
00048 #include "nsPresContext.h"
00049 #include "nsIContent.h"
00050 #include "nsCOMPtr.h"
00051 #include "nsUnitConversion.h"
00052 #include "nsINameSpaceManager.h"
00053 #include "nsXULAtoms.h"
00054 #include "nsHTMLAtoms.h"
00055 #include "nsHTMLParts.h"
00056 #include "nsIPresShell.h"
00057 #include "nsStyleChangeList.h"
00058 #include "nsCSSRendering.h"
00059 #include "nsHTMLAtoms.h"
00060 #include "nsIDOMEventReceiver.h"
00061 #include "nsIViewManager.h"
00062 #include "nsIWidget.h"
00063 #include "nsIDOMMouseEvent.h"
00064 #include "nsIDocument.h"
00065 #include "nsScrollbarButtonFrame.h"
00066 #include "nsIScrollbarListener.h"
00067 #include "nsIScrollbarMediator.h"
00068 #include "nsIScrollbarFrame.h"
00069 #include "nsISupportsArray.h"
00070 #include "nsXULAtoms.h"
00071 #include "nsHTMLAtoms.h"
00072 #include "nsLayoutAtoms.h"
00073 #include "nsIScrollableView.h"
00074 #include "nsRepeatService.h"
00075 #include "nsBoxLayoutState.h"
00076 #include "nsSprocketLayout.h"
00077 #include "nsIServiceManager.h"
00078 #include "nsGUIEvent.h"
00079 #include "nsContentUtils.h"
00080 #include "nsIPrivateDOMEvent.h"
00081 
00082 PRBool nsSliderFrame::gMiddlePref = PR_FALSE;
00083 PRInt32 nsSliderFrame::gSnapMultiplier = 6;
00084 
00085 // Turn this on if you want to debug slider frames.
00086 #undef DEBUG_SLIDER
00087 
00088 static already_AddRefed<nsIContent>
00089 GetContentOfBox(nsIBox *aBox)
00090 {
00091   nsIContent* content = aBox->GetContent();
00092   NS_IF_ADDREF(content);
00093   return content;
00094 }
00095 
00096 nsresult
00097 NS_NewSliderFrame ( nsIPresShell* aPresShell, nsIFrame** aNewFrame)
00098 {
00099   NS_PRECONDITION(aNewFrame, "null OUT ptr");
00100   if (nsnull == aNewFrame) {
00101     return NS_ERROR_NULL_POINTER;
00102   }
00103   nsSliderFrame* it = new (aPresShell) nsSliderFrame(aPresShell);
00104   if (nsnull == it)
00105     return NS_ERROR_OUT_OF_MEMORY;
00106 
00107   *aNewFrame = it;
00108   return NS_OK;
00109 
00110 } // NS_NewSliderFrame
00111 
00112 nsSliderFrame::nsSliderFrame(nsIPresShell* aPresShell):nsBoxFrame(aPresShell),
00113  mCurPos(0), mScrollbarListener(nsnull),mChange(0), mMediator(nsnull)
00114 {
00115 }
00116 
00117 // stop timer
00118 nsSliderFrame::~nsSliderFrame()
00119 {
00120 }
00121 
00122 NS_IMETHODIMP
00123 nsSliderFrame::Init(nsPresContext*  aPresContext,
00124                     nsIContent*      aContent,
00125                     nsIFrame*        aParent,
00126                     nsStyleContext*  aContext,
00127                     nsIFrame*        aPrevInFlow)
00128 {
00129   nsresult  rv = nsBoxFrame::Init(aPresContext, aContent, aParent, aContext,
00130                                   aPrevInFlow);
00131 
00132   static PRBool gotPrefs = PR_FALSE;
00133   if (!gotPrefs) {
00134     gotPrefs = PR_TRUE;
00135 
00136     gMiddlePref = nsContentUtils::GetBoolPref("middlemouse.scrollbarPosition");
00137     gSnapMultiplier = nsContentUtils::GetIntPref("slider.snapMultiplier");
00138   }
00139 
00140   CreateViewForFrame(aPresContext,this,aContext,PR_TRUE);
00141   nsIView* view = GetView();
00142   view->GetViewManager()->SetViewContentTransparency(view, PR_TRUE);
00143   return rv;
00144 }
00145 
00146 NS_IMETHODIMP
00147 nsSliderFrame::RemoveFrame(nsIAtom*        aListName,
00148                            nsIFrame*       aOldFrame)
00149 {
00150   nsresult rv = nsBoxFrame::RemoveFrame(aListName, aOldFrame);
00151   PRInt32 start = GetChildCount();
00152   if (start == 0)
00153     RemoveListener();
00154 
00155   return rv;
00156 }
00157 
00158 NS_IMETHODIMP
00159 nsSliderFrame::InsertFrames(nsIAtom*        aListName,
00160                             nsIFrame*       aPrevFrame,
00161                             nsIFrame*       aFrameList)
00162 {
00163   PRInt32 start = GetChildCount();
00164   nsresult rv = nsBoxFrame::InsertFrames(aListName, aPrevFrame, aFrameList);
00165   if (start == 0)
00166     AddListener();
00167 
00168   return rv;
00169 }
00170 
00171 NS_IMETHODIMP
00172 nsSliderFrame::AppendFrames(nsIAtom*        aListName,
00173                             nsIFrame*       aFrameList)
00174 {
00175   // if we have no children and on was added then make sure we add the
00176   // listener
00177   PRInt32 start = GetChildCount();
00178   nsresult rv = nsBoxFrame::AppendFrames(aListName, aFrameList);
00179   if (start == 0)
00180     AddListener();
00181 
00182   return rv;
00183 }
00184 
00185 PRInt32
00186 nsSliderFrame::GetCurrentPosition(nsIContent* content)
00187 {
00188   return GetIntegerAttribute(content, nsXULAtoms::curpos, 0);
00189 }
00190 
00191 PRInt32
00192 nsSliderFrame::GetMaxPosition(nsIContent* content)
00193 {
00194   return GetIntegerAttribute(content, nsXULAtoms::maxpos, 100);
00195 }
00196 
00197 PRInt32
00198 nsSliderFrame::GetIncrement(nsIContent* content)
00199 {
00200   return GetIntegerAttribute(content, nsXULAtoms::increment, 1);
00201 }
00202 
00203 
00204 PRInt32
00205 nsSliderFrame::GetPageIncrement(nsIContent* content)
00206 {
00207   return GetIntegerAttribute(content, nsXULAtoms::pageincrement, 10);
00208 }
00209 
00210 PRInt32
00211 nsSliderFrame::GetIntegerAttribute(nsIContent* content, nsIAtom* atom, PRInt32 defaultValue)
00212 {
00213     nsAutoString value;
00214     if (NS_CONTENT_ATTR_HAS_VALUE == content->GetAttr(kNameSpaceID_None, atom, value))
00215     {
00216       PRInt32 error;
00217 
00218       // convert it to an integer
00219       defaultValue = value.ToInteger(&error);
00220     }
00221 
00222     return defaultValue;
00223 }
00224 
00225 
00226 NS_IMETHODIMP
00227 nsSliderFrame::AttributeChanged(nsIContent* aChild,
00228                                 PRInt32 aNameSpaceID,
00229                                 nsIAtom* aAttribute,
00230                                 PRInt32 aModType)
00231 {
00232   nsresult rv = nsBoxFrame::AttributeChanged(aChild, aNameSpaceID,
00233                                              aAttribute, aModType);
00234   // if the current position changes
00235   if (aAttribute == nsXULAtoms::curpos) {
00236      rv = CurrentPositionChanged(GetPresContext(), PR_FALSE);
00237      NS_ASSERTION(NS_SUCCEEDED(rv), "failed to change position");
00238      if (NS_FAILED(rv))
00239         return rv;
00240   } else if (aAttribute == nsXULAtoms::maxpos) {
00241       // bounds check it.
00242 
00243       nsIBox* scrollbarBox = GetScrollbar();
00244       nsCOMPtr<nsIContent> scrollbar;
00245       scrollbar = GetContentOfBox(scrollbarBox);
00246       PRInt32 current = GetCurrentPosition(scrollbar);
00247       PRInt32 max = GetMaxPosition(scrollbar);
00248       if (current < 0 || current > max)
00249       {
00250         if (current < 0)
00251             current = 0;
00252         else if (current > max)
00253             current = max;
00254 
00255         // set the new position and notify observers
00256         nsCOMPtr<nsIScrollbarFrame> scrollbarFrame(do_QueryInterface(scrollbarBox));
00257         if (scrollbarFrame) {
00258           nsCOMPtr<nsIScrollbarMediator> mediator;
00259           scrollbarFrame->GetScrollbarMediator(getter_AddRefs(mediator));
00260           if (mediator) {
00261             mediator->PositionChanged(scrollbarFrame, GetCurrentPosition(scrollbar), current);
00262           }
00263         }
00264 
00265         nsAutoString currentStr;
00266         currentStr.AppendInt(current);
00267         scrollbar->SetAttr(kNameSpaceID_None, nsXULAtoms::curpos, currentStr, PR_TRUE);
00268       }
00269   }
00270 
00271   if (aAttribute == nsXULAtoms::maxpos ||
00272       aAttribute == nsXULAtoms::pageincrement ||
00273       aAttribute == nsXULAtoms::increment) {
00274 
00275       nsBoxLayoutState state(GetPresContext());
00276       MarkDirtyChildren(state);
00277   }
00278 
00279   return rv;
00280 }
00281 
00282 NS_IMETHODIMP
00283 nsSliderFrame::Paint(nsPresContext*      aPresContext,
00284                      nsIRenderingContext& aRenderingContext,
00285                      const nsRect&        aDirtyRect,
00286                      nsFramePaintLayer    aWhichLayer,
00287                      PRUint32             aFlags)
00288 {
00289   // if we are too small to have a thumb don't paint it.
00290   nsIBox* thumb;
00291   GetChildBox(&thumb);
00292 
00293   if (thumb) {
00294     nsRect thumbRect(thumb->GetRect());
00295     nsMargin m;
00296     thumb->GetMargin(m);
00297     thumbRect.Inflate(m);
00298 
00299     nsRect crect;
00300     GetClientRect(crect);
00301 
00302     if (crect.width < thumbRect.width || crect.height < thumbRect.height)
00303     {
00304       if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
00305         PaintSelf(aPresContext, aRenderingContext, aDirtyRect);
00306       }
00307       return NS_OK;
00308     }
00309   }
00310 
00311   return nsBoxFrame::Paint(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer);
00312 }
00313 
00314 NS_IMETHODIMP
00315 nsSliderFrame::DoLayout(nsBoxLayoutState& aState)
00316 {
00317   // get the thumb should be our only child
00318   nsIBox* thumbBox = nsnull;
00319   GetChildBox(&thumbBox);
00320 
00321   if (!thumbBox) {
00322     SyncLayout(aState);
00323     return NS_OK;
00324   }
00325 
00326   EnsureOrient();
00327 
00328 #ifdef DEBUG_LAYOUT
00329   if (mState & NS_STATE_DEBUG_WAS_SET) {
00330       if (mState & NS_STATE_SET_TO_DEBUG)
00331           SetDebug(aState, PR_TRUE);
00332       else
00333           SetDebug(aState, PR_FALSE);
00334   }
00335 #endif
00336 
00337   // get the content area inside our borders
00338   nsRect clientRect(0,0,0,0);
00339   GetClientRect(clientRect);
00340 
00341   // get the scrollbar
00342   nsIBox* scrollbarBox = GetScrollbar();
00343   nsCOMPtr<nsIContent> scrollbar;
00344   scrollbar = GetContentOfBox(scrollbarBox);
00345   PRBool isHorizontal = IsHorizontal();
00346 
00347   // get the thumb's pref size
00348   nsSize thumbSize(0,0);
00349   thumbBox->GetPrefSize(aState, thumbSize);
00350 
00351   if (isHorizontal)
00352     thumbSize.height = clientRect.height;
00353   else
00354     thumbSize.width = clientRect.width;
00355 
00356   // get our current position and max position from our content node
00357   PRInt32 curpospx = GetCurrentPosition(scrollbar);
00358   PRInt32 maxpospx = GetMaxPosition(scrollbar);
00359   PRInt32 pageIncrement = GetPageIncrement(scrollbar);
00360 
00361   if (curpospx < 0)
00362      curpospx = 0;
00363   else if (curpospx > maxpospx)
00364      curpospx = maxpospx;
00365 
00366   nscoord onePixel = aState.PresContext()->IntScaledPixelsToTwips(1);
00367 
00368   // get max pos in twips
00369   nscoord maxpos = maxpospx*onePixel;
00370 
00371   // get our maxpos in twips. This is the space we have left over in the scrollbar
00372   // after the height of the thumb has been removed
00373   nscoord& desiredcoord = isHorizontal ? clientRect.width : clientRect.height;
00374   nscoord& thumbcoord = isHorizontal ? thumbSize.width : thumbSize.height;
00375 
00376   nscoord ourmaxpos = desiredcoord;
00377 
00378   mRatio = 1;
00379 
00380   if ((pageIncrement + maxpospx) != 0)
00381   {
00382     // if the thumb is flexible make the thumb bigger.
00383     nscoord flex = 0;
00384     thumbBox->GetFlex(aState, flex);
00385 
00386     if (flex > 0)
00387     {
00388       mRatio = float(pageIncrement) / float(maxpospx + pageIncrement);
00389       nscoord thumbsize = NSToCoordRound(ourmaxpos * mRatio);
00390 
00391       // if there is more room than the thumb needs stretch the thumb
00392       if (thumbsize > thumbcoord)
00393         thumbcoord = thumbsize;
00394     }
00395   }
00396 
00397   ourmaxpos -= thumbcoord;
00398   if (float(maxpos) != 0)
00399     mRatio = float(ourmaxpos)/float(maxpos);
00400 
00401   nscoord curpos = curpospx*onePixel;
00402 
00403   // set the thumbs y coord to be the current pos * the ratio.
00404   nscoord pos = nscoord(float(curpos)*mRatio);
00405   nsRect thumbRect(clientRect.x, clientRect.y, thumbSize.width, thumbSize.height);
00406 
00407   if (isHorizontal)
00408     thumbRect.x += pos;
00409   else
00410     thumbRect.y += pos;
00411 
00412   nsRect oldThumbRect(thumbBox->GetRect());
00413   LayoutChildAt(aState, thumbBox, thumbRect);
00414 
00415 
00416   SyncLayout(aState);
00417 
00418 #ifdef DEBUG_SLIDER
00419   PRInt32 c = GetCurrentPosition(scrollbar);
00420   PRInt32 m = GetMaxPosition(scrollbar);
00421   printf("Current=%d, max=%d\n", c, m);
00422 #endif
00423 
00424   // redraw only if thumb changed size.
00425   if (oldThumbRect != thumbRect)
00426     Redraw(aState);
00427 
00428   return NS_OK;
00429 }
00430 
00431 
00432 NS_IMETHODIMP
00433 nsSliderFrame::HandleEvent(nsPresContext* aPresContext,
00434                                       nsGUIEvent* aEvent,
00435                                       nsEventStatus* aEventStatus)
00436 {
00437   nsIBox* scrollbarBox = GetScrollbar();
00438   nsCOMPtr<nsIContent> scrollbar;
00439   scrollbar = GetContentOfBox(scrollbarBox);
00440   PRBool isHorizontal = IsHorizontal();
00441 
00442   if (isDraggingThumb())
00443   {
00444     switch (aEvent->message) {
00445     case NS_MOUSE_MOVE: {
00446       if (mChange) {
00447         // We're in the process of moving the thumb to the mouse,
00448         // but the mouse just moved.  Make sure to update our
00449         // destination point.
00450         mDestinationPoint = EventPointInOurCoords(aEvent);
00451         nsRepeatService::GetInstance()->Stop();
00452         nsRepeatService::GetInstance()->Start(mMediator);
00453         break;
00454       }
00455 
00456        // convert coord to pixels
00457        nsPoint eventPoint = EventPointInOurCoords(aEvent);
00458        nscoord pos = isHorizontal ? eventPoint.x : eventPoint.y;
00459          
00460        nscoord onePixel = aPresContext->IntScaledPixelsToTwips(1);
00461 
00462        nsIFrame* thumbFrame = mFrames.FirstChild();
00463 
00464        // take our current position and substract the start location
00465        pos -= mDragStart;
00466        PRBool isMouseOutsideThumb = PR_FALSE;
00467        if (gSnapMultiplier) {
00468          nsSize thumbSize = thumbFrame->GetSize();
00469          if (isHorizontal) {
00470            // horizontal scrollbar - check if mouse is above or below thumb
00471            // XXXbz what about looking at the .y of the thumb's rect?  Is that
00472            // always zero here?
00473            if (eventPoint.y < -gSnapMultiplier * thumbSize.height ||
00474                eventPoint.y > thumbSize.height +
00475                                 gSnapMultiplier * thumbSize.height)
00476              isMouseOutsideThumb = PR_TRUE;
00477          }
00478          else {
00479            // vertical scrollbar - check if mouse is left or right of thumb
00480            if (eventPoint.x < -gSnapMultiplier * thumbSize.width ||
00481                eventPoint.x > thumbSize.width +
00482                                 gSnapMultiplier * thumbSize.width)
00483              isMouseOutsideThumb = PR_TRUE;
00484          }
00485        }
00486        if (isMouseOutsideThumb)
00487        {
00488          // XXX see bug 81586
00489          SetCurrentPosition(scrollbar, thumbFrame,
00490                             (int) (mThumbStart / onePixel / mRatio),
00491                             PR_FALSE, PR_TRUE);
00492          return NS_OK;
00493        }
00494 
00495 
00496        // convert to pixels
00497        nscoord pospx = pos/onePixel;
00498 
00499        // convert to our internal coordinate system
00500        pospx = nscoord(pospx/mRatio);
00501 
00502        // set it
00503        SetCurrentPosition(scrollbar, thumbFrame, pospx, PR_FALSE, PR_TRUE);
00504 
00505     }
00506     break;
00507 
00508     case NS_MOUSE_MIDDLE_BUTTON_UP:
00509     if(!gMiddlePref)
00510         break;
00511 
00512     case NS_MOUSE_LEFT_BUTTON_UP:
00513        // stop capturing
00514       //printf("stop capturing\n");
00515       AddListener();
00516       DragThumb(PR_FALSE);
00517       if (mChange) {
00518         nsRepeatService::GetInstance()->Stop();
00519         mChange = 0;
00520       }
00521       // we MUST call nsFrame HandleEvent for mouse ups to maintain the selection state and capture state.
00522       return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
00523     }
00524 
00525     //return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
00526     return NS_OK;
00527   }
00528   else if ((aEvent->message == NS_MOUSE_LEFT_BUTTON_DOWN && ((nsMouseEvent *)aEvent)->isShift)
00529       || (gMiddlePref && aEvent->message == NS_MOUSE_MIDDLE_BUTTON_DOWN)) {
00530     // convert coord from twips to pixels
00531     nsPoint eventPoint = EventPointInOurCoords(aEvent);
00532     nscoord pos = isHorizontal ? eventPoint.x : eventPoint.y;
00533 
00534     nscoord onePixel = aPresContext->IntScaledPixelsToTwips(1);
00535     nscoord pospx = pos/onePixel;
00536 
00537    // adjust so that the middle of the thumb is placed under the click
00538     nsIFrame* thumbFrame = mFrames.FirstChild();
00539     nsSize thumbSize = thumbFrame->GetSize();
00540     nscoord thumbLength = isHorizontal ? thumbSize.width : thumbSize.height;
00541     thumbLength /= onePixel;
00542     pospx -= (thumbLength/2);
00543 
00544 
00545     // convert to our internal coordinate system
00546     pospx = nscoord(pospx/mRatio);
00547 
00548     // set it
00549     nsWeakFrame weakFrame(this);
00550     SetCurrentPosition(scrollbar, thumbFrame, pospx, PR_FALSE, PR_FALSE);
00551     NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_OK);
00552 
00553     DragThumb(PR_TRUE);
00554 
00555     if (isHorizontal)
00556       mThumbStart = thumbFrame->GetPosition().x;
00557     else
00558       mThumbStart = thumbFrame->GetPosition().y;
00559 
00560     mDragStart = pos - mThumbStart;
00561   }
00562 
00563   // XXX hack until handle release is actually called in nsframe.
00564 //  if (aEvent->message == NS_MOUSE_EXIT_SYNTH || aEvent->message == NS_MOUSE_RIGHT_BUTTON_UP || aEvent->message == NS_MOUSE_LEFT_BUTTON_UP)
00565   //   HandleRelease(aPresContext, aEvent, aEventStatus);
00566 
00567   if (aEvent->message == NS_MOUSE_EXIT_SYNTH && mChange)
00568      HandleRelease(aPresContext, aEvent, aEventStatus);
00569 
00570   return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
00571 }
00572 
00573 
00574 
00575 nsIBox*
00576 nsSliderFrame::GetScrollbar()
00577 {
00578   // if we are in a scrollbar then return the scrollbar's content node
00579   // if we are not then return ours.
00580    nsIFrame* scrollbar;
00581    nsScrollbarButtonFrame::GetParentWithTag(nsXULAtoms::scrollbar, this, scrollbar);
00582 
00583    if (scrollbar == nsnull)
00584        return this;
00585 
00586    return scrollbar->IsBoxFrame() ? scrollbar : this;
00587 }
00588 
00589 void
00590 nsSliderFrame::PageUpDown(nsIFrame* aThumbFrame, nscoord change)
00591 {
00592   // on a page up or down get our page increment. We get this by getting the scrollbar we are in and
00593   // asking it for the current position and the page increment. If we are not in a scrollbar we will
00594   // get the values from our own node.
00595   nsIBox* scrollbarBox = GetScrollbar();
00596   nsCOMPtr<nsIContent> scrollbar;
00597   scrollbar = GetContentOfBox(scrollbarBox);
00598 
00599   if (mScrollbarListener)
00600     mScrollbarListener->PagedUpDown(); // Let the listener decide our increment.
00601 
00602   nscoord pageIncrement = GetPageIncrement(scrollbar);
00603   PRInt32 curpos = GetCurrentPosition(scrollbar);
00604   SetCurrentPosition(scrollbar, aThumbFrame, curpos + change*pageIncrement,
00605                      PR_TRUE, PR_FALSE);
00606 }
00607 
00608 // called when the current position changed and we need to update the thumb's location
00609 nsresult
00610 nsSliderFrame::CurrentPositionChanged(nsPresContext* aPresContext,
00611                                       PRBool aImmediateRedraw)
00612 {
00613   nsIBox* scrollbarBox = GetScrollbar();
00614   nsCOMPtr<nsIContent> scrollbar;
00615   scrollbar = GetContentOfBox(scrollbarBox);
00616 
00617   PRBool isHorizontal = IsHorizontal();
00618 
00619     // get the current position
00620     PRInt32 curpos = GetCurrentPosition(scrollbar);
00621 
00622     // do nothing if the position did not change
00623     if (mCurPos == curpos)
00624         return NS_OK;
00625 
00626     // get our current position and max position from our content node
00627     PRInt32 maxpos = GetMaxPosition(scrollbar);
00628 
00629     if (curpos < 0)
00630       curpos = 0;
00631          else if (curpos > maxpos)
00632       curpos = maxpos;
00633 
00634     // convert to pixels
00635     nscoord onePixel = aPresContext->IntScaledPixelsToTwips(1);
00636     nscoord curpospx = curpos*onePixel;
00637 
00638     // get the thumb's rect
00639     nsIFrame* thumbFrame = mFrames.FirstChild();
00640     if (!thumbFrame)
00641       return NS_OK; // The thumb may stream in asynchronously via XBL.
00642 
00643     nsRect thumbRect = thumbFrame->GetRect();
00644 
00645     nsRect clientRect;
00646     GetClientRect(clientRect);
00647 
00648     // figure out the new rect
00649     nsRect newThumbRect(thumbRect);
00650 
00651     if (isHorizontal)
00652        newThumbRect.x = clientRect.x + nscoord(float(curpospx)*mRatio);
00653     else
00654        newThumbRect.y = clientRect.y + nscoord(float(curpospx)*mRatio);
00655 
00656     // set the rect
00657     thumbFrame->SetRect(newThumbRect);
00658 
00659     // Figure out the union of the rect so we know what to redraw.
00660     // Combine the old and new thumb overflow areas.
00661     nsRect changeRect;
00662     changeRect.UnionRect(thumbFrame->GetOverflowRect() + thumbRect.TopLeft(),
00663                          thumbFrame->GetOverflowRect() + newThumbRect.TopLeft());
00664 
00665     // redraw just the change
00666     Invalidate(changeRect, aImmediateRedraw);
00667     
00668     if (mScrollbarListener)
00669       mScrollbarListener->PositionChanged(aPresContext, mCurPos, curpos);
00670 
00671     mCurPos = curpos;
00672 
00673     return NS_OK;
00674 }
00675 
00676 static void UpdateAttribute(nsIContent* aScrollbar, nscoord aNewPos, PRBool aNotify, PRBool aIsSmooth) {
00677   nsAutoString str;
00678   str.AppendInt(aNewPos);
00679   
00680   if (aIsSmooth) {
00681     aScrollbar->SetAttr(kNameSpaceID_None, nsXULAtoms::smooth, NS_LITERAL_STRING("true"), PR_FALSE);
00682   }
00683   aScrollbar->SetAttr(kNameSpaceID_None, nsXULAtoms::curpos, str, aNotify);
00684   if (aIsSmooth) {
00685     aScrollbar->UnsetAttr(kNameSpaceID_None, nsXULAtoms::smooth, PR_FALSE);
00686   }
00687 }
00688 
00689 void
00690 nsSliderFrame::SetCurrentPosition(nsIContent* scrollbar, nsIFrame* aThumbFrame,
00691                                   nscoord newpos, PRBool aIsSmooth,
00692                                   PRBool aImmediateRedraw)
00693 {
00694 
00695    // get our current position and max position from our content node
00696   PRInt32 maxpos = GetMaxPosition(scrollbar);
00697 
00698   // get the new position and make sure it is in bounds
00699   if (newpos > maxpos)
00700       newpos = maxpos;
00701   else if (newpos < 0)
00702       newpos = 0;
00703 
00704   nsIBox* scrollbarBox = GetScrollbar();
00705   nsIScrollbarFrame* scrollbarFrame = nsnull;
00706   CallQueryInterface(scrollbarBox, &scrollbarFrame);
00707 
00708 
00709   if (scrollbarFrame) {
00710     // See if we have a mediator. Mediator is not refcounted.
00711     nsIScrollbarMediator* mediator = nsnull;
00712     scrollbarFrame->GetScrollbarMediator(&mediator);
00713     if (mediator) {
00714       nsRefPtr<nsPresContext> context = GetPresContext();
00715       nsCOMPtr<nsIContent> content = GetContent();
00716       mediator->PositionChanged(scrollbarFrame, GetCurrentPosition(scrollbar), newpos);
00717       UpdateAttribute(scrollbar, newpos, PR_FALSE, aIsSmooth);
00718       nsIPresShell* shell = context->GetPresShell();
00719       if (shell) {
00720         nsIFrame* frame = nsnull;
00721         shell->GetPrimaryFrameFor(content, &frame);
00722         if (frame && frame->GetType() == nsLayoutAtoms::sliderFrame) {
00723           NS_STATIC_CAST(nsSliderFrame*, frame)->
00724             CurrentPositionChanged(frame->GetPresContext(), aImmediateRedraw);
00725         }
00726       }
00727       return;
00728     }
00729   }
00730 
00731   UpdateAttribute(scrollbar, newpos, PR_TRUE, aIsSmooth);
00732 
00733 #ifdef DEBUG_SLIDER
00734   printf("Current Pos=%d\n",newpos);
00735 #endif
00736 
00737 }
00738 
00739 NS_IMETHODIMP  nsSliderFrame::GetFrameForPoint(const nsPoint& aPoint,
00740                                              nsFramePaintLayer aWhichLayer,
00741                                              nsIFrame**     aFrame)
00742 {
00743   // This is EVIL, we shouldn't be messing with GetFrameForPoint just to get
00744   // thumb mouse drag events to arrive at the slider!
00745   if (isDraggingThumb())
00746   {
00747     // XXX I assume it's better not to test for visibility here.
00748     *aFrame = this;
00749     return NS_OK;
00750   }
00751 
00752   if (NS_SUCCEEDED(nsBoxFrame::GetFrameForPoint(aPoint, aWhichLayer, aFrame)))
00753     return NS_OK;
00754 
00755   // always return us (if visible)
00756   if (mRect.Contains(aPoint) && GetStyleVisibility()->IsVisible()) {
00757     *aFrame = this;
00758     return NS_OK;
00759   }
00760 
00761   return NS_ERROR_FAILURE;
00762 }
00763 
00764 nsIAtom*
00765 nsSliderFrame::GetType() const
00766 {
00767   return nsLayoutAtoms::sliderFrame;
00768 }
00769 
00770 NS_IMETHODIMP
00771 nsSliderFrame::SetInitialChildList(nsPresContext* aPresContext,
00772                                               nsIAtom*        aListName,
00773                                               nsIFrame*       aChildList)
00774 {
00775   nsresult r = nsBoxFrame::SetInitialChildList(aPresContext, aListName, aChildList);
00776 
00777   AddListener();
00778 
00779   return r;
00780 }
00781 
00782 nsresult
00783 nsSliderMediator::MouseDown(nsIDOMEvent* aMouseEvent)
00784 {
00785   // Only process the event if the thumb is not being dragged.
00786   if (mSlider && !mSlider->isDraggingThumb())
00787     return mSlider->MouseDown(aMouseEvent);
00788 
00789   return NS_OK;
00790 }
00791 
00792 nsresult
00793 nsSliderMediator::MouseUp(nsIDOMEvent* aMouseEvent)
00794 {
00795   // Only process the event if the thumb is not being dragged.
00796   if (mSlider && !mSlider->isDraggingThumb())
00797     return mSlider->MouseUp(aMouseEvent);
00798 
00799   return NS_OK;
00800 }
00801 
00802 nsresult
00803 nsSliderFrame::MouseDown(nsIDOMEvent* aMouseEvent)
00804 {
00805   //printf("Begin dragging\n");
00806 
00807   PRBool isHorizontal = IsHorizontal();
00808 
00809   nsCOMPtr<nsIDOMMouseEvent> mouseEvent(do_QueryInterface(aMouseEvent));
00810 
00811   PRUint16 button = 0;
00812   PRBool scrollToClick = PR_FALSE;
00813   mouseEvent->GetShiftKey(&scrollToClick);
00814   mouseEvent->GetButton(&button);
00815   if (button != 0) {
00816     if (button != 1 || !gMiddlePref)
00817       return NS_OK;
00818     scrollToClick = PR_TRUE;
00819   }
00820 
00821   PRInt32 clientPosPx;
00822   nsIntRect screenRect = GetScreenRect();
00823   if (isHorizontal) {
00824     mouseEvent->GetScreenX(&clientPosPx);
00825     clientPosPx -= screenRect.x;
00826   } else {
00827     mouseEvent->GetScreenY(&clientPosPx);
00828     clientPosPx -= screenRect.y;
00829   }
00830 
00831   nsPresContext* presContext = GetPresContext();
00832   nscoord pos = presContext->IntScaledPixelsToTwips(clientPosPx);
00833 
00834   // If shift click or middle button, first
00835   // place the middle of the slider thumb under the click
00836   nsCOMPtr<nsIContent> scrollbar;
00837   nscoord pospx = 0;
00838   if (scrollToClick) {
00839     nscoord onePixel = GetPresContext()->IntScaledPixelsToTwips(1);
00840     pospx = pos/onePixel;
00841 
00842     // adjust so that the middle of the thumb is placed under the click
00843     nsIFrame* thumbFrame = mFrames.FirstChild();
00844     nsSize thumbSize = thumbFrame->GetSize();
00845     nscoord thumbLength = isHorizontal ? thumbSize.width : thumbSize.height;
00846     thumbLength /= onePixel;
00847     pospx -= (thumbLength/2);
00848 
00849     // finally, convert to scrollbar's internal coordinate system
00850     pospx = nscoord(pospx/mRatio);
00851 
00852     nsIBox* scrollbarBox = GetScrollbar();
00853     scrollbar = GetContentOfBox(scrollbarBox);
00854   }
00855 
00856   DragThumb(PR_TRUE);
00857 
00858   nsIFrame* thumbFrame = mFrames.FirstChild();
00859 
00860   if (isHorizontal)
00861      mThumbStart = thumbFrame->GetPosition().x;
00862   else
00863      mThumbStart = thumbFrame->GetPosition().y;
00864 
00865   mDragStart = pos - mThumbStart;
00866   //printf("Pressed mDragStart=%d\n",mDragStart);
00867 
00868   if (scrollToClick) {
00869     SetCurrentPosition(scrollbar, thumbFrame, pospx, PR_FALSE, PR_FALSE);
00870   }
00871 
00872   return NS_OK;
00873 }
00874 
00875 nsresult
00876 nsSliderFrame::MouseUp(nsIDOMEvent* aMouseEvent)
00877 {
00878  // printf("Finish dragging\n");
00879 
00880   return NS_OK;
00881 }
00882 
00883 void
00884 nsSliderFrame::DragThumb(PRBool aGrabMouseEvents)
00885 {
00886     // get its view
00887   nsIView* view = GetView();
00888 
00889   if (view) {
00890     nsIViewManager* viewMan = view->GetViewManager();
00891 
00892     if (viewMan) {
00893       PRBool result;
00894 
00895       if (aGrabMouseEvents) {
00896         viewMan->GrabMouseEvents(view,result);
00897       } else {
00898         viewMan->GrabMouseEvents(nsnull,result);
00899       }
00900     }
00901   }
00902 }
00903 
00904 PRBool
00905 nsSliderFrame::isDraggingThumb()
00906 {
00907     // get its view
00908   nsIView* view = GetView();
00909 
00910   if (view) {
00911     nsIViewManager* viewMan = view->GetViewManager();
00912 
00913     if (viewMan) {
00914         nsIView* grabbingView;
00915         viewMan->GetMouseEventGrabber(grabbingView);
00916         if (grabbingView == view)
00917           return PR_TRUE;
00918     }
00919   }
00920 
00921   return PR_FALSE;
00922 }
00923 
00924 nsPoint
00925 nsSliderFrame::EventPointInOurCoords(nsEvent* aEvent)
00926 {
00927   // aEvent->point is in the coordinate system of the view returned by
00928   // GetOffsetFromView.
00929   nsPoint eventPoint = aEvent->point;
00930   nsPoint viewOffset;
00931   nsIView* dummy;
00932   GetOffsetFromView(viewOffset, &dummy);
00933   return eventPoint - viewOffset;
00934 }
00935 
00936 void
00937 nsSliderFrame::AddListener()
00938 {
00939   if (!mMediator) {
00940     mMediator = new nsSliderMediator(this);
00941     NS_ADDREF(mMediator);
00942   }
00943 
00944   nsIFrame* thumbFrame = mFrames.FirstChild();
00945   if (thumbFrame) {
00946     nsCOMPtr<nsIDOMEventReceiver>
00947       receiver(do_QueryInterface(thumbFrame->GetContent()));
00948 
00949     receiver->AddEventListenerByIID(mMediator, NS_GET_IID(nsIDOMMouseListener));
00950   }
00951 }
00952 
00953 void
00954 nsSliderFrame::RemoveListener()
00955 {
00956   NS_ASSERTION(mMediator, "No listener was ever added!!");
00957 
00958   nsIFrame* thumbFrame = mFrames.FirstChild();
00959   if (!thumbFrame)
00960     return;
00961 
00962   nsCOMPtr<nsIDOMEventReceiver>
00963     receiver(do_QueryInterface(thumbFrame->GetContent()));
00964 
00965   receiver->RemoveEventListenerByIID(mMediator, NS_GET_IID(nsIDOMMouseListener));
00966 }
00967 
00968 NS_IMETHODIMP
00969 nsSliderFrame::HandlePress(nsPresContext* aPresContext,
00970                            nsGUIEvent*     aEvent,
00971                            nsEventStatus*  aEventStatus)
00972 {
00973   if (((nsMouseEvent *)aEvent)->isShift)
00974     return NS_OK;
00975 
00976   nsIFrame* thumbFrame = mFrames.FirstChild();
00977   if (!thumbFrame) // display:none?
00978     return NS_OK;
00979   
00980   nsRect thumbRect = thumbFrame->GetRect();
00981   
00982   nscoord change = 1;
00983   nsPoint eventPoint = EventPointInOurCoords(aEvent);
00984   if (IsHorizontal() ? eventPoint.x < thumbRect.x 
00985                      : eventPoint.y < thumbRect.y)
00986     change = -1;
00987 
00988   mChange = change;
00989   DragThumb(PR_TRUE);
00990   mDestinationPoint = eventPoint;
00991   nsRepeatService::GetInstance()->Start(mMediator);
00992   PageUpDown(thumbFrame, change);
00993   return NS_OK;
00994 }
00995 
00996 NS_IMETHODIMP
00997 nsSliderFrame::HandleRelease(nsPresContext* aPresContext,
00998                                  nsGUIEvent*     aEvent,
00999                                  nsEventStatus*  aEventStatus)
01000 {
01001   nsRepeatService::GetInstance()->Stop();
01002 
01003   return NS_OK;
01004 }
01005 
01006 NS_IMETHODIMP
01007 nsSliderFrame::Destroy(nsPresContext* aPresContext)
01008 {
01009   // tell our mediator if we have one we are gone.
01010   if (mMediator) {
01011     mMediator->SetSlider(nsnull);
01012     NS_RELEASE(mMediator);
01013     mMediator = nsnull;
01014   }
01015 
01016   // call base class Destroy()
01017   return nsBoxFrame::Destroy(aPresContext);
01018 }
01019 
01020 NS_IMETHODIMP
01021 nsSliderFrame::GetPrefSize(nsBoxLayoutState& aState, nsSize& aSize)
01022 {
01023   EnsureOrient();
01024   return nsBoxFrame::GetPrefSize(aState, aSize);
01025 }
01026 
01027 NS_IMETHODIMP
01028 nsSliderFrame::GetMinSize(nsBoxLayoutState& aState, nsSize& aSize)
01029 {
01030   EnsureOrient();
01031 
01032   // our min size is just our borders and padding
01033   return nsBox::GetMinSize(aState, aSize);
01034 }
01035 
01036 NS_IMETHODIMP
01037 nsSliderFrame::GetMaxSize(nsBoxLayoutState& aState, nsSize& aSize)
01038 {
01039   EnsureOrient();
01040   return nsBoxFrame::GetMaxSize(aState, aSize);
01041 }
01042 
01043 void
01044 nsSliderFrame::EnsureOrient()
01045 {
01046   nsIBox* scrollbarBox = GetScrollbar();
01047 
01048   PRBool isHorizontal = (scrollbarBox->GetStateBits() & NS_STATE_IS_HORIZONTAL) != 0;
01049   if (isHorizontal)
01050       mState |= NS_STATE_IS_HORIZONTAL;
01051   else
01052       mState &= ~NS_STATE_IS_HORIZONTAL;
01053 }
01054 
01055 
01056 void
01057 nsSliderFrame::SetScrollbarListener(nsIScrollbarListener* aListener)
01058 {
01059   // Don't addref/release this, since it's actually a frame.
01060   mScrollbarListener = aListener;
01061 }
01062 
01063 NS_IMETHODIMP nsSliderMediator::Notify(nsITimer *timer)
01064 {
01065   if (mSlider)
01066     mSlider->Notify(timer);
01067   return NS_OK;
01068 }
01069 
01070 NS_IMETHODIMP_(void) nsSliderFrame::Notify(nsITimer *timer)
01071 {
01072     PRBool stop = PR_FALSE;
01073 
01074     nsIFrame* thumbFrame = mFrames.FirstChild();
01075     nsRect thumbRect = thumbFrame->GetRect();
01076 
01077     PRBool isHorizontal = IsHorizontal();
01078 
01079     // See if the thumb has moved past our destination point.
01080     // if it has we want to stop.
01081     if (isHorizontal) {
01082         if (mChange < 0) {
01083             if (thumbRect.x < mDestinationPoint.x)
01084                 stop = PR_TRUE;
01085         } else {
01086             if (thumbRect.x + thumbRect.width > mDestinationPoint.x)
01087                 stop = PR_TRUE;
01088         }
01089     } else {
01090          if (mChange < 0) {
01091             if (thumbRect.y < mDestinationPoint.y)
01092                 stop = PR_TRUE;
01093         } else {
01094             if (thumbRect.y + thumbRect.height > mDestinationPoint.y)
01095                 stop = PR_TRUE;
01096         }
01097     }
01098 
01099 
01100     if (stop) {
01101        nsRepeatService::GetInstance()->Stop();
01102     } else {
01103       PageUpDown(thumbFrame, mChange);
01104     }
01105 }
01106 
01107 NS_INTERFACE_MAP_BEGIN(nsSliderMediator)
01108   NS_INTERFACE_MAP_ENTRY(nsIDOMMouseListener)
01109   NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
01110   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsITimerCallback)
01111 NS_INTERFACE_MAP_END
01112 
01113 NS_IMPL_ADDREF(nsSliderMediator)
01114 NS_IMPL_RELEASE(nsSliderMediator)
01115