gui/widgets/control.cpp

Go to the documentation of this file.
00001 /* $Id: control.cpp 54220 2012-05-19 08:46:20Z mordante $ */
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 "control.hpp"
00019 
00020 #include "font.hpp"
00021 #include "foreach.hpp"
00022 #include "formula_string_utils.hpp"
00023 #include "gui/auxiliary/iterator/walker_widget.hpp"
00024 #include "gui/auxiliary/log.hpp"
00025 #include "gui/auxiliary/event/message.hpp"
00026 #include "gui/dialogs/tip.hpp"
00027 #include "gui/widgets/settings.hpp"
00028 #include "gui/widgets/window.hpp"
00029 #include "gui/auxiliary/window_builder/control.hpp"
00030 #include "marked-up_text.hpp"
00031 
00032 #include <boost/bind.hpp>
00033 
00034 #include <iomanip>
00035 
00036 #define LOG_SCOPE_HEADER "tcontrol(" + get_control_type() + ") [" \
00037         + id() + "] " + __func__
00038 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
00039 
00040 namespace gui2 {
00041 
00042 tcontrol::tcontrol(const unsigned canvas_count)
00043     : definition_("default")
00044     , label_()
00045     , use_markup_(false)
00046     , use_tooltip_on_label_overflow_(true)
00047     , tooltip_()
00048     , help_message_()
00049     , canvas_(canvas_count)
00050     , config_(NULL)
00051     , renderer_()
00052     , text_maximum_width_(0)
00053     , text_alignment_(PANGO_ALIGN_LEFT)
00054     , shrunken_(false)
00055 {
00056     connect_signal<event::SHOW_TOOLTIP>(boost::bind(
00057               &tcontrol::signal_handler_show_tooltip
00058             , this
00059             , _2
00060             , _3
00061             , _5));
00062 
00063     connect_signal<event::SHOW_HELPTIP>(boost::bind(
00064               &tcontrol::signal_handler_show_helptip
00065             , this
00066             , _2
00067             , _3
00068             , _5));
00069 
00070     connect_signal<event::NOTIFY_REMOVE_TOOLTIP>(boost::bind(
00071               &tcontrol::signal_handler_notify_remove_tooltip
00072             , this
00073             , _2
00074             , _3));
00075 }
00076 
00077 tcontrol::tcontrol(
00078           const implementation::tbuilder_control& builder
00079         , const unsigned canvas_count
00080         , const std::string& control_type)
00081     : twidget(builder)
00082     , definition_(builder.definition)
00083     , label_(builder.label)
00084     , use_markup_(false)
00085     , use_tooltip_on_label_overflow_(builder.use_tooltip_on_label_overflow)
00086     , tooltip_(builder.tooltip)
00087     , help_message_(builder.help)
00088     , canvas_(canvas_count)
00089     , config_(NULL)
00090     , renderer_()
00091     , text_maximum_width_(0)
00092     , text_alignment_(PANGO_ALIGN_LEFT)
00093     , shrunken_(false)
00094 {
00095     definition_load_configuration(control_type);
00096 
00097     connect_signal<event::SHOW_TOOLTIP>(boost::bind(
00098               &tcontrol::signal_handler_show_tooltip
00099             , this
00100             , _2
00101             , _3
00102             , _5));
00103 
00104     connect_signal<event::SHOW_HELPTIP>(boost::bind(
00105               &tcontrol::signal_handler_show_helptip
00106             , this
00107             , _2
00108             , _3
00109             , _5));
00110 
00111     connect_signal<event::NOTIFY_REMOVE_TOOLTIP>(boost::bind(
00112               &tcontrol::signal_handler_notify_remove_tooltip
00113             , this
00114             , _2
00115             , _3));
00116 }
00117 
00118 void tcontrol::set_members(const string_map& data)
00119 {
00120     /** @todo document this feature on the wiki. */
00121     /** @todo do we need to add the debug colors here as well? */
00122     string_map::const_iterator itor = data.find("id");
00123     if(itor != data.end()) {
00124         set_id(itor->second);
00125     }
00126 
00127     itor = data.find("linked_group");
00128     if(itor != data.end()) {
00129         set_linked_group(itor->second);
00130     }
00131 
00132     itor = data.find("label");
00133     if(itor != data.end()) {
00134         set_label(itor->second);
00135     }
00136 
00137     itor = data.find("tooltip");
00138     if(itor != data.end()) {
00139         set_tooltip(itor->second);
00140     }
00141 
00142     itor = data.find("help");
00143     if(itor != data.end()) {
00144         set_help_message(itor->second);
00145     }
00146 
00147     itor = data.find("use_markup");
00148     if(itor != data.end()) {
00149         set_use_markup(utils::string_bool(itor->second));
00150     }
00151 }
00152 
00153 bool tcontrol::disable_click_dismiss() const
00154 {
00155     return get_visible() == twidget::VISIBLE && get_active();
00156 }
00157 
00158 iterator::twalker_* tcontrol::create_walker()
00159 {
00160     return new iterator::walker::twidget(*this);
00161 }
00162 
00163 tpoint tcontrol::get_config_minimum_size() const
00164 {
00165     assert(config_);
00166 
00167     tpoint result(config_->min_width, config_->min_height);
00168 
00169     DBG_GUI_L << LOG_HEADER << " result " << result << ".\n";
00170     return result;
00171 }
00172 
00173 tpoint tcontrol::get_config_default_size() const
00174 {
00175     assert(config_);
00176 
00177     tpoint result(config_->default_width, config_->default_height);
00178 
00179     DBG_GUI_L << LOG_HEADER << " result " << result << ".\n";
00180     return result;
00181 }
00182 
00183 tpoint tcontrol::get_config_maximum_size() const
00184 {
00185     assert(config_);
00186 
00187     tpoint result(config_->max_width, config_->max_height);
00188 
00189     DBG_GUI_L << LOG_HEADER << " result " << result << ".\n";
00190     return result;
00191 }
00192 
00193 unsigned tcontrol::get_characters_per_line() const
00194 {
00195     return 0;
00196 }
00197 
00198 void tcontrol::layout_init(const bool full_initialization)
00199 {
00200     // Inherited.
00201     twidget::layout_init(full_initialization);
00202 
00203     if(full_initialization) {
00204         shrunken_ = false;
00205     }
00206 }
00207 
00208 void tcontrol::request_reduce_width(const unsigned maximum_width)
00209 {
00210     assert(config_);
00211 
00212     if(!label_.empty() && can_wrap()) {
00213 
00214         tpoint size = get_best_text_size(
00215                 tpoint(0,0),
00216                 tpoint(maximum_width - config_->text_extra_width, 0));
00217 
00218         size.x += config_->text_extra_width;
00219         size.y += config_->text_extra_height;
00220 
00221         set_layout_size(size);
00222 
00223         DBG_GUI_L << LOG_HEADER
00224                 << " label '" << debug_truncate(label_)
00225                 << "' maximum_width " << maximum_width
00226                 << " result " << size
00227                 << ".\n";
00228 
00229     } else {
00230         DBG_GUI_L << LOG_HEADER
00231                 << " label '" << debug_truncate(label_)
00232                 << "' failed; either no label or wrapping not allowed.\n";
00233     }
00234 }
00235 
00236 tpoint tcontrol::calculate_best_size() const
00237 {
00238     assert(config_);
00239     if(label_.empty()) {
00240         DBG_GUI_L << LOG_HEADER << " empty label return default.\n";
00241         return get_config_default_size();
00242     }
00243 
00244     const tpoint minimum = get_config_default_size();
00245     const tpoint maximum = get_config_maximum_size();
00246 
00247     /**
00248      * @todo The value send should subtract the border size
00249      * and read it after calculation to get the proper result.
00250      */
00251     tpoint result = get_best_text_size(minimum, maximum);
00252     DBG_GUI_L << LOG_HEADER
00253             << " label '" << debug_truncate(label_)
00254             << "' result " << result
00255             << ".\n";
00256     return result;
00257 }
00258 
00259 void tcontrol::place(const tpoint& origin, const tpoint& size)
00260 {
00261     // resize canvasses
00262     foreach(tcanvas& canvas, canvas_) {
00263         canvas.set_width(size.x);
00264         canvas.set_height(size.y);
00265     }
00266 
00267     // Note we assume that the best size has been queried but otherwise it
00268     // should return false.
00269     if(renderer_.is_truncated()
00270             && use_tooltip_on_label_overflow_ && tooltip_.empty()) {
00271 
00272          set_tooltip(label_);
00273     }
00274 
00275     // inherited
00276     twidget::place(origin, size);
00277 
00278     // update the state of the canvas after the sizes have been set.
00279     update_canvas();
00280 }
00281 
00282 void tcontrol::load_config()
00283 {
00284     if(!config()) {
00285 
00286         definition_load_configuration(get_control_type());
00287 
00288         load_config_extra();
00289     }
00290 }
00291 
00292 void tcontrol::set_definition(const std::string& definition)
00293 {
00294     assert(!config());
00295     definition_ = definition;
00296     load_config();
00297     assert(config());
00298 
00299 #ifdef GUI2_EXPERIMENTAL_LISTBOX
00300     init();
00301 #endif
00302 }
00303 
00304 void tcontrol::set_label(const t_string& label)
00305 {
00306     if(label == label_) {
00307         return;
00308     }
00309 
00310     label_ = label;
00311     set_layout_size(tpoint(0, 0));
00312     update_canvas();
00313     set_dirty();
00314 }
00315 
00316 void tcontrol::set_use_markup(bool use_markup)
00317 {
00318     if(use_markup == use_markup_) {
00319         return;
00320     }
00321 
00322     use_markup_ = use_markup;
00323     update_canvas();
00324     set_dirty();
00325 }
00326 
00327 void tcontrol::set_text_alignment(const PangoAlignment text_alignment)
00328 {
00329     if(text_alignment_ == text_alignment) {
00330         return;
00331     }
00332 
00333     text_alignment_ = text_alignment;
00334     update_canvas();
00335     set_dirty();
00336 }
00337 
00338 void tcontrol::update_canvas()
00339 {
00340     const int max_width = get_text_maximum_width();
00341     const int max_height = get_text_maximum_height();
00342 
00343     // set label in canvases
00344     foreach(tcanvas& canvas, canvas_) {
00345         canvas.set_variable("text", variant(label_));
00346         canvas.set_variable("text_markup", variant(use_markup_));
00347         canvas.set_variable("text_alignment"
00348                 , variant(encode_text_alignment(text_alignment_)));
00349         canvas.set_variable("text_maximum_width", variant(max_width));
00350         canvas.set_variable("text_maximum_height", variant(max_height));
00351         canvas.set_variable("text_wrap_mode", variant(can_wrap()
00352             ? PANGO_ELLIPSIZE_NONE : PANGO_ELLIPSIZE_END));
00353         canvas.set_variable(
00354                   "text_characters_per_line"
00355                 , variant(get_characters_per_line()));
00356     }
00357 }
00358 
00359 int tcontrol::get_text_maximum_width() const
00360 {
00361     assert(config_);
00362 
00363     return text_maximum_width_ != 0
00364         ? text_maximum_width_
00365         : get_width() - config_->text_extra_width;
00366 }
00367 
00368 int tcontrol::get_text_maximum_height() const
00369 {
00370     assert(config_);
00371 
00372     return get_height() - config_->text_extra_height;
00373 }
00374 
00375 void tcontrol::impl_draw_background(surface& frame_buffer)
00376 {
00377     DBG_GUI_D << LOG_HEADER
00378             << " label '" << debug_truncate(label_)
00379             << "' size " << get_rect()
00380             << ".\n";
00381 
00382     canvas(get_state()).blit(frame_buffer, get_rect());
00383 }
00384 
00385 void tcontrol::impl_draw_background(
00386           surface& frame_buffer
00387         , int x_offset
00388         , int y_offset)
00389 {
00390     DBG_GUI_D << LOG_HEADER
00391             << " label '" << debug_truncate(label_)
00392             << "' size " << get_rect()
00393             << ".\n";
00394 
00395     canvas(get_state()).blit(
00396               frame_buffer
00397             , calculate_blitting_rectangle(x_offset, y_offset));
00398 }
00399 
00400 void tcontrol::definition_load_configuration(const std::string& control_type)
00401 {
00402     assert(!config());
00403 
00404     set_config(get_control(control_type, definition_));
00405 
00406     assert(canvas().size() == config()->state.size());
00407     for(size_t i = 0; i < canvas().size(); ++i) {
00408         canvas(i) = config()->state[i].canvas;
00409     }
00410 
00411     update_canvas();
00412 }
00413 
00414 tpoint tcontrol::get_best_text_size(
00415           const tpoint& minimum_size
00416         , const tpoint& maximum_size) const
00417 {
00418     log_scope2(log_gui_layout, LOG_SCOPE_HEADER);
00419 
00420     assert(!label_.empty());
00421 
00422     const tpoint border(config_->text_extra_width, config_->text_extra_height);
00423     tpoint size = minimum_size - border;
00424 
00425     renderer_.set_text(label_, use_markup_);
00426 
00427     renderer_.set_font_size(config_->text_font_size);
00428     renderer_.set_font_style(config_->text_font_style);
00429     renderer_.set_alignment(text_alignment_);
00430 
00431     // Try with the minimum wanted size.
00432     const int maximum_width = text_maximum_width_ != 0
00433             ? text_maximum_width_
00434             : maximum_size.x;
00435 
00436     renderer_.set_maximum_width(maximum_width);
00437 
00438     if(can_wrap()) {
00439         renderer_.set_ellipse_mode(PANGO_ELLIPSIZE_NONE);
00440     }
00441 
00442     renderer_.set_characters_per_line(get_characters_per_line());
00443     if(get_characters_per_line() != 0 && !can_wrap()) {
00444         WRN_GUI_L << LOG_HEADER
00445                 << " Limited the number of characters per line, "
00446                 << "but wrapping is not set, output may not be as expected.\n";
00447     }
00448 
00449     DBG_GUI_L << LOG_HEADER
00450             << " label '" << debug_truncate(label_)
00451             << "' status: "
00452             << " minimum_size " << minimum_size
00453             << " maximum_size " << maximum_size
00454             << " text_maximum_width_ " << text_maximum_width_
00455             << " can_wrap " << can_wrap()
00456             << " characters_per_line " << get_characters_per_line()
00457             << " truncated " << renderer_.is_truncated()
00458             << " renderer size " << renderer_.get_size()
00459             << ".\n";
00460 
00461     // If doesn't fit try the maximum.
00462     if(renderer_.is_truncated() && !can_wrap()) {
00463         // FIXME if maximum size is defined we should look at that
00464         // but also we don't adjust for the extra text space yet!!!
00465         const tpoint maximum_size(config_->max_width, config_->max_height);
00466         renderer_.set_maximum_width(maximum_size.x
00467                 ? maximum_size.x - border.x
00468                 : -1);
00469     }
00470 
00471     size = renderer_.get_size() + border;
00472 
00473     if(size.x < minimum_size.x) {
00474         size.x = minimum_size.x;
00475     }
00476 
00477     if(size.y < minimum_size.y) {
00478         size.y = minimum_size.y;
00479     }
00480 
00481     DBG_GUI_L << LOG_HEADER
00482             << " label '" << debug_truncate(label_)
00483             << "' result " << size
00484             << ".\n";
00485     return size;
00486 }
00487 
00488 void tcontrol::signal_handler_show_tooltip(
00489           const event::tevent event
00490         , bool& handled
00491         , const tpoint& location)
00492 {
00493     DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
00494 
00495     if(!tooltip_.empty()) {
00496         std::string tip = tooltip_;
00497         if(!help_message_.empty()) {
00498             utils::string_map symbols;
00499             symbols["hotkey"] =
00500                     hotkey::get_hotkey(hotkey::GLOBAL__HELPTIP).get_name();
00501 
00502             tip = tooltip_ + utils::interpolate_variables_into_string(
00503                       settings::has_helptip_message
00504                     , &symbols);
00505         }
00506 
00507         event::tmessage_show_tooltip message(tip, location);
00508         handled = fire(event::MESSAGE_SHOW_TOOLTIP, *this, message);
00509     }
00510 }
00511 
00512 void tcontrol::signal_handler_show_helptip(
00513           const event::tevent event
00514         , bool& handled
00515         , const tpoint& location)
00516 {
00517     DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
00518 
00519     if(!help_message_.empty()) {
00520         event::tmessage_show_helptip message(help_message_, location);
00521         handled = fire(event::MESSAGE_SHOW_HELPTIP, *this, message);
00522     }
00523 }
00524 
00525 void tcontrol::signal_handler_notify_remove_tooltip(
00526           const event::tevent event
00527         , bool& handled)
00528 {
00529     DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
00530 
00531     /*
00532      * This makes the class know the tip code rather intimately. An
00533      * alternative is to add a message to the window to remove the tip.
00534      * Might be done later.
00535      */
00536     tip::remove();
00537 
00538     handled = true;
00539 }
00540 
00541 } // namespace gui2
00542 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

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