text_box.cpp

Go to the documentation of this file.
00001 /* $Id: text_box.cpp 26488 2008-05-09 16:25:26Z mordante $ */
00002 /*
00003    copyright (C) 2008 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 version 2
00008    or at your option any later version.
00009    this program is distributed in the hope that it will be useful,
00010    but without any warranty.
00011 
00012    see the copying file for more details.
00013 */
00014 
00015 #include "gui/widgets/text_box.hpp"
00016 
00017 #include "font.hpp"
00018 #include "foreach.hpp"
00019 #include "gui/widgets/event_handler.hpp"
00020 #include "log.hpp"
00021 #include "serialization/string_utils.hpp"
00022 #include "game_preferences.hpp"
00023 
00024 #include <numeric>
00025 
00026 #define DBG_G LOG_STREAM_INDENT(debug, gui)
00027 #define LOG_G LOG_STREAM_INDENT(info, gui)
00028 #define WRN_G LOG_STREAM_INDENT(warn, gui)
00029 #define ERR_G LOG_STREAM_INDENT(err, gui)
00030 
00031 #define DBG_G_D LOG_STREAM_INDENT(debug, gui_draw)
00032 #define LOG_G_D LOG_STREAM_INDENT(info, gui_draw)
00033 #define WRN_G_D LOG_STREAM_INDENT(warn, gui_draw)
00034 #define ERR_G_D LOG_STREAM_INDENT(err, gui_draw)
00035 
00036 #define DBG_G_E LOG_STREAM_INDENT(debug, gui_event)
00037 #define LOG_G_E LOG_STREAM_INDENT(info, gui_event)
00038 #define WRN_G_E LOG_STREAM_INDENT(warn, gui_event)
00039 #define ERR_G_E LOG_STREAM_INDENT(err, gui_event)
00040 
00041 #define DBG_G_P LOG_STREAM_INDENT(debug, gui_parse)
00042 #define LOG_G_P LOG_STREAM_INDENT(info, gui_parse)
00043 #define WRN_G_P LOG_STREAM_INDENT(warn, gui_parse)
00044 #define ERR_G_P LOG_STREAM_INDENT(err, gui_parse)
00045 
00046 
00047 namespace gui2 {
00048 
00049 static surface render_text(const std::string& text, unsigned font_size)
00050 {
00051     static SDL_Color col = {0, 0, 0, 0};
00052     return font::get_rendered_text(text, font_size, col, TTF_STYLE_NORMAL);
00053 }
00054 
00055 //! Helper function for text more efficient as set_text.
00056 //! Inserts a character at the cursor.
00057 void ttext_box::insert_char(Uint16 unicode)
00058 {
00059     delete_selection();
00060 
00061     // Determine the width of the new character.
00062     std::string tmp_text;
00063     tmp_text.insert(tmp_text.begin(), unicode);
00064 
00065     surface surf = render_text(tmp_text, config()->text_font_size);
00066     assert(surf);
00067     const unsigned width = surf->w;
00068 
00069     // Insert the char in the buffer, we need to assume it's a wide string.
00070     wide_string tmp = utils::string_to_wstring(text());
00071     tmp.insert(tmp.begin() + sel_start(), unicode);
00072     text() = utils::wstring_to_string(tmp);
00073 
00074     // Update the widths.
00075     character_offset_.insert(character_offset_.begin() + sel_start(), width);
00076     if(sel_start() != 0) {
00077         character_offset_[sel_start()] += character_offset_[sel_start() - 1]; 
00078     }
00079 
00080     ++sel_start();
00081     for(size_t i = sel_start(); i < character_offset_.size(); ++i) {
00082         character_offset_[i] += width;
00083     }
00084 
00085     set_cursor(sel_start(), false);
00086     set_canvas_text();
00087     set_dirty();
00088 }
00089 
00090 //! Deletes the character.
00091 //!
00092 //! @param before_cursor     If true it deletes the character before the cursor
00093 //!                          (backspace) else the character after the cursor
00094 //!                          (delete). 
00095 void ttext_box::delete_char(const bool before_cursor)
00096 {
00097     if(before_cursor) {
00098         --sel_start();
00099         set_cursor(sel_start(), false);
00100     }
00101 
00102     sel_len() = 1;
00103 
00104     delete_selection();
00105 }
00106 
00107 //! Deletes the current selection.
00108 void ttext_box::delete_selection()
00109 {
00110     if(sel_len() == 0) {
00111         return;
00112     }
00113 
00114     // If we have a negative range change it to a positive range.
00115     // This makes the rest of the algoritms easier.
00116     int len = sel_len();
00117     unsigned  start = sel_start();
00118     if(len < 0) {
00119         len = - len;
00120         start -= len;
00121     }
00122 
00123     // Update the text, we need to assume it's a wide string.
00124     wide_string tmp = utils::string_to_wstring(text());
00125     tmp.erase(tmp.begin() + start, tmp.begin() + start + len);
00126     const std::string& text = utils::wstring_to_string(tmp);
00127     set_text(text);
00128     set_cursor(start, false);
00129 }
00130 
00131 //! Inherited from tcontrol.
00132 void ttext_box::set_canvas_text()
00133 {
00134     foreach(tcanvas& tmp, canvas()) {
00135 
00136         // NOTE when sel_start() == - sel_len() then the offset calculation will
00137         // access character_offset_[-1] so add special cases to use 0 instead.
00138         // The same can happen if sel_start() == 0.
00139 
00140         // Set the general variables.
00141         tmp.set_variable("text", variant(text()));
00142         tmp.set_variable("text_x_offset", variant(text_x_offset_));
00143         tmp.set_variable("text_y_offset", variant(text_y_offset_));
00144 
00145         // Set the cursor info.
00146         const unsigned start = sel_start();
00147         const int len = sel_len();
00148         if(text().empty() || start + len == 0) {
00149             tmp.set_variable("cursor_offset", variant(0));
00150         } else {
00151             tmp.set_variable("cursor_offset", variant(character_offset_[start - 1 + len]));
00152         }
00153 
00154         // Set the seleciton info
00155         unsigned start_offset = 0;
00156         unsigned end_offset = 0;
00157         if(len == 0) {
00158             // No nothing.
00159         } else if(len > 0) {
00160             start_offset = start == 0 ? 0 :character_offset_[start - 1];
00161             end_offset = character_offset_[start - 1 + len];
00162         } else {
00163             start_offset = 
00164                 (start + len == 0) ? 0 : character_offset_[start - 1 + len];
00165             end_offset = character_offset_[start - 1];
00166         }
00167         tmp.set_variable("selection_offset", variant(start_offset));
00168         tmp.set_variable("selection_width", variant(end_offset  - start_offset ));
00169     }
00170 }
00171 
00172 void ttext_box::set_size(const SDL_Rect& rect)
00173 {
00174     // Inherited.
00175     tcontrol::set_size(rect);
00176 
00177     update_offsets();
00178 }   
00179 
00180 //! Handles the selection in a mouse down or mouse move event.
00181 void ttext_box::handle_mouse_selection(
00182         tevent_handler& event, const bool start_selection)
00183 {
00184     tpoint mouse = event.get_mouse();
00185     mouse.x -= get_x();
00186     mouse.y -= get_y();
00187     // FIXME we dont test for overflow in width
00188     if(mouse.x < text_x_offset_ || mouse.y < text_y_offset_ 
00189             || mouse.y >= text_y_offset_ + text_height_) {
00190         return;
00191     }
00192 
00193     int offset = get_character_offset_at(mouse.x - text_x_offset_);
00194     if(offset < 0) {
00195         return;
00196     }
00197 
00198 
00199     set_cursor(offset, !start_selection);
00200     set_canvas_text();
00201     set_dirty();
00202     dragging_ |= start_selection;
00203 }
00204 
00205 //! Inherited from twidget.
00206 void ttext_box::mouse_left_button_down(tevent_handler& event)
00207 {
00208     DBG_G_E << "Text box: left mouse down.\n";
00209 
00210     handle_mouse_selection(event, true);
00211 }
00212 
00213 //! Inherited from twidget.
00214 void ttext_box::mouse_move(tevent_handler& event)
00215 {
00216     DBG_G_E << "Text box: mouse move.\n";
00217 
00218     if(!dragging_) {
00219         return;
00220     }
00221 
00222     handle_mouse_selection(event, false);
00223 }
00224 
00225 //! Inherited from twidget.
00226 void ttext_box::mouse_left_button_up(tevent_handler& /*event*/)
00227 {
00228     DBG_G_E << "Text box: left mouse up.\n";
00229 
00230     dragging_ = false;
00231 }
00232 
00233 //! Inherited from twidget.
00234 void ttext_box::mouse_left_button_double_click(tevent_handler&)
00235 {
00236     DBG_G_E << "Text box: left mouse double click.\n";
00237 
00238     select_all();
00239 }
00240 
00241 //! Calculates the offsets of all chars.
00242 void ttext_box::calculate_char_offset()
00243 {
00244     assert(config());
00245     character_offset_.clear();
00246 
00247     std::string rendered_text;
00248     const unsigned font_size = config()->text_font_size;
00249 
00250     foreach(const wchar_t& unicode, utils::string_to_wstring(text())) {
00251         rendered_text.insert(rendered_text.end(), unicode);
00252         surface surf = render_text(rendered_text, font_size);
00253         assert(surf);
00254         character_offset_.push_back(surf->w);
00255 
00256     }
00257 }
00258 
00259 //! Gets the character at the wanted offset, everything beyond will
00260 //! select the last character.
00261 unsigned ttext_box::get_character_offset_at(const unsigned offset)
00262 {
00263     unsigned result = 0;
00264     foreach(unsigned off, character_offset_) {
00265         if(offset < off) {
00266             return result;
00267         }
00268 
00269         ++result;
00270     }
00271     return text().size();
00272 }
00273 
00274 void ttext_box::handle_key_clear_line(SDLMod /*modifier*/, bool& handled)
00275 {
00276     handled = true;
00277 
00278     set_text("");
00279 }
00280 
00281 void ttext_box::handle_key_up_arrow(SDLMod /*modifier*/, bool& handled)
00282 {
00283     if (history_.get_enabled()) {
00284         std::string s = history_.up(text());
00285         if (!s.empty()) {
00286             set_text(s);
00287         }
00288                 
00289         handled = true;
00290     }
00291             
00292 }
00293 
00294 void ttext_box::handle_key_down_arrow(SDLMod /*modifier*/, bool& handled)
00295 {
00296     if (history_.get_enabled()) {
00297         set_text(history_.down(text()));
00298         handled = true;
00299     }
00300 }
00301 
00302 //! Inherited from tcontrol.
00303 void ttext_box::load_config_extra()
00304 {
00305     update_offsets();
00306 }
00307 
00308 // Updates text_x_offset_ and text_x_offset_.
00309 void ttext_box::update_offsets()
00310 {
00311     assert(config());
00312 
00313     ttext_box_definition::tresolution* conf = 
00314         dynamic_cast<ttext_box_definition::tresolution*>(config());
00315     assert(conf);
00316 
00317     text_height_ = font::get_max_height(conf->text_font_size);
00318     
00319     game_logic::map_formula_callable variables;
00320     variables.add("height", variant(get_height()));
00321     variables.add("width", variant(get_width()));
00322     variables.add("text_font_height", variant(text_height_));
00323 
00324     text_x_offset_ = conf->text_x_offset(variables);
00325     text_y_offset_ = conf->text_y_offset(variables);
00326 
00327     // Since this variable doesn't change set it here instead of in
00328     // set_canvas_text().
00329     foreach(tcanvas& tmp, canvas()) {
00330         tmp.set_variable("text_font_height", variant(text_height_));
00331     }
00332  
00333     // Force an update of the canvas since now text_font_height is known.
00334     set_canvas_text();
00335 }
00336 
00337 ttext_history ttext_history::get_history(const std::string& id, const bool enabled) 
00338 {
00339     std::vector<std::string>* vec = preferences::get_history(id);
00340     return ttext_history(vec, enabled);
00341 }
00342 
00343 void ttext_history::push(const std::string& text) 
00344 {
00345     if (!enabled_) {
00346         return; 
00347     } else {        
00348         if (!text.empty() && (history_->empty() || text != history_->back())) {
00349             history_->push_back(text); 
00350         }
00351         
00352         pos_ = history_->size();
00353     }
00354 }
00355 
00356 std::string ttext_history::up(const std::string& text)
00357 {
00358     
00359     if (!enabled_) {
00360         return "";
00361     } else if (pos_ == history_->size()) {
00362         unsigned curr = pos_;
00363         push(text);
00364         pos_ = curr;
00365     }   
00366 
00367     if (pos_ != 0) {
00368         --pos_;
00369     }
00370     
00371     return get_value();
00372 }
00373 
00374 // Will push text to history if it is pointing at the end of the vector.
00375 std::string ttext_history::down(const std::string& text)
00376 {
00377     if (!enabled_) {
00378         return "";
00379     } else if (pos_ == history_->size()) {
00380         push(text);
00381     } else {
00382         pos_++;
00383     }
00384         
00385     return get_value();
00386 }
00387 
00388 std::string ttext_history::get_value() const 
00389 {
00390     if (!enabled_ || pos_ == history_->size()) {
00391         return "";
00392     } else { 
00393         return history_->at(pos_);
00394     }
00395 }
00396 
00397 } //namespace gui2
00398 
00399 

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs