00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #define GETTEXT_DOMAIN "wesnoth-lib"
00017
00018 #include "gui/widgets/text.hpp"
00019
00020 #include "clipboard.hpp"
00021 #include "gui/auxiliary/log.hpp"
00022 #include "serialization/string_utils.hpp"
00023
00024 #include <boost/bind.hpp>
00025
00026 #define LOG_SCOPE_HEADER get_control_type() + " [" + id() + "] " + __func__
00027 #define LOG_HEADER LOG_SCOPE_HEADER + ':'
00028
00029 namespace gui2 {
00030
00031 ttext_::ttext_()
00032 : tcontrol(COUNT)
00033 , state_(ENABLED)
00034 , text_()
00035 , selection_start_(0)
00036 , selection_length_(0)
00037 , text_changed_callback_()
00038 {
00039 #ifdef __unix__
00040
00041 connect_signal<event::MIDDLE_BUTTON_CLICK>(boost::bind(
00042 &ttext_::signal_handler_middle_button_click, this, _2, _3));
00043
00044 #endif
00045
00046 connect_signal<event::SDL_KEY_DOWN>(boost::bind(
00047 &ttext_::signal_handler_sdl_key_down, this, _2, _3, _5, _6, _7));
00048
00049 connect_signal<event::RECEIVE_KEYBOARD_FOCUS>(boost::bind(
00050 &ttext_::signal_handler_receive_keyboard_focus, this, _2));
00051 connect_signal<event::LOSE_KEYBOARD_FOCUS>(boost::bind(
00052 &ttext_::signal_handler_lose_keyboard_focus, this, _2));
00053 }
00054
00055 void ttext_::set_maximum_length(const size_t maximum_length)
00056 {
00057 const bool need_update = text_.get_length() > maximum_length;
00058
00059 text_.set_maximum_length(maximum_length);
00060
00061 if(need_update) {
00062 if(selection_start_ > maximum_length) {
00063 selection_start_ = maximum_length;
00064 selection_length_ = 0;
00065 } else if(selection_start_ + selection_length_ > maximum_length) {
00066 selection_length_ = maximum_length - selection_start_;
00067 }
00068 update_canvas();
00069 set_dirty();
00070 }
00071 }
00072
00073 void ttext_::set_value(const std::string& text)
00074 {
00075 if(text != text_.text()) {
00076 text_.set_text(text, false);
00077
00078
00079 selection_start_ = text_.get_length();
00080 selection_length_ = 0;
00081 update_canvas();
00082 set_dirty();
00083 }
00084 }
00085
00086 void ttext_::set_cursor(const size_t offset, const bool select)
00087 {
00088 if(select) {
00089
00090 if(selection_start_ == offset) {
00091 selection_length_ = 0;
00092 } else {
00093 selection_length_ = - static_cast<int>(selection_start_ - offset);
00094 }
00095
00096 #ifdef __unix__
00097
00098 copy_selection(true);
00099 #endif
00100 update_canvas();
00101 set_dirty();
00102
00103 } else {
00104 assert(offset <= text_.get_length());
00105 selection_start_ = offset;
00106 selection_length_ = 0;
00107
00108 update_canvas();
00109 set_dirty();
00110 }
00111 }
00112
00113 void ttext_::insert_char(const Uint16 unicode)
00114 {
00115 delete_selection();
00116
00117 if(text_.insert_unicode(selection_start_, unicode)) {
00118
00119
00120 set_cursor(selection_start_ + 1, false);
00121 update_canvas();
00122 set_dirty();
00123 }
00124 }
00125
00126 void ttext_::copy_selection(const bool mouse)
00127 {
00128 int length = selection_length_;
00129 unsigned start = selection_start_;
00130
00131 if(length == 0) {
00132 return;
00133 }
00134
00135 if(length < 0) {
00136 length = - length;
00137 start -= length;
00138 }
00139
00140 const wide_string& wtext = utils::string_to_wstring(text_.text());
00141 const std::string& text = utils::wstring_to_string(
00142 wide_string(wtext.begin() + start, wtext.begin() + start + length));
00143
00144 copy_to_clipboard(text, mouse);
00145 }
00146
00147 void ttext_::paste_selection(const bool mouse)
00148 {
00149 const std::string& text = copy_from_clipboard(mouse);
00150 if(text.empty()) {
00151 return;
00152 }
00153
00154 delete_selection();
00155
00156 selection_start_ += text_.insert_text(selection_start_, text);
00157
00158 update_canvas();
00159 set_dirty();
00160 fire(event::NOTIFY_MODIFIED, *this, NULL);
00161 }
00162
00163 void ttext_::set_selection_start(const size_t selection_start)
00164 {
00165 if(selection_start != selection_start_) {
00166 selection_start_ = selection_start;
00167 set_dirty();
00168 }
00169 }
00170
00171 void ttext_::set_selection_length(const int selection_length)
00172 {
00173 if(selection_length != selection_length_) {
00174 selection_length_ = selection_length;
00175 set_dirty();
00176 }
00177 }
00178
00179 void ttext_::set_state(const tstate state)
00180 {
00181 if(state != state_) {
00182 state_ = state;
00183 set_dirty(true);
00184 }
00185 }
00186
00187 void ttext_::handle_key_left_arrow(SDLMod modifier, bool& handled)
00188 {
00189
00190 DBG_GUI_E << LOG_SCOPE_HEADER << '\n';
00191
00192 handled = true;
00193 const int offset = selection_start_ - 1 + selection_length_;
00194 if(offset >= 0) {
00195 set_cursor(offset, (modifier & KMOD_SHIFT) != 0);
00196 }
00197 }
00198
00199 void ttext_::handle_key_right_arrow(SDLMod modifier, bool& handled)
00200 {
00201
00202 DBG_GUI_E << LOG_SCOPE_HEADER << '\n';
00203
00204 handled = true;
00205 const size_t offset = selection_start_ + 1 + selection_length_;
00206 if(offset <= text_.get_length()) {
00207 set_cursor(offset, (modifier & KMOD_SHIFT) != 0);
00208 }
00209 }
00210
00211 void ttext_::handle_key_home(SDLMod modifier, bool& handled)
00212 {
00213 DBG_GUI_E << LOG_SCOPE_HEADER << '\n';
00214
00215 handled = true;
00216 if(modifier & KMOD_CTRL) {
00217 goto_start_of_data((modifier & KMOD_SHIFT) != 0);
00218 } else {
00219 goto_start_of_line((modifier & KMOD_SHIFT) != 0);
00220 }
00221 }
00222
00223 void ttext_::handle_key_end(SDLMod modifier, bool& handled)
00224 {
00225 DBG_GUI_E << LOG_SCOPE_HEADER << '\n';
00226
00227 handled = true;
00228 if(modifier & KMOD_CTRL) {
00229 goto_end_of_data((modifier & KMOD_SHIFT) != 0);
00230 } else {
00231 goto_end_of_line((modifier & KMOD_SHIFT) != 0);
00232 }
00233 }
00234
00235 void ttext_::handle_key_backspace(SDLMod , bool& handled)
00236 {
00237 DBG_GUI_E << LOG_SCOPE_HEADER << '\n';
00238
00239 handled = true;
00240 if(selection_length_ != 0) {
00241 delete_selection();
00242 } else if(selection_start_){
00243 delete_char(true);
00244 }
00245 fire(event::NOTIFY_MODIFIED, *this, NULL);
00246 }
00247
00248 void ttext_::handle_key_delete(SDLMod , bool& handled)
00249 {
00250 DBG_GUI_E << LOG_SCOPE_HEADER << '\n';
00251
00252 handled = true;
00253 if(selection_length_ != 0) {
00254 delete_selection();
00255 } else if (selection_start_ < text_.get_length()) {
00256 delete_char(false);
00257 }
00258 fire(event::NOTIFY_MODIFIED, *this, NULL);
00259 }
00260
00261 void ttext_::handle_key_default(
00262 bool& handled, SDLKey , SDLMod , Uint16 unicode)
00263 {
00264 DBG_GUI_E << LOG_SCOPE_HEADER << '\n';
00265
00266 if(unicode >= 32 && unicode != 127) {
00267 handled = true;
00268 insert_char(unicode);
00269 fire(event::NOTIFY_MODIFIED, *this, NULL);
00270 }
00271 }
00272
00273 void ttext_::signal_handler_middle_button_click(
00274 const event::tevent event, bool& handled)
00275 {
00276 DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
00277
00278 paste_selection(true);
00279
00280 handled = true;
00281 }
00282
00283 void ttext_::signal_handler_sdl_key_down(const event::tevent event
00284 , bool& handled
00285 , const SDLKey key
00286 , SDLMod modifier
00287 , const Uint16 unicode)
00288 {
00289
00290 DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
00291
00292
00293
00294
00295 #ifdef __APPLE__
00296 const unsigned copypaste_modifier = KMOD_LMETA | KMOD_RMETA;
00297 #else
00298 const unsigned copypaste_modifier = KMOD_CTRL;
00299 #endif
00300
00301 switch(key) {
00302
00303 case SDLK_LEFT :
00304 handle_key_left_arrow(modifier, handled);
00305 break;
00306
00307 case SDLK_RIGHT :
00308 handle_key_right_arrow(modifier, handled);
00309 break;
00310
00311 case SDLK_UP :
00312 handle_key_up_arrow(modifier, handled);
00313 break;
00314
00315 case SDLK_DOWN :
00316 handle_key_down_arrow(modifier, handled);
00317 break;
00318
00319 case SDLK_PAGEUP :
00320 handle_key_page_up(modifier, handled);
00321 break;
00322
00323 case SDLK_PAGEDOWN :
00324 handle_key_page_down(modifier, handled);
00325 break;
00326
00327 case SDLK_a :
00328 if(!(modifier & KMOD_CTRL)) {
00329 handle_key_default(handled, key, modifier, unicode);
00330 break;
00331 }
00332
00333
00334 modifier = static_cast<SDLMod>(modifier &~ KMOD_CTRL);
00335
00336
00337 case SDLK_HOME :
00338 handle_key_home(modifier, handled);
00339 break;
00340
00341 case SDLK_e :
00342 if(!(modifier & KMOD_CTRL)) {
00343 handle_key_default(handled, key, modifier, unicode);
00344 break;
00345 }
00346
00347
00348 modifier = static_cast<SDLMod>(modifier &~ KMOD_CTRL);
00349
00350
00351 case SDLK_END :
00352 handle_key_end(modifier, handled);
00353 break;
00354
00355 case SDLK_BACKSPACE :
00356 handle_key_backspace(modifier, handled);
00357 break;
00358
00359 case SDLK_u :
00360 if(modifier & KMOD_CTRL) {
00361 handle_key_clear_line(modifier, handled);
00362 } else {
00363 handle_key_default(handled, key, modifier, unicode);
00364 }
00365 break;
00366
00367 case SDLK_DELETE :
00368 handle_key_delete(modifier, handled);
00369 break;
00370
00371 case SDLK_c :
00372 if(!(modifier & copypaste_modifier)) {
00373 handle_key_default(handled, key, modifier, unicode);
00374 break;
00375 }
00376
00377
00378
00379 copy_selection(false);
00380 handled = true;
00381 break;
00382
00383 case SDLK_x :
00384 if(!(modifier & copypaste_modifier)) {
00385 handle_key_default(handled, key, modifier, unicode);
00386 break;
00387 }
00388
00389 copy_selection(false);
00390 delete_selection();
00391 handled = true;
00392 break;
00393
00394 case SDLK_v :
00395 if(!(modifier & copypaste_modifier)) {
00396 handle_key_default(handled, key, modifier, unicode);
00397 break;
00398 }
00399
00400 paste_selection(false);
00401 handled = true;
00402 break;
00403
00404 default :
00405 handle_key_default(handled, key, modifier, unicode);
00406
00407 }
00408
00409 if(text_changed_callback_) {
00410 text_changed_callback_(this, this->text());
00411 }
00412
00413 }
00414
00415 void ttext_::signal_handler_receive_keyboard_focus(const event::tevent event)
00416 {
00417 DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
00418
00419 set_state(FOCUSSED);
00420 }
00421
00422 void ttext_::signal_handler_lose_keyboard_focus(const event::tevent event)
00423 {
00424 DBG_GUI_E << LOG_HEADER << ' ' << event << ".\n";
00425
00426 set_state(ENABLED);
00427 }
00428
00429 }
00430