Files
haiku-beta6/src/kits/interface/ScrollBar.cpp
T

1151 lines
26 KiB
C++
Raw Normal View History

2003-07-10 00:07:56 +00:00
//------------------------------------------------------------------------------
// Copyright (c) 2001-2002, OpenBeOS
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
// File Name: ScrollBar.cpp
// Author: Marc Flerackers ([email protected])
// DarkWyrm ([email protected])
// Description: Client-side class for scrolling
//
//------------------------------------------------------------------------------
#include <Message.h>
#include <Rect.h>
#include <stdio.h>
#include <string.h>
#include <OS.h>
#include <Window.h>
#include "ScrollBar.h"
#define TEST_MODE
typedef enum
{
ARROW_LEFT=0,
ARROW_RIGHT,
ARROW_UP,
ARROW_DOWN,
ARROW_NONE
} arrow_direction;
#define SBC_SCROLLBYVALUE 0
#define SBC_SETDOUBLE 1
#define SBC_SETPROPORTIONAL 2
#define SBC_SETSTYLE 3
// Quick constants for determining which arrow is down and are defined with respect
// to double arrow mode. ARROW1 and ARROW4 refer to the outer pair of arrows and
// ARROW2 and ARROW3 refer to the inner ones. ARROW1 points left/up and ARROW4
// points right/down.
#define ARROW1 0
#define ARROW2 1
#define ARROW3 2
#define ARROW4 3
#define NOARROW -1
class BScrollBarPrivateData
{
public:
BScrollBarPrivateData(void)
{
thumbframe.Set(0,0,B_V_SCROLL_BAR_WIDTH,B_H_SCROLL_BAR_HEIGHT);
enabled=true;
tracking=false;
mousept.Set(0,0);
thumbinc=1.0;
repeaterid=-1;
exit_repeater=false;
arrowdown=ARROW_NONE;
buttondown=NOARROW;
#ifdef TEST_MODE
sbinfo.proportional=true;
sbinfo.double_arrows=false;
sbinfo.knob=0;
sbinfo.min_knob_size=14;
#else
get_scroll_bar_info(&sbinfo);
#endif
}
~BScrollBarPrivateData(void)
{
if(repeaterid!=-1)
{
exit_repeater=false;
kill_thread(repeaterid);
}
}
void DrawScrollBarButton(BScrollBar *owner, arrow_direction direction,
const BPoint &offset, bool down=false);
static int32 ButtonRepeaterThread(void *data);
thread_id repeaterid;
scroll_bar_info sbinfo;
BRect thumbframe;
bool enabled;
bool tracking;
BPoint mousept;
float thumbinc;
bool exit_repeater;
arrow_direction arrowdown;
int8 buttondown;
};
int32 BScrollBarPrivateData::ButtonRepeaterThread(void *data)
{
BScrollBar *sb=(BScrollBar *)data;
BRect oldframe(sb->privatedata->thumbframe);
// BRect update(sb->privatedata->thumbframe);
snooze(250000);
bool exitval=false;
status_t returnval;
sb->Window()->Lock();
exitval=sb->privatedata->exit_repeater;
sb->Window()->Unlock();
float scrollvalue;
if(sb->privatedata->arrowdown==ARROW_LEFT || sb->privatedata->arrowdown==ARROW_UP)
scrollvalue=-sb->fSmallStep;
else
if(sb->privatedata->arrowdown!=ARROW_NONE)
scrollvalue=sb->fSmallStep;
else
exitval=true;
while(!exitval)
{
oldframe=sb->privatedata->thumbframe;
returnval=control_scrollbar(SBC_SCROLLBYVALUE, &scrollvalue, sb);
snooze(50000);
sb->Window()->Lock();
exitval=sb->privatedata->exit_repeater;
if(returnval==B_OK)
{
sb->CopyBits(oldframe,sb->privatedata->thumbframe);
// TODO: Redraw the old area here
sb->ValueChanged(sb->fValue);
}
sb->Window()->Unlock();
}
sb->Window()->Lock();
sb->privatedata->exit_repeater=false;
sb->privatedata->repeaterid=-1;
sb->Window()->Unlock();
return 0;
exit_thread(0);
}
BScrollBar::BScrollBar(BRect frame,const char *name,BView *target,float min,
float max,orientation direction)
: BView(frame, name, B_FOLLOW_NONE, B_WILL_DRAW)
{
SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
fMin=min;
fMax=max;
fOrientation=direction;
fValue=0;
fSmallStep=1.0;
fLargeStep=10.0;
fTarget=target;
privatedata=new BScrollBarPrivateData;
if(fTarget)
{
fTargetName=new char[strlen(fTarget->Name()+1)];
strcpy(fTargetName,target->Name());
// theoretically, we should also set the target BView's scrollbar
// pointer here
}
else
fTargetName=NULL;
if(direction==B_VERTICAL)
{
if(frame.Width()>B_V_SCROLL_BAR_WIDTH)
ResizeTo(B_V_SCROLL_BAR_WIDTH,frame.Height()+1);
privatedata->thumbframe.bottom=privatedata->sbinfo.min_knob_size;
if(privatedata->sbinfo.double_arrows)
privatedata->thumbframe.OffsetBy(0,(B_H_SCROLL_BAR_HEIGHT+1)*2);
else
privatedata->thumbframe.OffsetBy(0,B_H_SCROLL_BAR_HEIGHT+1);
}
else
{
if(frame.Height()>B_H_SCROLL_BAR_HEIGHT)
ResizeTo(frame.Width()+1,B_H_SCROLL_BAR_HEIGHT);
privatedata->thumbframe.right=privatedata->sbinfo.min_knob_size;
if(privatedata->sbinfo.double_arrows)
privatedata->thumbframe.OffsetBy((B_V_SCROLL_BAR_WIDTH+1)*2,0);
else
privatedata->thumbframe.OffsetBy(B_V_SCROLL_BAR_WIDTH+1,0);
}
SetResizingMode( (direction==B_VERTICAL)?
B_FOLLOW_TOP_BOTTOM | B_FOLLOW_RIGHT :
B_FOLLOW_LEFT_RIGHT | B_FOLLOW_BOTTOM );
}
BScrollBar::BScrollBar(BMessage *data)
: BView(data)
{
}
BScrollBar::~BScrollBar()
{
delete privatedata;
if(fTargetName)
delete fTargetName;
}
BArchivable *BScrollBar::Instantiate(BMessage *data)
{
return NULL;
}
2003-05-14 17:21:46 +00:00
status_t BScrollBar::Archive(BMessage *data, bool deep) const
{
return B_ERROR;
}
void BScrollBar::AttachedToWindow()
{
// if fValue!=0, BScrollbars tell the server its value
}
void BScrollBar::SetValue(float value)
{
fValue=value;
ValueChanged(fValue);
}
float BScrollBar::Value() const
{
return fValue;
}
void BScrollBar::SetProportion(float value)
{
fProportion=value;
}
float BScrollBar::Proportion() const
{
return fProportion;
}
void BScrollBar::ValueChanged(float newValue)
{
}
void BScrollBar::SetRange(float min, float max)
{
fMin=min;
fMax=max;
if(fValue>fMax)
fValue=fMax;
else
if(fValue<fMin)
fValue=fMin;
Draw(Bounds());
// Just a sort-of hack for now. ValueChanged is called, but with
// what value??
ValueChanged(fValue);
}
void BScrollBar::GetRange(float *min, float *max) const
{
*min=fMin;
*max=fMax;
}
void BScrollBar::SetSteps(float smallStep, float largeStep)
{
// Under R5, steps can be set only after being attached to a window
// We'll just remove that limitation... :P
fSmallStep=smallStep;
fLargeStep=largeStep;
}
void BScrollBar::GetSteps(float *smallStep, float *largeStep) const
{
*smallStep=fSmallStep;
*largeStep=fLargeStep;
}
void BScrollBar::SetTarget(BView *target)
{
fTarget=target;
if(fTargetName)
delete fTargetName;
if(fTarget)
{
fTargetName=new char[strlen(target->Name())+1];
strcpy(fTargetName,target->Name());
// theoretically, we should also set the target BView's scrollbar
// pointer here
}
else
fTargetName=NULL;
}
void BScrollBar::SetTarget(const char *targetName)
{
}
BView *BScrollBar::Target() const
{
return fTarget;
}
orientation BScrollBar::Orientation() const
{
return fOrientation;
}
void BScrollBar::MessageReceived(BMessage *msg)
{
switch(msg->what)
{
case B_VALUE_CHANGED:
{
int32 value;
if(msg->FindInt32("value",&value)==B_OK)
ValueChanged(value);
break;
}
default:
BView::MessageReceived(msg);
break;
}
}
void BScrollBar::MouseDown(BPoint pt)
{
if(!(fMin==0 && fMax==0)) // if enabled
{
// Hit test for thumb
if(privatedata->thumbframe.Contains(pt))
{
privatedata->tracking=true;
privatedata->mousept=pt;
SetMouseEventMask(0,B_LOCK_WINDOW_FOCUS);
Draw(privatedata->thumbframe);
return;
}
BRect buttonrect(0,0,B_V_SCROLL_BAR_WIDTH,B_H_SCROLL_BAR_HEIGHT);
float scrollval=0;
status_t returnval=B_ERROR;
// Hit test for arrow buttons
if(fOrientation==B_VERTICAL)
{
if(buttonrect.Contains(pt))
{
scrollval= -fSmallStep;
privatedata->arrowdown=ARROW_UP;
privatedata->buttondown=ARROW1;
returnval=control_scrollbar(SBC_SCROLLBYVALUE,&scrollval,this);
if(returnval==B_OK)
{
Draw(buttonrect);
ValueChanged(fValue);
if(privatedata->repeaterid==-1)
{
privatedata->exit_repeater=false;
privatedata->repeaterid=spawn_thread(privatedata->ButtonRepeaterThread,
"scroll repeater",B_NORMAL_PRIORITY,this);
resume_thread(privatedata->repeaterid);
}
}
return;
}
buttonrect.OffsetTo(0,Bounds().Height() - (B_H_SCROLL_BAR_HEIGHT));
if(buttonrect.Contains(pt))
{
scrollval= fSmallStep;
privatedata->arrowdown=ARROW_DOWN;
privatedata->buttondown=ARROW4;
returnval=control_scrollbar(SBC_SCROLLBYVALUE,&scrollval,this);
if(returnval==B_OK)
{
Draw(buttonrect);
ValueChanged(fValue);
if(privatedata->repeaterid==-1)
{
privatedata->exit_repeater=false;
privatedata->repeaterid=spawn_thread(privatedata->ButtonRepeaterThread,
"scroll repeater",B_NORMAL_PRIORITY,this);
resume_thread(privatedata->repeaterid);
}
}
return;
}
if(privatedata->sbinfo.double_arrows)
{
buttonrect.OffsetTo(0,B_H_SCROLL_BAR_HEIGHT+1);
if(buttonrect.Contains(pt))
{
scrollval= fSmallStep;
privatedata->arrowdown=ARROW_DOWN;
privatedata->buttondown=ARROW2;
returnval=control_scrollbar(SBC_SCROLLBYVALUE,&scrollval,this);
if(returnval==B_OK)
{
Draw(buttonrect);
ValueChanged(fValue);
if(privatedata->repeaterid==-1)
{
privatedata->exit_repeater=false;
privatedata->repeaterid=spawn_thread(privatedata->ButtonRepeaterThread,
"scroll repeater",B_NORMAL_PRIORITY,this);
resume_thread(privatedata->repeaterid);
}
}
return;
}
buttonrect.OffsetTo(0,Bounds().Height()-( (B_H_SCROLL_BAR_HEIGHT*2)+1 ));
if(buttonrect.Contains(pt))
{
scrollval= -fSmallStep;
privatedata->arrowdown=ARROW_UP;
privatedata->buttondown=ARROW3;
returnval=control_scrollbar(SBC_SCROLLBYVALUE,&scrollval,this);
if(returnval==B_OK)
{
Draw(buttonrect);
ValueChanged(fValue);
if(privatedata->repeaterid==-1)
{
privatedata->exit_repeater=false;
privatedata->repeaterid=spawn_thread(privatedata->ButtonRepeaterThread,
"scroll repeater",B_NORMAL_PRIORITY,this);
resume_thread(privatedata->repeaterid);
}
}
return;
}
}
// TODO: add a repeater thread for large stepping and a call to it
if(pt.y<privatedata->thumbframe.top)
{
scrollval=-fLargeStep;
control_scrollbar(SBC_SCROLLBYVALUE,&scrollval,this);
}
else
{
scrollval=fLargeStep;
control_scrollbar(SBC_SCROLLBYVALUE,&scrollval,this);
}
}
else
{
if(buttonrect.Contains(pt))
{
scrollval= -fSmallStep;
privatedata->arrowdown=ARROW_LEFT;
privatedata->buttondown=ARROW1;
returnval=control_scrollbar(SBC_SCROLLBYVALUE,&scrollval,this);
if(returnval==B_OK)
{
Draw(buttonrect);
ValueChanged(fValue);
if(privatedata->repeaterid==-1)
{
privatedata->exit_repeater=false;
privatedata->repeaterid=spawn_thread(privatedata->ButtonRepeaterThread,
"scroll repeater",B_NORMAL_PRIORITY,this);
resume_thread(privatedata->repeaterid);
}
}
return;
}
buttonrect.OffsetTo(Bounds().Width() - (B_V_SCROLL_BAR_WIDTH),0);
if(buttonrect.Contains(pt))
{
scrollval= fSmallStep;
privatedata->arrowdown=ARROW_RIGHT;
privatedata->buttondown=ARROW4;
returnval=control_scrollbar(SBC_SCROLLBYVALUE,&scrollval,this);
if(returnval==B_OK)
{
Draw(buttonrect);
ValueChanged(fValue);
if(privatedata->repeaterid==-1)
{
privatedata->exit_repeater=false;
privatedata->repeaterid=spawn_thread(privatedata->ButtonRepeaterThread,
"scroll repeater",B_NORMAL_PRIORITY,this);
resume_thread(privatedata->repeaterid);
}
}
return;
}
if(privatedata->sbinfo.proportional)
{
buttonrect.OffsetTo(B_V_SCROLL_BAR_WIDTH+1,0);
if(buttonrect.Contains(pt))
{
scrollval= fSmallStep;
privatedata->buttondown=ARROW2;
privatedata->arrowdown=ARROW_LEFT;
returnval=control_scrollbar(SBC_SCROLLBYVALUE,&scrollval,this);
if(returnval==B_OK)
{
Draw(buttonrect);
ValueChanged(fValue);
if(privatedata->repeaterid==-1)
{
privatedata->exit_repeater=false;
privatedata->repeaterid=spawn_thread(privatedata->ButtonRepeaterThread,
"scroll repeater",B_NORMAL_PRIORITY,this);
resume_thread(privatedata->repeaterid);
}
}
return;
}
buttonrect.OffsetTo(Bounds().Width()-( (B_V_SCROLL_BAR_WIDTH*2)+1 ),0);
if(buttonrect.Contains(pt))
{
scrollval= -fSmallStep;
privatedata->buttondown=ARROW3;
privatedata->arrowdown=ARROW_RIGHT;
returnval=control_scrollbar(SBC_SCROLLBYVALUE,&scrollval,this);
if(returnval==B_OK)
{
Draw(buttonrect);
ValueChanged(fValue);
if(privatedata->repeaterid==-1)
{
privatedata->exit_repeater=false;
privatedata->repeaterid=spawn_thread(privatedata->ButtonRepeaterThread,
"scroll repeater",B_NORMAL_PRIORITY,this);
resume_thread(privatedata->repeaterid);
}
}
return;
}
}
// We got this far, so apparently the user has clicked on something
// that isn't the thumb or a scroll button, so scroll by a large step
// TODO: add a repeater thread for large stepping and a call to it
if(pt.x<privatedata->thumbframe.left)
{
scrollval=-fLargeStep;
control_scrollbar(SBC_SCROLLBYVALUE,&scrollval,this);
}
else
{
scrollval=fLargeStep;
control_scrollbar(SBC_SCROLLBYVALUE,&scrollval,this);
}
}
ValueChanged(fValue);
Draw(Bounds());
}
}
void BScrollBar::MouseUp(BPoint pt)
{
privatedata->arrowdown=ARROW_NONE;\
privatedata->buttondown=NOARROW;
privatedata->exit_repeater=true;
// We'll be lazy here and just draw all the possible arrow regions for now...
// TODO: optimize
BRect r(0,0,B_V_SCROLL_BAR_WIDTH,B_H_SCROLL_BAR_HEIGHT);
if(fOrientation==B_VERTICAL)
{
r.bottom+=B_H_SCROLL_BAR_HEIGHT+1;
Draw(r);
r.OffsetTo(0,Bounds().Height()-((B_H_SCROLL_BAR_HEIGHT*2)+1) );
Draw(r);
}
else
{
r.bottom+=B_V_SCROLL_BAR_WIDTH+1;
Draw(r);
r.OffsetTo(0,Bounds().Height()-((B_V_SCROLL_BAR_WIDTH*2)+1) );
Draw(r);
}
if(privatedata->tracking)
{
privatedata->tracking=false;
SetMouseEventMask(0,0);
Draw(privatedata->thumbframe);
}
}
void BScrollBar::MouseMoved(BPoint pt, uint32 transit, const BMessage *msg)
{
if(!privatedata->enabled)
return;
if(transit==B_EXITED_VIEW || transit==B_OUTSIDE_VIEW)
MouseUp(privatedata->mousept);
if(privatedata->tracking)
{
float delta;
if(fOrientation==B_VERTICAL)
{
if( (pt.y>privatedata->thumbframe.bottom && fValue==fMax) ||
(pt.y<privatedata->thumbframe.top && fValue==fMin) )
return;
delta=pt.y-privatedata->mousept.y;
}
else
{
if( (pt.x>privatedata->thumbframe.right && fValue==fMax) ||
(pt.x<privatedata->thumbframe.left && fValue==fMin) )
return;
delta=pt.x-privatedata->mousept.x;
}
control_scrollbar(SBC_SCROLLBYVALUE, &delta, this);
ValueChanged(fValue);
Draw(Bounds());
privatedata->mousept=pt;
}
}
void BScrollBar::DoScroll(float delta)
{
if(!fTarget)
return;
float scrollval;
if(delta>0)
scrollval=(fValue+delta<=fMax)?delta:(fMax-fValue);
else
scrollval=(fValue-delta>=fMin)?delta:(fValue-fMin);
if(fOrientation==B_VERTICAL)
{
fTarget->ScrollBy(0,scrollval);
privatedata->thumbframe.OffsetBy(0,scrollval);
}
else
{
fTarget->ScrollBy(scrollval,0);
privatedata->thumbframe.OffsetBy(scrollval,0);
}
fValue+=scrollval;
}
void BScrollBar::DetachedFromWindow()
{
fTarget=NULL;
delete fTargetName;
fTargetName=NULL;
}
void BScrollBar::Draw(BRect updateRect)
{
rgb_color light, dark,normal,c;
c=ui_color(B_PANEL_BACKGROUND_COLOR);
if(privatedata->enabled)
{
light=tint_color(c,B_LIGHTEN_MAX_TINT);
dark=tint_color(c,B_DARKEN_3_TINT);
normal=c;
}
else
{
light=tint_color(c,B_LIGHTEN_MAX_TINT);
dark=tint_color(c,B_DARKEN_3_TINT);
normal=c;
}
// Draw main area
SetHighColor(normal);
FillRect(updateRect);
SetHighColor(dark);
StrokeRect(Bounds());
// Draw arrows
BPoint buttonpt(0,0);
if(fOrientation==B_HORIZONTAL)
{
privatedata->DrawScrollBarButton(this, ARROW_LEFT, buttonpt,
privatedata->buttondown==ARROW1);
if(privatedata->sbinfo.double_arrows)
{
buttonpt.Set(B_V_SCROLL_BAR_WIDTH+1,0);
privatedata->DrawScrollBarButton(this, ARROW_RIGHT, buttonpt,
privatedata->buttondown==ARROW2);
buttonpt.Set(Bounds().Width()-( (B_V_SCROLL_BAR_WIDTH*2)+1 ),0);
privatedata->DrawScrollBarButton(this, ARROW_LEFT, buttonpt,
privatedata->buttondown==ARROW3);
}
buttonpt.Set(Bounds().Width()-(B_V_SCROLL_BAR_WIDTH),0);
privatedata->DrawScrollBarButton(this, ARROW_RIGHT, buttonpt,
privatedata->buttondown==ARROW4);
}
else
{
privatedata->DrawScrollBarButton(this, ARROW_UP, buttonpt,
privatedata->buttondown==ARROW1);
if(privatedata->sbinfo.double_arrows)
{
buttonpt.Set(0,B_H_SCROLL_BAR_HEIGHT+1);
privatedata->DrawScrollBarButton(this, ARROW_DOWN, buttonpt,
privatedata->buttondown==ARROW2);
buttonpt.Set(0,Bounds().Height()-( (B_H_SCROLL_BAR_HEIGHT*2)+1 ));
privatedata->DrawScrollBarButton(this, ARROW_UP, buttonpt,
privatedata->buttondown==ARROW3);
}
buttonpt.Set(0,Bounds().Height()-(B_H_SCROLL_BAR_HEIGHT));
privatedata->DrawScrollBarButton(this, ARROW_DOWN, buttonpt,
privatedata->buttondown==ARROW4);
}
// Draw scroll thumb
if(privatedata->enabled)
{
BRect r(privatedata->thumbframe);
SetHighColor(dark);
StrokeRect(privatedata->thumbframe);
r.InsetBy(1,1);
SetHighColor(tint_color(c,B_DARKEN_2_TINT));
StrokeLine(r.LeftBottom(),r.RightBottom());
StrokeLine(r.RightTop(),r.RightBottom());
SetHighColor(light);
StrokeLine(r.LeftTop(),r.RightTop());
StrokeLine(r.LeftTop(),r.LeftBottom());
r.InsetBy(1,1);
if(privatedata->tracking)
SetHighColor(tint_color(normal,B_DARKEN_1_TINT));
else
SetHighColor(normal);
FillRect(r);
}
// TODO: Add the other thumb styles - dots and lines
}
void BScrollBar::FrameMoved(BPoint new_position)
{
}
void BScrollBar::FrameResized(float new_width, float new_height)
{
}
BHandler *BScrollBar::ResolveSpecifier(BMessage *msg,int32 index,
BMessage *specifier,int32 form,const char *property)
{
return NULL;
}
void BScrollBar::ResizeToPreferred()
{
}
void BScrollBar::GetPreferredSize(float *width, float *height)
{
2003-05-14 17:21:46 +00:00
if (fOrientation == B_VERTICAL)
*width = B_V_SCROLL_BAR_WIDTH;
else if (fOrientation == B_HORIZONTAL)
*height = B_H_SCROLL_BAR_HEIGHT;
}
void BScrollBar::MakeFocus(bool state)
{
if(fTarget)
fTarget->MakeFocus(state);
}
void BScrollBar::AllAttached()
{
}
void BScrollBar::AllDetached()
{
}
status_t BScrollBar::GetSupportedSuites(BMessage *data)
{
return B_ERROR;
}
status_t BScrollBar::Perform(perform_code d, void *arg)
{
return B_OK;
}
void BScrollBar::_ReservedScrollBar1()
{
}
void BScrollBar::_ReservedScrollBar2()
{
}
void BScrollBar::_ReservedScrollBar3()
{
}
void BScrollBar::_ReservedScrollBar4()
{
}
BScrollBar &BScrollBar::operator=(const BScrollBar &)
{
return *this;
}
/*
This cheat function will allow the scrollbar prefs app to act exactly like R5's and
perform other stuff without mucking around with the virtual tables B_BAD_VALUE is
returned when a NULL scrollbar pointer is passed to it
Command list:
0: Scroll By Value: increment or decrement scrollbar's value by a certain amount.
data parameter is cast to a float * and used to modify the value.
Returns B_OK if everthing went well and the caller is to redraw the scrollbar
B_ERROR is returned if the caller doesn't need to do anything
B_BAD_VALUE is returned if data is NULL
1: Set Double: set usage mode to either single or double.
data parameter is cast to a bool. true sets it to double, false sets to single
B_OK is always returned
B_BAD_VALUE is returned if data is NULL
2: Set Proportional: set usage mode to either proportional or fixed thumbsize.
data parameter is cast to a bool. true sets it to proportional, false sets to fixed
B_OK is always returned
B_BAD_VALUE is returned if data is NULL
3: Set Style: set thumb style to blank, 3 dots, or 3 lines
data parameter is cast to an int8. 0 is blank, 1 is dotted, 2 is lined
B_ERROR is returned for values != 0, 1, or 2
B_BAD_VALUE is returned if data is NULL
*/
status_t control_scrollbar(int8 command, void *data, BScrollBar *sb)
{
if(!sb)
return B_BAD_VALUE;
switch(command)
{
case 0: // Scroll By Value
{
if(!data)
return B_BAD_VALUE;
float datavalue=*((float *)data);
if(datavalue==0)
return B_ERROR;
if(sb->fOrientation==B_VERTICAL)
{
if(datavalue<0)
{
if(sb->fValue + datavalue >= sb->fMin)
{
sb->fValue += datavalue;
if(sb->fTarget)
sb->fTarget->ScrollBy(0,datavalue);
sb->privatedata->thumbframe.OffsetBy(0,datavalue);
sb->fValue--;
return B_OK;
}
// fall through to return B_ERROR
}
else
{
if(sb->fValue + datavalue <= sb->fMax)
{
sb->fValue += datavalue;
if(sb->fTarget)
sb->fTarget->ScrollBy(0,datavalue);
sb->privatedata->thumbframe.OffsetBy(0,datavalue);
sb->fValue++;
return B_OK;
}
// fall through to return B_ERROR
}
}
else
{
if(datavalue<0)
{
if(sb->fValue + datavalue >= sb->fMin)
{
sb->fValue += datavalue;
if(sb->fTarget)
sb->fTarget->ScrollBy(datavalue,0);
sb->privatedata->thumbframe.OffsetBy(datavalue,0);
sb->fValue--;
return B_OK;
}
// fall through to return B_ERROR
}
else
{
if(sb->fValue + datavalue <= sb->fMax)
{
sb->fValue += datavalue;
if(sb->fTarget)
sb->fTarget->ScrollBy(datavalue,0);
sb->privatedata->thumbframe.OffsetBy(datavalue,0);
sb->fValue++;
return B_OK;
}
// fall through to return B_ERROR
}
}
return B_ERROR;
}// end case 0 (Scroll By Value)
case 1: // Set Double
{
if(!data)
return B_BAD_VALUE;
bool datavalue=*((bool *)data);
if(sb->privatedata->sbinfo.double_arrows==datavalue)
return B_OK;
sb->privatedata->sbinfo.double_arrows=datavalue;
int8 multiplier=(datavalue)?1:-1;
if(sb->fOrientation==B_VERTICAL)
sb->privatedata->thumbframe.OffsetBy(0,multiplier*B_H_SCROLL_BAR_HEIGHT);
else
sb->privatedata->thumbframe.OffsetBy(multiplier*B_V_SCROLL_BAR_WIDTH,0);
return B_OK;
}
case 2: // Set Proportional
{
// TODO: Figure out how proportional relates to the size of the thumb
if(!data)
return B_BAD_VALUE;
bool datavalue=*((bool *)data);
sb->privatedata->sbinfo.proportional=datavalue;
return B_OK;
}
case 3: // Set Style
{
// TODO: Add redraw code to reflect the changes
if(!data)
return B_BAD_VALUE;
int8 datavalue=*((int8 *)data);
if(datavalue!=0 && datavalue!=1 && datavalue!=2)
return B_BAD_VALUE;
sb->privatedata->sbinfo.knob=datavalue;
return B_OK;
}
default:
{
printf("Unknown command value %d in control_scrollbar\n",command);
break;
}
} // end command switch
// We should never get here unless I've done something dumb in calling the function
return B_BAD_VALUE;
}
void BScrollBarPrivateData::DrawScrollBarButton(BScrollBar *owner, arrow_direction direction,
const BPoint &offset, bool down)
{
// Another hack for code size
BRect r(offset.x,offset.y,offset.x+14,offset.y+14);
rgb_color c=ui_color(B_PANEL_BACKGROUND_COLOR);
rgb_color light, dark, normal,arrow,arrow2;
if(down)
{
light=tint_color(c,B_DARKEN_3_TINT);
arrow2=dark=tint_color(c,B_LIGHTEN_MAX_TINT);
normal=c;
arrow=tint_color(c,B_DARKEN_MAX_TINT);
}
else
{
bool use_enabled_colors=enabled;
// Add a usability perk - disable buttons if they would not do anything -
// like a left arrow if the value==fMin
if( (direction==ARROW_LEFT || direction==ARROW_UP) &&
(owner->fValue==owner->fMin) )
use_enabled_colors=false;
else
if( (direction==ARROW_RIGHT || direction==ARROW_DOWN) &&
(owner->fValue==owner->fMax) )
use_enabled_colors=false;
if(use_enabled_colors)
{
arrow2=light=tint_color(c,B_LIGHTEN_MAX_TINT);
dark=tint_color(c,B_DARKEN_3_TINT);
normal=c;
arrow=tint_color(c,B_DARKEN_MAX_TINT);
}
else
{
arrow2=light=tint_color(c,B_LIGHTEN_1_TINT);
dark=tint_color(c,B_DARKEN_1_TINT);
normal=c;
arrow=tint_color(c,B_DARKEN_1_TINT);
}
}
BPoint tri1,tri2,tri3;
switch(direction)
{
case ARROW_LEFT:
{
tri1.Set(r.left+3,(r.top+r.bottom)/2);
tri2.Set(r.right-3,r.top+3);
tri3.Set(r.right-3,r.bottom-3);
break;
}
case ARROW_RIGHT:
{
tri1.Set(r.left+3,r.bottom-3);
tri2.Set(r.left+3,r.top+3);
tri3.Set(r.right-3,(r.top+r.bottom)/2);
break;
}
case ARROW_UP:
{
tri1.Set(r.left+3,r.bottom-3);
tri2.Set((r.left+r.right)/2,r.top+3);
tri3.Set(r.right-3,r.bottom-3);
break;
}
default:
{
tri1.Set(r.left+3,r.top+3);
tri2.Set(r.right-3,r.top+3);
tri3.Set((r.left+r.right)/2,r.bottom-3);
break;
}
}
r.InsetBy(1,1);
owner->SetHighColor(normal);
owner->FillRect(r);
owner->SetHighColor(arrow);
owner->FillTriangle(tri1,tri2,tri3);
r.InsetBy(-1,-1);
owner->SetHighColor(dark);
owner->StrokeLine(r.LeftBottom(),r.RightBottom());
owner->StrokeLine(r.RightTop(),r.RightBottom());
owner->StrokeLine(tri2,tri3);
owner->StrokeLine(tri1,tri3);
owner->SetHighColor(light);
owner->StrokeLine(r.LeftTop(),r.RightTop());
owner->StrokeLine(r.LeftTop(),r.LeftBottom());
owner->SetHighColor(arrow2);
owner->StrokeLine(tri1,tri2);
}