00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #ifndef GUI2_EXPERIMENTAL_LISTBOX
00017
00018 #define GETTEXT_DOMAIN "wesnoth-lib"
00019
00020 #include "gui/widgets/listbox.hpp"
00021
00022 #include "gui/auxiliary/layout_exception.hpp"
00023 #include "gui/auxiliary/log.hpp"
00024 #include "gui/auxiliary/widget_definition/listbox.hpp"
00025 #include "gui/auxiliary/window_builder/listbox.hpp"
00026 #include "gui/auxiliary/window_builder/horizontal_listbox.hpp"
00027 #include "gui/widgets/settings.hpp"
00028 #include "gui/widgets/window.hpp"
00029
00030 #include <boost/bind.hpp>
00031
00032 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
00033 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
00034
00035 namespace gui2 {
00036
00037 REGISTER_WIDGET(listbox)
00038
00039 namespace {
00040
00041 REGISTER_WIDGET3(tlistbox_definition, horizontal_listbox, _4)
00042
00043 void callback_list_item_clicked(twidget* caller)
00044 {
00045 get_parent<tlistbox>(caller)->list_item_clicked(caller);
00046 }
00047
00048 }
00049
00050 tlistbox::tlistbox(const bool has_minimum, const bool has_maximum,
00051 const tgenerator_::tplacement placement, const bool select)
00052 : tscrollbar_container(2)
00053 , generator_(NULL)
00054 , list_builder_(NULL)
00055 , callback_value_changed_(NULL)
00056 , need_layout_(false)
00057 {
00058 generator_ = tgenerator_::build(
00059 has_minimum, has_maximum, placement, select);
00060 }
00061
00062 void tlistbox::add_row(const string_map& item, const int index)
00063 {
00064 assert(generator_);
00065 generator_->create_item(
00066 index, list_builder_, item, callback_list_item_clicked);
00067 }
00068
00069 void tlistbox::add_row(
00070 const std::map<std::string /* widget id */, string_map>& data
00071 , const int index)
00072 {
00073 assert(generator_);
00074 generator_->create_item(
00075 index, list_builder_, data, callback_list_item_clicked);
00076 }
00077
00078 void tlistbox::remove_row(const unsigned row, unsigned count)
00079 {
00080 assert(generator_);
00081
00082 if(row >= get_item_count()) {
00083 return;
00084 }
00085
00086 if(!count || count > get_item_count()) {
00087 count = get_item_count();
00088 }
00089
00090 int height_reduced = 0;
00091 for(; count; --count) {
00092 if(generator_->item(row).get_visible() != INVISIBLE) {
00093 height_reduced += generator_->item(row).get_height();
00094 }
00095 generator_->delete_item(row);
00096 }
00097
00098 if(height_reduced != 0) {
00099 resize_content(0, -height_reduced);
00100 }
00101 }
00102
00103 void tlistbox::clear()
00104 {
00105
00106
00107 remove_row(0, 0);
00108 }
00109
00110 unsigned tlistbox::get_item_count() const
00111 {
00112 assert(generator_);
00113 return generator_->get_item_count();
00114 }
00115
00116 void tlistbox::set_row_active(const unsigned row, const bool active)
00117 {
00118 assert(generator_);
00119 generator_->item(row).set_active(active);
00120 }
00121
00122 void tlistbox::set_row_shown(const unsigned row, const bool shown)
00123 {
00124 assert(generator_);
00125
00126 twindow *window = get_window();
00127 assert(window);
00128
00129 const int selected_row = get_selected_row();
00130
00131 bool resize_needed;
00132 {
00133 twindow::tinvalidate_layout_blocker invalidate_layout_blocker(*window);
00134
00135 generator_->set_item_shown(row, shown);
00136 generator_->place(generator_->get_origin()
00137 , generator_->calculate_best_size());
00138 resize_needed = !content_resize_request();
00139 }
00140
00141 if(resize_needed) {
00142 window->invalidate_layout();
00143 } else {
00144 content_grid_->set_visible_area(content_visible_area());
00145 set_dirty();
00146 }
00147
00148 if(selected_row != get_selected_row() && callback_value_changed_) {
00149 callback_value_changed_(this);
00150 }
00151 }
00152
00153 void tlistbox::set_row_shown(const std::vector<bool>& shown)
00154 {
00155 assert(generator_);
00156 assert(shown.size() == get_item_count());
00157
00158 twindow *window = get_window();
00159 assert(window);
00160
00161 const int selected_row = get_selected_row();
00162
00163 bool resize_needed;
00164 {
00165 twindow::tinvalidate_layout_blocker invalidate_layout_blocker(*window);
00166
00167 for(size_t i = 0; i < shown.size(); ++i) {
00168 generator_->set_item_shown(i, shown[i]);
00169 }
00170 generator_->place(generator_->get_origin()
00171 , generator_->calculate_best_size());
00172 resize_needed = !content_resize_request();
00173 }
00174
00175 if(resize_needed) {
00176 window->invalidate_layout();
00177 } else {
00178 content_grid_->set_visible_area(content_visible_area());
00179 set_dirty();
00180 }
00181
00182 if(selected_row != get_selected_row() && callback_value_changed_) {
00183 callback_value_changed_(this);
00184 }
00185 }
00186
00187 const tgrid* tlistbox::get_row_grid(const unsigned row) const
00188 {
00189 assert(generator_);
00190
00191 return &generator_->item(row);
00192 }
00193
00194 tgrid* tlistbox::get_row_grid(const unsigned row)
00195 {
00196 assert(generator_);
00197 return &generator_->item(row);
00198 }
00199
00200 bool tlistbox::select_row(const unsigned row, const bool select)
00201 {
00202 assert(generator_);
00203
00204 generator_->select_item(row, select);
00205
00206 return true;
00207 }
00208
00209 int tlistbox::get_selected_row() const
00210 {
00211 assert(generator_);
00212
00213 return generator_->get_selected_item();
00214 }
00215
00216 void tlistbox::list_item_clicked(twidget* caller)
00217 {
00218 assert(caller);
00219 assert(generator_);
00220
00221
00222 get_window()->keyboard_capture(this);
00223
00224 for(size_t i = 0; i < generator_->get_item_count(); ++i) {
00225
00226 if(generator_->item(i).has_widget(caller)) {
00227 generator_->toggle_item(i);
00228 if(callback_value_changed_) {
00229 callback_value_changed_(this);
00230 }
00231 return;
00232 }
00233 }
00234 assert(false);
00235 }
00236
00237 bool tlistbox::update_content_size()
00238 {
00239 if(get_visible() == twidget::INVISIBLE) {
00240 return true;
00241 }
00242
00243 if(get_size() == tpoint(0, 0)) {
00244 return false;
00245 }
00246
00247 if(content_resize_request(true)) {
00248 content_grid_->set_visible_area(content_visible_area());
00249 set_dirty();
00250 return true;
00251 }
00252
00253 return false;
00254 }
00255
00256 void tlistbox::place(const tpoint& origin, const tpoint& size)
00257 {
00258
00259 tscrollbar_container::place(origin, size);
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269 const int selected_item = generator_->get_selected_item();
00270 if(selected_item != -1) {
00271 const SDL_Rect& visible = content_visible_area();
00272 SDL_Rect rect = generator_->item(selected_item).get_rect();
00273
00274 rect.x = visible.x;
00275 rect.w = visible.w;
00276
00277 show_content_rect(rect);
00278 }
00279 }
00280
00281 void tlistbox::resize_content(
00282 const int width_modification
00283 , const int height_modification)
00284 {
00285 DBG_GUI_L << LOG_HEADER << " current size " << content_grid()->get_size()
00286 << " width_modification " << width_modification
00287 << " height_modification " << height_modification
00288 << ".\n";
00289
00290 if(content_resize_request(width_modification, height_modification)) {
00291
00292
00293 tpoint size = content_grid()->get_size();
00294 size.x += width_modification;
00295 size.y += height_modification;
00296
00297
00298 content_grid()->set_size(size);
00299
00300
00301 need_layout_ = true;
00302
00303 if(width_modification < 0 || height_modification < 0) {
00304 set_dirty();
00305 }
00306 DBG_GUI_L << LOG_HEADER << " succeeded.\n";
00307 } else {
00308 DBG_GUI_L << LOG_HEADER << " failed.\n";
00309 }
00310 }
00311
00312 void tlistbox::layout_children()
00313 {
00314 layout_children(false);
00315 }
00316
00317 void tlistbox::child_populate_dirty_list(twindow& caller,
00318 const std::vector<twidget*>& call_stack)
00319 {
00320
00321 tscrollbar_container::child_populate_dirty_list(caller, call_stack);
00322
00323 assert(generator_);
00324 std::vector<twidget*> child_call_stack = call_stack;
00325 generator_->populate_dirty_list(caller, child_call_stack);
00326 }
00327
00328 void tlistbox::handle_key_up_arrow(SDLMod modifier, bool& handled)
00329 {
00330 assert(generator_);
00331
00332 generator_->handle_key_up_arrow(modifier, handled);
00333
00334 if(handled) {
00335
00336
00337 const SDL_Rect& visible = content_visible_area();
00338 SDL_Rect rect = generator_->item(
00339 generator_->get_selected_item()).get_rect();
00340
00341 rect.x = visible.x;
00342 rect.w = visible.w;
00343
00344 show_content_rect(rect);
00345
00346 if(callback_value_changed_) {
00347 callback_value_changed_(this);
00348 }
00349 } else {
00350
00351 tscrollbar_container::handle_key_up_arrow(modifier, handled);
00352 }
00353 }
00354
00355 void tlistbox::handle_key_down_arrow(SDLMod modifier, bool& handled)
00356 {
00357 assert(generator_);
00358
00359 generator_->handle_key_down_arrow(modifier, handled);
00360
00361 if(handled) {
00362
00363
00364 const SDL_Rect& visible = content_visible_area();
00365 SDL_Rect rect = generator_->item(
00366 generator_->get_selected_item()).get_rect();
00367
00368 rect.x = visible.x;
00369 rect.w = visible.w;
00370
00371 show_content_rect(rect);
00372
00373 if(callback_value_changed_) {
00374 callback_value_changed_(this);
00375 }
00376 } else {
00377
00378 tscrollbar_container::handle_key_up_arrow(modifier, handled);
00379 }
00380 }
00381
00382 void tlistbox::handle_key_left_arrow(SDLMod modifier, bool& handled)
00383 {
00384 assert(generator_);
00385
00386 generator_->handle_key_left_arrow(modifier, handled);
00387
00388
00389 if(handled) {
00390
00391
00392 const SDL_Rect& visible = content_visible_area();
00393 SDL_Rect rect = generator_->item(
00394 generator_->get_selected_item()).get_rect();
00395
00396 rect.y = visible.y;
00397 rect.h = visible.h;
00398
00399 show_content_rect(rect);
00400
00401 if(callback_value_changed_) {
00402 callback_value_changed_(this);
00403 }
00404 } else {
00405 tscrollbar_container::handle_key_left_arrow(modifier, handled);
00406 }
00407 }
00408
00409 void tlistbox::handle_key_right_arrow(SDLMod modifier, bool& handled)
00410 {
00411 assert(generator_);
00412
00413 generator_->handle_key_right_arrow(modifier, handled);
00414
00415
00416 if(handled) {
00417
00418
00419 const SDL_Rect& visible = content_visible_area();
00420 SDL_Rect rect = generator_->item(
00421 generator_->get_selected_item()).get_rect();
00422
00423 rect.y = visible.y;
00424 rect.h = visible.h;
00425
00426 show_content_rect(rect);
00427
00428 if(callback_value_changed_) {
00429 callback_value_changed_(this);
00430 }
00431 } else {
00432 tscrollbar_container::handle_key_left_arrow(modifier, handled);
00433 }
00434 }
00435
00436 namespace {
00437
00438
00439
00440 void swap_grid(tgrid* grid,
00441 tgrid* content_grid, twidget* widget, const std::string& id)
00442 {
00443 assert(content_grid);
00444 assert(widget);
00445
00446
00447 widget->set_id(id);
00448
00449
00450 tgrid* parent_grid = NULL;
00451 if(grid) {
00452 parent_grid = find_widget<tgrid>(grid, id, false, false);
00453 }
00454 if(!parent_grid) {
00455 parent_grid = find_widget<tgrid>(content_grid, id, true, false);
00456 }
00457 parent_grid = dynamic_cast<tgrid*>(parent_grid->parent());
00458 assert(parent_grid);
00459
00460
00461 widget = parent_grid->swap_child(id, widget, false);
00462 assert(widget);
00463
00464 delete widget;
00465 }
00466
00467 }
00468
00469 void tlistbox::finalize(
00470 tbuilder_grid_const_ptr header,
00471 tbuilder_grid_const_ptr footer,
00472 const std::vector<string_map>& list_data)
00473 {
00474
00475 tscrollbar_container::finalize_setup();
00476
00477 assert(generator_);
00478
00479 if(header) {
00480 swap_grid(&grid(), content_grid(), header->build(), "_header_grid");
00481 }
00482
00483 if(footer) {
00484 swap_grid(&grid(), content_grid(), footer->build(), "_footer_grid");
00485 }
00486
00487 generator_->create_items(
00488 -1, list_builder_, list_data, callback_list_item_clicked);
00489 swap_grid(NULL, content_grid(), generator_, "_list_grid");
00490
00491 }
00492
00493 void tlistbox::set_content_size(const tpoint& origin, const tpoint& size)
00494 {
00495
00496 assert(content_grid());
00497
00498 const int best_height = content_grid()->get_best_size().y;
00499 const tpoint s(size.x, size.y < best_height ? size.y : best_height);
00500
00501 content_grid()->place(origin, s);
00502 }
00503
00504 void tlistbox::layout_children(const bool force)
00505 {
00506 assert(content_grid());
00507
00508 if(need_layout_ || force) {
00509 content_grid()->place(
00510 content_grid()->get_origin()
00511 , content_grid()->get_size());
00512
00513 content_grid()->set_visible_area(content_visible_area_);
00514
00515 need_layout_ = false;
00516 set_dirty();
00517 }
00518 }
00519
00520 const std::string& tlistbox::get_control_type() const
00521 {
00522 static const std::string type = "listbox";
00523 return type;
00524 }
00525 }
00526
00527 #endif