gui/widgets/text.cpp

Go to the documentation of this file.
00001 /* $Id: text.cpp 54028 2012-04-29 20:48:43Z mordante $ */
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.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         // pastes on UNIX systems.
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         // default to put the cursor at the end of the buffer.
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         // selecting copies on UNIX systems.
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         // Update status
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     /** @todo implement the ctrl key. */
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     /** @todo implement the ctrl key. */
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 /*modifier*/, 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 /*modifier*/, 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 /*key*/, SDLMod /*modifier*/, 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 // For copy/paste we use a different key on the MAC. Other ctrl modifiers won't
00293 // be modifed seems not to be required when I read the comment in
00294 // widgets/textbox.cpp:516. Would be nice if somebody on a MAC would test it.
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             // If ctrl-a is used for home drop the control modifier
00334             modifier = static_cast<SDLMod>(modifier &~ KMOD_CTRL);
00335             /* FALL DOWN */
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             // If ctrl-e is used for end drop the control modifier
00348             modifier = static_cast<SDLMod>(modifier &~ KMOD_CTRL);
00349             /* FALL DOWN */
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             // atm we don't care whether there is something to copy or paste
00378             // if nothing is there we still don't want to be chained.
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 } // namespace gui2
00430 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

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