00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
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"
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
00107 tcontrol::place(origin, size);
00108
00109 recalculate();
00110 }
00111
00112 void tscrollbar_::set_item_position(const unsigned item_position)
00113 {
00114
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
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
00153
00154 if(!get_length()) {
00155 return;
00156 }
00157
00158
00159 const int available_length =
00160 get_length() - offset_before() - offset_after();
00161
00162 assert(available_length > 0);
00163
00164
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
00176
00177
00178
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
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
00254
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
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
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
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
00340
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
00384 } else if(bar == 1) {
00385 scroll(HALF_JUMP_FORWARD);
00386 fire(event::NOTIFY_MODIFIED, *this, NULL);
00387
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 }
00421