/* * Copyright (c) 1987, 1988, 1989 Stanford University * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided * that the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Stanford not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Stanford makes no representations about * the suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * STANFORD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. * IN NO EVENT SHALL STANFORD BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Button management. */ #include #include #include #include #include #include #include #include #include #include #include #include #include static const int sep = 3; static const int pad = 3; inline int HalfRadioButtonSize (int h) { return round(.4*h); } inline int HalfCheckBoxSize (int h) { return round(.4*h); } /* * A button state is a value that is settable from one or more buttons. * When the state is modified, it updates all its buttons. */ ButtonState::ButtonState () { value = nil; Reference(); } ButtonState::ButtonState (int v) { value = (void*)v; Reference(); } ButtonState::ButtonState (void* v) { value = v; Reference(); } void ButtonState::SetValue (int v) { Modify((void*)v); } void ButtonState::SetValue (void* v) { Modify(v); } void ButtonState::operator= (ButtonState& s) { Modify(s.value); } void ButtonState::Modify (void* v) { if (value != v) { value = v; Notify(); } } /* * Simple list of buttons. */ class ButtonList { public: Button* cur; ButtonList* next; ButtonList (Button* b) { cur = b; next = nil; } }; static void Remove (ButtonList*& blist, Button* b) { register ButtonList* bl; register ButtonList* prev; prev = nil; for (bl = blist; bl != nil; bl = bl->next) { if (bl->cur == b) { if (prev == nil) { blist = bl->next; } else { prev->next = bl->next; } delete bl; break; } prev = bl; } } static void DeleteList (ButtonList* blist) { register ButtonList* bl; register ButtonList* next; for (bl = blist; bl != nil; bl = next) { next = bl->next; delete bl; } } /* * A button has a ButtonState subject that it modifies when pressed. * Also, a button may have associated buttons that are enabled/disabled * when it is chosen/unchosen. */ Button::Button (ButtonState* s, void* v) { Init(s, v); } Button::Button (const char* name, ButtonState* s, void* v) { SetInstance(name); Init(s, v); } Button::Button (Painter* out, ButtonState* s, void* v) : Interactor(nil, out) { Init(s, v); } void Button::Init (ButtonState* s, void* v) { SetClassName("Button"); value = v; subject = s; associates = nil; enabled = true; hit = false; if (!subject) subject = new ButtonState(); subject->Attach(this); Update(); input = new Sensor(updownEvents); input->Catch(EnterEvent); input->Catch(LeaveEvent); } Button::~Button () { if (subject != nil) { subject->Detach(this); } DeleteList(associates); } void ButtonState::GetValue (int& v) { v = (int)value; } void ButtonState::GetValue (void*& v) { v = value; } void Button::Attach (Button* b) { ButtonList* head; head = new ButtonList(b); head->next = associates; associates = head; if (chosen) { b->Enable(); } else { b->Disable(); } } void Button::Detach (Button* b) { Remove(associates, b); } void Button::Enable () { if (!enabled) { enabled = true; if (canvas != nil) { Draw(); } } } void Button::Disable () { if (enabled) { enabled = false; if (canvas != nil) { Draw(); } } } void Button::Choose () { register ButtonList* bl; if (!chosen) { chosen = true; if (enabled) { if (canvas != nil) { Refresh(); } for (bl = associates; bl != nil; bl = bl->next) { bl->cur->Enable(); } } } } void Button::UnChoose () { register ButtonList* bl; if (chosen) { chosen = false; if (enabled) { if (canvas != nil) { Refresh(); } for (bl = associates; bl != nil; bl = bl->next) { bl->cur->Disable(); } } } } void Button::SetDimensions (int width, int height) { shape->width = width; shape->height = height; shape->Rigid(); } void Button::Refresh () { /* default shouldn't happen */ } void Button::Handle (register Event& e) { if (e.eventType == DownEvent && e.target == this) { bool inside = true; do { if (enabled && e.target == this) { if (e.eventType == EnterEvent) { inside = true; } else if (e.eventType == LeaveEvent) { inside = false; } if (inside) { if (!hit) { hit = true; Refresh(); } } else { if (hit) { hit = false; Refresh(); } } } Read(e); } while (e.eventType != UpEvent); if (hit) { hit = false; Refresh(); } if (enabled && inside) { Press(); } } } void Button::Press () { if (subject != nil) { subject->SetValue(value); } else { Refresh(); } } void Button::Update () { void* v; // if (! subject) // Choose(); // else { subject->GetValue(v); if (!chosen && value == v) { Choose(); } else if (chosen && value != v) { UnChoose(); } // } } TextButton::TextButton (const char* str, ButtonState* s, void* v ) : Button(s, v) { Init(str); } TextButton::TextButton ( const char* name, const char* str, ButtonState* s, void* v ) : Button(name, s, v) { Init(str); } TextButton::TextButton ( const char* str, ButtonState* s, void* v, Painter* out ) : Button(out, s, v) { Init(str); } void TextButton::Init (const char* str) { SetClassName("TextButton"); text = str; background = nil; grayout = nil; } void TextButton::MakeBackground () { Unref(background); background = new Painter(output); background->Reference(); background->SetColors(output->GetBgColor(), output->GetFgColor()); Unref(grayout); grayout = new Painter(background); grayout->Reference(); grayout->SetPattern(gray); grayout->FillBg(false); } void TextButton::MakeShape () { if (text != nil) { Font* f = output->GetFont(); shape->width += f->Width(text); shape->height += f->Height(); } shape->Rigid(); } TextButton::~TextButton () { Unref(background); Unref(grayout); } PushButton::PushButton ( const char* str, ButtonState* s, int v ) : TextButton(str, s, (void*)v) { Init(); } PushButton::PushButton ( const char* str, ButtonState* s, void* v ) : TextButton(str, s, v) { Init(); } PushButton::PushButton ( const char* name, const char* str, ButtonState* s, int v ) : TextButton(name, str, s, (void*)v) { Init(); } PushButton::PushButton ( const char* name, const char* str, ButtonState* s, void* v ) : TextButton(name, str, s, v) { Init(); } PushButton::PushButton ( const char* str, ButtonState* s, int v, Painter* out ) : TextButton(str, s, (void*)v, out) { Init(); } PushButton::PushButton ( const char* str, ButtonState* s, void* v, Painter* out ) : TextButton(str, s, v, out) { Init(); } void PushButton::Init () { SetClassName("PushButton"); } void PushButton::Reconfig () { MakeBackground(); if (shape->Undefined()) { MakeShape(); shape->width += output->GetFont()->Width(" "); shape->height += 2*pad; } } void PushButton::Redraw (Coord x1, Coord y1, Coord x2, Coord y2) { output->ClearRect(canvas, x1, y1, x2, y2); Refresh(); } void PushButton::Refresh () { register int r; Coord x[16], y[16]; Coord tx, ty; r = min(10*pixels, min(xmax+1, ymax+1)/6); x[0] = 0; y[0] = r; x[1] = 0; y[1] = r + r; x[2] = 0; y[2] = ymax - r - r; x[3] = 0; y[3] = ymax - r; x[4] = r; y[4] = ymax; x[5] = r + r; y[5] = ymax; x[6] = xmax - r - r; y[6] = ymax; x[7] = xmax - r; y[7] = ymax; x[8] = xmax; y[8] = ymax - r; x[9] = xmax; y[9] = ymax - r - r; x[10] = xmax; y[10] = r + r; x[11] = xmax; y[11] = r; x[12] = xmax - r; y[12] = 0; x[13] = xmax - r - r; y[13] = 0; x[14] = r + r; y[14] = 0; x[15] = r; y[15] = 0; tx = (xmax - output->GetFont()->Width(text))/2; ty = pad; if (chosen || hit) { output->FillBSpline(canvas, x, y, 16); background->Text(canvas, text, tx, ty); } else { background->FillRect(canvas, 0, 0, xmax, ymax); output->ClosedBSpline(canvas, x, y, 16); output->Text(canvas, text, tx, ty); } if (!enabled) { grayout->FillRect(canvas, 0, 0, xmax, ymax); } } static Bitmap* radioMask; static Bitmap* radioPlain; static Bitmap* radioHit; static Bitmap* radioChosen; static Bitmap* radioBoth; RadioButton::RadioButton ( const char* str, ButtonState* s, int v ) : TextButton(str, s, (void*)v) { Init(); } RadioButton::RadioButton ( const char* str, ButtonState* s, void* v ) : TextButton(str, s, v) { Init(); } RadioButton::RadioButton ( const char* name, const char* str, ButtonState* s, int v ) : TextButton(name, str, s, (void*)v) { Init(); } RadioButton::RadioButton ( const char* name, const char* str, ButtonState* s, void* v ) : TextButton(name, str, s, v) { Init(); } RadioButton::RadioButton ( const char* str, ButtonState* s, int v, Painter* out ) : TextButton(str, s, (void*)v, out) { Init(); } RadioButton::RadioButton ( const char* str, ButtonState* s, void* v, Painter* out ) : TextButton(str, s, v, out) { Init(); } void RadioButton::Init () { SetClassName("RadioButton"); } void RadioButton::Reconfig () { MakeBackground(); if (shape->Undefined()) { MakeShape(); shape->width += shape->height + sep; } if (radioMask == nil) { radioMask = new Bitmap( radio_mask_bits, radio_mask_width, radio_mask_height ); radioMask->Reference(); radioPlain = new Bitmap( radio_plain_bits, radio_plain_width, radio_plain_height ); radioPlain->Reference(); radioHit = new Bitmap( radio_hit_bits, radio_hit_width, radio_hit_height ); radioHit->Reference(); radioChosen = new Bitmap( radio_chosen_bits, radio_chosen_width, radio_chosen_height ); radioChosen->Reference(); radioBoth = new Bitmap( radio_both_bits, radio_both_width, radio_both_height ); radioBoth->Reference(); } } void RadioButton::Redraw (Coord x1, Coord y1, Coord x2, Coord y2) { int h = output->GetFont()->Height(); int r = radio_plain_width; output->ClearRect(canvas, x1, y1, x2, y2); Coord tx = r + sep; Coord ty = (ymax + 1 - h) / 2; output->Text(canvas, text, tx, ty); Refresh(); } void RadioButton::Refresh () { Coord x = 0; Coord y = (ymax+1 - radio_plain_height)/2; if (!hit && !chosen) { output->Stencil(canvas, x, y, radioPlain, radioMask); } else if (hit && !chosen) { output->Stencil(canvas, x, y, radioHit, radioMask); } else if (!hit && chosen) { output->Stencil(canvas, x, y, radioChosen, radioMask); } else if (hit && chosen) { output->Stencil(canvas, x, y, radioBoth, radioMask); } if (!enabled) { grayout->FillRect(canvas, 0, 0, xmax, ymax); } } CheckBox::CheckBox ( const char* str, ButtonState* s, int on, int off ) : TextButton(str, s, (void*)on) { Init((void*)off); } CheckBox::CheckBox ( const char* str, ButtonState* s, void* on, void* off ) : TextButton(str, s, on) { Init(off); } CheckBox::CheckBox ( const char* name, const char* str, ButtonState* s, int on, int off ) : TextButton(name, str, s, (void*)on) { Init((void*)off); } CheckBox::CheckBox ( const char* name, const char* str, ButtonState* s, void* on, void* off ) : TextButton(name, str, s, on) { Init(off); } CheckBox::CheckBox ( const char* str, ButtonState* s, int on, int off, Painter* out ) : TextButton(str, s, (void*)on, out) { Init((void*)off); } CheckBox::CheckBox ( const char* str, ButtonState* s, void* on, void* off, Painter* out ) : TextButton(str, s, on, out) { Init(off); } void CheckBox::Init (void* v) { SetClassName("CheckBox"); offvalue = v; } void CheckBox::Reconfig () { MakeBackground(); if (shape->Undefined()) { MakeShape(); shape->width += shape->height + sep; } Update(); } void CheckBox::Press () { if (chosen) { subject->GetValue(value); subject->SetValue(offvalue); } else { subject->SetValue(value); } } void CheckBox::Update () { void* v; subject->GetValue(v); if (v != offvalue) { Choose(); value = v; } else { UnChoose(); } } void CheckBox::Redraw (Coord x1, Coord y1, Coord x2, Coord y2) { int h = output->GetFont()->Height(); int t = HalfCheckBoxSize(h); output->ClearRect(canvas, x1, y1, x2, y2); Coord tx = 2*t + sep; Coord ty = (ymax + 1 - h) / 2; output->Text(canvas, text, tx, ty); Refresh(); } void CheckBox::Refresh () { int h = output->GetFont()->Height(); int t = HalfCheckBoxSize(h); Coord cx = t; Coord cy = (ymax + 1)/2; Coord left = cx - t; Coord right = cx + t; Coord bottom = cy - t; Coord top = cy + t; output->Rect(canvas, left, bottom, right, top); background->FillRect(canvas, left+1, bottom+1, right-1, top-1); if (hit) { output->Rect(canvas, left+1, bottom+1, right-1, top-1); } if (chosen) { output->Line(canvas, left, bottom, right, top); output->Line(canvas, left, top, right, bottom); } if (!enabled) { grayout->FillRect(canvas, 0, 0, xmax, ymax); } }