// scroldlg.cc - implements scrollable dialog and group classes. // Copyright (C) 2000 Laurynas Biveinis // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // #include "scroldlg.h" #define Uses_TEvent #define Uses_TGroup #define Uses_TKeys #define Uses_TRect #define Uses_TScrollBar #include scrollable_group::scrollable_group(const TRect & bounds) : TGroup(bounds), //scroll_group_init(&init_background), hsb(new TScrollBar(TRect(bounds.a.x + 1, bounds.b.y - 1, bounds.b.x - 1, bounds.b.y))), vsb(new TScrollBar(TRect(bounds.b.x - 1, bounds.a.y + 1, bounds.b.x, bounds.b.y - 1))) { eventMask |= evBroadcast; options |= ofFramed; delta.x = delta.y = limit.x = limit.y = 0; insert(new TBackground(TRect(0, 0, size.x, size.y), ' ')); } void scrollable_group::changeBounds(const TRect & bounds) { lock(); TGroup::changeBounds(bounds); setLimit(limit.x, limit.y); unlock(); drawView(); } static Boolean is_view(TView * view, void * args) { return Boolean(view == args); } void scrollable_group::handleEvent(TEvent& event) { TGroup::handleEvent(event); if(event.what == evBroadcast) { if(event.message.command == cmScrollBarChanged && (event.message.infoPtr == hsb || event.message.infoPtr == vsb)) scrollDraw(); else if(event.message.command == cmReceivedFocus && firstThat(is_view, event.message.infoPtr) != 0) focusSubView((TView*) event.message.infoPtr); } } static void do_scroll(TView * view, void * args) { if (strcmp(view->name, "TBackground")) { TPoint dest = view->origin + (*(TPoint *)args); view->moveTo(dest.x, dest.y); } } void scrollable_group::scrollDraw() { TPoint d; d.x = hsb ? hsb->value : 0; d.y = vsb ? vsb->value : 0; if (d.x != delta.x || d.y != delta.y) { TPoint info; info = delta - d; lock(); forEach(do_scroll, &info); delta = d; unlock(); drawView(); } } void scrollable_group::scrollTo(int x, int y) { lock(); if (hsb && x != hsb->value) hsb->setValue(x); if (vsb && y != vsb->value) vsb->setValue(y); unlock(); scrollDraw(); } void scrollable_group::setLimit(int x, int y) { limit.x = x; limit.y = y; lock(); if (hsb) hsb->setParams(hsb->value, 0, x - size.x, size.x - 1, 1); if (vsb) vsb->setParams(vsb->value, 0, y - size.y, size.y - 1, 1); unlock(); scrollDraw(); } void scrollable_group::setState(ushort aState, Boolean enable) { TGroup::setState(aState, enable); if (aState & (sfActive | sfSelected)) { if (hsb) if (enable) hsb->show(); else hsb->hide(); if (vsb) if(enable) vsb->show(); else vsb->hide(); } } void scrollable_group::focusSubView(TView* view) { TRect rview = view->getBounds(); TRect r = getExtent(); r.intersect(rview); if(r != rview) { int dx, dy; dx = delta.x; if (view->origin.x < 0) dx = delta.x + view->origin.x; else if (view->origin.x + view->size.x > size.x) dx = delta.x+view->origin.x+view->size.x-size.x; dy = delta.y; if(view->origin.y < 0) dy = delta.y + view->origin.y; else if(view->origin.y + view->size.y > size.y) dy = delta.y+view->origin.y+view->size.y-size.y; scrollTo(dx, dy); } } // Returns true if selecting next selectable control in given group // in given direction wraps around - crosses first or last control in a group. static bool next_selectable_wraps(TGroup * where, int forwards) { bool rez = false; if (where->current) { TView * p = where->current; do { if (forwards) p = p->next; else p = p->prev(); if ((where->last == p) || (where->first() == p)) rez = true; } while (!((((p->state & (sfVisible + sfDisabled)) == sfVisible) && (p->options & ofSelectable)) || (p == where->current))); } return rez; } void scroll_dialog::handleEvent(TEvent& event) { // This handles proper switching order between scrollable groups and // normal controls. Note that TVision must be compiled *without* // #define NO_STREAM for this to work - it needs data member 'name'. // Sure, it is possible to acomplish this with RTTI, but that's overkill, if /* ( */ (event.what == evKeyDown) /* && */ { if ((event.keyDown.keyCode == kbTab) || (event.keyDown.keyCode == kbShiftTab)) /* ) */ { select_next(event.keyDown.keyCode == kbShiftTab); clearEvent(event); } else TDialog::handleEvent(event); } TDialog::handleEvent(event); } void scroll_dialog::select_next(int direction) { if (current != scroll_group) { // We are selecting away from a simple control selectNext(direction); // But maybe we have entered the group ? if (current == scroll_group) scroll_group->selectNext(direction); } else { // If selecting next member in a group will wrap around, // deselect the whole group and select next dialog control. if (next_selectable_wraps(scroll_group, direction)) selectNext(direction); else scroll_group->selectNext(direction); } }