mirror of
https://github.com/wesnoth/wesnoth
synced 2025-05-06 11:45:34 +00:00

The old name was proper when there were three modi, now there are only two so the new name is better.
417 lines
9.5 KiB
C++
417 lines
9.5 KiB
C++
/* $Id$ */
|
|
/*
|
|
Copyright (C) 2008 - 2009 by Mark de Wever <koraq@xs4all.nl>
|
|
Part of the Battle for Wesnoth Project http://www.wesnoth.org/
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License version 2
|
|
or at your option any later version.
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY.
|
|
|
|
See the COPYING file for more details.
|
|
*/
|
|
|
|
#define GETTEXT_DOMAIN "wesnoth-lib"
|
|
|
|
#include "control.hpp"
|
|
|
|
#include "font.hpp"
|
|
#include "foreach.hpp"
|
|
#include "gui/auxiliary/log.hpp"
|
|
#include "gui/widgets/window.hpp"
|
|
#include "marked-up_text.hpp"
|
|
|
|
#include <iomanip>
|
|
|
|
namespace gui2 {
|
|
|
|
tcontrol::tcontrol(const unsigned canvas_count)
|
|
: label_()
|
|
, use_markup_(false)
|
|
, use_tooltip_on_label_overflow_(true)
|
|
, tooltip_()
|
|
, help_message_()
|
|
, canvas_(canvas_count)
|
|
, config_(0)
|
|
, renderer_()
|
|
, text_maximum_width_(0)
|
|
, shrunken_(false)
|
|
{
|
|
}
|
|
|
|
void tcontrol::set_members(const string_map& data)
|
|
{
|
|
/** @todo document this feature on the wiki. */
|
|
/** @todo do we need to add the debug colours here as well? */
|
|
string_map::const_iterator itor = data.find("id");
|
|
if(itor != data.end()) {
|
|
set_id(itor->second);
|
|
}
|
|
|
|
itor = data.find("linked_group");
|
|
if(itor != data.end()) {
|
|
set_linked_group(itor->second);
|
|
}
|
|
|
|
itor = data.find("label");
|
|
if(itor != data.end()) {
|
|
set_label(itor->second);
|
|
}
|
|
|
|
itor = data.find("tooltip");
|
|
if(itor != data.end()) {
|
|
set_tooltip(itor->second);
|
|
}
|
|
|
|
itor = data.find("help");
|
|
if(itor != data.end()) {
|
|
set_help_message(itor->second);
|
|
}
|
|
|
|
itor = data.find("use_markup");
|
|
if(itor != data.end()) {
|
|
set_use_markup(utils::string_bool(itor->second));
|
|
}
|
|
}
|
|
|
|
bool tcontrol::disable_click_dismiss() const
|
|
{
|
|
return get_visible() == twidget::VISIBLE && get_active();
|
|
}
|
|
|
|
tpoint tcontrol::get_config_minimum_size() const
|
|
{
|
|
assert(config_);
|
|
|
|
tpoint result(config_->min_width, config_->min_height);
|
|
|
|
DBG_GUI_L << "tcontrol(" + get_control_type() + ") " + __func__ + ":"
|
|
<< " result " << result
|
|
<< ".\n";
|
|
return result;
|
|
}
|
|
|
|
tpoint tcontrol::get_config_default_size() const
|
|
{
|
|
assert(config_);
|
|
|
|
tpoint result(config_->default_width, config_->default_height);
|
|
|
|
DBG_GUI_L << "tcontrol(" + get_control_type() + ") " + __func__ + ":"
|
|
<< " result " << result
|
|
<< ".\n";
|
|
return result;
|
|
}
|
|
|
|
tpoint tcontrol::get_config_maximum_size() const
|
|
{
|
|
assert(config_);
|
|
|
|
tpoint result(config_->max_width, config_->max_height);
|
|
|
|
DBG_GUI_L << "tcontrol(" + get_control_type() + ") " + __func__ + ":"
|
|
<< " result " << result
|
|
<< ".\n";
|
|
return result;
|
|
}
|
|
|
|
void tcontrol::layout_init(const bool full_initialization)
|
|
{
|
|
// Inherited.
|
|
twidget::layout_init(full_initialization);
|
|
|
|
if(full_initialization) {
|
|
shrunken_ = false;
|
|
}
|
|
}
|
|
|
|
void tcontrol::request_reduce_width(const unsigned maximum_width)
|
|
{
|
|
// Inherited.
|
|
twidget::request_reduce_width(maximum_width);
|
|
|
|
assert(config_);
|
|
|
|
if(!label_.empty() && can_wrap()) {
|
|
|
|
tpoint size = get_best_text_size(
|
|
tpoint(0,0),
|
|
tpoint(maximum_width - config_->text_extra_width, 0));
|
|
|
|
size.x += config_->text_extra_width;
|
|
size.y += config_->text_extra_height;
|
|
|
|
set_layout_size(size);
|
|
|
|
DBG_GUI_L << "tcontrol(" + get_control_type() + ") " + __func__ + ":"
|
|
<< " maximum_width " << maximum_width
|
|
<< " result " << size
|
|
<< ".\n";
|
|
|
|
} else {
|
|
DBG_GUI_L << "tcontrol(" + get_control_type() + ") " + __func__ + ":"
|
|
<< " failed; either no label or wrapping not allowed.\n";
|
|
}
|
|
}
|
|
|
|
tpoint tcontrol::calculate_best_size() const
|
|
{
|
|
assert(config_);
|
|
tpoint result(config_->default_width, config_->default_height);
|
|
if(! label_.empty()) {
|
|
// If no label text set we use the predefined value.
|
|
|
|
/**
|
|
* @todo The value send should subtract the border size
|
|
* and readd it after calculation to get the proper result.
|
|
*/
|
|
result = get_best_text_size(result);
|
|
}
|
|
|
|
DBG_GUI_L << "tcontrol(" + get_control_type() + ") " + __func__ + ":"
|
|
<< " empty label " << label_.empty()
|
|
<< " result " << result
|
|
<< ".\n";
|
|
return result;
|
|
}
|
|
|
|
void tcontrol::set_size(const tpoint& origin, const tpoint& size)
|
|
{
|
|
// resize canvasses
|
|
foreach(tcanvas& canvas, canvas_) {
|
|
canvas.set_width(size.x);
|
|
canvas.set_height(size.y);
|
|
}
|
|
|
|
// Note we assume that the best size has been queried but otherwise it
|
|
// should return false.
|
|
if(renderer_.is_truncated()
|
|
&& use_tooltip_on_label_overflow_ && tooltip_.empty()) {
|
|
|
|
set_tooltip(label_);
|
|
}
|
|
|
|
// inherited
|
|
twidget::set_size(origin, size);
|
|
|
|
// update the state of the canvas after the sizes have been set.
|
|
update_canvas();
|
|
}
|
|
|
|
#ifdef GUI2_OLD_EVENT_HANDLING
|
|
void tcontrol::mouse_hover(tevent_handler& event)
|
|
{
|
|
DBG_GUI_E << "Control: mouse hover.\n";
|
|
event.show_tooltip(tooltip_, settings::popup_show_time);
|
|
}
|
|
|
|
void tcontrol::help_key(tevent_handler& event)
|
|
{
|
|
DBG_GUI_E << "Control: help key.\n";
|
|
event.show_help_popup(help_message_, settings::help_show_time);
|
|
}
|
|
#endif
|
|
|
|
void tcontrol::load_config()
|
|
{
|
|
if(!config()) {
|
|
set_config(get_control(get_control_type(), definition()));
|
|
|
|
assert(canvas().size() == config()->state.size());
|
|
for(size_t i = 0; i < canvas().size(); ++i) {
|
|
canvas(i) = config()->state[i].canvas;
|
|
}
|
|
|
|
update_canvas();
|
|
|
|
load_config_extra();
|
|
}
|
|
}
|
|
|
|
void tcontrol::set_definition(const std::string& definition)
|
|
{
|
|
assert(!config());
|
|
twidget::set_definition(definition);
|
|
load_config();
|
|
assert(config());
|
|
}
|
|
|
|
void tcontrol::set_label(const t_string& label)
|
|
{
|
|
if(label == label_) {
|
|
return;
|
|
}
|
|
|
|
label_ = label;
|
|
update_canvas();
|
|
set_dirty();
|
|
}
|
|
|
|
void tcontrol::set_use_markup(bool use_markup)
|
|
{
|
|
if(use_markup == use_markup_) {
|
|
return;
|
|
}
|
|
|
|
use_markup_ = use_markup;
|
|
update_canvas();
|
|
set_dirty();
|
|
}
|
|
|
|
void tcontrol::update_canvas()
|
|
{
|
|
const int max_width = get_text_maximum_width();
|
|
const int max_height = get_text_maximum_height();
|
|
|
|
// set label in canvases
|
|
foreach(tcanvas& canvas, canvas_) {
|
|
canvas.set_variable("text", variant(label_));
|
|
canvas.set_variable("text_markup", variant(use_markup_));
|
|
canvas.set_variable("text_maximum_width", variant(max_width));
|
|
canvas.set_variable("text_maximum_height", variant(max_height));
|
|
canvas.set_variable("text_wrap_mode", variant(can_wrap()
|
|
? PANGO_ELLIPSIZE_NONE : PANGO_ELLIPSIZE_END));
|
|
}
|
|
}
|
|
|
|
int tcontrol::get_text_maximum_width() const
|
|
{
|
|
assert(config_);
|
|
|
|
return text_maximum_width_ != 0
|
|
? text_maximum_width_
|
|
: get_width() - config_->text_extra_width;
|
|
}
|
|
|
|
int tcontrol::get_text_maximum_height() const
|
|
{
|
|
assert(config_);
|
|
|
|
return get_height() - config_->text_extra_height;
|
|
}
|
|
|
|
void tcontrol::impl_draw_background(surface& frame_buffer)
|
|
{
|
|
DBG_GUI_D << "tcontrol(" + get_control_type() + ") " + __func__ + ": "
|
|
<< " id " << id()
|
|
<< " size " << get_rect()
|
|
<< ".\n";
|
|
|
|
canvas(get_state()).blit(frame_buffer, get_rect());
|
|
}
|
|
|
|
tpoint tcontrol::get_best_text_size(const tpoint& minimum_size, const tpoint& maximum_size) const
|
|
{
|
|
log_scope2(log_gui_layout, "tcontrol(" + get_control_type() + ") " + __func__);
|
|
|
|
assert(!label_.empty());
|
|
|
|
const tpoint border(config_->text_extra_width, config_->text_extra_height);
|
|
tpoint size = minimum_size - border;
|
|
|
|
renderer_.set_text(label_, use_markup_);
|
|
|
|
renderer_.set_font_size(config_->text_font_size);
|
|
renderer_.set_font_style(config_->text_font_style);
|
|
|
|
// Try with the minimum wanted size.
|
|
const int maximum_width = text_maximum_width_ != 0
|
|
? text_maximum_width_
|
|
: maximum_size.x;
|
|
|
|
renderer_.set_maximum_width(maximum_width);
|
|
|
|
if(can_wrap()) {
|
|
renderer_.set_ellipse_mode(PANGO_ELLIPSIZE_NONE);
|
|
}
|
|
|
|
DBG_GUI_L << "tcontrol(" + get_control_type() + ") status:\n";
|
|
DBG_GUI_L << "minimum_size " << minimum_size
|
|
<< " maximum_size " << maximum_size
|
|
<< " text_maximum_width_ " << text_maximum_width_
|
|
<< " can_wrap " << can_wrap()
|
|
<< " truncated " << renderer_.is_truncated()
|
|
<< " renderer size " << renderer_.get_size()
|
|
<< ".\n";
|
|
|
|
// If doesn't fit try the maximum.
|
|
if(renderer_.is_truncated() && !can_wrap()) {
|
|
// FIXME if maximum size is defined we should look at that
|
|
// but also we don't adjust for the extra text space yet!!!
|
|
const tpoint maximum_size(config_->max_width, config_->max_height);
|
|
renderer_.set_maximum_width(maximum_size.x ? maximum_size.x - border.x : -1);
|
|
}
|
|
|
|
size = renderer_.get_size() + border;
|
|
|
|
if(size.x < minimum_size.x) {
|
|
size.x = minimum_size.x;
|
|
}
|
|
|
|
if(size.y < minimum_size.y) {
|
|
size.y = minimum_size.y;
|
|
}
|
|
|
|
DBG_GUI_L << "tcontrol(" + get_control_type() + ") result " << size << ".\n";
|
|
return size;
|
|
}
|
|
|
|
namespace {
|
|
|
|
/** Converts a SDL_Color to a pango colour prefix. */
|
|
std::string colour_prefix(const SDL_Color& colour)
|
|
{
|
|
std::stringstream result;
|
|
|
|
// Cast to unsigned is needed to avoid being interpreted as a char.
|
|
result << "<span foreground=\"#"
|
|
<< std::hex
|
|
<< std::setfill('0') << std::setw(2)
|
|
<< static_cast<unsigned>(colour.r)
|
|
<< std::setfill('0') << std::setw(2)
|
|
<< static_cast<unsigned>(colour.g)
|
|
<< std::setfill('0') << std::setw(2)
|
|
<< static_cast<unsigned>(colour.b)
|
|
<< "\">";
|
|
|
|
return result.str();
|
|
}
|
|
|
|
/**
|
|
* Escapes a string to be used in a pango formatted string.
|
|
*
|
|
* This function also changes every "\x" to "x" thus also "\\" to "\". This
|
|
* is used to mimic the old dialog behaviour.
|
|
*/
|
|
std::string escape_string(std::string str)
|
|
{
|
|
struct tconverter
|
|
{
|
|
tconverter(const std::string& string)
|
|
: str(g_markup_escape_text(string.c_str(), -1))
|
|
{
|
|
}
|
|
|
|
~tconverter()
|
|
{
|
|
g_free(str);
|
|
}
|
|
|
|
gchar* str;
|
|
};
|
|
|
|
size_t offset = str.find('\\', 0);
|
|
while (offset != std::string::npos) {
|
|
str.erase(offset, 1);
|
|
++offset;
|
|
offset = str.find('\\', offset);
|
|
}
|
|
|
|
tconverter converter(str);
|
|
return std::string(converter.str);
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace gui2
|