Ticket #8869: add_spinner.diff

File add_spinner.diff, 23.7 KB (added by Disreali, 12 years ago)

test 3

  • src/kits/interface/Spinner.cpp

     
     1/*
     2    Spinner.cpp: A number spinner control.
     3    Written by DarkWyrm <darkwyrm@earthlink.net>, Copyright 2004
     4    Released under the MIT license.
     5   
     6    Original BScrollBarButton class courtesy Haiku project
     7*/
     8#include "Spinner.h"
     9#include <String.h>
     10#include <ScrollBar.h>
     11#include <Window.h>
     12#include <stdio.h>
     13#include <Font.h>
     14#include <Box.h>
     15#include <MessageFilter.h>
     16#include <PropertyInfo.h>
     17
     18
     19static property_info sProperties[] = {
     20    { "MinValue", { B_GET_PROPERTY, 0 }, { B_DIRECT_SPECIFIER, 0 },
     21        "Returns the minimum value for the spinner.", 0, { B_INT32_TYPE }
     22    },
     23   
     24    { "MinValue", { B_SET_PROPERTY, 0 }, { B_DIRECT_SPECIFIER, 0},
     25        "Sets the minimum value for the spinner.", 0, { B_INT32_TYPE }
     26    },
     27   
     28    { "MaxValue", { B_GET_PROPERTY, 0 }, { B_DIRECT_SPECIFIER, 0 },
     29        "Returns the maximum value for the spinner.", 0, { B_INT32_TYPE }
     30    },
     31   
     32    { "MaxValue", { B_SET_PROPERTY, 0 }, { B_DIRECT_SPECIFIER, 0},
     33        "Sets the maximum value for the spinner.", 0, { B_INT32_TYPE }
     34    },
     35   
     36    { "Step", { B_GET_PROPERTY, 0 }, { B_DIRECT_SPECIFIER, 0 },
     37        "Returns the amount of change when an arrow button is clicked.",
     38        0, { B_INT32_TYPE }
     39    },
     40   
     41    { "Step", { B_SET_PROPERTY, 0 }, { B_DIRECT_SPECIFIER, 0},
     42        "Sets the amount of change when an arrow button is clicked.",
     43        0, { B_INT32_TYPE }
     44    },
     45   
     46    { "Value", { B_GET_PROPERTY, 0 }, { B_DIRECT_SPECIFIER, 0 },
     47        "Returns the value for the spinner.", 0, { B_INT32_TYPE }
     48    },
     49   
     50    { "Value", { B_SET_PROPERTY, 0 }, { B_DIRECT_SPECIFIER, 0},
     51        "Sets the value for the spinner.", 0, { B_INT32_TYPE }
     52    },
     53};
     54
     55enum {
     56    M_UP = 'mmup',
     57    M_DOWN,
     58    M_TEXT_CHANGED = 'mtch'
     59};
     60
     61
     62typedef enum {
     63    ARROW_LEFT = 0,
     64    ARROW_RIGHT,
     65    ARROW_UP,
     66    ARROW_DOWN,
     67    ARROW_NONE
     68} arrow_direction;
     69
     70
     71class SpinnerMsgFilter : public BMessageFilter
     72{
     73public:
     74                            SpinnerMsgFilter(void);
     75                            ~SpinnerMsgFilter(void);
     76    virtual filter_result   Filter(BMessage *msg, BHandler **target);
     77};
     78
     79
     80class SpinnerArrowButton : public BView
     81{
     82public:
     83                            SpinnerArrowButton(BPoint location, const char *name,
     84                                arrow_direction dir);
     85                            ~SpinnerArrowButton(void);
     86                    void    AttachedToWindow(void);
     87                    void    DetachedToWindow(void);
     88                    void    MouseDown(BPoint pt);
     89                    void    MouseUp(BPoint pt);
     90                    void    MouseMoved(BPoint pt, uint32 code, const BMessage *msg);
     91                    void    Draw(BRect update);
     92                    void    SetEnabled(bool value);
     93                    bool    IsEnabled(void) const { return fEnabled; }
     94   
     95private:
     96    arrow_direction     fDirection;
     97    BPoint              fTrianglePoint1,
     98                        fTrianglePoint2,
     99                        fTrianglePoint3;
     100    Spinner             *fParent;
     101    bool                fMouseDown;
     102    bool                fEnabled;
     103};
     104
     105class SpinnerPrivateData
     106{
     107public:
     108    SpinnerPrivateData(void)
     109    {
     110        fThumbFrame.Set(0,0,0,0);
     111        fEnabled = true;
     112        tracking = false;
     113        fMousePoint.Set(0,0);
     114        fThumbIncrement = 1.0;
     115        fRepeaterID = -1;
     116        fExitRepeater = false;
     117        fArrowDown = ARROW_NONE;
     118       
     119        #ifdef TEST_MODE
     120            sbinfo.proportional = true;
     121            sbinfo.double_arrows = false;
     122            sbinfo.knob = 0;
     123            sbinfo.min_knob_size = 14;
     124        #else
     125            get_scroll_bar_info(&fScrollbarInfo);
     126        #endif
     127    }
     128   
     129    ~SpinnerPrivateData(void)
     130    {
     131        if (fRepeaterID != -1)
     132        {
     133            fExitRepeater = false;
     134            kill_thread(fRepeaterID);
     135        }
     136    }
     137   
     138    static  int32   ButtonRepeaterThread(void *data);
     139   
     140            thread_id       fRepeaterID;
     141            scroll_bar_info fScrollbarInfo;
     142            BRect           fThumbFrame;
     143            bool            fEnabled;
     144            bool            tracking;
     145            BPoint          fMousePoint;
     146            float           fThumbIncrement;
     147            bool            fExitRepeater;
     148            arrow_direction fArrowDown;
     149};
     150
     151
     152Spinner::Spinner(BRect frame, const char *name, const char *label, BMessage *msg,
     153                uint32 resize,uint32 flags)
     154 :  BControl(frame,name,label,msg,resize,flags),
     155    fStep(1),
     156    fMin(0),
     157    fMax(100)
     158{
     159    _InitObject();
     160}
     161
     162
     163Spinner::Spinner(BMessage *data)
     164 :  BControl(data)
     165{
     166    if (data->FindInt32("_min",&fMin) != B_OK)
     167        fMin = 0;
     168    if (data->FindInt32("_max",&fMax) != B_OK)
     169        fMin = 100;
     170    if (data->FindInt32("_step",&fStep) != B_OK)
     171        fMin = 1;
     172    _InitObject();
     173}
     174
     175
     176Spinner::~Spinner(void)
     177{
     178    delete fPrivateData;
     179    delete fFilter;
     180}
     181
     182
     183void
     184Spinner::_InitObject(void)
     185{
     186    SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
     187    BRect r(Bounds());
     188    if (r.Height() < B_H_SCROLL_BAR_HEIGHT * 2)
     189        r.bottom = r.top + 1 + B_H_SCROLL_BAR_HEIGHT * 2;
     190    ResizeTo(r.Width(),r.Height());
     191   
     192    r.right -= B_V_SCROLL_BAR_WIDTH;
     193   
     194    font_height fh;
     195    BFont font;
     196    font.GetHeight(&fh);
     197    float textheight = fh.ascent + fh.descent + fh.leading;
     198   
     199    r.top = 0;
     200    r.bottom = textheight;
     201   
     202    fTextControl = new BTextControl(r,"textcontrol",Label(),"0",
     203                                    new BMessage(M_TEXT_CHANGED), B_FOLLOW_TOP |
     204                                    B_FOLLOW_LEFT_RIGHT,
     205                                    B_WILL_DRAW | B_NAVIGABLE);
     206    AddChild(fTextControl);
     207    fTextControl->ResizeTo(r.Width(), MAX(textheight, fTextControl->TextView()->LineHeight(0) + 4.0));
     208    fTextControl->MoveTo(0,
     209        ((B_H_SCROLL_BAR_HEIGHT * 2) - fTextControl->Bounds().Height()) / 2);
     210       
     211    fTextControl->SetDivider(StringWidth(Label()) + 5);
     212   
     213    BTextView *tview = fTextControl->TextView();
     214    tview->SetAlignment(B_ALIGN_LEFT);
     215    tview->SetWordWrap(false);
     216   
     217    BString string("QWERTYUIOP[]\\ASDFGHJKL;'ZXCVBNM,/qwertyuiop{}| "
     218        "asdfghjkl:\"zxcvbnm<>?!@#$%^&*()-_=+`~\r");
     219   
     220    for (int32 i = 0; i < string.CountChars(); i++) {
     221        char c = string.ByteAt(i);
     222        tview->DisallowChar(c);
     223    }
     224   
     225    r = Bounds();
     226    r.left = r.right - B_V_SCROLL_BAR_WIDTH;
     227    r.bottom = B_H_SCROLL_BAR_HEIGHT;
     228   
     229    fUpButton = new SpinnerArrowButton(r.LeftTop(),"up",ARROW_UP);
     230    AddChild(fUpButton);
     231   
     232    r.OffsetBy(0,r.Height() + 1);
     233    fDownButton = new SpinnerArrowButton(r.LeftTop(),"down",ARROW_DOWN);
     234    AddChild(fDownButton);
     235   
     236   
     237    fPrivateData = new SpinnerPrivateData;
     238    fFilter = new SpinnerMsgFilter;
     239}
     240
     241
     242BArchivable *
     243Spinner::Instantiate(BMessage *data)
     244{
     245    if (validate_instantiation(data, "Spinner"))
     246        return new Spinner(data);
     247
     248    return NULL;
     249}
     250
     251
     252status_t
     253Spinner::Archive(BMessage *data, bool deep) const
     254{
     255    status_t status = BControl::Archive(data, deep);
     256    data->AddString("class","Spinner");
     257   
     258    if (status == B_OK)
     259        status = data->AddInt32("_min",fMin);
     260   
     261    if (status == B_OK)
     262        status = data->AddInt32("_max",fMax);
     263   
     264    if (status == B_OK)
     265        status = data->AddInt32("_step",fStep);
     266   
     267    return status;
     268}
     269
     270
     271status_t
     272Spinner::GetSupportedSuites(BMessage *msg)
     273{
     274    msg->AddString("suites","suite/vnd.DW-spinner");
     275   
     276    BPropertyInfo prop_info(sProperties);
     277    msg->AddFlat("messages",&prop_info);
     278    return BControl::GetSupportedSuites(msg);
     279}
     280
     281
     282BHandler *
     283Spinner::ResolveSpecifier(BMessage *msg, int32 index, BMessage *specifier,
     284                                    int32 form, const char *property)
     285{
     286    return BControl::ResolveSpecifier(msg, index, specifier, form, property);
     287}
     288
     289
     290void
     291Spinner::AttachedToWindow(void)
     292{
     293    Window()->AddCommonFilter(fFilter);
     294    fTextControl->SetTarget(this);
     295}
     296
     297
     298void
     299Spinner::DetachedFromWindow(void)
     300{
     301    Window()->RemoveCommonFilter(fFilter);
     302}
     303
     304
     305void
     306Spinner::SetValue(int32 value)
     307{
     308    if (value > GetMax() || value < GetMin())
     309        return;
     310   
     311    BControl::SetValue(value);
     312   
     313    char string[50];
     314    sprintf(string,"%ld",value);
     315    fTextControl->SetText(string);
     316}
     317
     318
     319void
     320Spinner::SetLabel(const char *text)
     321{
     322    fTextControl->SetLabel(text);
     323}
     324
     325
     326void
     327Spinner::ValueChanged(int32 value)
     328{
     329}
     330
     331
     332void
     333Spinner::MessageReceived(BMessage *msg)
     334{
     335    if (msg->what == M_TEXT_CHANGED) {
     336        BString string(fTextControl->Text());
     337        int32 newvalue = 0;
     338       
     339        sscanf(string.String(),"%ld",&newvalue);
     340        if (newvalue >= GetMin() && newvalue <= GetMax()) {
     341            // new value is in range, so set it and go
     342            SetValue(newvalue);
     343            Invoke();
     344            Draw(Bounds());
     345            ValueChanged(Value());
     346        } else {
     347            // new value is out of bounds. Clip to range if current value is not
     348            // at the end of its range
     349            if (newvalue < GetMin() && Value() != GetMin()) {
     350                SetValue(GetMin());
     351                Invoke();
     352                Draw(Bounds());
     353                ValueChanged(Value());
     354            } else if (newvalue>GetMax() && Value() != GetMax()) {
     355                SetValue(GetMax());
     356                Invoke();
     357                Draw(Bounds());
     358                ValueChanged(Value());
     359            } else {
     360                char string[100];
     361                sprintf(string,"%ld",Value());
     362                fTextControl->SetText(string);
     363            }
     364        }
     365    }
     366    else
     367        BControl::MessageReceived(msg);
     368}
     369
     370
     371void
     372Spinner::GetPreferredSize(float *width, float *height)
     373{
     374    float w, h;
     375   
     376    // This code shamelessly copied from the Haiku TextControl.cpp and adapted
     377   
     378    // we need enough space for the label and the child text view
     379    font_height fontHeight;
     380    fTextControl->GetFontHeight(&fontHeight);
     381    float labelHeight = ceil(fontHeight.ascent + fontHeight.descent + fontHeight.leading);
     382    float textHeight = fTextControl->TextView()->LineHeight(0) + 4.0;
     383
     384    h = max_c(labelHeight, textHeight);
     385   
     386    w = 25.0f + ceilf(fTextControl->StringWidth(fTextControl->Label())) +
     387        ceilf(fTextControl->StringWidth(fTextControl->Text()));
     388   
     389    w += B_V_SCROLL_BAR_WIDTH;
     390    if (h < fDownButton->Frame().bottom)
     391        h = fDownButton->Frame().bottom;
     392   
     393    if (width)
     394        *width = w;
     395    if (height)
     396        *height = h;
     397}
     398
     399
     400void
     401Spinner::ResizeToPreferred(void)
     402{
     403    float w,h;
     404    GetPreferredSize(&w,&h);
     405    ResizeTo(w,h);
     406}
     407
     408
     409void
     410Spinner::SetSteps(int32 stepsize)
     411{
     412    fStep = stepsize;
     413}
     414
     415
     416void
     417Spinner::SetRange(int32 min, int32 max)
     418{
     419    SetMin(min);
     420    SetMax(max);
     421}
     422
     423
     424void
     425Spinner::GetRange(int32 *min, int32 *max)
     426{
     427    *min = fMin;
     428    *max = fMax;
     429}
     430
     431
     432void
     433Spinner::SetMax(int32 max)
     434{
     435    fMax = max;
     436    if (Value() > fMax)
     437        SetValue(fMax);
     438}
     439
     440
     441void
     442Spinner::SetMin(int32 min)
     443{
     444    fMin = min;
     445    if (Value() < fMin)
     446        SetValue(fMin);
     447}
     448
     449
     450void
     451Spinner::SetEnabled(bool value)
     452{
     453    if (IsEnabled() == value)
     454        return;
     455   
     456    BControl::SetEnabled(value);
     457    fTextControl->SetEnabled(value);
     458    fUpButton->SetEnabled(value);
     459    fDownButton->SetEnabled(value);
     460}
     461
     462
     463void
     464Spinner::MakeFocus(bool value)
     465{
     466    fTextControl->MakeFocus(value);
     467}
     468
     469
     470int32
     471SpinnerPrivateData::ButtonRepeaterThread(void *data)
     472{
     473    Spinner *sp = (Spinner *)data;
     474   
     475    snooze(250000);
     476   
     477    bool exitval = false;
     478   
     479    sp->Window()->Lock();
     480    exitval = sp->fPrivateData->fExitRepeater;
     481   
     482    int32 scrollvalue = 0;
     483    if (sp->fPrivateData->fArrowDown == ARROW_UP)
     484        scrollvalue = sp->fStep;
     485    else if (sp->fPrivateData->fArrowDown != ARROW_NONE)
     486        scrollvalue = -sp->fStep;
     487    else
     488        exitval = true;
     489   
     490    sp->Window()->Unlock();
     491   
     492    while (!exitval) {
     493        sp->Window()->Lock();
     494       
     495        int32 newvalue = sp->Value() + scrollvalue;
     496        if (newvalue >= sp->GetMin() && newvalue <= sp->GetMax()) {
     497            // new value is in range, so set it and go
     498            sp->SetValue(newvalue);
     499            sp->Invoke();
     500            sp->Draw(sp->Bounds());
     501            sp->ValueChanged(sp->Value());
     502        } else {
     503            // new value is out of bounds. Clip to range if current value is not
     504            // at the end of its range
     505            if (newvalue<sp->GetMin() && sp->Value() != sp->GetMin()) {
     506                sp->SetValue(sp->GetMin());
     507                sp->Invoke();
     508                sp->Draw(sp->Bounds());
     509                sp->ValueChanged(sp->Value());
     510            } else  if (newvalue>sp->GetMax() && sp->Value() != sp->GetMax())
     511            {
     512                sp->SetValue(sp->GetMax());
     513                sp->Invoke();
     514                sp->Draw(sp->Bounds());
     515                sp->ValueChanged(sp->Value());
     516            }
     517        }
     518        sp->Window()->Unlock();
     519       
     520        snooze(50000);
     521       
     522        sp->Window()->Lock();
     523        exitval = sp->fPrivateData->fExitRepeater;
     524        sp->Window()->Unlock();
     525    }
     526   
     527    sp->Window()->Lock();
     528    sp->fPrivateData->fExitRepeater = false;
     529    sp->fPrivateData->fRepeaterID = -1;
     530    sp->Window()->Unlock();
     531    return 0;
     532    exit_thread(0);
     533}
     534
     535
     536SpinnerArrowButton::SpinnerArrowButton(BPoint location, const char *name,
     537                                        arrow_direction dir)
     538 :BView(BRect(0,0,B_V_SCROLL_BAR_WIDTH - 1,B_H_SCROLL_BAR_HEIGHT - 1).OffsetToCopy(location),
     539        name, B_FOLLOW_RIGHT | B_FOLLOW_TOP, B_WILL_DRAW)
     540{
     541    fDirection = dir;
     542    fEnabled = true;
     543    BRect r = Bounds();
     544   
     545    switch (fDirection) {
     546        case ARROW_LEFT: {
     547            fTrianglePoint1.Set(r.left + 3,(r.top + r.bottom) / 2);
     548            fTrianglePoint2.Set(r.right - 3,r.top + 3);
     549            fTrianglePoint3.Set(r.right - 3,r.bottom - 3);
     550            break;
     551        }
     552        case ARROW_RIGHT: {
     553            fTrianglePoint1.Set(r.left + 3,r.bottom - 3);
     554            fTrianglePoint2.Set(r.left + 3,r.top + 3);
     555            fTrianglePoint3.Set(r.right - 3,(r.top + r.bottom) / 2);
     556            break;
     557        }
     558        case ARROW_UP: {
     559            fTrianglePoint1.Set(r.left + 3,r.bottom - 3);
     560            fTrianglePoint2.Set((r.left + r.right) / 2,r.top + 3);
     561            fTrianglePoint3.Set(r.right - 3,r.bottom - 3);
     562            break;
     563        }
     564        default: {
     565            fTrianglePoint1.Set(r.left + 3,r.top + 3);
     566            fTrianglePoint2.Set(r.right - 3,r.top + 3);
     567            fTrianglePoint3.Set((r.left + r.right) / 2,r.bottom - 3);
     568            break;
     569        }
     570    }
     571    fMouseDown = false;
     572}
     573
     574
     575SpinnerArrowButton::~SpinnerArrowButton(void)
     576{
     577}
     578
     579
     580void
     581SpinnerArrowButton::MouseDown(BPoint pt)
     582{
     583    if (fEnabled == false)
     584        return;
     585   
     586/*  fMouseDown = true;
     587    Draw(Bounds());
     588
     589    if (fParent)
     590    {
     591        int32 step = fParent->GetSteps();
     592               
     593        int32 newvalue = fParent->Value();
     594       
     595        if (fDirection == ARROW_UP)
     596        {
     597            fParent->fPrivateData->fArrowDown = ARROW_UP;
     598            newvalue += step;
     599        }
     600        else
     601        {
     602            fParent->fPrivateData->fArrowDown = ARROW_DOWN;
     603            newvalue -= step;
     604        }
     605
     606        if ( newvalue >= fParent->GetMin() && newvalue <= fParent->GetMax())
     607        {
     608            // new value is in range, so set it and go
     609            fParent->SetValue(newvalue);
     610            fParent->Invoke();
     611            fParent->Draw(fParent->Bounds());
     612            fParent->ValueChanged(fParent->Value());
     613        }
     614        else
     615        {
     616            // new value is out of bounds. Clip to range if current value is not
     617            // at the end of its range
     618            if (newvalue<fParent->GetMin() && fParent->Value() != fParent->GetMin())
     619            {
     620                fParent->SetValue(fParent->GetMin());
     621                fParent->Invoke();
     622                fParent->Draw(fParent->Bounds());
     623                fParent->ValueChanged(fParent->Value());
     624            }
     625            else
     626            if (newvalue>fParent->GetMax() && fParent->Value() != fParent->GetMax())
     627            {
     628                fParent->SetValue(fParent->GetMax());
     629                fParent->Invoke();
     630                fParent->Draw(fParent->Bounds());
     631                fParent->ValueChanged(fParent->Value());
     632            }
     633            else
     634            {
     635                // cases which go here are if new value is <minimum and value already at
     636                // minimum or if > maximum and value already at maximum
     637                return;
     638            }
     639        }
     640
     641        if (fParent->fPrivateData->fRepeaterID == -1)
     642        {
     643            fParent->fPrivateData->fExitRepeater = false;
     644            fParent->fPrivateData->fRepeaterID = spawn_thread(fParent->fPrivateData->ButtonRepeaterThread,
     645                "scroll repeater",B_NORMAL_PRIORITY,fParent);
     646            resume_thread(fParent->fPrivateData->fRepeaterID);
     647        }
     648    }
     649*/
     650    if (!IsEnabled())
     651        return;
     652
     653    // TODO: Handle asynchronous window controls flag
     654//  if (Window()->Flags() & B_ASYNCHRONOUS_CONTROLS)
     655//  {
     656//      SetTracking(true);
     657//  SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
     658//  }
     659//  else
     660 // {
     661//      BRect bounds = Bounds();
     662        uint32 buttons;
     663        BPoint point;
     664
     665        int32 step = fParent->GetSteps();
     666       
     667        int32 newvalue = fParent->Value();
     668       
     669        int32 waitvalue = 250000;
     670       
     671        do {
     672            if (fDirection == ARROW_UP) {
     673                fParent->fPrivateData->fArrowDown = ARROW_UP;
     674                newvalue += step;
     675            } else {
     676                fParent->fPrivateData->fArrowDown = ARROW_DOWN;
     677                newvalue -= step;
     678            }
     679           
     680            if (newvalue >= fParent->GetMin() && newvalue <= fParent->GetMax()) {
     681                // new value is in range, so set it and go
     682                fParent->SetValue(newvalue);
     683                fParent->Invoke();
     684//              fParent->Draw(fParent->Bounds());
     685                fParent->ValueChanged(fParent->Value());
     686            } else {
     687                // new value is out of bounds. Clip to range if current value is not
     688                // at the end of its range
     689                if (newvalue < fParent->GetMin() &&
     690                        fParent->Value() != fParent->GetMin()) {
     691                    fParent->SetValue(fParent->GetMin());
     692                    fParent->Invoke();
     693//                  fParent->Draw(fParent->Bounds());
     694                    fParent->ValueChanged(fParent->Value());
     695                } else if (newvalue>fParent->GetMax() &&
     696                            fParent->Value() != fParent->GetMax()) {
     697                    fParent->SetValue(fParent->GetMax());
     698                    fParent->Invoke();
     699//                  fParent->Draw(fParent->Bounds());
     700                    fParent->ValueChanged(fParent->Value());
     701                } else {
     702                    // cases which go here are if new value is <minimum and value already at
     703                    // minimum or if > maximum and value already at maximum
     704                    return;
     705                }
     706            }
     707           
     708            Window()->UpdateIfNeeded();
     709           
     710            snooze(waitvalue);
     711           
     712            GetMouse(&point, &buttons, true);
     713           
     714//          bool inside = bounds.Contains(point);
     715           
     716//          if ((Value() == B_CONTROL_ON) != inside)
     717//              SetValue(inside ? B_CONTROL_ON : B_CONTROL_OFF);
     718           
     719            if (waitvalue > 150000)
     720                waitvalue = 150000;
     721               
     722        } while (buttons != 0);
     723
     724//  }
     725
     726}
     727
     728
     729void
     730SpinnerArrowButton::MouseUp(BPoint pt)
     731{
     732    if (fEnabled) {
     733        fMouseDown = false;
     734       
     735        if (fParent) {
     736            fParent->fPrivateData->fArrowDown = ARROW_NONE;
     737            fParent->fPrivateData->fExitRepeater = true;
     738        }
     739        Draw(Bounds());
     740    }
     741}
     742
     743
     744void
     745SpinnerArrowButton::MouseMoved(BPoint pt, uint32 transit, const BMessage *msg)
     746{
     747    if (fEnabled == false)
     748        return;
     749   
     750    if (transit == B_ENTERED_VIEW) {
     751        BPoint point;
     752        uint32 buttons;
     753        GetMouse(&point,&buttons);
     754        if ((buttons & B_PRIMARY_MOUSE_BUTTON) == 0 &&
     755            (buttons & B_SECONDARY_MOUSE_BUTTON) == 0 &&
     756            (buttons & B_PRIMARY_MOUSE_BUTTON) == 0 )
     757            fMouseDown = false;
     758        else
     759            fMouseDown = true;
     760        Draw(Bounds());
     761    }
     762   
     763    if (transit == B_EXITED_VIEW || transit == B_OUTSIDE_VIEW)
     764        MouseUp(Bounds().LeftTop());
     765}
     766
     767
     768void
     769SpinnerArrowButton::Draw(BRect update)
     770{
     771    rgb_color c = ui_color(B_PANEL_BACKGROUND_COLOR);
     772    BRect r(Bounds());
     773   
     774    rgb_color light, dark, normal,arrow,arrow2;
     775    if (fMouseDown) {   
     776        light = tint_color(c,B_DARKEN_3_TINT);
     777        arrow2 = dark = tint_color(c,B_LIGHTEN_MAX_TINT);
     778        normal = c;
     779        arrow = tint_color(c,B_DARKEN_MAX_TINT);
     780    } else if (fEnabled) {
     781        arrow2 = light = tint_color(c,B_LIGHTEN_MAX_TINT);
     782        dark = tint_color(c,B_DARKEN_3_TINT);
     783        normal = c;
     784        arrow = tint_color(c,B_DARKEN_MAX_TINT);
     785    } else {
     786        arrow2 = light = tint_color(c,B_LIGHTEN_1_TINT);
     787        dark = tint_color(c,B_DARKEN_1_TINT);
     788        normal = c;
     789        arrow = tint_color(c,B_DARKEN_1_TINT);
     790    }
     791   
     792    r.InsetBy(1,1);
     793    SetHighColor(normal);
     794    FillRect(r);
     795   
     796    SetHighColor(arrow);
     797    FillTriangle(fTrianglePoint1,fTrianglePoint2,fTrianglePoint3);
     798   
     799    r.InsetBy(-1,-1);
     800    SetHighColor(dark);
     801    StrokeLine(r.LeftBottom(),r.RightBottom());
     802    StrokeLine(r.RightTop(),r.RightBottom());
     803    StrokeLine(fTrianglePoint2,fTrianglePoint3);
     804    StrokeLine(fTrianglePoint1,fTrianglePoint3);
     805   
     806    SetHighColor(light);
     807    StrokeLine(r.LeftTop(),r.RightTop());
     808    StrokeLine(r.LeftTop(),r.LeftBottom());
     809   
     810    SetHighColor(arrow2);
     811    StrokeLine(fTrianglePoint1,fTrianglePoint2);
     812}
     813
     814
     815void
     816SpinnerArrowButton::AttachedToWindow(void)
     817{
     818    fParent = (Spinner*)Parent();
     819}
     820
     821
     822void
     823SpinnerArrowButton::DetachedToWindow(void)
     824{
     825    fParent = NULL;
     826}
     827
     828
     829void
     830SpinnerArrowButton::SetEnabled(bool value)
     831{
     832    fEnabled = value;
     833    Invalidate();
     834}
     835
     836
     837SpinnerMsgFilter::SpinnerMsgFilter(void)
     838 : BMessageFilter(B_PROGRAMMED_DELIVERY, B_ANY_SOURCE,B_KEY_DOWN)
     839{
     840}
     841
     842
     843SpinnerMsgFilter::~SpinnerMsgFilter(void)
     844{
     845}
     846
     847
     848filter_result
     849SpinnerMsgFilter::Filter(BMessage *msg, BHandler **target)
     850{
     851    int32 c;
     852    msg->FindInt32("raw_char",&c);
     853    switch (c) {
     854        case B_ENTER: {
     855            BTextView *text = dynamic_cast<BTextView*>(*target);
     856            if (text && text->IsFocus()) {
     857                BView *view = text->Parent();
     858                while (view) {                 
     859                    Spinner *spin = dynamic_cast<Spinner*>(view);
     860                    if (spin) {
     861                        BString string(text->Text());
     862                        int32 newvalue = 0;
     863                       
     864                        sscanf(string.String(),"%ld",&newvalue);
     865                        if (newvalue != spin->Value()) {
     866                            spin->SetValue(newvalue);
     867                            spin->Invoke();
     868                        }
     869                        return B_SKIP_MESSAGE;
     870                    }
     871                    view = view->Parent();
     872                }
     873            }
     874            return B_DISPATCH_MESSAGE;
     875        }
     876        case B_TAB: {
     877            // Cause Tab characters to perform keybaord navigation
     878            BHandler *h = *target;
     879            BView *v = NULL;
     880           
     881            h = h->NextHandler();
     882            while (h) {
     883                v = dynamic_cast<BView*>(h);
     884                if (v) {
     885                    *target = v->Window();
     886                    return B_DISPATCH_MESSAGE;
     887                }
     888                h = h->NextHandler();
     889            }
     890            return B_SKIP_MESSAGE;
     891        }
     892        case B_UP_ARROW:
     893        case B_DOWN_ARROW: {
     894            BTextView *text = dynamic_cast<BTextView*>(*target);
     895            if (text && text->IsFocus()) {
     896                // We have a text view. See if it currently has the focus and belongs
     897                // to a Spinner control. If so, change the value of the spinner
     898               
     899                // TextViews are complicated beasts which are actually multiple views.
     900                // Travel up the hierarchy to see if any of the target's ancestors are
     901                // a Spinner.
     902               
     903                BView *view = text->Parent();
     904                while (view) {                 
     905                    Spinner *spin = dynamic_cast<Spinner*>(view);
     906                    if (spin) {
     907                        int32 step = spin->GetSteps();
     908                        if (c == B_DOWN_ARROW)
     909                            step = 0 - step;
     910                       
     911                        spin->SetValue(spin->Value() + step);
     912                        spin->Invoke();
     913                        return B_SKIP_MESSAGE;
     914                    }
     915                    view = view->Parent();
     916                }
     917            }
     918           
     919            return B_DISPATCH_MESSAGE;
     920        }
     921        default:
     922            return B_DISPATCH_MESSAGE;
     923    }
     924   
     925    // shut the stupid compiler up
     926    return B_SKIP_MESSAGE;
     927}
  • headers/os/interface/Spinner.h

     
     1/*
     2    Spinner.h: A number spinner control.
     3    Written by DarkWyrm <darkwyrm@earthlink.net>, Copyright 2004
     4    Released under the MIT license.
     5   
     6    Original BScrollBarButton class courtesy Haiku project
     7*/
     8#ifndef SPINNER_H_
     9#define SPINNER_H_
     10
     11#include <Control.h>
     12#include <TextView.h>
     13#include <Button.h>
     14#include <StringView.h>
     15#include <TextControl.h>
     16
     17class SpinnerPrivateData;
     18class SpinnerArrowButton;
     19class SpinnerMsgFilter;
     20
     21/*
     22    The Spinner control provides a numeric input which can be nudged by way of two
     23    small arrow buttons at the right side. The API is quite similar to that of
     24    BScrollBar.
     25*/
     26
     27class Spinner : public BControl
     28{
     29public:
     30                            Spinner(BRect frame, const char *name,
     31                                    const char *label, BMessage *msg,
     32                                    uint32 resize = B_FOLLOW_LEFT | B_FOLLOW_TOP,
     33                                    uint32 flags = B_WILL_DRAW | B_NAVIGABLE);
     34                            Spinner(BMessage *data);
     35    virtual                 ~Spinner(void);
     36   
     37    static  BArchivable *   Instantiate(BMessage *data);
     38    virtual status_t        Archive(BMessage *data, bool deep = true) const;
     39       
     40    virtual status_t        GetSupportedSuites(BMessage *msg);
     41    virtual BHandler *      ResolveSpecifier(BMessage *msg, int32 index,
     42                                            BMessage *specifier, int32 form,
     43                                            const char *property);
     44   
     45    virtual void            AttachedToWindow(void);
     46    virtual void            DetachedFromWindow(void);
     47    virtual void            ValueChanged(int32 value);
     48    virtual void            MessageReceived(BMessage *msg);
     49   
     50    virtual void            GetPreferredSize(float *width, float *height);
     51    virtual void            ResizeToPreferred(void);
     52   
     53    virtual void            SetSteps(int32 stepsize);
     54            int32           GetSteps(void) const { return fStep; }
     55   
     56    virtual void            SetRange(int32 min, int32 max);
     57            void            GetRange(int32 *min, int32 *max);
     58   
     59    virtual void            SetMax(int32 max);
     60            int32           GetMax(void) const { return fMax; }
     61    virtual void            SetMin(int32 min);
     62            int32           GetMin(void) const { return fMin; }
     63   
     64    virtual void            MakeFocus(bool value = true);
     65   
     66    virtual void            SetValue(int32 value);
     67    virtual void            SetLabel(const char *text);
     68            BTextControl *  TextControl(void) const { return fTextControl; }
     69   
     70    virtual void            SetEnabled(bool value);
     71   
     72private:
     73            void            _InitObject(void);
     74           
     75    friend  class SpinnerArrowButton;
     76    friend  class SpinnerPrivateData;
     77   
     78    BTextControl        *fTextControl;
     79    SpinnerArrowButton  *fUpButton,
     80                        *fDownButton;
     81    SpinnerPrivateData  *fPrivateData;
     82   
     83    int32               fStep;
     84    int32               fMin,
     85                        fMax;
     86    SpinnerMsgFilter    *fFilter;
     87};
     88
     89#endif