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