gui/widgets/scrollbar.cpp

Go to the documentation of this file.
00001 /* $Id: scrollbar.cpp 52533 2012-01-07 02:35:17Z shadowmaster $ */
00002 /*
00003    Copyright (C) 2008 - 2012 by Mark de Wever <koraq@xs4all.nl>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 #define GETTEXT_DOMAIN "wesnoth-lib"
00017 
00018 #include "gui/widgets/scrollbar.hpp"
00019 
00020 #include "foreach.hpp"
00021 #include "gui/auxiliary/log.hpp"
00022 #include "gui/widgets/window.hpp" // Needed for invalidate_layout()
00023 
00024 #include <boost/bind.hpp>
00025 
00026 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
00027 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
00028 
00029 namespace gui2 {
00030 
00031 tscrollbar_::tscrollbar_()
00032     : tcontrol(COUNT)
00033     , state_(ENABLED)
00034     , item_count_(0)
00035     , item_position_(0)
00036     , visible_items_(1)
00037     , step_size_(1)
00038     , pixels_per_step_(0.0)
00039     , mouse_(0, 0)
00040     , positioner_offset_(0)
00041     , positioner_length_(0)
00042 {
00043     connect_signal<event::MOUSE_ENTER>(boost::bind(
00044                 &tscrollbar_::signal_handler_mouse_enter, this, _2, _3, _4));
00045     connect_signal<event::MOUSE_MOTION>(boost::bind(
00046                   &tscrollbar_::signal_handler_mouse_motion
00047                 , this
00048                 , _2
00049                 , _3
00050                 , _4
00051                 , _5));
00052     connect_signal<event::MOUSE_LEAVE>(boost::bind(
00053                 &tscrollbar_::signal_handler_mouse_leave, this, _2, _3));
00054     connect_signal<event::LEFT_BUTTON_DOWN>(boost::bind(
00055                 &tscrollbar_::signal_handler_left_button_down, this, _2, _3));
00056     connect_signal<event::LEFT_BUTTON_UP>(boost::bind(
00057                 &tscrollbar_::signal_handler_left_button_up, this, _2, _3));
00058 }
00059 
00060 void tscrollbar_::scroll(const tscroll scroll)
00061 {
00062     switch(scroll) {
00063         case BEGIN :
00064             set_item_position(0);
00065             break;
00066 
00067         case ITEM_BACKWARDS :
00068             if(item_position_) {
00069                 set_item_position(item_position_ - 1);
00070             }
00071             break;
00072 
00073         case HALF_JUMP_BACKWARDS :
00074             set_item_position(item_position_ > (visible_items_ / 2) ?
00075                 item_position_ - (visible_items_ / 2) : 0);
00076             break;
00077 
00078         case JUMP_BACKWARDS :
00079             set_item_position(item_position_ > visible_items_ ?
00080                 item_position_ - visible_items_  : 0);
00081             break;
00082 
00083         case END :
00084             set_item_position(item_count_ - 1);
00085             break;
00086 
00087         case ITEM_FORWARD :
00088             set_item_position(item_position_ + 1);
00089             break;
00090 
00091         case HALF_JUMP_FORWARD :
00092             set_item_position(item_position_ +  (visible_items_ / 2));
00093             break;
00094 
00095         case JUMP_FORWARD :
00096             set_item_position(item_position_ +  visible_items_ );
00097             break;
00098 
00099         default :
00100             assert(false);
00101         }
00102 }
00103 
00104 void tscrollbar_::place(const tpoint& origin, const tpoint& size)
00105 {
00106     // Inherited.
00107     tcontrol::place(origin, size);
00108 
00109     recalculate();
00110 }
00111 
00112 void tscrollbar_::set_item_position(const unsigned item_position)
00113 {
00114     // Set the value always execute since we update a part of the state.
00115     item_position_ = item_position > item_count_ - visible_items_
00116             ? item_count_ - visible_items_
00117             : item_position;
00118 
00119     item_position_ = (item_position_ + step_size_ - 1) / step_size_;
00120 
00121     if(all_items_visible()) {
00122         item_position_ = 0;
00123     }
00124 
00125     // Determine the pixel offset of the item position.
00126     positioner_offset_ = static_cast<unsigned>(item_position_ * pixels_per_step_);
00127 
00128     update_canvas();
00129 
00130     assert(item_position_ <= item_count_ - visible_items_);
00131 }
00132 
00133 void tscrollbar_::update_canvas() {
00134 
00135     foreach(tcanvas& tmp, canvas()) {
00136         tmp.set_variable("positioner_offset", variant(positioner_offset_));
00137         tmp.set_variable("positioner_length", variant(positioner_length_));
00138     }
00139     set_dirty();
00140 }
00141 
00142 void tscrollbar_::set_state(const tstate state)
00143 {
00144     if(state != state_) {
00145         state_ = state;
00146         set_dirty(true);
00147     }
00148 }
00149 
00150 void tscrollbar_::recalculate()
00151 {
00152     // We can be called before the size has been set up in that case we can't do
00153     // the proper recalcultion so stop before we die with an assert.
00154     if(!get_length()) {
00155         return;
00156     }
00157 
00158     // Get the available size for the slider to move.
00159     const int available_length =
00160         get_length() - offset_before() - offset_after();
00161 
00162     assert(available_length > 0);
00163 
00164     // All visible.
00165     if(item_count_ <= visible_items_) {
00166         positioner_offset_ = offset_before();
00167         positioner_length_ = available_length;
00168         recalculate_positioner();
00169         item_position_ = 0;
00170         update_canvas();
00171         return;
00172     }
00173 
00174     /**
00175      * @todo In the MP lobby it can happen that a listbox has first zero items,
00176      * then gets filled and since there are no visible items the second assert
00177      * after this block will be triggered. Use this ugly hack to avoid that
00178      * case. (This hack also added the gui/widgets/window.hpp include.)
00179      */
00180     if(!visible_items_) {
00181         twindow* window = get_window();
00182         assert(window);
00183         window->invalidate_layout();
00184         ERR_GUI_G << LOG_HEADER
00185                 << " Can't recalculate size, force a window layout phase.\n";
00186         return;
00187     }
00188 
00189     assert(step_size_);
00190     assert(visible_items_);
00191 
00192     const unsigned steps = (item_count_ - visible_items_ + step_size_ - 1) / step_size_;
00193 
00194     positioner_length_ = available_length * visible_items_ / item_count_;
00195     recalculate_positioner();
00196 
00197     // Make sure we can also show the last step, so add one more step.
00198     pixels_per_step_ =
00199         (available_length - positioner_length_)
00200         / static_cast<float>(steps + 1);
00201 
00202     set_item_position(item_position_);
00203 #if 0
00204     std::cerr << "Scrollbar recalculate overview:\n"
00205         << "item_count_ " << item_count_
00206         << " visible_items_ " << visible_items_
00207         << " step_size_ " << step_size_
00208         << " steps " << steps
00209         << "\n"
00210         << "minimum_positioner_length() " << minimum_positioner_length()
00211         << " maximum_positioner_length() " << maximum_positioner_length()
00212         << "\n"
00213         << " positioner_length_ " << positioner_length_
00214         << " positioner_offset_ " << positioner_offset_
00215         << "\n"
00216         << "available_length " << available_length
00217         << " pixels_per_step_ " << pixels_per_step_
00218         << ".\n\n";
00219 #endif
00220 }
00221 
00222 void tscrollbar_::recalculate_positioner()
00223 {
00224     const unsigned minimum = minimum_positioner_length();
00225     const unsigned maximum = maximum_positioner_length();
00226 
00227     if(minimum == maximum) {
00228         positioner_length_ = maximum;
00229     } else if(maximum != 0 && positioner_length_ > maximum) {
00230         positioner_length_ = maximum;
00231     } else if(positioner_length_ < minimum) {
00232         positioner_length_ = minimum;
00233     }
00234 }
00235 
00236 void tscrollbar_::move_positioner(const int distance)
00237 {
00238     if(distance < 0 && -distance > static_cast<int>(positioner_offset_)) {
00239         positioner_offset_ = 0;
00240     } else {
00241         positioner_offset_ += distance;
00242     }
00243 
00244     const unsigned length = get_length() - offset_before() - offset_after() - positioner_length_;
00245 
00246     if(positioner_offset_ > length) {
00247         positioner_offset_ = length;
00248     }
00249 
00250     unsigned position =
00251         static_cast<unsigned>(positioner_offset_ / pixels_per_step_);
00252 
00253     // Note due to floating point rounding the position might be outside the
00254     // available positions so set it back.
00255     if(position > item_count_ - visible_items_) {
00256         position = item_count_ - visible_items_;
00257     }
00258 
00259     if(position != item_position_) {
00260         item_position_ = position;
00261 
00262         child_callback_positioner_moved();
00263 
00264         fire(event::NOTIFY_MODIFIED, *this, NULL);
00265 
00266 //      positioner_moved_notifier_.notify();
00267     }
00268 #if 0
00269     std::cerr << "Scrollbar move overview:\n"
00270         << "item_count_ " << item_count_
00271         << " visible_items_ " << visible_items_
00272         << " step_size_ " << step_size_
00273         << "\n"
00274         << "minimum_positioner_length() " << minimum_positioner_length()
00275         << " maximum_positioner_length() " << maximum_positioner_length()
00276         << "\n"
00277         << " positioner_length_ " << positioner_length_
00278         << " positioner_offset_ " << positioner_offset_
00279         << "\n"
00280         << " pixels_per_step_ " << pixels_per_step_
00281         << " item_position_ " << item_position_
00282         << ".\n\n";
00283 #endif
00284     update_canvas();
00285 }
00286 
00287 void tscrollbar_::load_config_extra()
00288 {
00289     // These values won't change so set them here.
00290     foreach(tcanvas& tmp, canvas()) {
00291         tmp.set_variable("offset_before", variant(offset_before()));
00292         tmp.set_variable("offset_after", variant(offset_after()));
00293     }
00294 }
00295 
00296 void tscrollbar_::signal_handler_mouse_enter(
00297         const event::tevent event, bool& handled, bool& halt)
00298 {
00299     DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
00300 
00301     // Send the motion under our event id to make debugging easier.
00302     signal_handler_mouse_motion(event, handled, halt, get_mouse_position());
00303 }
00304 
00305 void tscrollbar_::signal_handler_mouse_motion(
00306           const event::tevent event
00307         , bool& handled
00308         , bool& halt
00309         , const tpoint& coordinate)
00310 {
00311     DBG_GUI_E << LOG_HEADER << ' ' << event << " at " << coordinate << ".\n";
00312 
00313     tpoint mouse = coordinate;
00314     mouse.x -= get_x();
00315     mouse.y -= get_y();
00316 
00317     switch(state_) {
00318         case ENABLED :
00319             if(on_positioner(mouse)) {
00320                 set_state(FOCUSSED);
00321             }
00322 
00323             break;
00324 
00325         case PRESSED : {
00326                 const int distance = get_length_difference(mouse_, mouse);
00327                 mouse_ = mouse;
00328                 move_positioner(distance);
00329             }
00330             break;
00331 
00332         case FOCUSSED :
00333             if(!on_positioner(mouse)) {
00334                 set_state(ENABLED);
00335             }
00336             break;
00337 
00338         case DISABLED :
00339             // Shouldn't be possible, but seems to happen in the lobby
00340             // if a resize layout happens during dragging.
00341             halt = true;
00342             break;
00343 
00344         default :
00345             assert(false);
00346     }
00347     handled = true;
00348 }
00349 
00350 void tscrollbar_::signal_handler_mouse_leave(
00351         const event::tevent event, bool& handled)
00352 {
00353     DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
00354 
00355     if(state_ == FOCUSSED) {
00356         set_state(ENABLED);
00357     }
00358     handled = true;
00359 }
00360 
00361 
00362 void tscrollbar_::signal_handler_left_button_down(
00363         const event::tevent event, bool& handled)
00364 {
00365     DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
00366 
00367     tpoint mouse = get_mouse_position();
00368     mouse.x -= get_x();
00369     mouse.y -= get_y();
00370 
00371     if(on_positioner(mouse)) {
00372         assert(get_window());
00373         mouse_ = mouse;
00374         get_window()->mouse_capture();
00375         set_state(PRESSED);
00376     }
00377 
00378     const int bar = on_bar(mouse);
00379 
00380     if(bar == -1) {
00381         scroll(HALF_JUMP_BACKWARDS);
00382         fire(event::NOTIFY_MODIFIED, *this, NULL);
00383 //      positioner_moved_notifier_.notify();
00384     } else if(bar == 1) {
00385         scroll(HALF_JUMP_FORWARD);
00386         fire(event::NOTIFY_MODIFIED, *this, NULL);
00387 //      positioner_moved_notifier_.notify();
00388     } else {
00389         assert(bar == 0);
00390     }
00391 
00392     handled = true;
00393 }
00394 
00395 void tscrollbar_::signal_handler_left_button_up(
00396         const event::tevent event, bool& handled)
00397 {
00398     DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
00399 
00400     tpoint mouse = get_mouse_position();
00401     mouse.x -= get_x();
00402     mouse.y -= get_y();
00403 
00404     if(state_ != PRESSED) {
00405         return;
00406     }
00407 
00408     assert(get_window());
00409     get_window()->mouse_capture(false);
00410 
00411     if(on_positioner(mouse)) {
00412         set_state(FOCUSSED);
00413     } else {
00414         set_state(ENABLED);
00415     }
00416 
00417     handled = true;
00418 }
00419 
00420 } // namespace gui2
00421 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:02:59 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs