Back to index

lightning-sunbird  0.9+nobinonly
nsNativeScrollbar.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 <ControlDefinitions.h>
00040 
00041 #include "nsNativeScrollbar.h"
00042 #include "nsIDeviceContext.h"
00043 
00044 #include "nsWidgetAtoms.h"
00045 #include "nsINameSpaceManager.h"
00046 #include "nsIDOMElement.h"
00047 #include "nsIScrollbarMediator.h"
00048 
00049 
00050 inline void BoundsCheck(PRInt32 low, PRUint32& value, PRUint32 high)
00051 {
00052   if ((PRInt32) value < low)
00053     value = low;
00054   if (value > high)
00055     value = high;
00056 }
00057 
00058 //
00059 // StControlActionProcOwner
00060 //
00061 // A class that wraps a control action proc so that it is disposed of
00062 // correctly when the shared library shuts down
00063 //
00064 class StNativeControlActionProcOwner {
00065 public:
00066   
00067   StNativeControlActionProcOwner ( )
00068   {
00069     sControlActionProc = NewControlActionUPP(nsNativeScrollbar::ScrollActionProc);
00070     NS_ASSERTION(sControlActionProc, "Couldn't create live scrolling action proc");
00071   }
00072   ~StNativeControlActionProcOwner ( )
00073   {
00074     if ( sControlActionProc )
00075       DisposeControlActionUPP(sControlActionProc);
00076   }
00077 
00078   ControlActionUPP ActionProc() { return sControlActionProc; }
00079   
00080 private:
00081   ControlActionUPP sControlActionProc;  
00082 };
00083 
00084 
00085 static ControlActionUPP 
00086 ScrollbarActionProc( )
00087 {
00088   static StNativeControlActionProcOwner sActionProcOwner;
00089   return sActionProcOwner.ActionProc();
00090 }
00091 
00092 
00093 NS_IMPL_ISUPPORTS_INHERITED1(nsNativeScrollbar, nsWindow, nsINativeScrollbar)
00094 
00095 nsNativeScrollbar::nsNativeScrollbar()
00096   : nsMacControl()
00097   , mContent(nsnull)
00098   , mMediator(nsnull)
00099   , mScrollbar(nsnull)
00100   , mMaxValue(0)
00101   , mVisibleImageSize(0)
00102   , mLineIncrement(0)
00103   , mMouseDownInScroll(PR_FALSE)
00104   , mClickedPartCode(0)
00105 {
00106   mMax = 0;   // override the base class default
00107 
00108   WIDGET_SET_CLASSNAME("nsNativeScrollbar");
00109   SetControlType(kControlScrollBarLiveProc);
00110 }
00111 
00112 
00113 nsNativeScrollbar::~nsNativeScrollbar()
00114 {
00115 }
00116 
00117 
00118 
00119 //
00120 // Destroy
00121 //
00122 // Now you're gone, gone, gone, whoa-oh...
00123 //
00124 NS_IMETHODIMP
00125 nsNativeScrollbar::Destroy()
00126 {
00127   if (mMouseDownInScroll)
00128   {
00129     ::PostEvent(mouseUp, 0);
00130   }
00131   return nsMacControl::Destroy();
00132 }
00133 
00134 
00135 //
00136 // ScrollActionProc
00137 //
00138 // Called from the OS toolbox while the scrollbar is being tracked.
00139 //
00140 pascal void
00141 nsNativeScrollbar::ScrollActionProc(ControlHandle ctrl, ControlPartCode part)
00142 {
00143   nsNativeScrollbar* self = (nsNativeScrollbar*)(::GetControlReference(ctrl));
00144   NS_ASSERTION(self, "NULL nsNativeScrollbar");
00145   if ( self )
00146     self->DoScrollAction(part);
00147 }
00148 
00149 
00150 //
00151 // DoScrollAction
00152 //
00153 // Called from the action proc of the scrollbar, adjust the control's
00154 // value as well as the value in the content node which communicates
00155 // to gecko that the document is scrolling.
00156 // 
00157 void
00158 nsNativeScrollbar::DoScrollAction(ControlPartCode part)
00159 {
00160   PRUint32 oldPos, newPos;
00161   PRUint32 incr;
00162   PRUint32 visibleImageSize;
00163 
00164   if (mOnDestroyCalled)
00165     return;
00166 
00167   nsCOMPtr<nsIWidget> parent ( dont_AddRef(GetParent()) );
00168   if (!parent)
00169   {
00170     // parent disappeared while scrolling was in progress.  Handling Destroy
00171     // should have prevented this.  Bail out.
00172     NS_ASSERTION(parent, "no parent in DoScrollAction");
00173     return;
00174   }
00175 
00176   if (!IsQDStateOK()) {
00177     // Something on a PLEvent messed with the QD state.  When the Control
00178     // Manager tried to figure out where the mouse was relative to the
00179     // control, it will have come up with some wacky results.  The received
00180     // |part| code and the value returned by |GetControl32BitValue| will not
00181     // be correct.  There's nothing that can be done about it this time
00182     // through the action proc, so drop the bad data on the floor.  The
00183     // port state is reset to what's appropriate for the control, and a fake
00184     // mouse-down event is posted, which will force the Control Manager to
00185     // look at the scrollbar again, hopefully while the corrected QD state
00186     // is still in effect.
00187     //
00188     // This works in concert with |nsMacControl::HandleControlEvent|.
00189     //
00190     // This relies on the Control Manager responding to mouse-down events
00191     // while the mouse is already down in a tracking loop by reexamining
00192     // the position of the scrollbar.
00193     EndDraw();
00194     StartDraw();
00195     ::PostEvent(mouseDown, 0);
00196     return;
00197   }
00198 
00199   GetPosition(&oldPos);
00200   GetLineIncrement(&incr);
00201   GetViewSize(&visibleImageSize);
00202 
00203   PRBool buttonPress = PR_FALSE;
00204 
00205   switch (part)
00206   {
00207     case kControlUpButtonPart:
00208       newPos = oldPos - (mLineIncrement ? mLineIncrement : 1);
00209       buttonPress = PR_TRUE;
00210       break;
00211     case kControlDownButtonPart:
00212       newPos = oldPos + (mLineIncrement ? mLineIncrement : 1);
00213       buttonPress = PR_TRUE;
00214       break;
00215     
00216     case kControlPageUpPart:
00217       newPos = oldPos - visibleImageSize;
00218       break;
00219     case kControlPageDownPart:
00220       newPos = oldPos + visibleImageSize;
00221       break;
00222 
00223     case kControlIndicatorPart:
00224       newPos = ::GetControl32BitValue(GetControl());
00225       break;
00226 
00227     default:
00228       // Huh?
00229       return;
00230   }
00231 
00232   if (buttonPress) {
00233     //
00234     // For the up/down buttons, scroll up or down by the line height and 
00235     // update the attributes on the content node (the scroll frame listens
00236     // for these attributes and will scroll accordingly). However,
00237     // if we have a mediator, we're in an outliner and we have to scroll by
00238     // lines. Outliner ignores the indexes in ScrollbarButtonPressed() except
00239     // to check if one is greater than the other to indicate direction.
00240     //
00241     UpdateContentPosition(newPos);
00242     if (mMediator) {
00243       BoundsCheck(0, newPos, mMaxValue);
00244       mMediator->ScrollbarButtonPressed(mScrollbar, oldPos, newPos);
00245     }
00246   }
00247   else {
00248     //
00249     // For page up/down and dragging the thumb, scroll by the page height
00250     // (or directly report the value of the scrollbar) and update the attributes
00251     // on the content node (as above). If we have a mediator, we're in an
00252     // outliner so tell it directly that the position has changed. Note that
00253     // outliner takes the new position as a signed reference, so we have to
00254     // convert our unsigned to signed first.
00255     //
00256     UpdateContentPosition(newPos);
00257     if (mMediator) {
00258       PRInt32 np = newPos;
00259       if (np < 0) {
00260         np = 0;
00261       }
00262       mMediator->PositionChanged(mScrollbar, oldPos, np);
00263     }
00264   }
00265 
00266   EndDraw();
00267     
00268   // update the area of the parent uncovered by the scrolling. Since
00269   // we may be in a tight loop, we need to manually validate the area
00270   // we just updated so the update rect doesn't continue to get bigger
00271   // and bigger the more we scroll.
00272   parent->Update();
00273   parent->Validate();
00274 
00275   StartDraw();
00276 }
00277 
00278 
00279 //
00280 // UpdateContentPosition
00281 //
00282 // Tell the content node that the scrollbar has changed value and
00283 // then update the scrollbar's position
00284 //
00285 void
00286 nsNativeScrollbar::UpdateContentPosition(PRUint32 inNewPos)
00287 {
00288   if ( (PRInt32)inNewPos == mValue || !mContent )   // break any possible recursion
00289     return;
00290 
00291   // guarantee |inNewPos| is in the range of [0, mMaxValue] so it's correctly unsigned
00292   BoundsCheck(0, inNewPos, mMaxValue);
00293 
00294   // convert the int to a string
00295   nsAutoString buffer;
00296   buffer.AppendInt(inNewPos);
00297   
00298   mContent->SetAttr(kNameSpaceID_None, nsWidgetAtoms::curpos, buffer, PR_TRUE);
00299   SetPosition(inNewPos);
00300 }
00301 
00302 //-------------------------------------------------------------------------
00303 //
00304 // Get the current hilite state of the control (disables the scrollbar
00305 // if there is nowhere to scroll)
00306 // 
00307 //-------------------------------------------------------------------------
00308 ControlPartCode
00309 nsNativeScrollbar::GetControlHiliteState()
00310 {
00311   if (mMaxValue == 0)
00312     return kControlInactivePart;
00313   
00314   return Inherited::GetControlHiliteState();
00315 }
00316 
00323 PRBool
00324 nsNativeScrollbar::DispatchMouseEvent(nsMouseEvent &aEvent)
00325 {
00326   PRBool eatEvent = PR_FALSE;
00327   switch (aEvent.message)
00328   {
00329     case NS_MOUSE_LEFT_DOUBLECLICK:
00330     case NS_MOUSE_LEFT_BUTTON_DOWN:
00331       mMouseDownInScroll = PR_TRUE;
00332       NS_ASSERTION(this != 0, "NULL nsNativeScrollbar2");
00333       ::SetControlReference(mControl, (UInt32) this);
00334       StartDraw();
00335       {
00336         Point thePoint;
00337         thePoint.h = aEvent.point.x;
00338         thePoint.v = aEvent.point.y;
00339         mClickedPartCode = ::TestControl(mControl, thePoint);
00340         if (mClickedPartCode > 0)
00341           ::HiliteControl(mControl, mClickedPartCode);
00342 
00343         switch (mClickedPartCode)
00344         {
00345           case kControlUpButtonPart:
00346           case kControlDownButtonPart:
00347           case kControlPageUpPart:
00348           case kControlPageDownPart:
00349           case kControlIndicatorPart:
00350             // We are assuming Appearance 1.1 or later, so we
00351             // have the "live scroll" variant of the scrollbar,
00352             // which lets you pass the action proc to TrackControl
00353             // for the thumb (this was illegal in previous
00354             // versions of the defproc).
00355             ::TrackControl(mControl, thePoint, ScrollbarActionProc());
00356             ::HiliteControl(mControl, 0);
00357             // We don't dispatch the mouseDown event because mouseUp is eaten
00358             // by TrackControl anyway and the only messages the app really
00359             // cares about are the NS_SCROLLBAR_xxx messages.
00360             eatEvent = PR_TRUE;
00361             break;
00362         }
00363         SetPosition(mValue);
00364       }
00365       EndDraw();
00366       break;
00367 
00368 
00369     case NS_MOUSE_LEFT_BUTTON_UP:
00370       mMouseDownInScroll = PR_FALSE;
00371       mClickedPartCode = 0;
00372       break;
00373 
00374     case NS_MOUSE_EXIT:
00375       if (mWidgetArmed)
00376       {
00377         StartDraw();
00378         ::HiliteControl(mControl, 0);
00379         EndDraw();
00380       }
00381       break;
00382 
00383     case NS_MOUSE_ENTER:
00384       if (mWidgetArmed)
00385       {
00386         StartDraw();
00387         ::HiliteControl(mControl, mClickedPartCode);
00388         EndDraw();
00389       }
00390       break;
00391   }
00392 
00393   if (eatEvent)
00394     return PR_TRUE;
00395   return (Inherited::DispatchMouseEvent(aEvent));
00396 
00397 }
00398 
00399 
00400 //
00401 // SetMaxRange
00402 //
00403 // Set the maximum range of a scroll bar. This should be set to the
00404 // full scrollable area minus the visible area.
00405 //
00406 NS_IMETHODIMP
00407 nsNativeScrollbar::SetMaxRange(PRUint32 aEndRange)
00408 {
00409   if ((PRInt32)aEndRange < 0)
00410     aEndRange = 0;
00411 
00412   mMaxValue = aEndRange;
00413 
00414   if ( GetControl() ) {
00415     StartDraw();
00416     ::SetControl32BitMaximum(GetControl(), mMaxValue);
00417     EndDraw();
00418   }
00419   return NS_OK;
00420 }
00421 
00422 
00423 //
00424 // GetMaxRange
00425 //
00426 // Get the maximum range of a scroll bar
00427 //
00428 NS_IMETHODIMP
00429 nsNativeScrollbar::GetMaxRange(PRUint32* aMaxRange)
00430 {
00431   *aMaxRange = mMaxValue;
00432   return NS_OK;
00433 }
00434 
00435 
00436 //
00437 // SetPosition
00438 //
00439 // Set the current position of the slider and redraw
00440 //
00441 NS_IMETHODIMP
00442 nsNativeScrollbar::SetPosition(PRUint32 aPos)
00443 {
00444   if ((PRInt32)aPos < 0)
00445     aPos = 0;
00446 
00447   PRInt32 oldValue = mValue;
00448   
00449   // while we _should_ be ensuring that we don't set our value higher
00450   // than our max value, the gfx scrollview code plays fast and loose
00451   // with the rules while going back/forward and adjusts the value to the
00452   // previous value long before it sets the max. As a result, we would
00453   // lose the given value (since max would most likely be 0). The only
00454   // way around that is to relax our restrictions a little bit. (bug 135191)
00455   //   mValue = ((PRInt32)aPos) > mMaxValue ? mMaxValue : ((int)aPos);
00456   mValue = aPos;
00457   
00458   // redraw the scrollbar. It needs to be synchronous otherwise we end
00459   // up drawing at 0,0, probably because of the associated view.
00460   if ( mValue != oldValue )
00461     Invalidate(PR_TRUE);
00462   
00463   return NS_OK;
00464 }
00465 
00466 
00467 //
00468 // GetPosition
00469 //
00470 // Get the current position of the slider
00471 //
00472 NS_IMETHODIMP
00473 nsNativeScrollbar::GetPosition(PRUint32* aPos)
00474 {
00475   *aPos = mValue;
00476   return NS_OK;
00477 }
00478 
00479 
00480 //
00481 // SetViewSize
00482 //
00483 // According to the toolbox docs, we pass the height of the
00484 // visible view area to SetControlViewSize(). Assuming we've set
00485 // the max to the total area - view height, this will give us a correct
00486 // proportional scrollbar.
00487 //
00488 NS_IMETHODIMP
00489 nsNativeScrollbar::SetViewSize(PRUint32 aSize)
00490 {
00491   if ((PRInt32)aSize < 0)
00492     aSize = 0;
00493 
00494   mVisibleImageSize = aSize;
00495     
00496   if ( GetControl() )  {
00497     StartDraw();
00498     ::SetControlViewSize(GetControl(), mVisibleImageSize);
00499     EndDraw();
00500   }
00501   return NS_OK;
00502 }
00503 
00504 
00505 //
00506 // GetViewSize
00507 //
00508 // Get the height of the visible view area.
00509 //
00510 NS_IMETHODIMP
00511 nsNativeScrollbar::GetViewSize(PRUint32* aSize)
00512 {
00513   *aSize = mVisibleImageSize;
00514   return NS_OK;
00515 }
00516 
00517 
00518 //
00519 // SetLineIncrement
00520 //
00521 // Set the line increment of the scroll bar
00522 //
00523 NS_IMETHODIMP
00524 nsNativeScrollbar::SetLineIncrement(PRUint32 aLineIncrement)
00525 {
00526   mLineIncrement  = (((int)aLineIncrement) > 0 ? aLineIncrement : 1);
00527   return NS_OK;
00528 }
00529 
00530 
00531 //
00532 // GetLineIncrement
00533 //
00534 // Get the line increment of the scroll bar
00535 //
00536 NS_IMETHODIMP
00537 nsNativeScrollbar::GetLineIncrement(PRUint32* aLineIncrement)
00538 {
00539   *aLineIncrement = mLineIncrement;
00540   return NS_OK;
00541 }
00542 
00543 
00544 //
00545 // GetNarrowSize
00546 //
00547 // Ask the appearance manager for the dimensions of the narrow axis
00548 // of the scrollbar. We cheat and assume the width of a vertical scrollbar
00549 // is the same as the height of a horizontal scrollbar. *shrug*. Shoot me.
00550 //
00551 NS_IMETHODIMP
00552 nsNativeScrollbar::GetNarrowSize(PRInt32* outSize)
00553 {
00554   if ( *outSize )
00555     return NS_ERROR_FAILURE;
00556   SInt32 width = 0;
00557   ::GetThemeMetric(kThemeMetricScrollBarWidth, &width);
00558   *outSize = width;
00559   return NS_OK;
00560 }
00561 
00562 
00563 //
00564 // SetContent
00565 //
00566 // Hook up this native scrollbar to the rest of gecko. We care about
00567 // the content so we can set attributes on it to affect the scrollview. We
00568 // care about the mediator for <outliner> so we can do row-based scrolling.
00569 //
00570 NS_IMETHODIMP
00571 nsNativeScrollbar::SetContent(nsIContent* inContent, nsISupports* inScrollbar, 
00572                               nsIScrollbarMediator* inMediator)
00573 {
00574   mContent = inContent;
00575   mMediator = inMediator;
00576   mScrollbar = inScrollbar;
00577   return NS_OK;
00578 }