widgets/button.cpp

Go to the documentation of this file.
00001 /* $Id: button.cpp 52533 2012-01-07 02:35:17Z shadowmaster $ */
00002 /*
00003    Copyright (C) 2003 - 2012 by David White <dave@whitevine.net>
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 "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         // check buttons should keep their state
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     //if we have a list of items, use the first one that isn't an image
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         // the cursor is over the widget
00281         if (state_ == NORMAL)
00282             state_ = ACTIVE;
00283         else if (state_ == PRESSED && type_ == TYPE_CHECK)
00284             state_ = PRESSED_ACTIVE;
00285     } else {
00286         // the cursor is not over the widget
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     // the user has stopped pressing the mouse left button while on the widget
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

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