00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "gui/widgets/listbox.hpp"
00016
00017 #include "foreach.hpp"
00018 #include "gui/widgets/button.hpp"
00019 #include "gui/widgets/helper.hpp"
00020 #include "gui/widgets/scrollbar.hpp"
00021 #include "gui/widgets/spacer.hpp"
00022 #include "gui/widgets/toggle_button.hpp"
00023 #include "log.hpp"
00024
00025 #define DBG_G LOG_STREAM_INDENT(debug, gui)
00026 #define LOG_G LOG_STREAM_INDENT(info, gui)
00027 #define WRN_G LOG_STREAM_INDENT(warn, gui)
00028 #define ERR_G LOG_STREAM_INDENT(err, gui)
00029
00030 #define DBG_G_D LOG_STREAM_INDENT(debug, gui_draw)
00031 #define LOG_G_D LOG_STREAM_INDENT(info, gui_draw)
00032 #define WRN_G_D LOG_STREAM_INDENT(warn, gui_draw)
00033 #define ERR_G_D LOG_STREAM_INDENT(err, gui_draw)
00034
00035 #define DBG_G_E LOG_STREAM_INDENT(debug, gui_event)
00036 #define LOG_G_E LOG_STREAM_INDENT(info, gui_event)
00037 #define WRN_G_E LOG_STREAM_INDENT(warn, gui_event)
00038 #define ERR_G_E LOG_STREAM_INDENT(err, gui_event)
00039
00040 #define DBG_G_P LOG_STREAM_INDENT(debug, gui_parse)
00041 #define LOG_G_P LOG_STREAM_INDENT(info, gui_parse)
00042 #define WRN_G_P LOG_STREAM_INDENT(warn, gui_parse)
00043 #define ERR_G_P LOG_STREAM_INDENT(err, gui_parse)
00044
00045 namespace gui2 {
00046
00047 static tlistbox* get_listbox(twidget* widget)
00048 {
00049 do {
00050 widget = widget->parent();
00051
00052 } while (widget && !dynamic_cast<tlistbox*>(widget));
00053
00054 tlistbox* listbox = dynamic_cast<tlistbox*>(widget);
00055 assert(listbox);
00056 return listbox;
00057 }
00058
00059 static void callback_select_list_item(twidget* caller)
00060 {
00061 get_listbox(caller)->list_item_selected(caller);
00062 }
00063
00064 static void callback_scrollbar(twidget* caller)
00065 {
00066 get_listbox(caller)->scrollbar_moved(caller);
00067 }
00068
00069 static void callback_scrollbar_button(twidget* caller)
00070 {
00071 get_listbox(caller)->scrollbar_click(caller);
00072 }
00073
00074 tlistbox::tlistbox() :
00075 tcontainer_(COUNT),
00076 state_(ENABLED),
00077 list_builder_(0),
00078 assume_fixed_row_size_(true),
00079 selected_row_(-1),
00080 selection_count_(0),
00081 row_select_(true),
00082 must_select_(true),
00083 multi_select_(false),
00084 list_rect_(),
00085 list_background_(),
00086 best_spacer_size_(0, 0),
00087 rows_()
00088 {
00089 }
00090
00091 void tlistbox::list_item_selected(twidget* caller)
00092 {
00093 for(unsigned i = 0; i < scrollbar()->get_visible_items(); ++i) {
00094
00095 const unsigned row = i + scrollbar()->get_item_position();
00096
00097 assert(rows_[row].grid());
00098 if(rows_[row].grid()->has_widget(caller)) {
00099
00100 if(!select_row(row, !rows_[row].get_selected())) {
00101
00102 tselectable_* selectable = dynamic_cast<tselectable_*>(caller);
00103 assert(selectable);
00104 selectable->set_selected();
00105 }
00106
00107 return;
00108 }
00109 }
00110
00111
00112 assert(false);
00113 }
00114
00115 void tlistbox::scrollbar_click(twidget* caller)
00116 {
00117 if(caller->id() == "_begin") {
00118 scrollbar()->scroll(tscrollbar_::BEGIN);
00119 } else if(caller->id() == "_line_up") {
00120 scrollbar()->scroll(tscrollbar_::ITEM_BACKWARDS);
00121 } else if(caller->id() == "_half_page_up") {
00122 scrollbar()->scroll(tscrollbar_::HALF_JUMP_BACKWARDS);
00123 } else if(caller->id() == "_page_up") {
00124 scrollbar()->scroll(tscrollbar_::JUMP_BACKWARDS);
00125 } else if(caller->id() == "_end") {
00126 scrollbar()->scroll(tscrollbar_::END);
00127 } else if(caller->id() == "_line_down") {
00128 scrollbar()->scroll(tscrollbar_::ITEM_FORWARD);
00129 } else if(caller->id() == "_half_page_down") {
00130 scrollbar()->scroll(tscrollbar_::HALF_JUMP_FORWARD);
00131 } else if(caller->id() == "_page_down") {
00132 scrollbar()->scroll(tscrollbar_::JUMP_FORWARD);
00133 } else {
00134 assert(false);
00135 }
00136
00137 set_scrollbar_button_status();
00138 }
00139
00140 void tlistbox::finalize_setup()
00141 {
00142
00143 tgrid* list = dynamic_cast<tgrid*>(tcontainer_::find_widget("_list", false));
00144 if(list) {
00145 const unsigned col_count = list->get_cols();
00146 const unsigned row_count = list->get_rows();
00147
00148
00149
00150
00151 for(unsigned row = 0; row < row_count; ++row) {
00152 for(unsigned col = 0; col < col_count; ++col) {
00153 twidget* widget = list->widget(row, col);
00154 assert(widget);
00155
00156
00157 tgrid* grid = dynamic_cast<tgrid*>(widget);
00158 tselectable_* selectable = dynamic_cast<tselectable_*>(widget);
00159
00160 if(selectable) {
00161
00162
00163 ttoggle_button* btn = dynamic_cast<ttoggle_button*>(widget);
00164 assert(btn);
00165
00166 btn->set_callback_mouse_left_click(callback_select_list_item);
00167
00168 } else if(grid) {
00169
00170 assert(false);
00171 } else {
00172 std::cerr << "Widget type " << typeid(*widget).name() << ".\n";
00173 assert(false);
00174 }
00175 }
00176 }
00177 } else {
00178
00179 tspacer* spacer = tlistbox::list();
00180 }
00181
00182 scrollbar()->set_callback_positioner_move(callback_scrollbar);
00183
00184 static std::vector<std::string> button_names;
00185 if(button_names.empty()) {
00186 button_names.push_back("_begin");
00187 button_names.push_back("_line_up");
00188 button_names.push_back("_half_page_up");
00189 button_names.push_back("_page_up");
00190
00191 button_names.push_back("_end");
00192 button_names.push_back("_line_down");
00193 button_names.push_back("_half_page_down");
00194 button_names.push_back("_page_down");
00195 }
00196
00197 foreach(const std::string& name, button_names) {
00198 tbutton* button = dynamic_cast<tbutton*>(tcontainer_::find_widget(name, false));
00199 if(button) {
00200 button->set_callback_mouse_left_click(callback_scrollbar_button);
00201 }
00202 }
00203 }
00204
00205 void tlistbox::set_scrollbar_button_status()
00206 {
00207
00208 static std::vector<std::string> button_up_names;
00209 if(button_up_names.empty()) {
00210 button_up_names.push_back("_begin");
00211 button_up_names.push_back("_line_up");
00212 button_up_names.push_back("_half_page_up");
00213 button_up_names.push_back("_page_up");
00214 }
00215
00216 foreach(const std::string& name, button_up_names) {
00217 tbutton* button = dynamic_cast<tbutton*>(tcontainer_::find_widget(name, false));
00218 if(button) {
00219 button->set_active(!scrollbar()->at_begin());
00220 }
00221 }
00222
00223
00224 static std::vector<std::string> button_down_names;
00225 if(button_down_names.empty()) {
00226 button_down_names.push_back("_end");
00227 button_down_names.push_back("_line_down");
00228 button_down_names.push_back("_half_page_down");
00229 button_down_names.push_back("_page_down");
00230 }
00231
00232 foreach(const std::string& name, button_down_names) {
00233 tbutton* button = dynamic_cast<tbutton*>(tcontainer_::find_widget(name, false));
00234 if(button) {
00235 button->set_active(!scrollbar()->at_end());
00236 }
00237 }
00238
00239
00240 scrollbar()->set_active(!(scrollbar()->at_begin() && scrollbar()->at_end()));
00241 }
00242
00243 tpoint tlistbox::get_best_size() const
00244 {
00245
00246
00247 unsigned width = 0;
00248 unsigned height = 0;
00249
00250 if(best_spacer_size_ != tpoint(0, 0)) {
00251
00252 height = best_spacer_size_.y;
00253 width = best_spacer_size_.x;
00254 } else {
00255
00256 foreach(const trow& row, rows_) {
00257 assert(row.grid());
00258 const tpoint best_size = row.grid()->get_best_size();
00259 width = width >= best_size.x ? width : best_size.x;
00260
00261 height += best_size.y;
00262 }
00263 }
00264
00265
00266
00267
00268
00269 const_cast<tspacer*>(list())->set_best_size(tpoint(width, height));
00270
00271
00272 return tcontainer_::get_best_size();
00273 }
00274
00275 void tlistbox::draw(surface& surface)
00276 {
00277
00278 tcontainer_::draw(surface);
00279
00280
00281 if(!list_background_) {
00282 list_background_.assign(gui2::save_background(surface, list_rect_));
00283 } else {
00284 gui2::restore_background(list_background_, surface, list_rect_);
00285 }
00286
00287
00288 unsigned offset = list_rect_.y;
00289 for(unsigned i = 0; i < scrollbar()->get_visible_items(); ++i) {
00290 trow& row = rows_[i + scrollbar()->get_item_position()];
00291
00292 assert(row.grid());
00293 if(row.grid()->dirty()) {
00294 row.grid()->draw(row.canvas());
00295 }
00296
00297
00298 const SDL_Rect rect = {list_rect_.x, offset, list_rect_.w, row.get_height() };
00299
00300
00301 blit_surface(row.canvas(), 0, surface, &rect);
00302
00303 offset += row.get_height();
00304 }
00305 }
00306
00307 void tlistbox::set_size(const SDL_Rect& rect)
00308 {
00309
00310 assert(best_spacer_size_ == tpoint(0, 0));
00311 SDL_Rect best_rect = rect;
00312 if(rows_.size()) {
00313
00314 const tpoint best_size = get_best_size();
00315
00316 if(best_size.y > rect.h) {
00317 best_spacer_size_ = list()->get_best_size();
00318 best_spacer_size_.y -= (best_size.y - rect.h);
00319 if(assume_fixed_row_size_) {
00320 const unsigned row_height = rows_[0].grid()->get_best_size().y;
00321 const unsigned orig_height = best_spacer_size_.y;
00322 best_spacer_size_.y = (best_spacer_size_.y / row_height) * row_height;
00323 best_rect.h -= (orig_height - best_spacer_size_.y);
00324
00325
00326 get_best_size();
00327 }
00328 }
00329 }
00330
00331
00332 tcontainer_::set_size(best_rect);
00333
00334 best_spacer_size_ = tpoint(0, 0);
00335
00336
00337 tspacer* spacer = list();
00338 list_rect_ = spacer->get_rect();
00339
00340 foreach(trow& row, rows_) {
00341 assert(row.grid());
00342
00343 const unsigned height = row.grid()->get_best_size().y;
00344 row.set_height(height);
00345
00346 row.grid()->set_size(::create_rect(0, 0, list_rect_.w, height));
00347 row.canvas().assign(SDL_CreateRGBSurface(SDL_SWSURFACE,
00348 list_rect_.w, height, 32, 0xFF0000, 0xFF00, 0xFF, 0xFF000000));
00349 }
00350
00351
00352 if(rows_.size() > 0) {
00353 const unsigned rows = list()->get_best_size().y / rows_[0].get_height();
00354 scrollbar()->set_visible_items(rows);
00355 } else {
00356 scrollbar()->set_visible_items(1);
00357 }
00358 set_scrollbar_button_status();
00359 }
00360
00361 twidget* tlistbox::find_widget(const tpoint& coordinate, const bool must_be_active)
00362 {
00363
00364 twidget* result = tcontainer_::find_widget(coordinate, must_be_active);
00365
00366
00367 if(result && result->id() == "_list") {
00368 int offset = coordinate.y - list_rect_.y;
00369 assert(offset >= 0);
00370 for(unsigned i = 0; i < scrollbar()->get_visible_items(); ++i) {
00371
00372 trow& row = rows_[i + scrollbar()->get_item_position()];
00373
00374 if(offset < row.get_height()) {
00375 assert(row.grid());
00376 return row.grid()->find_widget(
00377 tpoint(coordinate.x - list_rect_.x, offset), must_be_active);
00378 } else {
00379 offset -= row.get_height();
00380 }
00381 }
00382 }
00383
00384 return result;
00385 }
00386
00387 const twidget* tlistbox::find_widget(const tpoint& coordinate, const bool must_be_active) const
00388 {
00389
00390 const twidget* result = tcontainer_::find_widget(coordinate, must_be_active);
00391
00392
00393 if(result && result->id() == "_list") {
00394 int offset = coordinate.y - list_rect_.y;
00395 assert(offset >= 0);
00396 for(unsigned i = 0; i < scrollbar()->get_visible_items(); ++i) {
00397
00398 const trow& row = rows_[i + scrollbar()->get_item_position()];
00399
00400 if(offset < row.get_height()) {
00401 assert(row.grid());
00402 return row.grid()->find_widget(
00403 tpoint(coordinate.x - list_rect_.x, offset), must_be_active);
00404 } else {
00405 offset -= row.get_height();
00406 }
00407 }
00408 }
00409
00410 return result;
00411 }
00412
00413 void tlistbox::add_item(const t_string& label)
00414 {
00415 assert(list_builder_);
00416
00417 trow row(*list_builder_, label);
00418 assert(row.grid());
00419
00420 row.grid()->set_parent(this);
00421 rows_.push_back(row);
00422
00423 if(must_select_ && !selection_count_) {
00424 select_row(get_item_count() - 1);
00425 }
00426
00427 scrollbar()->set_item_count(get_item_count());
00428 set_scrollbar_button_status();
00429 }
00430
00431 tscrollbar_* tlistbox::scrollbar()
00432 {
00433
00434 tscrollbar_* result =
00435 dynamic_cast<tscrollbar_*>(tcontainer_::find_widget("_scrollbar", false));
00436 assert(result);
00437 return result;
00438 }
00439
00440 const tscrollbar_* tlistbox::scrollbar() const
00441 {
00442
00443 const tscrollbar_* result =
00444 dynamic_cast<const tscrollbar_*>(tcontainer_::find_widget("_scrollbar", false));
00445 assert(result);
00446 return result;
00447 }
00448
00449 tspacer* tlistbox::list()
00450 {
00451 tspacer* list =
00452 dynamic_cast<tspacer*>(tcontainer_::find_widget("_list", false));
00453 assert(list);
00454 return list;
00455 }
00456
00457 const tspacer* tlistbox::list() const
00458 {
00459 const tspacer* list =
00460 dynamic_cast<const tspacer*>(tcontainer_::find_widget("_list", false));
00461 assert(list);
00462 return list;
00463 }
00464
00465 bool tlistbox::select_row(const unsigned row, const bool select)
00466 {
00467 if(!select && must_select_ && selection_count_ < 2) {
00468 return false;
00469 }
00470
00471 if((select && rows_[row].get_selected())
00472 || (!select && !rows_[row].get_selected())) {
00473 return true;
00474 }
00475
00476 if(select && !multi_select_ && selection_count_ == 1) {
00477 assert(selected_row_ < get_item_count());
00478 rows_[selected_row_].select(false);
00479 --selection_count_;
00480 }
00481
00482 if(select) {
00483 ++selection_count_;
00484 } else {
00485 --selection_count_;
00486 }
00487
00488 assert(row < get_item_count());
00489 selected_row_ = row;
00490 rows_[row].select();
00491
00492 return true;
00493 }
00494
00495 void tlistbox::set_row_active(const unsigned row, const bool active)
00496 {
00497 assert(row < get_item_count());
00498 assert(rows_[row].grid());
00499 rows_[row].grid()->set_active(active);
00500 }
00501
00502 tlistbox::trow::trow(const tbuilder_grid& list_builder_,const t_string& label) :
00503 grid_(dynamic_cast<tgrid*>(list_builder_.build())),
00504 height_(0),
00505 selected_(false)
00506 {
00507 assert(grid_);
00508 init_in_grid(grid_, label);
00509 }
00510
00511 void tlistbox::trow::init_in_grid(tgrid* grid, const t_string& label)
00512 {
00513 for(unsigned row = 0; row < grid->get_rows(); ++row) {
00514 for(unsigned col = 0; col < grid->get_cols(); ++col) {
00515 twidget* widget = grid->widget(row, col);
00516 assert(widget);
00517
00518
00519 tgrid* child_grid = dynamic_cast<tgrid*>(widget);
00520 ttoggle_button* btn = dynamic_cast<ttoggle_button*>(widget);
00521
00522 if(btn) {
00523 btn->set_callback_mouse_left_click(callback_select_list_item);
00524 btn->set_label(label);
00525 } else if(grid) {
00526 init_in_grid(child_grid, label);
00527 } else {
00528 std::cerr << "Widget type " << typeid(*widget).name() << ".\n";
00529 assert(false);
00530 }
00531 }
00532 }
00533 }
00534
00535 void tlistbox::trow::select(const bool sel)
00536 {
00537 selected_ = sel;
00538 assert(grid_);
00539 select_in_grid(grid_, sel);
00540 }
00541
00542 void tlistbox::trow::select_in_grid(tgrid* grid, const bool sel)
00543 {
00544 for(unsigned row = 0; row < grid->get_rows(); ++row) {
00545 for(unsigned col = 0; col < grid->get_cols(); ++col) {
00546 twidget* widget = grid->widget(row, col);
00547 assert(widget);
00548
00549 tgrid* child_grid = dynamic_cast<tgrid*>(widget);
00550 tselectable_* selectable = dynamic_cast<tselectable_*>(widget);
00551
00552 if(selectable) {
00553 selectable->set_selected(sel);
00554 } else if(grid) {
00555 select_in_grid(child_grid, sel);
00556 } else {
00557 std::cerr << "Widget type " << typeid(*widget).name() << ".\n";
00558 assert(false);
00559 }
00560 }
00561 }
00562 }
00563
00564 }
00565
00566