00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
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
00121
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
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
00249
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
00262 foreach(tcanvas& canvas, canvas_) {
00263 canvas.set_width(size.x);
00264 canvas.set_height(size.y);
00265 }
00266
00267
00268
00269 if(renderer_.is_truncated()
00270 && use_tooltip_on_label_overflow_ && tooltip_.empty()) {
00271
00272 set_tooltip(label_);
00273 }
00274
00275
00276 twidget::place(origin, size);
00277
00278
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
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
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
00462 if(renderer_.is_truncated() && !can_wrap()) {
00463
00464
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
00533
00534
00535
00536 tip::remove();
00537
00538 handled = true;
00539 }
00540
00541 }
00542