gui/widgets/listbox.cpp

Go to the documentation of this file.
00001 /* $Id: listbox.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 #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 // in separate namespace to avoid name classes
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 } // namespace
00049 
00050 tlistbox::tlistbox(const bool has_minimum, const bool has_maximum,
00051         const tgenerator_::tplacement placement, const bool select)
00052     : tscrollbar_container(2) // FIXME magic number
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     // Due to the removing from the linked group, don't use
00106     // generator_->clear() directly.
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     // rename this function and can we return a reference??
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; // FIXME test what result should have been!!!
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     /** @todo Hack to capture the keyboard focus. */
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     // Inherited.
00259     tscrollbar_container::place(origin, size);
00260 
00261     /**
00262      * @todo Work-around to set the selected item visible again.
00263      *
00264      * At the moment the listboxes and dialogs in general are resized a lot as
00265      * work-around for sizing. So this function makes the selected item in view
00266      * again. It doesn't work great in all cases but the proper fix is to avoid
00267      * resizing dialogs a lot. Need more work later on.
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         // Calculate new size.
00293         tpoint size = content_grid()->get_size();
00294         size.x += width_modification;
00295         size.y += height_modification;
00296 
00297         // Set new size.
00298         content_grid()->set_size(size);
00299 
00300         // Set status.
00301         need_layout_ = true;
00302         // If the content grows assume it "overwrites" the old content.
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     // Inherited.
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         // When scrolling make sure the new items is visible but leave the
00336         // horizontal scrollbar position.
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         // Inherited.
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         // When scrolling make sure the new items is visible but leave the
00363         // horizontal scrollbar position.
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         // Inherited.
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     // Inherited.
00389     if(handled) {
00390         // When scrolling make sure the new items is visible but leave the
00391         // vertical scrollbar position.
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     // Inherited.
00416     if(handled) {
00417         // When scrolling make sure the new items is visible but leave the
00418         // vertical scrollbar position.
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  * Swaps an item in a grid for another one.*/
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     // Make sure the new child has same id.
00447     widget->set_id(id);
00448 
00449     // Get the container containing the wanted widget.
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     // Replace the child.
00461     widget = parent_grid->swap_child(id, widget, false);
00462     assert(widget);
00463 
00464     delete widget;
00465 }
00466 
00467 } // namespace
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     // "Inherited."
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     /** @todo This function needs more testing. */
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 } // namespace gui2
00526 
00527 #endif
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

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