button.cpp

Go to the documentation of this file.
00001 /* $Id: button.cpp 24835 2008-03-19 16:06:10Z brunowolff $ */
00002 /*
00003    Copyright (C) 2003 - 2008 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 version 2
00008    or at your option any later version.
00009    This program is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY.
00011 
00012    See the COPYING file for more details.
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         // check buttons should keep their state
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     //if we have a list of items, use the first one that isn't an image
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         // the cursor is over the widget
00262         if (state_ == NORMAL)
00263             state_ = ACTIVE;
00264         else if (state_ == PRESSED && type_ == TYPE_CHECK)
00265             state_ = PRESSED_ACTIVE;
00266     } else {
00267         // the cursor is not over the widget
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     // the user has stopped pressing the mouse left button while on the widget
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 }

Generated by doxygen 1.5.5 on 23 May 2008 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs