editor_palettes.cpp

Go to the documentation of this file.
00001 /* $Id: editor_palettes.cpp 26757 2008-05-21 18:28:16Z mog $ */
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 editor/editor_palettes.cpp
00016 //! Manage the terrain-palette in the editor.
00017 
00018 #include "SDL.h"
00019 #include "SDL_keysym.h"
00020 
00021 #include "editor_palettes.hpp"
00022 #include "editor_layout.hpp"
00023 #include "../config.hpp"
00024 #include "../sdl_utils.hpp"
00025 #include "../serialization/string_utils.hpp"
00026 #include "../image.hpp"
00027 #include "../reports.hpp"
00028 #include "../gettext.hpp"
00029 #include "../tooltips.hpp"
00030 #include "../util.hpp"
00031 #include "../video.hpp"
00032 
00033 #include <cassert>
00034 
00035 namespace map_editor {
00036 
00037 static bool is_invalid_terrain(t_translation::t_terrain c) {
00038     return (c == t_translation::VOID_TERRAIN || c == t_translation::FOGGED);
00039 }
00040 
00041 terrain_group::terrain_group(const config& cfg, display& gui):
00042     id(cfg["id"]), name(cfg["name"]),
00043     button(gui.video(), "", gui::button::TYPE_CHECK, cfg["icon"])
00044 {
00045 }
00046 
00047 terrain_palette::terrain_palette(display &gui, const size_specs &sizes,
00048                                  const gamemap &map, const config& cfg)
00049     : gui::widget(gui.video()), size_specs_(sizes), gui_(gui), tstart_(0),
00050       checked_group_btn_(0), map_(map),
00051       top_button_(gui.video(), "", gui::button::TYPE_PRESS, "uparrow-button"),
00052       bot_button_(gui.video(), "", gui::button::TYPE_PRESS, "downarrow-button")
00053 {
00054 
00055     // Get the available terrains temporary in terrains_
00056     terrains_ = map_.get_terrain_list();
00057     terrains_.erase(std::remove_if(terrains_.begin(), terrains_.end(), is_invalid_terrain),
00058                     terrains_.end());
00059 
00060     // Get the available groups and add them to the structure
00061     const config::child_list& groups = cfg.get_children("editor_group");
00062     config::child_list::const_iterator g_itor = groups.begin();
00063     for(; g_itor != groups.end(); ++ g_itor) {
00064         terrain_groups_.push_back(terrain_group(**g_itor, gui));
00065 
00066         // By default the 'all'-button is pressed
00067         if(terrain_groups_.back().id == "all") {
00068             terrain_groups_.back().button.set_check(true);
00069             checked_group_btn_ = &terrain_groups_.back().button;
00070         }
00071     }
00072     // The rest of the code assumes this is a valid pointer
00073     assert(checked_group_btn_ != 0);
00074 
00075     // add the groups for all terrains to the map
00076     t_translation::t_list::const_iterator t_itor = terrains_.begin();
00077     for(; t_itor != terrains_.end(); ++t_itor) {
00078         const terrain_type& t_info = map_.get_terrain_info(*t_itor);
00079 
00080         // don't display terrains that were automatically created from base+overlay
00081         if (t_info.is_combined())
00082             continue;
00083 
00084         // add the terrain to the requested groups
00085         const std::vector<std::string>& key = 
00086             utils::split(t_info.editor_group());
00087         
00088         for(std::vector<std::string>::const_iterator k_itor = key.begin(); 
00089                 k_itor != key.end(); ++k_itor)
00090          {
00091             terrain_map_[*k_itor].push_back(*t_itor);
00092         }
00093 
00094         // Add the terrain to the default group
00095         terrain_map_["all"].push_back(*t_itor);
00096     }
00097 
00098     // Set the default group
00099     terrains_ = terrain_map_["all"];
00100 
00101     if(terrains_.empty()) {
00102         std::cerr << "No terrain found.\n";
00103     }
00104     else {
00105         selected_fg_terrain_ = terrains_[0];
00106         selected_bg_terrain_ = terrains_[0];
00107     }
00108     update_report();
00109     //adjust_size();
00110 }
00111 
00112 void terrain_palette::adjust_size() {
00113 
00114     scroll_top();
00115     const size_t button_height = 24;
00116     const size_t button_palette_padding = 8;
00117 
00118     // Values for the group buttons fully hardcoded for now
00119     //! @todo will be fixed later
00120     const size_t group_button_height   = 24;
00121     const size_t group_button_padding  =  2;
00122     const size_t group_buttons_per_row =  5;
00123 
00124     // Determine number of theme button rows
00125     size_t group_rows = terrain_groups_.size() / group_buttons_per_row;
00126     if(terrain_groups_.size() % group_buttons_per_row != 0) {
00127         ++group_rows;
00128     }
00129     const size_t group_height = group_rows * (group_button_height + group_button_padding);
00130 
00131     SDL_Rect rect = { size_specs_.palette_x, size_specs_.palette_y, size_specs_.palette_w, size_specs_.palette_h };
00132     set_location(rect);
00133     top_button_y_ = size_specs_.palette_y + group_height ;
00134     button_x_ = size_specs_.palette_x + size_specs_.palette_w/2 - button_height/2;
00135     terrain_start_ = top_button_y_ + button_height + button_palette_padding;
00136     const size_t space_for_terrains = size_specs_.palette_h - (button_height + button_palette_padding) * 2 - group_height;
00137     rect.y = terrain_start_;
00138     rect.h = space_for_terrains;
00139     bg_register(rect);
00140     const unsigned terrains_fitting =
00141         static_cast<unsigned> (space_for_terrains / size_specs_.terrain_space) *
00142         size_specs_.terrain_width;
00143     const unsigned total_terrains = num_terrains();
00144     nterrains_ = minimum<int>(terrains_fitting, total_terrains);
00145     bot_button_y_ = size_specs_.palette_y + (nterrains_ / size_specs_.terrain_width) * size_specs_.terrain_space + \
00146         button_palette_padding * size_specs_.terrain_width + button_height + group_height;
00147     top_button_.set_location(button_x_, top_button_y_);
00148     bot_button_.set_location(button_x_, bot_button_y_);
00149 
00150     size_t top = size_specs_.palette_y;
00151     size_t left = size_specs_.palette_x - 8;
00152     for(size_t i = 0; i < terrain_groups_.size(); ++i) {
00153         terrain_groups_[i].button.set_location(left, top);
00154         if(i % group_buttons_per_row == (group_buttons_per_row - 1)) {
00155             left = size_specs_.palette_x - 8;
00156             top += group_button_height + group_button_padding;
00157         } else {
00158             left += group_button_height + group_button_padding;
00159         }
00160     }
00161 
00162     set_dirty();
00163 }
00164 
00165 void terrain_palette::set_dirty(bool dirty) {
00166     widget::set_dirty(dirty);
00167     if (dirty) {
00168         top_button_.set_dirty();
00169         bot_button_.set_dirty();
00170         for(size_t i = 0; i < terrain_groups_.size(); ++i) {
00171             terrain_groups_[i].button.set_dirty();
00172         }
00173     }
00174 }
00175 
00176 void terrain_palette::scroll_down() {
00177     if(tstart_ + nterrains_ + size_specs_.terrain_width <= num_terrains()) {
00178         tstart_ += size_specs_.terrain_width;
00179         bg_restore();
00180         set_dirty();
00181     }
00182     else if (tstart_ + nterrains_ + (num_terrains() % size_specs_.terrain_width) <= num_terrains()) {
00183         tstart_ += num_terrains() % size_specs_.terrain_width;
00184         bg_restore();
00185         set_dirty();
00186     }
00187 }
00188 
00189 void terrain_palette::scroll_up() {
00190     unsigned int decrement = size_specs_.terrain_width;
00191     if (tstart_ + nterrains_ == num_terrains() && num_terrains() % size_specs_.terrain_width != 0) {
00192         decrement = num_terrains() % size_specs_.terrain_width;
00193     }
00194     if(tstart_ >= decrement) {
00195         bg_restore();
00196         set_dirty();
00197         tstart_ -= decrement;
00198     }
00199 }
00200 
00201 void terrain_palette::scroll_top() {
00202     tstart_ = 0;
00203     bg_restore();
00204     set_dirty();
00205 }
00206 
00207 void terrain_palette::scroll_bottom() {
00208     unsigned int old_start = num_terrains();
00209     while (old_start != tstart_) {
00210         old_start = tstart_;
00211         scroll_down();
00212     }
00213 }
00214 
00215 void terrain_palette::set_group(const std::string& id)
00216 {
00217     terrains_ = terrain_map_[id];
00218     if(terrains_.empty()) {
00219         std::cerr << "No terrain found.\n";
00220     }
00221     scroll_top();
00222 }
00223 
00224 t_translation::t_terrain terrain_palette::selected_fg_terrain() const
00225 {
00226     return selected_fg_terrain_;
00227 }
00228 
00229 t_translation::t_terrain terrain_palette::selected_bg_terrain() const
00230 {
00231     return selected_bg_terrain_;
00232 }
00233 
00234 void terrain_palette::select_fg_terrain(t_translation::t_terrain terrain)
00235 {
00236     if (selected_fg_terrain_ != terrain) {
00237         set_dirty();
00238         selected_fg_terrain_ = terrain;
00239         update_report();
00240     }
00241 }
00242 
00243 void terrain_palette::select_bg_terrain(t_translation::t_terrain terrain)
00244 {
00245     if (selected_bg_terrain_ != terrain) {
00246         set_dirty();
00247         selected_bg_terrain_ = terrain;
00248         update_report();
00249     }
00250 }
00251 
00252 
00253 //! After the language is changed, the selected terrains needs an update.
00254 void terrain_palette::update_selected_terrains(void)
00255 {
00256     set_dirty();
00257     update_report();
00258 }
00259 
00260 std::string terrain_palette::get_terrain_string(const t_translation::t_terrain t)
00261 {
00262     std::stringstream str;
00263     const std::string& name = map_.get_terrain_info(t).name();
00264     const t_translation::t_list& underlying = map_.underlying_union_terrain(t);
00265     str << name;
00266     if(underlying.size() != 1 || underlying[0] != t) {
00267         str << " (";
00268         for(t_translation::t_list::const_iterator i = underlying.begin();
00269             i != underlying.end(); ++i) {
00270 
00271             str << map_.get_terrain_info(*i).name();
00272             if(i+1 != underlying.end()) {
00273                 str << ",";
00274             }
00275         }
00276         str << ")";
00277     }
00278     return str.str();
00279 }
00280 
00281 void terrain_palette::left_mouse_click(const int mousex, const int mousey) {
00282     int tselect = tile_selected(mousex, mousey);
00283     if(tselect >= 0) {
00284         select_fg_terrain(terrains_[tstart_+tselect]);
00285         gui_.invalidate_game_status();
00286     }
00287 }
00288 
00289 void terrain_palette::right_mouse_click(const int mousex, const int mousey) {
00290     int tselect = tile_selected(mousex, mousey);
00291     if(tselect >= 0) {
00292         select_bg_terrain(terrains_[tstart_+tselect]);
00293         gui_.invalidate_game_status();
00294     }
00295 }
00296 
00297 size_t terrain_palette::num_terrains() const {
00298     return terrains_.size();
00299 }
00300 
00301 void terrain_palette::draw() {
00302     draw(false);
00303 }
00304 
00305 void terrain_palette::handle_event(const SDL_Event& event) {
00306     if (event.type == SDL_MOUSEMOTION) {
00307         // If the mouse is inside the palette, give it focus.
00308         if (point_in_rect(event.button.x, event.button.y, location())) {
00309             if (!focus(&event)) {
00310                 set_focus(true);
00311             }
00312         }
00313         // If the mouse is outside, remove focus.
00314         else {
00315             if (focus(&event)) {
00316                 set_focus(false);
00317             }
00318         }
00319     }
00320     if (!focus(&event)) {
00321         return;
00322     }
00323     int mousex, mousey;
00324     SDL_GetMouseState(&mousex,&mousey);
00325     const SDL_MouseButtonEvent mouse_button_event = event.button;
00326     if (mouse_button_event.type == SDL_MOUSEBUTTONDOWN) {
00327         if (mouse_button_event.button == SDL_BUTTON_LEFT) {
00328             left_mouse_click(mousex, mousey);
00329         }
00330         if (mouse_button_event.button == SDL_BUTTON_RIGHT) {
00331             right_mouse_click(mousex, mousey);
00332         }
00333         if (mouse_button_event.button == SDL_BUTTON_WHEELUP) {
00334             scroll_up();
00335         }
00336         if (mouse_button_event.button == SDL_BUTTON_WHEELDOWN) {
00337             scroll_down();
00338         }
00339     }
00340     if (mouse_button_event.type == SDL_MOUSEBUTTONUP) {
00341         if (mouse_button_event.button == SDL_BUTTON_LEFT) {
00342         }
00343     }
00344 }
00345 
00346 void terrain_palette::draw(bool force) {
00347 
00348     if (top_button_.pressed()) {
00349         scroll_up();
00350     }
00351     if (bot_button_.pressed()) {
00352         scroll_down();
00353     }
00354     for(size_t i = 0; i < terrain_groups_.size(); ++i) {
00355         if(terrain_groups_[i].button.pressed()) {
00356             if(&terrain_groups_[i].button == checked_group_btn_) {
00357                 checked_group_btn_->set_check(true);
00358             } else {
00359                 checked_group_btn_->set_check(false);
00360                 checked_group_btn_ = &terrain_groups_[i].button;
00361                 set_group(terrain_groups_[i].id);
00362             }
00363             break;
00364         }
00365     }
00366     if (!dirty() && !force) {
00367         return;
00368     }
00369     unsigned int starting = tstart_;
00370     unsigned int ending = starting + nterrains_;
00371     SDL_Surface* const screen = gui_.video().getSurface();
00372     if(ending > num_terrains()){
00373         ending = num_terrains();
00374     }
00375     const SDL_Rect &loc = location();
00376     int y = terrain_start_;
00377     for(unsigned int counter = starting; counter < ending; counter++){
00378         const t_translation::t_terrain terrain = terrains_[counter];
00379         const t_translation::t_terrain base_terrain = map_.get_terrain_info(terrain).default_base();
00380 
00381         const int counter_from_zero = counter - starting;
00382         SDL_Rect dstrect;
00383         dstrect.x = loc.x + (counter_from_zero % size_specs_.terrain_width) * size_specs_.terrain_space;
00384         dstrect.y = y;
00385         dstrect.w = size_specs_.terrain_size;
00386         dstrect.h = size_specs_.terrain_size;
00387 
00388         //Draw default base for overlay terrains
00389         if(base_terrain != t_translation::NONE_TERRAIN) {
00390             const std::string base_filename = "terrain/" + map_.get_terrain_info(base_terrain).editor_image() + ".png";
00391             surface base_image(image::get_image(base_filename));
00392 
00393             if(base_image == NULL) {
00394                 std::cerr << "image for terrain " << counter << ": '" << base_filename << "' not found\n";
00395                 return;
00396             }
00397 
00398             if(static_cast<unsigned>(base_image->w) != size_specs_.terrain_size ||
00399                static_cast<unsigned>(base_image->h) != size_specs_.terrain_size) {
00400 
00401                 base_image.assign(scale_surface(base_image,
00402                    size_specs_.terrain_size, size_specs_.terrain_size));
00403             }
00404 
00405             SDL_BlitSurface(base_image, NULL, screen, &dstrect);
00406         }
00407 
00408         const std::string filename = "terrain/" + map_.get_terrain_info(terrain).editor_image() + ".png";
00409         surface image(image::get_image(filename));
00410         if(image == NULL) {
00411             std::cerr << "image for terrain " << counter << ": '" << filename << "' not found\n";
00412             return;
00413         }
00414 
00415         if(static_cast<unsigned>(image->w) != size_specs_.terrain_size ||
00416             static_cast<unsigned>(image->h) != size_specs_.terrain_size) {
00417 
00418             image.assign(scale_surface(image,
00419                 size_specs_.terrain_size, size_specs_.terrain_size));
00420         }
00421 
00422         SDL_BlitSurface(image, NULL, screen, &dstrect);
00423 
00424         SDL_Surface* const screen = gui_.video().getSurface();
00425         Uint32 color;
00426         if (terrain == selected_bg_terrain() && terrain == selected_fg_terrain()) {
00427             color = SDL_MapRGB(screen->format,0xFF,0x00,0xFF);
00428         }
00429         else if (terrain == selected_bg_terrain()) {
00430             color = SDL_MapRGB(screen->format,0x00,0x00,0xFF);
00431         }
00432         else if (terrain == selected_fg_terrain()) {
00433             color = SDL_MapRGB(screen->format,0xFF,0x00,0x00);
00434         }
00435         else {
00436             color = SDL_MapRGB(screen->format,0x00,0x00,0x00);
00437         }
00438         draw_rectangle(dstrect.x, dstrect.y, image->w, image->h, color, screen);
00439         if (counter_from_zero % size_specs_.terrain_width == size_specs_.terrain_width - 1)
00440             y += size_specs_.terrain_space;
00441     }
00442     update_rect(loc);
00443     set_dirty(false);
00444 }
00445 
00446 int terrain_palette::tile_selected(const int x, const int y) const {
00447     for(unsigned int i = 0; i != nterrains_; i++) {
00448         const int px = size_specs_.palette_x + (i % size_specs_.terrain_width) * size_specs_.terrain_space;
00449         const int py = terrain_start_ + (i / size_specs_.terrain_width) * size_specs_.terrain_space;
00450         const int pw = size_specs_.terrain_space;
00451         const int ph = size_specs_.terrain_space;
00452 
00453         if(x > px && x < px + pw && y > py && y < py + ph) {
00454             return i;
00455         }
00456     }
00457     return -1;
00458 }
00459 
00460 void terrain_palette::update_report() {
00461     const std::string msg = std::string(_("FG")) + ": "
00462         + get_terrain_string(selected_fg_terrain()) + "\n"
00463         + std::string(_("BG")) +
00464         ": " + get_terrain_string(selected_bg_terrain());
00465     gui_.set_report_content(reports::SELECTED_TERRAIN, msg);
00466 }
00467 
00468 void terrain_palette::load_tooltips()
00469 {
00470     for(size_t i = 0; i < terrain_groups_.size(); ++i) {
00471         const std::string& text = terrain_groups_[i].name;
00472         if(text !="") {
00473             const SDL_Rect tooltip_rect = terrain_groups_[i].button.location();
00474             tooltips::add_tooltip(tooltip_rect, text);
00475         }
00476     }
00477 }
00478 
00479 // void terrain_palette::bg_backup() {
00480 //  restorer_ = surface_restorer(&gui_.video(), get_rect());
00481 // }
00482 
00483 // void terrain_palette::bg_restore() {
00484 //  restorer_.restore();
00485 // }
00486 
00487 brush_bar::brush_bar(display &gui, const size_specs &sizes)
00488     : gui::widget(gui.video()), size_specs_(sizes), gui_(gui), selected_(0), total_brush_(3),
00489       size_(30) {
00490     adjust_size();
00491 }
00492 
00493 void brush_bar::adjust_size() {// TODO
00494     set_location(size_specs_.brush_x, size_specs_.brush_y);
00495     set_measurements(size_ * total_brush_ + (total_brush_ - 1) * size_specs_.brush_padding, size_);
00496     set_dirty();
00497 }
00498 
00499 unsigned int brush_bar::selected_brush_size() {
00500     return selected_ + 1;
00501 }
00502 
00503 void brush_bar::select_brush_size(int new_size) {
00504     assert(new_size > 0 && new_size <= total_brush_);
00505     selected_ = new_size - 1;
00506 }
00507 
00508 void brush_bar::left_mouse_click(const int mousex, const int mousey) {
00509     int index = selected_index(mousex, mousey);
00510     if(index >= 0) {
00511         if (static_cast<unsigned>(index) != selected_) {
00512             set_dirty();
00513             selected_ = index;
00514         }
00515     }
00516 }
00517 
00518 void brush_bar::handle_event(const SDL_Event& event) {
00519     if (event.type == SDL_MOUSEMOTION) {
00520         // If the mouse is inside the palette, give it focus.
00521         if (point_in_rect(event.button.x, event.button.y, location())) {
00522             if (!focus(&event)) {
00523                 set_focus(true);
00524             }
00525         }
00526         // If the mouse is outside, remove focus.
00527         else {
00528             if (focus(&event)) {
00529                 set_focus(false);
00530             }
00531         }
00532     }
00533     if (!focus(&event)) {
00534         return;
00535     }
00536     int mousex, mousey;
00537     SDL_GetMouseState(&mousex,&mousey);
00538     const SDL_MouseButtonEvent mouse_button_event = event.button;
00539     if (mouse_button_event.type == SDL_MOUSEBUTTONDOWN) {
00540         if (mouse_button_event.button == SDL_BUTTON_LEFT) {
00541             left_mouse_click(mousex, mousey);
00542         }
00543     }
00544 }
00545 
00546 void brush_bar::draw() {
00547     draw(false);
00548 }
00549 
00550 void brush_bar::draw(bool force) {
00551     if (!dirty() && !force) {
00552         return;
00553     }
00554     const SDL_Rect loc = location();
00555     int x = loc.x;
00556     // Everything will be redrawn even though only one little part may
00557     // have changed, but that happens so seldom so we'll settle with this.
00558     SDL_Surface* const screen = gui_.video().getSurface();
00559     for (int i = 1; i <= total_brush_; i++) {
00560         std::stringstream filename;
00561         filename << "editor/brush-" << i << ".png";
00562         surface image(image::get_image(filename.str()));
00563         if (image == NULL) {
00564             std::cerr << "Image " << filename.str() << " not found." << std::endl;
00565             continue;
00566         }
00567         if (static_cast<unsigned>(image->w) != size_ ||
00568                 static_cast<unsigned>(image->h) != size_) {
00569 
00570             image.assign(scale_surface(image, size_, size_));
00571         }
00572         SDL_Rect dstrect;
00573         dstrect.x = x;
00574         dstrect.y = size_specs_.brush_y;
00575         dstrect.w = image->w;
00576         dstrect.h = image->h;
00577         SDL_BlitSurface(image, NULL, screen, &dstrect);
00578         const Uint32 color = static_cast<unsigned>(i) == selected_brush_size() ?
00579             SDL_MapRGB(screen->format,0xFF,0x00,0x00) :
00580             SDL_MapRGB(screen->format,0x00,0x00,0x00);
00581         draw_rectangle(dstrect.x, dstrect.y, image->w, image->h, color, screen);
00582         x += image->w + size_specs_.brush_padding;
00583     }
00584     update_rect(loc);
00585     set_dirty(false);
00586 }
00587 
00588 int brush_bar::selected_index(const int x, const int y) const {
00589     const int bar_x = size_specs_.brush_x;
00590     const int bar_y = size_specs_.brush_y;
00591 
00592     if ((x < bar_x || static_cast<unsigned>(x) > bar_x + size_ * total_brush_ +
00593             total_brush_ * size_specs_.brush_padding) ||
00594             (y < bar_y || static_cast<unsigned>(y) > bar_y + size_)) {
00595 
00596         return -1;
00597     }
00598 
00599     for(int i = 0; i < total_brush_; i++) {
00600         const int px = bar_x + size_ * i + i * size_specs_.brush_padding;
00601 
00602         if(x >= px && static_cast<unsigned>(x) <= px + size_ &&
00603                 y >= bar_y && static_cast<unsigned>(y) <= bar_y + size_) {
00604 
00605             return i;
00606         }
00607     }
00608     return -1;
00609 }
00610 
00611 } // end namespace map_editor
00612 

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