00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #define GETTEXT_DOMAIN "wesnoth-lib"
00017
00018 #include "global.hpp"
00019
00020 #include "widgets/button.hpp"
00021 #include "game_config.hpp"
00022 #include "font.hpp"
00023 #include "marked-up_text.hpp"
00024 #include "image.hpp"
00025 #include "log.hpp"
00026 #include "serialization/string_utils.hpp"
00027 #include "sound.hpp"
00028 #include "video.hpp"
00029 #include "wml_separators.hpp"
00030
00031 static lg::log_domain log_display("display");
00032 #define ERR_DP LOG_STREAM(err, log_display)
00033
00034 namespace gui {
00035
00036 const int font_size = font::SIZE_SMALL;
00037 const int horizontal_padding = font::SIZE_SMALL;
00038 const int checkbox_horizontal_padding = font::SIZE_SMALL / 2;
00039 const int vertical_padding = font::SIZE_SMALL / 2;
00040
00041 button::button(CVideo& video, const std::string& label, button::TYPE type,
00042 std::string button_image_name, SPACE_CONSUMPTION spacing, const bool auto_join)
00043 : widget(video, auto_join), type_(type), label_(label),
00044 image_(NULL), pressedImage_(NULL), activeImage_(NULL), pressedActiveImage_(NULL),
00045 button_(true), state_(NORMAL), pressed_(false),
00046 spacing_(spacing), base_height_(0), base_width_(0)
00047 {
00048 if(button_image_name.empty() && type == TYPE_PRESS) {
00049 button_image_name = "button";
00050 } else if(button_image_name.empty() && type == TYPE_CHECK) {
00051 button_image_name = "checkbox";
00052 }
00053
00054 const std::string button_image_file = "buttons/" + button_image_name + ".png";
00055 surface button_image(image::get_image(button_image_file));
00056 surface pressed_image(image::get_image("buttons/" + button_image_name + "-pressed.png"));
00057 surface active_image(image::get_image("buttons/" + button_image_name + "-active.png"));
00058 surface pressed_active_image;
00059
00060 if (pressed_image.null())
00061 pressed_image.assign(button_image);
00062
00063 if (active_image.null())
00064 active_image.assign(button_image);
00065
00066 if (type == TYPE_CHECK) {
00067 pressed_active_image.assign(image::get_image("buttons/" + button_image_name + "-active-pressed.png"));
00068 if (pressed_active_image.null())
00069 pressed_active_image.assign(pressed_image);
00070 }
00071
00072 if (button_image.null()) {
00073 ERR_DP << "error initializing button!\n";
00074 throw error();
00075 }
00076
00077 base_height_ = button_image->h;
00078 base_width_ = button_image->w;
00079
00080 if (type_ != TYPE_IMAGE){
00081 set_label(label);
00082 }
00083
00084 if(type == TYPE_PRESS) {
00085 image_.assign(scale_surface(button_image,location().w,location().h));
00086 pressedImage_.assign(scale_surface(pressed_image,location().w,location().h));
00087 activeImage_.assign(scale_surface(active_image,location().w,location().h));
00088 } else {
00089 image_.assign(scale_surface(button_image,button_image->w,button_image->h));
00090 pressedImage_.assign(scale_surface(pressed_image,button_image->w,button_image->h));
00091 activeImage_.assign(scale_surface(active_image,button_image->w,button_image->h));
00092 if (type == TYPE_CHECK)
00093 pressedActiveImage_.assign(scale_surface(pressed_active_image, button_image->w, button_image->h));
00094 }
00095
00096 if (type_ == TYPE_IMAGE){
00097 calculate_size();
00098 }
00099 }
00100
00101 button::~button()
00102 {
00103 }
00104
00105 void button::calculate_size()
00106 {
00107 if (type_ == TYPE_IMAGE){
00108 SDL_Rect loc_image = location();
00109 loc_image.h = image_->h;
00110 loc_image.w = image_->w;
00111 set_location(loc_image);
00112 return;
00113 }
00114 SDL_Rect const &loc = location();
00115 bool change_size = loc.h == 0 || loc.w == 0;
00116
00117 if (!change_size) {
00118 unsigned w = loc.w - (type_ == TYPE_PRESS ? horizontal_padding : checkbox_horizontal_padding + base_width_);
00119 if (type_ != TYPE_IMAGE)
00120 {
00121 int fs = font_size;
00122 int style = TTF_STYLE_NORMAL;
00123 std::string::const_iterator i_beg = label_.begin(), i_end = label_.end(),
00124 i = font::parse_markup(i_beg, i_end, &fs, NULL, &style);
00125 if (i != i_end) {
00126 std::string tmp(i, i_end);
00127 label_.erase(i - i_beg, i_end - i_beg);
00128 label_ += font::make_text_ellipsis(tmp, fs, w, style);
00129 }
00130 }
00131 }
00132
00133 if (type_ != TYPE_IMAGE){
00134 textRect_ = font::draw_text(NULL, screen_area(), font_size,
00135 font::BUTTON_COLOR, label_, 0, 0);
00136 }
00137
00138 if (!change_size)
00139 return;
00140
00141 set_height(std::max(textRect_.h+vertical_padding,base_height_));
00142 if(type_ == TYPE_PRESS) {
00143 if(spacing_ == MINIMUM_SPACE) {
00144 set_width(textRect_.w + horizontal_padding);
00145 } else {
00146 set_width(std::max(textRect_.w+horizontal_padding,base_width_));
00147 }
00148 } else {
00149 if(label_.empty()) {
00150 set_width(base_width_);
00151 } else {
00152 set_width(checkbox_horizontal_padding + textRect_.w + base_width_);
00153 }
00154 }
00155 }
00156
00157 void button::set_check(bool check)
00158 {
00159 if (type_ != TYPE_CHECK)
00160 return;
00161 STATE new_state = check ? PRESSED : NORMAL;
00162 if (state_ != new_state) {
00163 state_ = new_state;
00164 set_dirty();
00165 }
00166 }
00167
00168 void button::set_active(bool active)
00169 {
00170 if ((state_ == NORMAL) && active) {
00171 state_ = ACTIVE;
00172 set_dirty();
00173 } else if ((state_ == ACTIVE) && !active) {
00174 state_ = NORMAL;
00175 set_dirty();
00176 }
00177 }
00178
00179 bool button::checked() const
00180 {
00181 return state_ == PRESSED || state_ == PRESSED_ACTIVE;
00182 }
00183
00184 void button::enable(bool new_val)
00185 {
00186 if(new_val != enabled())
00187 {
00188 pressed_ = false;
00189
00190 if(type_ != TYPE_CHECK) {
00191 state_ = NORMAL;
00192 }
00193 widget::enable(new_val);
00194 }
00195 }
00196
00197 void button::draw_contents()
00198 {
00199 surface image = image_;
00200 const int image_w = image_->w;
00201
00202 int offset = 0;
00203 switch(state_) {
00204 case ACTIVE:
00205 image = activeImage_;
00206 break;
00207 case PRESSED:
00208 image = pressedImage_;
00209 if (type_ == TYPE_PRESS)
00210 offset = 1;
00211 break;
00212 case PRESSED_ACTIVE:
00213 image = pressedActiveImage_;
00214 break;
00215 default:
00216 break;
00217 }
00218
00219 SDL_Rect const &loc = location();
00220 SDL_Rect clipArea = loc;
00221 const int texty = loc.y + loc.h / 2 - textRect_.h / 2 + offset;
00222 int textx;
00223
00224 if (type_ != TYPE_CHECK)
00225 textx = loc.x + image->w / 2 - textRect_.w / 2 + offset;
00226 else {
00227 clipArea.w += image_w + checkbox_horizontal_padding;
00228 textx = loc.x + image_w + checkbox_horizontal_padding / 2;
00229 }
00230
00231 SDL_Color button_color = font::BUTTON_COLOR;
00232
00233 if (!enabled()) {
00234 static const Uint32 disabled_btn_color = 0xAAAAAA;
00235 static const double disabled_btn_adjust = 0.18;
00236 image = blend_surface(greyscale_image(image), disabled_btn_adjust, disabled_btn_color);
00237 button_color = font::GRAY_COLOR;
00238 }
00239
00240 video().blit_surface(loc.x, loc.y, image);
00241 if (type_ != TYPE_IMAGE){
00242 clipArea.x += offset;
00243 clipArea.y += offset;
00244 clipArea.w -= 2*offset;
00245 clipArea.h -= 2*offset;
00246 font::draw_text(&video(), clipArea, font_size, button_color, label_, textx, texty);
00247 }
00248
00249 update_rect(loc);
00250 }
00251
00252 bool button::hit(int x, int y) const
00253 {
00254 return point_in_rect(x,y,location());
00255 }
00256
00257 static bool not_image(const std::string& str) { return !str.empty() && str[0] != IMAGE_PREFIX; }
00258
00259 void button::set_label(const std::string& val)
00260 {
00261 label_ = val;
00262
00263
00264 if (std::find(label_.begin(), label_.end(), COLUMN_SEPARATOR) != label_.end()) {
00265 const std::vector<std::string>& items = utils::split(label_, COLUMN_SEPARATOR);
00266 const std::vector<std::string>::const_iterator i = std::find_if(items.begin(),items.end(),not_image);
00267 if(i != items.end()) {
00268 label_ = *i;
00269 }
00270 }
00271
00272 calculate_size();
00273
00274 set_dirty(true);
00275 }
00276
00277 void button::mouse_motion(SDL_MouseMotionEvent const &event)
00278 {
00279 if (hit(event.x, event.y)) {
00280
00281 if (state_ == NORMAL)
00282 state_ = ACTIVE;
00283 else if (state_ == PRESSED && type_ == TYPE_CHECK)
00284 state_ = PRESSED_ACTIVE;
00285 } else {
00286
00287 if (state_ == PRESSED_ACTIVE)
00288 state_ = PRESSED;
00289 else if ((type_ != TYPE_CHECK && type_ != TYPE_IMAGE) || state_ != PRESSED)
00290 state_ = NORMAL;
00291 }
00292 }
00293
00294 void button::mouse_down(SDL_MouseButtonEvent const &event)
00295 {
00296 if (hit(event.x, event.y) && event.button == SDL_BUTTON_LEFT && type_ != TYPE_CHECK){
00297 state_ = PRESSED;
00298 sound::play_UI_sound(game_config::sounds::button_press);
00299 }
00300 }
00301
00302 void button::release(){
00303 state_ = NORMAL;
00304 draw_contents();
00305 }
00306
00307 void button::mouse_up(SDL_MouseButtonEvent const &event)
00308 {
00309 if (!(hit(event.x, event.y) && event.button == SDL_BUTTON_LEFT))
00310 return;
00311
00312 switch (type_) {
00313 case TYPE_CHECK:
00314 state_ = state_ == ACTIVE ? PRESSED_ACTIVE : ACTIVE;
00315 pressed_ = true;
00316 sound::play_UI_sound(game_config::sounds::checkbox_release);
00317 break;
00318 case TYPE_PRESS:
00319 if (state_ == PRESSED) {
00320 state_ = ACTIVE;
00321 pressed_ = true;
00322 }
00323 break;
00324 case TYPE_TURBO:
00325 state_ = ACTIVE;
00326 break;
00327 case TYPE_IMAGE:
00328 pressed_ = true;
00329 break;
00330 }
00331 }
00332
00333 void button::handle_event(const SDL_Event& event)
00334 {
00335 if (hidden() || !enabled())
00336 return;
00337
00338 STATE start_state = state_;
00339
00340 if (!mouse_locked())
00341 {
00342 switch(event.type) {
00343 case SDL_MOUSEBUTTONDOWN:
00344 mouse_down(event.button);
00345 break;
00346 case SDL_MOUSEBUTTONUP:
00347 mouse_up(event.button);
00348 break;
00349 case SDL_MOUSEMOTION:
00350 mouse_motion(event.motion);
00351 break;
00352 default:
00353 return;
00354 }
00355 }
00356
00357 if (start_state != state_)
00358 set_dirty(true);
00359 }
00360
00361 bool button::pressed()
00362 {
00363 if (type_ != TYPE_TURBO) {
00364 const bool res = pressed_;
00365 pressed_ = false;
00366 return res;
00367 } else
00368 return state_ == PRESSED || state_ == PRESSED_ACTIVE;
00369 }
00370
00371 }