display.cpp

Go to the documentation of this file.
00001 /* $Id: display.cpp 26233 2008-04-29 18:48:23Z alink $ */
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 //! @file display.cpp
00016 //! Routines to set up the display, scroll and zoom the map.
00017 
00018 #include "global.hpp"
00019 
00020 #include "actions.hpp"
00021 #include "cursor.hpp"
00022 #include "display.hpp"
00023 #include "events.hpp"
00024 #include "font.hpp"
00025 #include "game_config.hpp"
00026 #include "gettext.hpp"
00027 #include "hotkeys.hpp"
00028 #include "language.hpp"
00029 #include "log.hpp"
00030 #include "marked-up_text.hpp"
00031 #include "minimap.hpp"
00032 #include "pathfind.hpp"
00033 #include "preferences.hpp"
00034 #include "sdl_utils.hpp"
00035 #include "theme.hpp"
00036 #include "tooltips.hpp"
00037 #include "util.hpp"
00038 
00039 #include "SDL_image.h"
00040 
00041 #include <algorithm>
00042 #include <cassert>
00043 #ifdef __SUNPRO_CC
00044 // GCC doesn't have hypot in cmath so include it for Sun Studio
00045 #include <math.h>
00046 #endif
00047 #include <cmath>
00048 #include <iostream>
00049 #include <sstream>
00050 
00051 #define ERR_DP LOG_STREAM(err, display)
00052 #define INFO_DP LOG_STREAM(info, display)
00053 #define DBG_DP LOG_STREAM(debug, display)
00054 
00055 namespace {
00056 #ifdef USE_TINY_GUI
00057     const int DefaultZoom = 36;
00058     const int SmallZoom   = 16;
00059 #else
00060     const int DefaultZoom = 72;
00061     const int SmallZoom   = 36;
00062 #endif
00063 
00064     const int MinZoom = 4;
00065     const int MaxZoom = 200;
00066     size_t sunset_delay = 0;
00067     size_t sunset_timer = 0;
00068 
00069     bool benchmark = false;
00070 }
00071 
00072 display::display(CVideo& video, const gamemap& map, const config& theme_cfg, const config& cfg, const config& level) :
00073     screen_(video), 
00074     map_(map), 
00075     viewpoint_(NULL), 
00076     xpos_(0), 
00077     ypos_(0),
00078     theme_(theme_cfg,
00079     screen_area()),
00080     zoom_(DefaultZoom), 
00081     last_zoom_(SmallZoom),
00082     builder_(cfg, level, map, theme_.border().tile_image),
00083     minimap_(NULL), 
00084     minimap_location_(empty_rect),
00085     redrawMinimap_(false), 
00086     redraw_background_(true),
00087     invalidateAll_(true), 
00088     grid_(false),
00089     diagnostic_label_(0), 
00090     panelsDrawn_(false),
00091     turbo_speed_(2), 
00092     turbo_(false),
00093     invalidateGameStatus_(true),
00094     map_labels_(*this,map, 0),
00095     _scroll_event("scrolled"),
00096     nextDraw_(0),
00097     report_(),
00098     buttons_(),
00099     invalidated_(),
00100     hex_overlay_(),
00101     selected_hex_overlay_(0),
00102     mouseover_hex_overlay_(0),
00103     selectedHex_(),
00104     mouseoverHex_(),
00105     highlighted_locations_(),
00106     keys_(),
00107 #if TDRAWING_BUFFER_USES_VECTOR 
00108     drawing_buffer_(LAYER_LAST_LAYER),
00109 #else   
00110     drawing_buffer_(),
00111 #endif  
00112     map_screenshot_(false),
00113     fps_handle_(0),
00114     idle_anim_(preferences::idle_anim()),
00115     idle_anim_rate_(1.0),
00116     map_screenshot_surf_(NULL)
00117 {
00118     if(non_interactive()) {
00119         screen_.lock_updates(true);
00120     }
00121 
00122     set_idle_anim_rate(preferences::idle_anim_rate());
00123 
00124     std::fill(reportRects_,reportRects_+reports::NUM_REPORTS,empty_rect);
00125 
00126     image::set_zoom(zoom_);
00127 }
00128 
00129 display::~display()
00130 {
00131 }
00132 
00133 const SDL_Rect& display::max_map_area() const
00134 {
00135     static SDL_Rect max_area = {0, 0, 0, 0};
00136 
00137     // hex_size() is always a multiple of 4
00138     // and hex_width() a multiple of 3,
00139     // so there shouldn't be off-by-one-errors
00140     // due to rounding.
00141     // To display a hex fully on screen,
00142     // a little bit extra space is needed.
00143     // Also added the border two times.
00144     max_area.w  = static_cast<int>((map_.w() + 2 * theme_.border().size + 1.0/3.0) * hex_width());
00145     max_area.h = static_cast<int>((map_.h() + 2 * theme_.border().size + 0.5) * hex_size());
00146 
00147     return max_area;
00148 }
00149 
00150 const SDL_Rect& display::map_area() const
00151 {
00152     static SDL_Rect max_area;
00153     max_area = max_map_area();
00154 
00155     // if it's for map_screenshot, maximize and don't recenter
00156     if (map_screenshot_) {
00157         return max_area;
00158     }
00159 
00160     static SDL_Rect res;
00161     res = map_outside_area();
00162 
00163     if(max_area.w < res.w) {
00164         // map is smaller, center
00165         res.x += (res.w - max_area.w)/2;
00166         res.w = max_area.w;
00167     }
00168 
00169     if(max_area.h < res.h) {
00170         // map is smaller, center
00171         res.y += (res.h - max_area.h)/2;
00172         res.h = max_area.h;
00173     }
00174 
00175     return res;
00176 }
00177 
00178 bool display::outside_area(const SDL_Rect& area, const int x, const int y) const
00179 {
00180     const int x_thresh = hex_size();
00181     const int y_thresh = hex_size();
00182     return (x < area.x || x > area.x + area.w - x_thresh ||
00183         y < area.y || y > area.y + area.h - y_thresh);
00184 }
00185 
00186 // This function use the screen as reference
00187 const gamemap::location display::hex_clicked_on(int xclick, int yclick,
00188         gamemap::location::DIRECTION* nearest_hex,
00189         gamemap::location::DIRECTION* second_nearest_hex) const
00190 {
00191     const SDL_Rect& rect = map_area();
00192     if(point_in_rect(xclick,yclick,rect) == false) {
00193         return gamemap::location();
00194     }
00195 
00196     xclick -= rect.x;
00197     yclick -= rect.y;
00198 
00199     return pixel_position_to_hex(xpos_ + xclick, ypos_ + yclick, nearest_hex, second_nearest_hex);
00200 }
00201 
00202 
00203 // This function use the rect of map_area as reference
00204 const gamemap::location display::pixel_position_to_hex(int x, int y,
00205         gamemap::location::DIRECTION* nearest_hex,
00206         gamemap::location::DIRECTION* second_nearest_hex) const
00207 {
00208     // adjust for the border
00209     x -= static_cast<int>(theme_.border().size * hex_width());
00210     y -= static_cast<int>(theme_.border().size * hex_size());
00211     // The editor can modify the border and this will result in a negative y
00212     // value. Instead of adding extra cases we just shift the hex. Since the
00213     // editor doesn't use the direction this is no problem.
00214     const int offset = y < 0 ? 1 : 0;
00215     if(offset) {
00216         x += hex_width();
00217         y += hex_size();
00218     }
00219     const int s = hex_size();
00220     const int tesselation_x_size = hex_width() * 2;
00221     const int tesselation_y_size = s;
00222     const int x_base = x / tesselation_x_size * 2;
00223     const int x_mod  = x % tesselation_x_size;
00224     const int y_base = y / tesselation_y_size;
00225     const int y_mod  = y % tesselation_y_size;
00226 
00227     int x_modifier = 0;
00228     int y_modifier = 0;
00229 
00230     if (y_mod < tesselation_y_size / 2) {
00231         if ((x_mod * 2 + y_mod) < (s / 2)) {
00232             x_modifier = -1;
00233             y_modifier = -1;
00234         } else if ((x_mod * 2 - y_mod) < (s * 3 / 2)) {
00235             x_modifier = 0;
00236             y_modifier = 0;
00237         } else {
00238             x_modifier = 1;
00239             y_modifier = -1;
00240         }
00241 
00242     } else {
00243         if ((x_mod * 2 - (y_mod - s / 2)) < 0) {
00244             x_modifier = -1;
00245             y_modifier = 0;
00246         } else if ((x_mod * 2 + (y_mod - s / 2)) < s * 2) {
00247             x_modifier = 0;
00248             y_modifier = 0;
00249         } else {
00250             x_modifier = 1;
00251             y_modifier = 0;
00252         }
00253     }
00254 
00255     const gamemap::location res(x_base + x_modifier - offset, y_base + y_modifier - offset);
00256 
00257     if(nearest_hex != NULL) {
00258         // Our x and y use the map_area as reference.
00259         // The coordinates given by get_location use the screen as reference,
00260         // so we need to convert it.
00261         const int centerx = (get_location_x(res) - map_area().x + xpos_) + hex_size()/2 - hex_width();
00262         const int centery = (get_location_y(res) - map_area().y + ypos_) + hex_size()/2 - hex_size();
00263         const int x_offset = x - centerx;
00264         const int y_offset = y - centery;
00265         if(y_offset > 0) {
00266             if(x_offset > y_offset/2) {
00267                 *nearest_hex = gamemap::location::SOUTH_EAST;
00268                 if(second_nearest_hex != NULL) {
00269                     if(x_offset/2 > y_offset) {
00270                         *second_nearest_hex = gamemap::location::NORTH_EAST;
00271                     } else {
00272                         *second_nearest_hex = gamemap::location::SOUTH;
00273                     }
00274                 }
00275             } else if(-x_offset > y_offset/2) {
00276                 *nearest_hex = gamemap::location::SOUTH_WEST;
00277                 if(second_nearest_hex != NULL) {
00278                     if(-x_offset/2 > y_offset) {
00279                         *second_nearest_hex = gamemap::location::NORTH_WEST;
00280                     } else {
00281                         *second_nearest_hex = gamemap::location::SOUTH;
00282                     }
00283                 }
00284             } else {
00285                 *nearest_hex = gamemap::location::SOUTH;
00286                 if(second_nearest_hex != NULL) {
00287                     if(x_offset > 0) {
00288                         *second_nearest_hex = gamemap::location::SOUTH_EAST;
00289                     } else {
00290                         *second_nearest_hex = gamemap::location::SOUTH_WEST;
00291                     }
00292                 }
00293             }
00294         } else { // y_offset <= 0
00295             if(x_offset > -y_offset/2) {
00296                 *nearest_hex = gamemap::location::NORTH_EAST;
00297                 if(second_nearest_hex != NULL) {
00298                     if(x_offset/2 > -y_offset) {
00299                         *second_nearest_hex = gamemap::location::SOUTH_EAST;
00300                     } else {
00301                         *second_nearest_hex = gamemap::location::NORTH;
00302                     }
00303                 }
00304             } else if(-x_offset > -y_offset/2) {
00305                 *nearest_hex = gamemap::location::NORTH_WEST;
00306                 if(second_nearest_hex != NULL) {
00307                     if(-x_offset/2 > -y_offset) {
00308                         *second_nearest_hex = gamemap::location::SOUTH_WEST;
00309                     } else {
00310                         *second_nearest_hex = gamemap::location::NORTH;
00311                     }
00312                 }
00313             } else {
00314                 *nearest_hex = gamemap::location::NORTH;
00315                 if(second_nearest_hex != NULL) {
00316                     if(x_offset > 0) {
00317                         *second_nearest_hex = gamemap::location::NORTH_EAST;
00318                     } else {
00319                         *second_nearest_hex = gamemap::location::NORTH_WEST;
00320                     }
00321                 }
00322             }
00323         }
00324     }
00325 
00326     return res;
00327 }
00328 
00329 void display::get_rect_hex_bounds(SDL_Rect rect, gamemap::location &topleft, gamemap::location &bottomright) const
00330 {
00331     // Change the coordinates of the rect send
00332     // to be relative to the map area, instead of the screen area.
00333     const SDL_Rect& map_rect = map_area();
00334     rect.x -= map_rect.x;
00335     rect.y -= map_rect.y;
00336     // Only move the left side.
00337     // The right side should remain
00338     // at the same coordinates, so fix that
00339     rect.w += map_rect.x;
00340     rect.h += map_rect.y;
00341 
00342     const int tile_width = hex_width();
00343 
00344     // Adjust for the border
00345     topleft.x = static_cast<int>(-theme_.border().size + (xpos_ + rect.x) / tile_width);
00346     topleft.y = static_cast<int>(-theme_.border().size + (ypos_ + rect.y - (is_odd(topleft.x) ? zoom_/2 : 0)) / zoom_);
00347 
00348     bottomright.x = static_cast<int>(-theme_.border().size + (xpos_ + rect.x + rect.w) / tile_width);
00349     bottomright.y = static_cast<int>(-theme_.border().size + ((ypos_ + rect.y + rect.h) - (is_odd(bottomright.x) ? zoom_/2 : 0)) / zoom_);
00350 
00351     // This routine does a rough approximation, so might be off by one.
00352     // To be sure enough tiles are included, the boundaries are increased
00353     // by one if the terrain is "on the map" due to the extra border.
00354     // This uses a bit larger area.
00355     //! @todo FIXME This routine should properly determine what to update,
00356     //! and not increase by one just to be sure.
00357     if(topleft.x >= -1) {
00358         topleft.x--;
00359     }
00360     if(topleft.y >= -1) {
00361         topleft.y--;
00362     }
00363     if(bottomright.x <= map_.w()) {
00364         bottomright.x++;
00365     }
00366     if(bottomright.y <= map_.h()) {
00367         bottomright.y++;
00368     }
00369 }
00370 
00371 int display::get_location_x(const gamemap::location& loc) const
00372 {
00373     return static_cast<int>(map_area().x + (loc.x + theme_.border().size) * hex_width() - xpos_);
00374 }
00375 
00376 int display::get_location_y(const gamemap::location& loc) const
00377 {
00378     return static_cast<int>(map_area().y + (loc.y + theme_.border().size) * zoom_ - ypos_ + (is_odd(loc.x) ? zoom_/2 : 0));
00379 }
00380 
00381 gamemap::location display::minimap_location_on(int x, int y)
00382 {
00383     //TODO: don't return location for this,
00384     // instead directly scroll to the clicked pixel position
00385 
00386     if (!point_in_rect(x, y, minimap_area())) {
00387         return gamemap::location();
00388     }
00389 
00390     // we transfom the coordinates from minimap to the full map image
00391     // probably more adjustements to do (border, minimap shift...)
00392     // but the mouse and human capacity to evaluate the rectangle center
00393     // is not pixel precise.
00394     int px = (x - minimap_location_.x) * map_.w()*hex_width() / minimap_location_.w;
00395     int py = (y - minimap_location_.y) * map_.h()*hex_size() / minimap_location_.h;
00396 
00397     gamemap::location loc = pixel_position_to_hex(px, py);
00398     if (loc.x < 0)
00399         loc.x = 0;
00400     else if (loc.x >= map_.w())
00401         loc.x = map_.w() - 1;
00402     
00403     if (loc.y < 0)
00404         loc.y = 0;
00405     else if (loc.y >= map_.h())
00406         loc.y = map_.h() - 1;
00407 
00408     return loc;
00409 }
00410 
00411 void display::get_visible_hex_bounds(gamemap::location &topleft, gamemap::location &bottomright) const
00412 {
00413     SDL_Rect r = map_area();
00414     get_rect_hex_bounds(r, topleft, bottomright);
00415 }
00416 
00417 int display::screenshot(std::string filename, bool map_screenshot)
00418 {
00419     int size = 0;
00420     if (!map_screenshot) {
00421         surface screenshot_surf = screen_.getSurface();
00422         SDL_SaveBMP(screenshot_surf, filename.c_str());
00423         size = screenshot_surf->w * screenshot_surf->h;
00424     } else {
00425         if (map_.empty()) {
00426             // Map Screenshot are big, abort and warn the user if he does strange things
00427             std::cerr << "No map, can't do a Map Screenshot. If it was not wanted, check your hotkey.\n";
00428             return -1;
00429         }
00430 
00431         SDL_Rect area = max_map_area();
00432         map_screenshot_surf_ = create_compatible_surface(screen_.getSurface(), area.w, area.h);
00433         
00434         if (map_screenshot_surf_ == NULL) {
00435             // Memory problem ?
00436             std::cerr << "Can't create the screenshot surface. Maybe too big, try dezooming.\n";
00437             return -1;
00438         }
00439         size = map_screenshot_surf_->w * map_screenshot_surf_->h;
00440 
00441         // back up the current map view position and move to top-left
00442         int old_xpos = xpos_;
00443         int old_ypos = ypos_;
00444         xpos_ = 0;
00445         ypos_ = 0;
00446 
00447         // we reroute render output to the screenshot surface and invalidate all
00448         map_screenshot_= true ;
00449         invalidateAll_ = true;
00450         DBG_DP << "draw() with map_screenshot\n";
00451         draw(true,true);
00452 
00453         // finally save the image on disk
00454         SDL_SaveBMP(map_screenshot_surf_, filename.c_str());
00455 
00456         //NOTE: need to be sure that we free this huge surface (is it enough?)
00457         map_screenshot_surf_ = NULL;
00458 
00459         // restore normal rendering
00460         map_screenshot_= false;
00461         xpos_ = old_xpos;
00462         ypos_ = old_ypos;
00463         // some drawing functions are confused by the temporary change
00464         // of the map_area and thus affect the UI outside of the map
00465         redraw_everything();
00466     }
00467 
00468     // convert pixel size to BMP size
00469     size = (2048 + size*3);
00470     return size;
00471 }
00472 
00473 gui::button* display::find_button(const std::string& id)
00474 {
00475     for (size_t i = 0; i < buttons_.size(); ++i) {
00476         if(buttons_[i].id() == id) {
00477             return &buttons_[i];
00478         }
00479     }
00480     return NULL;
00481 }
00482 
00483 void display::create_buttons()
00484 {
00485     std::vector<gui::button> work;
00486 
00487     DBG_DP << "creating buttons...\n";
00488     const std::vector<theme::menu>& buttons = theme_.menus();
00489     for(std::vector<theme::menu>::const_iterator i = buttons.begin(); i != buttons.end(); ++i) {
00490         gui::button b(screen_,i->title(),string_to_button_type(i->type()),i->image());
00491         DBG_DP << "drawing button " << i->get_id() << "\n"; 
00492         b.set_id(i->get_id());
00493         const SDL_Rect& loc = i->location(screen_area());
00494         b.set_location(loc.x,loc.y);
00495         if (!i->tooltip().empty()){
00496             tooltips::add_tooltip(loc, i->tooltip());
00497         }
00498         if(rects_overlap(b.location(),map_outside_area())) {
00499             b.set_volatile(true);
00500         }
00501 
00502         gui::button* b_prev = find_button(b.id());
00503         if(b_prev) b.enable(b_prev->enabled());
00504 
00505         work.push_back(b);
00506     }
00507 
00508     buttons_.swap(work);
00509     DBG_DP << "buttons created\n";
00510 }
00511 
00512 gui::button::TYPE display::string_to_button_type(std::string type)
00513 {
00514     gui::button::TYPE res = gui::button::TYPE_PRESS;
00515     if (type == "checkbox") { res = gui::button::TYPE_CHECK; }
00516     else if (type == "image") { res = gui::button::TYPE_IMAGE; }
00517     return res;
00518 }
00519 
00520 static const std::string& get_direction(size_t n)
00521 {
00522     static std::string const dirs[6] = { "-n", "-ne", "-se", "-s", "-sw", "-nw" };
00523     return dirs[n >= sizeof(dirs)/sizeof(*dirs) ? 0 : n];
00524 }
00525 
00526 std::vector<std::string> display::get_fog_shroud_graphics(const gamemap::location& loc)
00527 {
00528     std::vector<std::string> res;
00529 
00530     gamemap::location adjacent[6];
00531     get_adjacent_tiles(loc,adjacent);
00532     t_translation::t_terrain tiles[6];
00533 
00534     static const t_translation::t_terrain terrain_types[] =
00535         { t_translation::FOGGED, t_translation::VOID_TERRAIN, t_translation::NONE_TERRAIN };
00536 
00537     for(int i = 0; i != 6; ++i) {
00538         if(shrouded(adjacent[i])) {
00539             tiles[i] = t_translation::VOID_TERRAIN;
00540         } else if(!fogged(loc) && fogged(adjacent[i])) {
00541             tiles[i] = t_translation::FOGGED;
00542         } else {
00543             tiles[i] = t_translation::NONE_TERRAIN;
00544         }
00545     }
00546 
00547 
00548     for(const t_translation::t_terrain *terrain = terrain_types;
00549             *terrain != t_translation::NONE_TERRAIN; terrain ++) {
00550 
00551         // Find somewhere that doesn't have overlap to use as a starting point
00552         int start;
00553         for(start = 0; start != 6; ++start) {
00554             if(tiles[start] != *terrain) {
00555                 break;
00556             }
00557         }
00558 
00559         if(start == 6) {
00560             start = 0;
00561         }
00562 
00563         // Find all the directions overlap occurs from
00564         for(int i = (start+1)%6, n = 0; i != start && n != 6; ++n) {
00565             if(tiles[i] == *terrain) {
00566                 std::ostringstream stream;
00567                 std::string name;
00568                 // if(*terrain == terrain_type::VOID_TERRAIN)
00569                 //  stream << "void";
00570                 //else
00571                 //  stream << "fog";
00572                 stream << "terrain/" << map_.get_terrain_info(*terrain).minimap_image();
00573 
00574                 for(int n = 0; *terrain == tiles[i] && n != 6; i = (i+1)%6, ++n) {
00575                     stream << get_direction(i);
00576 
00577                     if(!image::exists(stream.str() + ".png")) {
00578                         // If we don't have any surface at all,
00579                         // then move onto the next overlapped area
00580                         if(name.empty()) {
00581                             i = (i+1)%6;
00582                         }
00583                         break;
00584                     } else {
00585                         name = stream.str();
00586                     }
00587                 }
00588 
00589                 if(!name.empty()) {
00590                     res.push_back(name + ".png");
00591                 }
00592             } else {
00593                 i = (i+1)%6;
00594             }
00595         }
00596     }
00597 
00598     return res;
00599 }
00600 
00601 std::vector<surface> display::get_terrain_images(const gamemap::location &loc,
00602                              const std::string timeid,
00603         image::TYPE image_type,
00604         ADJACENT_TERRAIN_TYPE terrain_type)
00605 {
00606     std::vector<surface> res;
00607 
00608     if(terrain_type == ADJACENT_FOGSHROUD) {
00609         const std::vector<std::string> fog_shroud = get_fog_shroud_graphics(loc);
00610 
00611         if(!fog_shroud.empty()) {
00612             for(std::vector<std::string>::const_iterator it = fog_shroud.begin(); it != fog_shroud.end(); ++it) {
00613                 image::locator image(*it);
00614 
00615                 const surface surface(image::get_image(image, image_type));
00616                 if (!surface.null()) {
00617                     res.push_back(surface);
00618                 }
00619             }
00620 
00621         }
00622 
00623         return res;
00624     }
00625 
00626     terrain_builder::ADJACENT_TERRAIN_TYPE builder_terrain_type =
00627           (terrain_type == ADJACENT_FOREGROUND ?
00628           terrain_builder::ADJACENT_FOREGROUND : terrain_builder::ADJACENT_BACKGROUND);
00629     const terrain_builder::imagelist* const terrains = builder_.get_terrain_at(loc,
00630             timeid, builder_terrain_type);
00631 
00632     if(terrains != NULL) {
00633         // Cache the offmap name.
00634         // Since it is themabel it can change,
00635         // so don't make it static.
00636         const std::string off_map_name = "terrain/" + theme_.border().tile_image + ".png";
00637         for(std::vector<animated<image::locator> >::const_iterator it =
00638                 terrains->begin(); it != terrains->end(); ++it) {
00639 
00640             image::locator image = preferences::animate_map() ?
00641                 it->get_current_frame() : it->get_first_frame();
00642 
00643             // We prevent ToD colouring and brightening of off-map tiles,
00644             // except if we are not in_game and so in the editor.
00645             // We need to test for the tile to be rendered and
00646             // not the location, since the transitions are rendered
00647             // over the offmap-terrain and these need a ToD colouring.
00648             const bool off_map = (image.get_filename() == off_map_name);
00649             const surface surface(image::get_image(image,
00650                 off_map ? image::UNMASKED : image_type));
00651 
00652             if (!surface.null()) {
00653                 res.push_back(surface);
00654             }
00655         }
00656     }
00657 
00658     return res;
00659 }
00660 
00661 void display::drawing_buffer_commit()
00662 {
00663     SDL_Rect clip_rect = map_area();
00664     surface const dst(get_screen_surface());
00665     clip_rect_setter set_clip_rect(dst, clip_rect);
00666 
00667     // Simply go down all levels in the drawing_buffer_ and render them.
00668     for(tdrawing_buffer::const_iterator 
00669             layer_itor = drawing_buffer_.begin(), 
00670             layer_itor_end = drawing_buffer_.end();
00671             layer_itor != layer_itor_end; ++layer_itor) {
00672 
00673         for(std::map<int, std::vector<tblit> >::const_iterator 
00674 #if TDRAWING_BUFFER_USES_VECTOR     
00675                 drawing_iterator = layer_itor->begin(),
00676                 drawing_iterator_end = layer_itor->end();
00677 #else               
00678                 drawing_iterator = layer_itor->second.begin(),
00679                 drawing_iterator_end = layer_itor->second.end();
00680 #endif              
00681                 drawing_iterator != drawing_iterator_end; ++drawing_iterator) {
00682 
00683             for(std::vector<tblit>::const_iterator 
00684                     blit_itor = drawing_iterator->second.begin(),
00685                     blit_itor_end = drawing_iterator->second.end();
00686                     blit_itor != blit_itor_end; ++blit_itor) {
00687 
00688                 for(std::vector<surface>::const_iterator 
00689                         surface_itor = blit_itor->surf.begin(),
00690                         surface_itor_end = blit_itor->surf.end();
00691                         surface_itor != surface_itor_end; ++surface_itor) {
00692 
00693                     // Note that dstrect can be changed by SDL_BlitSurface
00694                     // and so a new instance should be initialized
00695                     // to pass to each call to SDL_BlitSurface.
00696                     SDL_Rect dstrect = { blit_itor->x, blit_itor->y, 0, 0 };
00697 
00698                     if(blit_itor->clip.x || blit_itor->clip.y 
00699                             ||blit_itor->clip.w ||blit_itor->clip.h) {
00700 
00701                         SDL_Rect srcrect = blit_itor->clip;
00702                         SDL_BlitSurface(*surface_itor, &srcrect, dst, &dstrect);
00703                     } else {
00704                         SDL_BlitSurface(*surface_itor, NULL, dst, &dstrect);
00705                     }
00706                 }
00707                 update_rect(blit_itor->x, blit_itor->y, zoom_, zoom_);
00708             }
00709         }
00710     }
00711 
00712     drawing_buffer_clear();
00713 }
00714 
00715 void display::drawing_buffer_clear()
00716 {
00717 #if TDRAWING_BUFFER_USES_VECTOR
00718     // Note clear the items, the vector should remain the same size.
00719     for(tdrawing_buffer::iterator layer_itor = 
00720             drawing_buffer_.begin(), 
00721             layer_itor_end = drawing_buffer_.end();
00722             layer_itor != layer_itor_end; ++layer_itor) {
00723 
00724         layer_itor->clear();
00725     }
00726 #else   
00727     drawing_buffer_.clear();
00728 #endif  
00729 }
00730 
00731 void display::sunset(const size_t delay)
00732 {
00733     // This allow both parametric and toggle use
00734     sunset_delay = (sunset_delay == 0 && delay == 0) ? 5 : delay;
00735 }
00736 
00737 void display::toggle_benchmark()
00738 {
00739     benchmark = !benchmark;
00740 }
00741 
00742 void display::flip()
00743 {
00744     if(video().faked()) {
00745         return;
00746     }
00747 
00748     const surface frameBuffer = get_video_surface();
00749 
00750     // This is just the debug function "sunset" to progressively darken the map area
00751     if (sunset_delay && ++sunset_timer > sunset_delay) {
00752         sunset_timer = 0;
00753         SDL_Rect r = map_outside_area(); // Use frameBuffer to also test the UI
00754         const Uint32 color =  SDL_MapRGBA(video().getSurface()->format,0,0,0,255);
00755         // Adjust the alpha if you want to balance cpu-cost / smooth sunset
00756         fill_rect_alpha(r, color, 1, frameBuffer);
00757         update_rect(r);
00758     }
00759 
00760     font::draw_floating_labels(frameBuffer);
00761     events::raise_volatile_draw_event();
00762     cursor::draw(frameBuffer);
00763 
00764     video().flip();
00765 
00766     cursor::undraw(frameBuffer);
00767     events::raise_volatile_undraw_event();
00768     font::undraw_floating_labels(frameBuffer);
00769 }
00770 
00771 void display::update_display()
00772 {
00773     if(screen_.update_locked()) {
00774         return;
00775     }
00776 
00777     if(preferences::show_fps() || benchmark) {
00778         static int last_sample = SDL_GetTicks();
00779         static int frames = 0;
00780         ++frames;
00781 
00782         if(frames == 10) {
00783             const int this_sample = SDL_GetTicks();
00784 
00785             const int fps = (frames*1000)/(this_sample - last_sample);
00786             last_sample = this_sample;
00787             frames = 0;
00788 
00789             if(fps_handle_ != 0) {
00790                 font::remove_floating_label(fps_handle_);
00791                 fps_handle_ = 0;
00792             }
00793             std::ostringstream stream;
00794             stream << fps << "fps";
00795             fps_handle_ = font::add_floating_label(stream.str(),12,
00796                 benchmark ? font::BAD_COLOUR : font::NORMAL_COLOUR,
00797                 10,100,0,0,-1,screen_area(),font::LEFT_ALIGN);
00798         }
00799     } else if(fps_handle_ != 0) {
00800         font::remove_floating_label(fps_handle_);
00801         fps_handle_ = 0;
00802     }
00803 
00804     flip();
00805 }
00806 
00807 static void draw_panel(CVideo& video, const theme::panel& panel, std::vector<gui::button>& buttons)
00808 {
00809     //log_scope("draw panel");
00810     DBG_DP << "drawing panel " << panel.get_id() << "\n";
00811             
00812     surface surf(image::get_image(panel.image()));
00813 
00814     const SDL_Rect screen = screen_area();
00815     SDL_Rect& loc = panel.location(screen);
00816 
00817     DBG_DP << "panel location: x=" << loc.x << ", y=" << loc.y 
00818             << ", w=" << loc.w << ", h=" << loc.h << "\n";
00819 
00820     if(!surf.null()) {
00821         if(surf->w != loc.w || surf->h != loc.h) {
00822             surf.assign(scale_surface(surf,loc.w,loc.h));
00823         }
00824 
00825         video.blit_surface(loc.x,loc.y,surf);
00826         update_rect(loc);
00827     }
00828 
00829     static bool first_time = true;
00830     for(std::vector<gui::button>::iterator b = buttons.begin(); b != buttons.end(); ++b) {
00831         if(rects_overlap(b->location(),loc)) {
00832             b->set_dirty(true);
00833             if (first_time){
00834                 //! @todo FixMe YogiHH:
00835                 // This is only made to have the buttons store
00836                 // their background information, otherwise
00837                 // the background will appear completely black.
00838                 // It would more straightforward to call bg_update,
00839                 // but that is not public and there seems to be
00840                 // no other way atm to call it.
00841                 // I will check if bg_update can be made public.
00842                 b->hide(true);
00843                 b->hide(false);
00844             }
00845         }
00846     }
00847 }
00848 
00849 static void draw_label(CVideo& video, surface target, const theme::label& label)
00850 {
00851     //log_scope("draw label");
00852 
00853         std::stringstream temp;
00854     Uint32 RGB=label.font_rgb();
00855         int red = (RGB & 0x00FF0000)>>16;
00856         int green = (RGB & 0x0000FF00)>>8;
00857         int blue = (RGB & 0x000000FF);
00858 
00859         std::string c_start="<";
00860         std::string c_sep=",";
00861         std::string c_end=">";
00862         std::stringstream color;
00863         color<< c_start << red << c_sep << green << c_sep << blue << c_end;
00864         std::string text = label.text();
00865 
00866         if(label.font_rgb_set()) {
00867         color<<text;
00868         text = color.str();
00869         }
00870     const std::string& icon = label.icon();
00871     SDL_Rect& loc = label.location(screen_area());
00872 
00873     if(icon.empty() == false) {
00874         surface surf(image::get_image(icon));
00875         if(!surf.null()) {
00876             if(surf->w > loc.w || surf->h > loc.h) {
00877                 surf.assign(scale_surface(surf,loc.w,loc.h));
00878             }
00879 
00880             SDL_BlitSurface(surf,NULL,target,&loc);
00881         }
00882 
00883         if(text.empty() == false) {
00884             tooltips::add_tooltip(loc,text);
00885         }
00886     } else if(text.empty() == false) {
00887         font::draw_text(&video,loc,label.font_size(),font::NORMAL_COLOUR,text,loc.x,loc.y);
00888     }
00889 
00890     update_rect(loc);
00891 }
00892 
00893 void display::draw_all_panels()
00894 {
00895     surface const screen(screen_.getSurface());
00896 
00897     const std::vector<theme::panel>& panels = theme_.panels();
00898     for(std::vector<theme::panel>::const_iterator p = panels.begin(); p != panels.end(); ++p) {
00899         draw_panel(video(),*p,buttons_);
00900     }
00901 
00902     const std::vector<theme::label>& labels = theme_.labels();
00903     for(std::vector<theme::label>::const_iterator i = labels.begin(); i != labels.end(); ++i) {
00904         draw_label(video(),screen,*i);
00905     }
00906 
00907     create_buttons();
00908 }
00909 
00910 static void draw_background(surface screen, const SDL_Rect& area, const std::string& image)
00911 {
00912     const surface background(image::get_image(image));
00913     if(background.null()) {
00914         return;
00915     }
00916     const unsigned int width = background->w;
00917     const unsigned int height = background->h;
00918 
00919     const unsigned int w_count = static_cast<int>(std::ceil(static_cast<double>(area.w) / static_cast<double>(width)));
00920     const unsigned int h_count = static_cast<int>(std::ceil(static_cast<double>(area.h) / static_cast<double>(height)));
00921 
00922     for(unsigned int w = 0, w_off = area.x; w < w_count; ++w, w_off += width) {
00923         for(unsigned int h = 0, h_off = area.y; h < h_count; ++h, h_off += height) {
00924             SDL_Rect clip = {w_off, h_off, 0, 0};
00925             SDL_BlitSurface(background, NULL, screen, &clip);
00926         }
00927     }
00928 }
00929 
00930 void display::draw_text_in_hex(const gamemap::location& loc, 
00931         const tdrawing_layer layer, const std::string& text,
00932         size_t font_size, SDL_Color color, double x_in_hex, double y_in_hex)
00933 {
00934     if (text.empty()) return;
00935 
00936     const int drawing_order = gamemap::get_drawing_order(loc);
00937 
00938     const size_t font_sz = static_cast<size_t>(font_size * get_zoom_factor()
00939 #ifdef USE_TINY_GUI
00940         / 2 // the hex is only half size
00941 #endif
00942     );
00943 
00944     surface text_surf = font::get_rendered_text(text, font_sz, color);
00945     surface back_surf = font::get_rendered_text(text, font_sz, font::BLACK_COLOUR);
00946     const int x = get_location_x(loc) - text_surf->w/2
00947                   + static_cast<int>(x_in_hex* hex_size());
00948     const int y = get_location_y(loc) - text_surf->h/2
00949                   + static_cast<int>(y_in_hex* hex_size());
00950 
00951     for (int dy=-1; dy <= 1; dy++) {
00952         for (int dx=-1; dx <= 1; dx++) {
00953             if (dx!=0 || dy!=0) {
00954                 drawing_buffer_add(layer, drawing_order, tblit(x + dx, y + dy, back_surf));
00955             }
00956         }
00957     }
00958     drawing_buffer_add(layer, drawing_order, tblit(x, y, text_surf));
00959 }
00960 
00961 void display::clear_hex_overlay(const gamemap::location& loc)
00962 {
00963     if(! hex_overlay_.empty()) {
00964         std::map<gamemap::location, surface>::iterator itor = hex_overlay_.find(loc);
00965         if(itor != hex_overlay_.end()) {
00966             hex_overlay_.erase(itor);
00967         }
00968     }
00969 }
00970 
00971 void display::render_unit_image(int x, int y, const bool /*fake_unit*/,
00972         const int drawing_order, surface image,
00973         bool hreverse, bool greyscale, fixed_t alpha,
00974         Uint32 blendto, double blend_ratio, double submerged,bool vreverse)
00975 {
00976 
00977     if (image==NULL)
00978         return;
00979 
00980     SDL_Rect image_rect = {x, y, image->w, image->h};
00981     SDL_Rect clip_rect = map_area();
00982     if (!rects_overlap(image_rect, clip_rect))
00983         return;
00984 
00985     surface surf(image);
00986 
00987     if(hreverse) {
00988         surf = image::reverse_image(surf);
00989     }
00990     if(vreverse) {
00991         surf = flop_surface(surf, false);
00992     }
00993 
00994     if(greyscale) {
00995         surf = greyscale_image(surf, false);
00996     }
00997 
00998     if(blend_ratio != 0) {
00999         surf = blend_surface(surf, blend_ratio, blendto, false);
01000     }
01001     if(alpha > ftofxp(1.0)) {
01002         surf = brighten_image(surf, alpha, false);
01003     //} else if(alpha != 1.0 && blendto != 0) {
01004     //  surf.assign(blend_surface(surf,1.0-alpha,blendto));
01005     } else if(alpha != ftofxp(1.0)) {
01006         surf = adjust_surface_alpha(surf, alpha, false);
01007     }
01008 
01009     if(surf == NULL) {
01010         ERR_DP << "surface lost...\n";
01011         return;
01012     }
01013 
01014     const int submerge_height = minimum<int>(surf->h,maximum<int>(0,int(surf->h*(1.0-submerged))));
01015 
01016     
01017     SDL_Rect srcrect = {0,0,surf->w,submerge_height};
01018 
01019     // NOTE: There is also a LAYER_UNIT_FAKE, but don't work well
01020     // when the fake unit move behind an other
01021     drawing_buffer_add(LAYER_UNIT_FIRST, drawing_order, tblit(x, y, surf, srcrect));
01022 
01023     if(submerge_height != surf->h) {
01024         surf.assign(adjust_surface_alpha(surf,ftofxp(0.2),false));
01025 
01026         srcrect.y = submerge_height;
01027         srcrect.h = surf->h-submerge_height;
01028         y += submerge_height;
01029 
01030         drawing_buffer_add(LAYER_UNIT_FIRST, drawing_order, tblit(x, y, surf, srcrect));
01031     }
01032 
01033 }
01034 
01035 void display::select_hex(gamemap::location hex)
01036 {
01037     invalidate(selectedHex_);
01038     selectedHex_ = hex;
01039     invalidate(selectedHex_);
01040 }
01041 
01042 void display::highlight_hex(gamemap::location hex)
01043 {
01044     invalidate(mouseoverHex_);
01045     mouseoverHex_ = hex;
01046     invalidate(mouseoverHex_);
01047 }
01048 
01049 void display::invalidate_locations_in_rect(SDL_Rect r)
01050 {
01051     gamemap::location topleft, bottomright;
01052     get_rect_hex_bounds(r, topleft, bottomright);
01053     for (int x = topleft.x; x <= bottomright.x; ++x) {
01054         for (int y = topleft.y; y <= bottomright.y; ++y) {
01055             gamemap::location loc(x, y);
01056             invalidate(loc);
01057         }
01058     }
01059 }
01060 
01061 void display::set_diagnostic(const std::string& msg)
01062 {
01063     if(diagnostic_label_ != 0) {
01064         font::remove_floating_label(diagnostic_label_);
01065         diagnostic_label_ = 0;
01066     }
01067 
01068     if(msg != "") {
01069         diagnostic_label_ = font::add_floating_label(msg,font::SIZE_PLUS,font::YELLOW_COLOUR,300.0,50.0,0.0,0.0,-1,map_outside_area());
01070     }
01071 }
01072 
01073 //! Initiate a redraw.
01074 //! May require redrawing panels and background.
01075 bool display::draw_init()
01076 {
01077     bool changed = false;
01078 
01079     if (map_.empty()) {
01080         return changed;
01081     }
01082 
01083     if(benchmark) {
01084         redraw_background_ = true;
01085         invalidateAll_ = true;
01086     }
01087 
01088     if(!panelsDrawn_) {
01089         draw_all_panels();
01090         panelsDrawn_ = true;
01091         changed = true;
01092     }
01093 
01094     if(redraw_background_) {
01095         // Full redraw of the background
01096         const SDL_Rect clip_rect = map_outside_area();
01097         const surface screen = get_screen_surface();
01098         clip_rect_setter set_clip_rect(screen, clip_rect);
01099         draw_background(screen, clip_rect, theme_.border().background_image);
01100         update_rect(clip_rect);
01101 
01102         redraw_background_ = false;
01103 
01104         // Force a full map redraw
01105         invalidateAll_ = true;
01106     }
01107 
01108     if(invalidateAll_) {
01109         DBG_DP << "draw() with invalidateAll\n";
01110         gamemap::location topleft;
01111         gamemap::location bottomright;
01112         get_visible_hex_bounds(topleft, bottomright);
01113         for(int x = topleft.x; x <= bottomright.x; ++x)
01114             for(int y = topleft.y; y <= bottomright.y; ++y)
01115                 invalidated_.insert(gamemap::location(x,y));
01116         invalidateAll_ = false;
01117 
01118         redrawMinimap_ = true;
01119     }
01120 
01121     return changed;
01122 }
01123 
01124 void display::draw_wrap(bool update,bool force,bool changed)
01125 {
01126     static const int time_between_draws = preferences::draw_delay();
01127     const int current_time = SDL_GetTicks();
01128     const int wait_time = nextDraw_ - current_time;
01129 
01130     if(redrawMinimap_) {
01131         redrawMinimap_ = false;
01132         draw_minimap();
01133         changed = true;
01134     }
01135 
01136     if(update) {
01137         if(force || changed) {
01138             if(!force && wait_time > 0) {
01139                 // If it's not time yet to draw, delay until it is
01140                 SDL_Delay(wait_time);
01141             }
01142             update_display();
01143         }
01144 
01145         // Set the theortical next draw time
01146         nextDraw_ += time_between_draws;
01147 
01148 
01149         // If the next draw already should have been finished,
01150         // we'll enter an update frenzy, so make sure that the
01151         // too late value doesn't keep growing.
01152         // Note: if force is used too often,
01153         // we can also get the opposite effect.
01154         nextDraw_ = maximum<int>(nextDraw_, SDL_GetTicks());
01155     }
01156 }
01157 
01158 //! Delay routines: use these instead of SDL_Delay (for --nogui).
01159 void display::delay(unsigned int milliseconds) const
01160 {
01161     if (!game_config::no_delay)
01162         SDL_Delay(milliseconds);
01163 }
01164 
01165 const theme::menu* display::menu_pressed()
01166 {
01167 
01168     for(std::vector<gui::button>::iterator i = buttons_.begin(); i != buttons_.end(); ++i) {
01169         if(i->pressed()) {
01170             const size_t index = i - buttons_.begin();
01171             if(index >= theme_.menus().size()) {
01172                 assert(false);
01173                 return 0;
01174             }
01175             return &theme_.menus()[index];
01176         }
01177     }
01178 
01179     return 0;
01180 }
01181 
01182 void display::enable_menu(const std::string& item, bool enable)
01183 {
01184     for(std::vector<theme::menu>::const_iterator menu = theme_.menus().begin();
01185             menu != theme_.menus().end(); ++menu) {
01186 
01187         std::vector<std::string>::const_iterator hasitem =
01188             std::find(menu->items().begin(), menu->items().end(), item);
01189 
01190         if(hasitem != menu->items().end()) {
01191             const size_t index = menu - theme_.menus().begin();
01192             if(index >= buttons_.size()) {
01193                 assert(false);
01194                 return;
01195             } 
01196             buttons_[index].enable(enable);
01197         }
01198     }
01199 }
01200 
01201 void display::add_highlighted_loc(const gamemap::location &hex)
01202 {
01203     // Only invalidate and insert if this is a new addition,
01204     // for efficiency.
01205     if (highlighted_locations_.find(hex) == highlighted_locations_.end()) {
01206         highlighted_locations_.insert(hex);
01207         invalidate(hex);
01208     }
01209 }
01210 
01211 void display::clear_highlighted_locs()
01212 {
01213     for (std::set<gamemap::location>::const_iterator it = highlighted_locations_.begin();
01214          it != highlighted_locations_.end(); it++) {
01215         invalidate(*it);
01216     }
01217     highlighted_locations_.clear();
01218 }
01219 
01220 void display::remove_highlighted_loc(const gamemap::location &hex)
01221 {
01222     std::set<gamemap::location>::iterator it = highlighted_locations_.find(hex);
01223     // Only invalidate and remove if the hex was found, for efficiency.
01224     if (it != highlighted_locations_.end()) {
01225         highlighted_locations_.erase(it);
01226         invalidate(hex);
01227     }
01228 }
01229 
01230 void display::announce(const std::string message, const SDL_Color& colour)
01231 {
01232     font::add_floating_label(message,
01233                  font::SIZE_XLARGE,
01234                  colour,
01235                  map_outside_area().w/2,
01236                  map_outside_area().h/3,
01237                  0.0,0.0,100,
01238                  map_outside_area(),
01239                  font::CENTER_ALIGN);
01240 }
01241 
01242 void display::draw_border(const gamemap::location& loc, const int xpos, const int ypos)
01243 {
01244     /**
01245      * at the moment the border must be between 0.0 and 0.5
01246      * and the image should always be prepared for a 0.5 border.
01247      * This way this code doesn't need modifications for other border sizes.
01248      */
01249 
01250     surface screen = get_screen_surface();
01251 
01252     // First handle the corners :
01253     if(loc.x == -1 && loc.y == -1) { // top left corner
01254         SDL_Rect rect = { xpos + zoom_/4, ypos, 3 * zoom_/4, zoom_ } ;
01255         const surface border(image::get_image(theme_.border().corner_image_top_left, image::SCALED_TO_ZOOM));
01256 
01257         SDL_BlitSurface( border, NULL, screen, &rect);
01258     } else if(loc.x == map_.w() && loc.y == -1) { // top right corner
01259         SDL_Rect rect = { xpos, -1, 3 * zoom_/4, zoom_ } ;
01260         surface border;
01261         if(loc.x%2 == 0) {
01262             rect.y = ypos + zoom_/2;
01263             rect.h = zoom_/2;
01264             // We use the map idea of odd and even,
01265             // and map coords are internal coords + 1
01266             border = image::get_image(theme_.border().corner_image_top_right_odd, image::SCALED_TO_ZOOM);
01267         } else {
01268             rect.y = ypos;
01269             border = image::get_image(theme_.border().corner_image_top_right_even, image::SCALED_TO_ZOOM);
01270         }
01271 
01272         SDL_BlitSurface( border, NULL, screen, &rect);
01273 
01274     } else if(loc.x == -1 && loc.y == map_.h()) { // bottom left corner
01275         SDL_Rect rect = { xpos + zoom_/4, ypos, 3 * zoom_/4, zoom_/2 } ;
01276 
01277         const surface border(image::get_image(theme_.border().corner_image_bottom_left, image::SCALED_TO_ZOOM));
01278 
01279         SDL_BlitSurface( border, NULL, screen, &rect);
01280 
01281     } else if(loc.x == map_.w() && loc.y == map_.h()) { // bottom right corner
01282         SDL_Rect rect = { xpos, ypos, 3 * zoom_/4, zoom_/2 } ;
01283         surface border;
01284         if(loc.x%2 == 1) {
01285             // We use the map idea of odd and even, and map coords are internal coords + 1
01286             border = image::get_image(theme_.border().corner_image_bottom_right_even, image::SCALED_TO_ZOOM);
01287         } else {
01288             border = image::get_image(theme_.border().corner_image_bottom_right_odd, image::SCALED_TO_ZOOM);
01289         }
01290 
01291         SDL_BlitSurface( border, NULL, screen, &rect);
01292 
01293     // Now handle the sides:
01294     } else if(loc.x == -1) { // left side
01295         SDL_Rect rect = { xpos + zoom_/4 , ypos, zoom_/2, zoom_ } ;
01296         const surface border(image::get_image(theme_.border().border_image_left, image::SCALED_TO_ZOOM));
01297 
01298         SDL_BlitSurface( border, NULL, screen, &rect);
01299 
01300     } else if(loc.x == map_.w()) { // right side
01301         SDL_Rect rect = { xpos + zoom_/4 , ypos, zoom_/2, zoom_ } ;
01302         const surface border(image::get_image(theme_.border().border_image_right, image::SCALED_TO_ZOOM));
01303 
01304         SDL_BlitSurface( border, NULL, screen, &rect);
01305 
01306     } else if(loc.y == -1) { // top side
01307         SDL_Rect rect = { xpos, -1, zoom_, zoom_/2 } ;
01308         surface border;
01309 
01310         if(loc.x%2 == 1) {
01311             rect.y = ypos;
01312             border = image::get_image(theme_.border().border_image_top_even, image::SCALED_TO_ZOOM);
01313         } else {
01314             rect.y = ypos + zoom_/2;
01315             border = image::get_image(theme_.border().border_image_top_odd, image::SCALED_TO_ZOOM);
01316         }
01317 
01318         SDL_BlitSurface( border, NULL, screen, &rect);
01319 
01320     } else if(loc.y == map_.h()) { // bottom side
01321         SDL_Rect rect = { xpos, -1, zoom_, zoom_/2 } ;
01322         surface border;
01323 
01324         if(loc.x%2 == 1) {
01325             rect.y = ypos;
01326             border = image::get_image(theme_.border().border_image_bottom_even, image::SCALED_TO_ZOOM);
01327         } else {
01328             rect.y = ypos + zoom_/2;
01329             border = image::get_image(theme_.border().border_image_bottom_odd, image::SCALED_TO_ZOOM);
01330         }
01331 
01332         SDL_BlitSurface( border, NULL, screen, &rect);
01333     }
01334 }
01335 
01336 void display::draw_minimap()
01337 {
01338     const SDL_Rect& area = minimap_area();
01339     if(minimap_ == NULL || minimap_->w > area.w || minimap_->h > area.h) {
01340         minimap_ = image::getMinimap(area.w, area.h, map_, viewpoint_);
01341         if(minimap_ == NULL) {
01342             return;
01343         }
01344     }
01345 
01346     const surface screen(screen_.getSurface());
01347     clip_rect_setter clip_setter(screen, area);
01348 
01349     SDL_Color back_color = {31,31,23,255};
01350     draw_centered_on_background(minimap_, area, back_color, screen);
01351 
01352     //update the minimap location for mouse and units functions
01353     minimap_location_.x = area.x + (area.w - minimap_->w) / 2;
01354     minimap_location_.y = area.y + (area.h - minimap_->h) / 2;
01355     minimap_location_.w = minimap_->w;
01356     minimap_location_.h = minimap_->h;
01357 
01358     draw_minimap_units();
01359 
01360     // calculate the visible portion of the map:
01361     // scaling between minimap and full map images
01362     double xscaling = 1.0*minimap_->w / (map_.w()*hex_width());
01363     double yscaling = 1.0*minimap_->h / (map_.h()*hex_size());
01364 
01365     // we need to shift with the border size
01366     // and the 0.25 from the minimap balanced drawing
01367     // and the possible difference between real map and outside off-map
01368     SDL_Rect map_rect = map_area();
01369     SDL_Rect map_out_rect = map_outside_area();
01370     double border = theme_.border().size;
01371     double shift_x = - border*hex_width() - (map_out_rect.w - map_rect.w) / 2;
01372     double shift_y = - (border+0.25)*hex_size() - (map_out_rect.h - map_rect.h) / 2;
01373 
01374     int view_x = static_cast<int>((xpos_ + shift_x) * xscaling);
01375     int view_y = static_cast<int>((ypos_ + shift_y) * yscaling);
01376     int view_w = static_cast<int>(map_out_rect.w * xscaling);
01377     int view_h = static_cast<int>(map_out_rect.h * yscaling);
01378 
01379     const Uint32 box_color = SDL_MapRGB(minimap_->format,0xFF,0xFF,0xFF);
01380     draw_rectangle(minimap_location_.x + view_x - 1,
01381                    minimap_location_.y + view_y - 1,
01382                    view_w + 2, view_h + 2,
01383                    box_color, screen);
01384 }
01385 
01386 void display::scroll(int xmove, int ymove)
01387 {
01388     const int orig_x = xpos_;
01389     const int orig_y = ypos_;
01390     xpos_ += xmove;
01391     ypos_ += ymove;
01392     bounds_check_position();
01393     const int dx = orig_x - xpos_; // dx = -xmove
01394     const int dy = orig_y - ypos_; // dy = -ymove
01395 
01396     // Only invalidate if we've actually moved
01397     if(dx == 0 && dy == 0)
01398         return;
01399 
01400     map_labels_.scroll(dx, dy);
01401     font::scroll_floating_labels(dx, dy);
01402 
01403     surface screen(screen_.getSurface());
01404 
01405     SDL_Rect dstrect = map_area();
01406     dstrect.x += dx;
01407     dstrect.y += dy;
01408     dstrect = intersect_rects(dstrect, map_area());
01409 
01410     SDL_Rect srcrect = dstrect;
01411     srcrect.x -= dx;
01412     srcrect.y -= dy;
01413 
01414     SDL_BlitSurface(screen,&srcrect,screen,&dstrect);
01415 
01416     // Invalidate locations in the newly visible rects
01417 
01418     if (dy != 0) {
01419         SDL_Rect r = map_area();
01420         r.x = 0;
01421         r.y = dy < 0 ? r.h+dy : 0;
01422         r.h = abs(dy);
01423         invalidate_locations_in_rect(r);
01424     }
01425     if (dx != 0) {
01426         SDL_Rect r = map_area();
01427         r.x = dx < 0 ? r.w+dx : 0;
01428         r.y = 0;
01429         r.w = abs(dx);
01430         invalidate_locations_in_rect(r);
01431     }
01432 
01433     _scroll_event.notify_observers();
01434     update_rect(map_area());
01435 
01436     redrawMinimap_ = true;
01437 }
01438 
01439 void display::set_zoom(int amount)
01440 {
01441     int new_zoom = zoom_ + amount;
01442     if (new_zoom < MinZoom) {
01443         new_zoom = MinZoom;
01444     }
01445     if (new_zoom > MaxZoom) {
01446         new_zoom = MaxZoom;
01447     }
01448     if (new_zoom != zoom_) {
01449         SDL_Rect const &area = map_area();
01450         xpos_ += (xpos_ + area.w / 2) * amount / zoom_;
01451         ypos_ += (ypos_ + area.h / 2) * amount / zoom_;
01452         zoom_ = new_zoom;
01453         bounds_check_position();
01454 
01455         image::set_zoom(zoom_);
01456         map_labels_.recalculate_labels();
01457         redraw_background_ = true;
01458         invalidate_all();
01459 
01460         // Forces a redraw after zooming.
01461         // This prevents some graphic glitches from occurring.
01462         draw();
01463     }
01464 }
01465 
01466 void display::set_default_zoom()
01467 {
01468     if (zoom_ != DefaultZoom) {
01469         last_zoom_ = zoom_;
01470         set_zoom(DefaultZoom - zoom_ );
01471     } else {
01472         // When we are already at the default zoom,
01473         // switch to the last zoom used
01474         set_zoom(last_zoom_ - zoom_);
01475         last_zoom_ = DefaultZoom;
01476     }
01477 }
01478 
01479 bool display::tile_on_screen(const gamemap::location& loc)
01480 {
01481     int x = get_location_x(loc);
01482     int y = get_location_y(loc);
01483     return !outside_area(map_area(), x, y);
01484 }
01485 
01486 void display::scroll_to_xy(int screenxpos, int screenypos, SCROLL_TYPE scroll_type)
01487 {
01488     if(screen_.update_locked()) {
01489         return;
01490     }
01491 
01492     const SDL_Rect area = map_area();
01493     const int xmove_expected = screenxpos - (area.x + area.w/2);
01494     const int ymove_expected = screenypos - (area.y + area.h/2);
01495 
01496     int xpos = xpos_ + xmove_expected;
01497     int ypos = ypos_ + ymove_expected;
01498     bounds_check_position(xpos, ypos);
01499     int xmove = xpos - xpos_;
01500     int ymove = ypos - ypos_;
01501 
01502     if(scroll_type == WARP || turbo_speed() > 2.0 || preferences::scroll_speed() > 99) {
01503         scroll(xmove,ymove);
01504         draw();
01505         return;
01506     }
01507 
01508     // Doing an animated scroll, with acceleration etc.
01509 
01510     int x_old = 0;
01511     int y_old = 0;
01512 
01513     const double dist_total = hypot(xmove, ymove);
01514     double dist_moved = 0.0;
01515 
01516     int t_prev = SDL_GetTicks();
01517 
01518     double velocity = 0.0;
01519     while (dist_moved < dist_total) {
01520         events::pump();
01521 
01522         int t = SDL_GetTicks();
01523         double dt = (t - t_prev) / 1000.0;
01524         if (dt > 0.200) {
01525             // Do not skip too many frames on slow PCs
01526             dt = 0.200;
01527         }
01528         t_prev = t;
01529 
01530         //std::cout << t << " " << hypot(x_old, y_old) << "\n";
01531 
01532         //! @todo Those values might need some fine-tuning:
01533         const double accel_time = 0.3 / turbo_speed(); // seconds until full speed is reached
01534         const double decel_time = 0.4 / turbo_speed(); // seconds from full speed to stop
01535 
01536         double velocity_max = preferences::scroll_speed() * 60.0;
01537         velocity_max *= turbo_speed();
01538         double accel = velocity_max / accel_time;
01539         double decel = velocity_max / decel_time;
01540 
01541         // If we started to decelerate now, where would we stop?
01542         double stop_time = velocity / decel;
01543         double dist_stop = dist_moved + velocity*stop_time - 0.5*decel*stop_time*stop_time;
01544         if (dist_stop > dist_total || velocity > velocity_max) {
01545             velocity -= decel * dt;
01546             if (velocity < 1.0) velocity = 1.0;
01547         } else {
01548             velocity += accel * dt;
01549             if (velocity > velocity_max) velocity = velocity_max;
01550         }
01551 
01552         dist_moved += velocity * dt;
01553         if (dist_moved > dist_total) dist_moved = dist_total;
01554 
01555         int x_new = round_double(xmove * dist_moved / dist_total);
01556         int y_new = round_double(ymove * dist_moved / dist_total);
01557 
01558         int dx = x_new - x_old;
01559         int dy = y_new - y_old;
01560 
01561         scroll(dx,dy);
01562         x_old += dx;
01563         y_old += dy;
01564         draw();
01565     }
01566 }
01567 
01568 void display::scroll_to_tile(const gamemap::location& loc, SCROLL_TYPE scroll_type, bool check_fogged)
01569 {
01570     if(map_.on_board(loc) == false) {
01571         ERR_DP << "Tile at " << loc << " isn't on the map, can't scroll to the tile.\n";
01572         return;
01573     }
01574 
01575     std::vector<gamemap::location> locs;
01576     locs.push_back(loc);
01577     scroll_to_tiles(locs, scroll_type, check_fogged);
01578 }
01579 
01580 void display::scroll_to_tiles(gamemap::location loc1, gamemap::location loc2,
01581                               SCROLL_TYPE scroll_type, bool check_fogged,
01582                   double add_spacing)
01583 {
01584     std::vector<gamemap::location> locs;
01585     locs.push_back(loc1);
01586     locs.push_back(loc2);
01587     scroll_to_tiles(locs, scroll_type, check_fogged, false, add_spacing);
01588 }
01589 
01590 void display::scroll_to_tiles(const std::vector<gamemap::location>& locs,
01591                               SCROLL_TYPE scroll_type, bool check_fogged,
01592                   bool only_if_possible, double add_spacing)
01593 {
01594     // basically we calculate the min/max coordinates we want to have on-screen
01595     int minx = 0;
01596     int maxx = 0;
01597     int miny = 0;
01598     int maxy = 0;
01599     bool valid = false;
01600     bool first_tile_on_screen = false;
01601 
01602     for(std::vector<gamemap::location>::const_iterator itor = locs.begin(); itor != locs.end() ; itor++) {
01603         if(map_.on_board(*itor) == false) continue;
01604         if(check_fogged && fogged(*itor)) continue;
01605 
01606         int x = get_location_x(*itor);
01607         int y = get_location_y(*itor);
01608 
01609         if (!valid) {
01610             minx = x;
01611             maxx = x;
01612             miny = y;
01613             maxy = y;
01614             valid = true;
01615             first_tile_on_screen = !outside_area(map_area(), x, y);
01616         } else {
01617             int minx_new = minimum<int>(minx,x);
01618             int miny_new = minimum<int>(miny,y);
01619             int maxx_new = maximum<int>(maxx,x);
01620             int maxy_new = maximum<int>(maxy,y);
01621             SDL_Rect r = map_area();
01622             r.x = minx_new;
01623             r.y = miny_new;
01624             if(outside_area(r, maxx_new, maxy_new)) {
01625                 // we cannot fit all locations to the screen
01626                 if (only_if_possible) return;
01627                 break;
01628             }
01629             minx = minx_new;
01630             miny = miny_new;
01631             maxx = maxx_new;
01632             maxy = maxy_new;
01633             
01634         }
01635     }
01636     //if everything is fogged or the locs list is empty
01637     if(!valid) return;
01638 
01639     if (scroll_type == ONSCREEN) {
01640         SDL_Rect r = map_area();
01641         int spacing = round_double(add_spacing*hex_size());
01642         r.x += spacing;
01643         r.y += spacing;
01644         r.w -= 2*spacing;
01645         r.h -= 2*spacing;
01646         if (!outside_area(r, minx,miny) && !outside_area(r, maxx,maxy)) {
01647             return;
01648         }
01649     }
01650 
01651     // let's do "normal" rectangle math from now on
01652     SDL_Rect locs_bbox;
01653     locs_bbox.x = minx;
01654     locs_bbox.y = miny;
01655     locs_bbox.w = maxx - minx + hex_size();
01656     locs_bbox.h = maxy - miny + hex_size();
01657 
01658     // target the center
01659     int target_x = locs_bbox.x + locs_bbox.w/2;
01660     int target_y = locs_bbox.y + locs_bbox.h/2;
01661 
01662     if (scroll_type == ONSCREEN) {
01663         // when doing an ONSCREEN scroll we do not center the target unless needed
01664         SDL_Rect r = map_area(); 
01665         int map_center_x = r.x + r.w/2;
01666         int map_center_y = r.y + r.h/2;
01667 
01668         int h = r.h;
01669         int w = r.w;
01670 
01671         // we do not want to be only inside the screen rect, but center a bit more
01672         double inside_frac = 0.5; // 0.0 = always center the target, 1.0 = scroll the minimum distance
01673         w = (int)(w * inside_frac);
01674         h = (int)(h * inside_frac);
01675 
01676         // shrink the rectangle by the size of the locations rectangle we found
01677         // such that the new task to fit a point into a rectangle instead of rectangle into rectangle
01678         w -= locs_bbox.w;
01679         h -= locs_bbox.h;
01680 
01681         if (w < 1) w = 1;
01682         if (h < 1) h = 1;
01683 
01684         r.x = target_x - w/2;
01685         r.y = target_y - h/2;
01686         r.w = w;
01687         r.h = h;
01688 
01689         // now any point within r is a possible target to scroll to
01690         // we take the one with the minimum distance to map_center
01691         // which will always be at the border of r
01692 
01693         if (map_center_x < r.x) {
01694             target_x = r.x;
01695             target_y = map_center_y;
01696             if (target_y < r.y) target_y = r.y;
01697             if (target_y > r.y+r.h-1) target_y = r.y+r.h-1;
01698         } else if (map_center_x > r.x+r.w-1) {
01699             target_x = r.x+r.w-1;
01700             target_y = map_center_y;
01701             if (target_y < r.y) target_y = r.y;
01702             if (target_y >= r.y+r.h) target_y = r.y+r.h-1;
01703         } else if (map_center_y < r.y) {
01704             target_y = r.y;
01705             target_x = map_center_x;
01706             if (target_x < r.x) target_x = r.x;
01707             if (target_x > r.x+r.w-1) target_x = r.x+r.w-1;
01708         } else if (map_center_y > r.y+r.h-1) {
01709             target_y = r.y+r.h-1;
01710             target_x = map_center_x;
01711             if (target_x < r.x) target_x = r.x;
01712             if (target_x > r.x+r.w-1) target_x = r.x+r.w-1;
01713         } else {
01714             ERR_DP << "Bug in the scrolling code? Looks like we would not need to scroll after all...\n";
01715             // keep the target at the center
01716         }
01717     }
01718 
01719     scroll_to_xy(target_x, target_y,scroll_type);
01720 }
01721 
01722 
01723 void display::bounds_check_position()
01724 {
01725     const int orig_zoom = zoom_;
01726 
01727     if(zoom_ < MinZoom) {
01728         zoom_ = MinZoom;
01729     }
01730 
01731     if(zoom_ > MaxZoom) {
01732         zoom_ = MaxZoom;
01733     }
01734 
01735     bounds_check_position(xpos_, ypos_);
01736 
01737     if(zoom_ != orig_zoom) {
01738         image::set_zoom(zoom_);
01739     }
01740 }
01741 
01742 void display::bounds_check_position(int& xpos, int& ypos)
01743 {
01744     const int tile_width = hex_width();
01745 
01746     // Adjust for the border 2 times
01747     const int xend = static_cast<int>(tile_width * (map_.w() + 2 * theme_.border().size) + tile_width/3);
01748     const int yend = static_cast<int>(zoom_ * (map_.h() + 2 * theme_.border().size) + zoom_/2);
01749 
01750     if(xpos > xend - map_area().w) {
01751         xpos = xend - map_area().w;
01752     }
01753 
01754     if(ypos > yend - map_area().h) {
01755         ypos = yend - map_area().h;
01756     }
01757 
01758     if(xpos < 0) {
01759         xpos = 0;
01760     }
01761 
01762     if(ypos < 0) {
01763         ypos = 0;
01764     }
01765 }
01766 
01767 void display::invalidate_all()
01768 {
01769     DBG_DP << "invalidate_all()\n";
01770     invalidateAll_ = true;
01771     invalidated_.clear();
01772     update_rect(map_area());
01773 }
01774 
01775 double display::turbo_speed() const
01776 {
01777     bool res = turbo_;
01778     if(keys_[SDLK_LSHIFT] || keys_[SDLK_RSHIFT]) {
01779         res = !res;
01780     }
01781 
01782     res |= screen_.faked();
01783     if (res)
01784         return turbo_speed_;
01785     else
01786         return 1.0;
01787 }
01788 
01789 void display::set_idle_anim_rate(int rate)
01790 {
01791     idle_anim_rate_ = std::pow(2.0, -rate/10.0);
01792 }
01793 
01794 void display::redraw_everything()
01795 {
01796     if(screen_.update_locked())
01797         return;
01798 
01799     invalidateGameStatus_ = true;
01800 
01801     for(size_t n = 0; n != reports::NUM_REPORTS; ++n) {
01802         reportRects_[n] = empty_rect;
01803         reportSurfaces_[n].assign(NULL);
01804         reports_[n] = reports::report();
01805     }
01806 
01807     bounds_check_position();
01808 
01809     tooltips::clear_tooltips();
01810 
01811     theme_.set_resolution(screen_area());
01812 
01813     if(buttons_.empty() == false) {
01814         create_buttons();
01815     }
01816 
01817     panelsDrawn_ = false;
01818 
01819     map_labels_.recalculate_labels();
01820 
01821     redraw_background_ = true;
01822 
01823     invalidate_all();
01824     draw(true,true);
01825 }
01826 
01827 void display::draw_image_for_report(surface& img, SDL_Rect& rect)
01828 {
01829     SDL_Rect visible_area = get_non_transparent_portion(img);
01830     SDL_Rect target = rect;
01831     if(visible_area.x != 0 || visible_area.y != 0 || visible_area.w != img->w || visible_area.h != img->h) {
01832         if(visible_area.w == 0 || visible_area.h == 0) {
01833             return;
01834         }
01835 
01836         if(visible_area.w > rect.w || visible_area.h > rect.h) {
01837             img.assign(get_surface_portion(img,visible_area));
01838             img.assign(scale_surface(img,rect.w,rect.h));
01839             visible_area.x = 0;
01840             visible_area.y = 0;
01841             visible_area.w = img->w;
01842             visible_area.h = img->h;
01843         } else {
01844             target.x = rect.x + (rect.w - visible_area.w)/2;
01845             target.y = rect.y + (rect.h - visible_area.h)/2;
01846             target.w = visible_area.w;
01847             target.h = visible_area.h;
01848         }
01849 
01850         SDL_BlitSurface(img,&visible_area,screen_.getSurface(),&target);
01851     } else {
01852         if(img->w != rect.w || img->h != rect.h) {
01853             img.assign(scale_surface(img,rect.w,rect.h));
01854         }
01855 
01856         SDL_BlitSurface(img,NULL,screen_.getSurface(),&target);
01857     }
01858 }
01859 
01860 void display:: set_report_content(const reports::TYPE which_report, const std::string &content) {
01861     report_[which_report] = content;
01862 }
01863 
01864 void display::refresh_report(reports::TYPE report_num, reports::report report,
01865                  bool brighten)
01866 {
01867     const theme::status_item* const item = theme_.get_status_item(reports::report_name(report_num));
01868     if(item != NULL) {
01869         SDL_Rect& rect = reportRects_[report_num];
01870         const SDL_Rect& new_rect = item->location(screen_area());
01871 
01872         // Report and its location is unchanged since last time. Do nothing.
01873         if(rect == new_rect && reports_[report_num] == report) {
01874             return;
01875         }
01876 
01877         reports_[report_num] = report;
01878 
01879         surface& surf = reportSurfaces_[report_num];
01880 
01881         if(surf != NULL) {
01882             SDL_BlitSurface(surf,NULL,screen_.getSurface(),&rect);
01883             update_rect(rect);
01884         }
01885         // If the rectangle has just changed, assign the surface to it
01886         if(new_rect != rect || surf == NULL) {
01887             surf.assign(NULL);
01888             rect = new_rect;
01889 
01890             // If the rectangle is present, and we are blitting text,
01891             // then we need to backup the surface.
01892             // (Images generally won't need backing up,
01893             // unless they are transperant, but that is done later).
01894             if(rect.w > 0 && rect.h > 0) {
01895                 surf.assign(get_surface_portion(screen_.getSurface(),rect));
01896                 if(reportSurfaces_[report_num] == NULL) {
01897                     ERR_DP << "Could not backup background for report!\n";
01898                 }
01899             }
01900 
01901             update_rect(rect);
01902         }
01903 
01904         tooltips::clear_tooltips(rect);
01905 
01906         SDL_Rect area = rect;
01907 
01908         int x = rect.x, y = rect.y;
01909 
01910         if(!report.empty()) {
01911             // Add prefix, postfix elements.
01912             // Make sure that they get the same tooltip
01913             // as the guys around them.
01914             std::stringstream temp;
01915             Uint32 RGB = item->font_rgb();
01916             int red   = (RGB & 0x00FF0000)>>16;
01917             int green = (RGB & 0x0000FF00)>>8;
01918             int blue  = (RGB & 0x000000FF);
01919 
01920             static const std::string c_start="<";
01921             static const std::string c_sep=",";
01922             static const std::string c_end=">";
01923             std::stringstream color;
01924             color<< c_start << red << c_sep << green << c_sep << blue << c_end;
01925             std::string str;
01926 
01927             str = item->prefix();
01928             if(str.empty() == false) {
01929               report.insert(report.begin(), reports::element(str,"",report.begin()->tooltip));
01930             }
01931             str = item->postfix();
01932             if(str.empty() == false) {
01933               report.push_back(reports::element(str,"",report.end()->tooltip));
01934             }
01935             // Loop through and display each report element
01936             size_t tallest = 0;
01937             int image_count = 0;
01938             bool used_ellipsis=false;
01939             std::stringstream ellipsis_tooltip;
01940             SDL_Rect ellipsis_area =rect;
01941             for(reports::report::iterator i = report.begin(); i != report.end(); ++i) {
01942               temp.str("");
01943                 if(i->text.empty() == false) {
01944                   if(used_ellipsis == false) {
01945                     // Draw a text element
01946                         if(item->font_rgb_set()) {
01947                         temp <<color.str();
01948                     }
01949                     temp << i->text;
01950                     str = temp.str();
01951                     area = font::draw_text(&screen_,rect,item->font_size(),font::NORMAL_COLOUR,str,x,y);
01952                     if(area.h > tallest) {
01953                         tallest = area.h;
01954                     }
01955                     if(i->text[i->text.size() - 1] == '\n') {
01956                         x = rect.x;
01957                         y += tallest;
01958                         tallest = 0;
01959                     } else {
01960                         x += area.w;
01961                     }
01962                   }
01963                 } else if(i->image.get_filename().empty() == false) {
01964                   if(used_ellipsis == false) {
01965                     // Draw an image element
01966                     surface img(image::get_image(i->image));
01967 
01968                     if(img == NULL) {
01969                         ERR_DP << "could not find image for report: '" << i->image.get_filename() << "'\n";
01970                         continue;
01971                     }
01972 
01973                     if(rect.w + rect.x - x < img->w && image_count) {
01974                       // We have more than one image, and this one doesn't fit.
01975                       img=surface(image::get_image(game_config::ellipsis_image));
01976                       used_ellipsis=true;
01977                     }
01978 
01979                     area.x = x;
01980                     area.y = y;
01981                     area.w = minimum<int>(rect.w + rect.x - x, img->w);
01982                     area.h = minimum<int>(rect.h + rect.y - y, img->h);
01983                     draw_image_for_report(img, area);
01984 
01985                     if(brighten) {
01986                         surface tod_bright(image::get_image(game_config:: tod_bright_image));
01987                         if(tod_bright != NULL) {
01988                             draw_image_for_report(tod_bright,area);
01989                         }
01990                     }
01991 
01992                     image_count++;
01993                     if(area.h > tallest) {
01994                         tallest = area.h;
01995                     }
01996 
01997                     if(! used_ellipsis) {
01998                         x += area.w;
01999                     } else {
02000                         ellipsis_area = area;
02001                     }
02002                   }
02003                 } else {
02004                     // No text or image, skip this element
02005                     continue;
02006                 }
02007                 if(i->tooltip.empty() == false) {
02008                     if(! used_ellipsis) {
02009                         tooltips::add_tooltip(area,i->tooltip);
02010                     } else { // Collect all tooltips for the ellipsis
02011                         ellipsis_tooltip<<i->tooltip<<"\n";
02012                     }
02013                 }
02014             }
02015             if(used_ellipsis) {
02016                 tooltips::add_tooltip(ellipsis_area,ellipsis_tooltip.str());
02017             }
02018         }
02019     } else {
02020         reportSurfaces_[report_num].assign(NULL);
02021     }
02022 }
02023 
02024 void display::invalidate_rectangle(const gamemap::location& first_corner, const gamemap::location& second_corner) {
02025     // unused variable - const SDL_Rect& rect = map_area();
02026     for (int x = minimum<int>(first_corner.x,second_corner.x); x <= maximum<int>(first_corner.x,second_corner.x);x++) {
02027         for (int y = minimum<int>(first_corner.y,second_corner.y); y <= maximum<int>(first_corner.y,second_corner.y);y++) {
02028             invalidate(gamemap::location(x,y));
02029         }
02030     }
02031 }
02032 
02033 void display::invalidate_zone(const int x1,const int y1, const int x2, const int y2) {
02034     const SDL_Rect& rect = map_area();
02035     invalidate_rectangle(pixel_position_to_hex(x1 - rect.x, y1 - rect.y),pixel_position_to_hex(x2 - rect.x, y2 - rect.y));
02036 }

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