gui/widgets/text_box.cpp

Go to the documentation of this file.
00001 /* $Id: text_box.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 #define GETTEXT_DOMAIN "wesnoth-lib"
00017 
00018 #include "gui/widgets/text_box.hpp"
00019 
00020 #include "font.hpp"
00021 #include "foreach.hpp"
00022 #include "gui/auxiliary/log.hpp"
00023 #include "gui/auxiliary/widget_definition/text_box.hpp"
00024 #include "gui/auxiliary/window_builder/text_box.hpp"
00025 #include "gui/widgets/settings.hpp"
00026 #include "gui/widgets/window.hpp"
00027 #include "game_preferences.hpp"
00028 
00029 #include <boost/bind.hpp>
00030 
00031 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
00032 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
00033 
00034 namespace gui2 {
00035 
00036 REGISTER_WIDGET(text_box)
00037 
00038 ttext_history ttext_history::get_history(const std::string& id, const bool enabled)
00039 {
00040     std::vector<std::string>* vec = preferences::get_history(id);
00041     return ttext_history(vec, enabled);
00042 }
00043 
00044 void ttext_history::push(const std::string& text)
00045 {
00046     if (!enabled_) {
00047         return;
00048     } else {
00049         if (!text.empty() && (history_->empty() || text != history_->back())) {
00050             history_->push_back(text);
00051         }
00052 
00053         pos_ = history_->size();
00054     }
00055 }
00056 
00057 std::string ttext_history::up(const std::string& text)
00058 {
00059 
00060     if (!enabled_) {
00061         return "";
00062     } else if (pos_ == history_->size()) {
00063         unsigned curr = pos_;
00064         push(text);
00065         pos_ = curr;
00066     }
00067 
00068     if (pos_ != 0) {
00069         --pos_;
00070     }
00071 
00072     return get_value();
00073 }
00074 
00075 std::string ttext_history::down(const std::string& text)
00076 {
00077     if (!enabled_) {
00078         return "";
00079     } else if (pos_ == history_->size()) {
00080         push(text);
00081     } else {
00082         pos_++;
00083     }
00084 
00085     return get_value();
00086 }
00087 
00088 std::string ttext_history::get_value() const
00089 {
00090     if (!enabled_ || pos_ == history_->size()) {
00091         return "";
00092     } else {
00093         return history_->at(pos_);
00094     }
00095 }
00096 
00097 ttext_box::ttext_box()
00098     : ttext_()
00099     , history_()
00100     , text_x_offset_(0)
00101     , text_y_offset_(0)
00102     , text_height_(0)
00103     , dragging_(false)
00104 {
00105     set_wants_mouse_left_double_click();
00106 
00107     connect_signal<event::MOUSE_MOTION>(boost::bind(
00108                 &ttext_box::signal_handler_mouse_motion, this, _2, _3, _5));
00109     connect_signal<event::LEFT_BUTTON_DOWN>(boost::bind(
00110                 &ttext_box::signal_handler_left_button_down, this, _2, _3));
00111     connect_signal<event::LEFT_BUTTON_UP>(boost::bind(
00112                 &ttext_box::signal_handler_left_button_up, this, _2, _3));
00113     connect_signal<event::LEFT_BUTTON_DOUBLE_CLICK>(boost::bind(&ttext_box
00114                 ::signal_handler_left_button_double_click, this, _2, _3));
00115 }
00116 
00117 void ttext_box::place(const tpoint& origin, const tpoint& size)
00118 {
00119     // Inherited.
00120     tcontrol::place(origin, size);
00121 
00122     set_maximum_width(get_text_maximum_width());
00123     set_maximum_height(get_text_maximum_height());
00124 
00125     update_offsets();
00126 }
00127 
00128 void ttext_box::update_canvas()
00129 {
00130     /***** Gather the info *****/
00131 
00132     // Set the cursor info.
00133     const unsigned start = get_selection_start();
00134     const int length = get_selection_length();
00135 
00136 
00137     PangoEllipsizeMode ellipse_mode = PANGO_ELLIPSIZE_NONE;
00138     if(!can_wrap()) {
00139         if((start + length) > (get_length() / 2)) {
00140             ellipse_mode = PANGO_ELLIPSIZE_START;
00141         } else {
00142             ellipse_mode = PANGO_ELLIPSIZE_END;
00143         }
00144     }
00145     set_ellipse_mode(ellipse_mode);
00146 
00147     // Set the selection info
00148     unsigned start_offset = 0;
00149     unsigned end_offset = 0;
00150     if(length == 0) {
00151         // No nothing.
00152     } else if(length > 0) {
00153         start_offset = get_cursor_position(start).x;
00154         end_offset = get_cursor_position(start + length).x;
00155     } else {
00156         start_offset = get_cursor_position(start + length).x;
00157         end_offset = get_cursor_position(start).x;
00158     }
00159 
00160     /***** Set in all canvases *****/
00161 
00162     const int max_width = get_text_maximum_width();
00163     const int max_height = get_text_maximum_height();
00164 
00165     foreach(tcanvas& tmp, canvas()) {
00166 
00167         tmp.set_variable("text", variant(get_value()));
00168         tmp.set_variable("text_x_offset", variant(text_x_offset_));
00169         tmp.set_variable("text_y_offset", variant(text_y_offset_));
00170         tmp.set_variable("text_maximum_width", variant(max_width));
00171         tmp.set_variable("text_maximum_height", variant(max_height));
00172 
00173         tmp.set_variable("cursor_offset",
00174             variant(get_cursor_position(start + length).x));
00175 
00176         tmp.set_variable("selection_offset", variant(start_offset));
00177         tmp.set_variable("selection_width", variant(end_offset  - start_offset ));
00178         tmp.set_variable("text_wrap_mode", variant(ellipse_mode));
00179     }
00180 }
00181 
00182 void ttext_box::delete_char(const bool before_cursor)
00183 {
00184     if(before_cursor) {
00185         set_cursor(get_selection_start() - 1, false);
00186     }
00187 
00188     set_selection_length(1);
00189 
00190     delete_selection();
00191 }
00192 
00193 void ttext_box::delete_selection()
00194 {
00195     if(get_selection_length() == 0) {
00196         return;
00197     }
00198 
00199     // If we have a negative range change it to a positive range.
00200     // This makes the rest of the algoritms easier.
00201     int len = get_selection_length();
00202     unsigned  start = get_selection_start();
00203     if(len < 0) {
00204         len = - len;
00205         start -= len;
00206     }
00207 
00208     // Update the text, we need to assume it's a wide string.
00209     wide_string tmp = utils::string_to_wstring(get_value());
00210     tmp.erase(tmp.begin() + start, tmp.begin() + start + len);
00211     const std::string& text = utils::wstring_to_string(tmp);
00212     set_value(text);
00213     set_cursor(start, false);
00214 }
00215 
00216 void ttext_box::handle_mouse_selection(tpoint mouse, const bool start_selection)
00217 {
00218     mouse.x -= get_x();
00219     mouse.y -= get_y();
00220     // FIXME we don't test for overflow in width
00221     if(mouse.x < static_cast<int>(text_x_offset_) ||
00222        mouse.y < static_cast<int>(text_y_offset_) ||
00223        mouse.y >= static_cast<int>(text_y_offset_ + text_height_)) {
00224         return;
00225     }
00226 
00227     int offset = get_column_line(tpoint(
00228         mouse.x - text_x_offset_, mouse.y - text_y_offset_)).x;
00229 
00230     if(offset < 0) {
00231         return;
00232     }
00233 
00234 
00235     set_cursor(offset, !start_selection);
00236     update_canvas();
00237     set_dirty();
00238     dragging_ |= start_selection;
00239 }
00240 
00241 void ttext_box::update_offsets()
00242 {
00243     assert(config());
00244 
00245     boost::intrusive_ptr<const ttext_box_definition::tresolution> conf =
00246         boost::dynamic_pointer_cast
00247         <const ttext_box_definition::tresolution>(config());
00248 
00249     assert(conf);
00250 
00251     text_height_ = font::get_max_height(conf->text_font_size);
00252 
00253     game_logic::map_formula_callable variables;
00254     variables.add("height", variant(get_height()));
00255     variables.add("width", variant(get_width()));
00256     variables.add("text_font_height", variant(text_height_));
00257 
00258     text_x_offset_ = conf->text_x_offset(variables);
00259     text_y_offset_ = conf->text_y_offset(variables);
00260 
00261     // Since this variable doesn't change set it here instead of in
00262     // update_canvas().
00263     foreach(tcanvas& tmp, canvas()) {
00264         tmp.set_variable("text_font_height", variant(text_height_));
00265     }
00266 
00267     // Force an update of the canvas since now text_font_height is known.
00268     update_canvas();
00269 }
00270 
00271 bool ttext_box::history_up()
00272 {
00273     if(!history_.get_enabled()) {
00274         return false;
00275     }
00276 
00277     const std::string str = history_.up(get_value());
00278     if(!str.empty()) {
00279         set_value(str);
00280     }
00281     return true;
00282 }
00283 
00284 bool ttext_box::history_down()
00285 {
00286     if(!history_.get_enabled()) {
00287         return false;
00288     }
00289 
00290     const std::string str = history_.down(get_value());
00291     if(!str.empty()) {
00292         set_value(str);
00293     }
00294     return true;
00295 }
00296 
00297 void ttext_box::handle_key_default(
00298         bool& handled, SDLKey key, SDLMod modifier, Uint16 unicode)
00299 {
00300     if(key == SDLK_TAB && (modifier & KMOD_CTRL)) {
00301         if(!(modifier& KMOD_SHIFT)) {
00302             handled = history_up();
00303         } else {
00304             handled = history_down();
00305         }
00306     }
00307 
00308     if(!handled) {
00309         // Inherited.
00310         ttext_::handle_key_default(handled, key, modifier, unicode);
00311     }
00312 }
00313 
00314 void ttext_box::handle_key_clear_line(SDLMod /*modifier*/, bool& handled)
00315 {
00316     handled = true;
00317 
00318     set_value("");
00319 }
00320 
00321 void ttext_box::load_config_extra()
00322 {
00323     assert(config());
00324 
00325     boost::intrusive_ptr<const ttext_box_definition::tresolution> conf =
00326         boost::dynamic_pointer_cast
00327         <const ttext_box_definition::tresolution>(config());
00328 
00329     assert(conf);
00330 
00331     set_font_size(conf->text_font_size);
00332     set_font_style(conf->text_font_style);
00333 
00334     update_offsets();
00335 }
00336 
00337 const std::string& ttext_box::get_control_type() const
00338 {
00339     static const std::string type = "text_box";
00340     return type;
00341 }
00342 
00343 void ttext_box::signal_handler_mouse_motion(
00344         const event::tevent event, bool& handled, const tpoint& coordinate)
00345 {
00346     DBG_GUI_E << get_control_type() << "[" << id() << "]: " << event << ".\n";
00347 
00348     if(dragging_) {
00349         handle_mouse_selection(coordinate, false);
00350     }
00351 
00352     handled = true;
00353 }
00354 
00355 void ttext_box::signal_handler_left_button_down(
00356         const event::tevent event, bool& handled)
00357 {
00358     DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
00359 
00360     /*
00361      * Copied from the base class see how we can do inheritance with the new
00362      * system...
00363      */
00364     get_window()->keyboard_capture(this);
00365     get_window()->mouse_capture();
00366 
00367     handle_mouse_selection(get_mouse_position(), true);
00368 
00369     handled = true;
00370 }
00371 
00372 void ttext_box::signal_handler_left_button_up(
00373         const event::tevent event, bool& handled)
00374 {
00375     DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
00376 
00377     dragging_ = false;
00378     handled = true;
00379 }
00380 
00381 void ttext_box::signal_handler_left_button_double_click(
00382         const event::tevent event, bool& handled)
00383 {
00384     DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
00385 
00386     select_all();
00387     handled = true;
00388 }
00389 
00390 } //namespace gui2
00391 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

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