00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
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
00056
00057 void ttext_box::insert_char(Uint16 unicode)
00058 {
00059 delete_selection();
00060
00061
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
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
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
00091
00092
00093
00094
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
00108 void ttext_box::delete_selection()
00109 {
00110 if(sel_len() == 0) {
00111 return;
00112 }
00113
00114
00115
00116 int len = sel_len();
00117 unsigned start = sel_start();
00118 if(len < 0) {
00119 len = - len;
00120 start -= len;
00121 }
00122
00123
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
00132 void ttext_box::set_canvas_text()
00133 {
00134 foreach(tcanvas& tmp, canvas()) {
00135
00136
00137
00138
00139
00140
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
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
00155 unsigned start_offset = 0;
00156 unsigned end_offset = 0;
00157 if(len == 0) {
00158
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
00175 tcontrol::set_size(rect);
00176
00177 update_offsets();
00178 }
00179
00180
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
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
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
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
00226 void ttext_box::mouse_left_button_up(tevent_handler& )
00227 {
00228 DBG_G_E << "Text box: left mouse up.\n";
00229
00230 dragging_ = false;
00231 }
00232
00233
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
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
00260
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 , bool& handled)
00275 {
00276 handled = true;
00277
00278 set_text("");
00279 }
00280
00281 void ttext_box::handle_key_up_arrow(SDLMod , 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 , bool& handled)
00295 {
00296 if (history_.get_enabled()) {
00297 set_text(history_.down(text()));
00298 handled = true;
00299 }
00300 }
00301
00302
00303 void ttext_box::load_config_extra()
00304 {
00305 update_offsets();
00306 }
00307
00308
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
00328
00329 foreach(tcanvas& tmp, canvas()) {
00330 tmp.set_variable("text_font_height", variant(text_height_));
00331 }
00332
00333
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
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 }
00398
00399