diff --git a/changelog b/changelog index c31f211a91e..4f4e0b0eac5 100644 --- a/changelog +++ b/changelog @@ -32,6 +32,8 @@ Version 1.5.5+svn: This fixes an issue with some window managers grabbing the event. * Added basic mask applying feature * Added basic mask creation ("diff") feature + * New multiple document interface, allows more than one map to be open at the + same time. Experimental, can be turned off in the editor settings dialog. * Graphics: * New or updated unit frames: Troll Shaman, Naga Fighter. * New idle animations: Deathblade, Orcish Assassin. diff --git a/data/gui/default/window/editor_settings.cfg b/data/gui/default/window/editor_settings.cfg index 5edd3831494..411aeb21ea0 100644 --- a/data/gui/default/window/editor_settings.cfg +++ b/data/gui/default/window/editor_settings.cfg @@ -189,7 +189,29 @@ [/row] [/grid] [/column] - [/row] + [/row] + [row] + grow_factor = 0 + [column] + grow_factor = 1 + horizontal_grow = "true" + [grid] + [row] + grow_factor = 0 + [column] + border = "all" + border_size = 5 + horizontal_alignment = "right" + [toggle_button] + id = "use_mdi" + definition = "default" + label = _ "Allow more than one map to be open at the same time (MDI)" + [/toggle_button] + [/column] + [/row] + [/grid] + [/column] + [/row] [row] grow_factor = 0 [column] diff --git a/data/themes/editor2.cfg b/data/themes/editor2.cfg index 4cda42a24dd..9c6d51da3ae 100644 --- a/data/themes/editor2.cfg +++ b/data/themes/editor2.cfg @@ -119,7 +119,7 @@ id=menu-editor-file title= _ "File" image=lite - items=editor-map-new,editor-map-load,editor-map-revert,editor-map-save,editor-map-save-as,preferences,editor-settings,quit,editor-quit-to-desktop + items=editor-map-new,editor-map-load,editor-map-revert,editor-map-save,editor-map-save-as,preferences,editor-settings,editor-close-map,quit,editor-quit-to-desktop ref=top-panel rect="=+3,=+1,+100,=-4" xanchor=fixed @@ -145,6 +145,16 @@ xanchor=fixed yanchor=fixed [/menu] + + [menu] + id=menu-editor-window + title= _ "Window" + image=lite + items=editor-switch-map + rect="+2,=,+100,=" + xanchor=fixed + yanchor=fixed + [/menu] [menu] id=menu-editor-selection @@ -240,7 +250,7 @@ [panel] id=villages-panel image=themes/status-bg.png - ref=menu-editor-map + ref=menu-editor-window rect="+5,=+1,+71,+19" xanchor=fixed yanchor=fixed diff --git a/players_changelog b/players_changelog index 86dab09607f..454fa7f10c3 100644 --- a/players_changelog +++ b/players_changelog @@ -46,6 +46,8 @@ Version 1.5.5+svn: * changed the base-terrain key modifier to shift from alt. * Added basic mask applying feature * Added basic mask creation ("diff") feature + * New multiple document interface, allows more than one map to be open at the + same time. Experimental, can be turned off in the editor settings dialog. * Graphics * New or updated unit frames: Troll Shaman, Naga Fighter. diff --git a/src/actions.cpp b/src/actions.cpp index 5802bd13af3..873ef5cdf3a 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -24,6 +24,7 @@ #include "hotkeys.hpp" #include "log.hpp" #include "map.hpp" +#include "map_label.hpp" #include "menu_events.hpp" #include "mouse_handler_base.hpp" #include "replay.hpp" diff --git a/src/builder.cpp b/src/builder.cpp index 5d79040ae9f..4fd64891a83 100644 --- a/src/builder.cpp +++ b/src/builder.cpp @@ -111,7 +111,7 @@ void terrain_builder::tile::clear() void terrain_builder::tilemap::reset() { - for(std::vector::iterator it = map_.begin(); it != map_.end(); ++it) + for(std::vector::iterator it = tiles_.begin(); it != tiles_.end(); ++it) it->clear(); } @@ -119,7 +119,7 @@ void terrain_builder::tilemap::reload(int x, int y) { x_ = x; y_ = y; - map_.resize((x + 4) * (y + 4)); + tiles_.resize((x + 4) * (y + 4)); } bool terrain_builder::tilemap::on_map(const map_location &loc) const @@ -136,20 +136,20 @@ terrain_builder::tile& terrain_builder::tilemap::operator[](const map_location & { assert(on_map(loc)); - return map_[(loc.x + 2) + (loc.y + 2) * (x_ + 4)]; + return tiles_[(loc.x + 2) + (loc.y + 2) * (x_ + 4)]; } const terrain_builder::tile& terrain_builder::tilemap::operator[] (const map_location &loc) const { assert(on_map(loc)); - return map_[(loc.x + 2) + (loc.y + 2) * (x_ + 4)]; + return tiles_[(loc.x + 2) + (loc.y + 2) * (x_ + 4)]; } terrain_builder::terrain_builder(const config& cfg, const config& level, - const gamemap& map, const std::string& offmap_image) : - map_(map), - tile_map_(map.w(), map.h()), + const gamemap* m, const std::string& offmap_image) : + map_(m), + tile_map_(map().w(), map().h()), terrain_by_type_(), building_rules_() { @@ -168,11 +168,17 @@ terrain_builder::terrain_builder(const config& cfg, const config& level, void terrain_builder::reload_map() { - tile_map_.reload(map_.w(), map_.h()); + tile_map_.reload(map().w(), map().h()); terrain_by_type_.clear(); build_terrains(); } +void terrain_builder::change_map(const gamemap* m) +{ + map_ = m; + reload_map(); +} + const terrain_builder::imagelist *terrain_builder::get_terrain_at(const map_location &loc, const std::string &tod, const ADJACENT_TERRAIN_TYPE terrain_type) { @@ -234,16 +240,16 @@ void terrain_builder::rebuild_terrain(const map_location &loc) btile.images_foreground.clear(); btile.images_background.clear(); const std::string filename = - map_.get_terrain_info(map_.get_terrain(loc)).minimap_image(); + map().get_terrain_info(map().get_terrain(loc)).minimap_image(); animated img_loc; img_loc.add_frame(100,image::locator("terrain/" + filename + ".png")); img_loc.start_animation(0, true); btile.images_background.push_back(img_loc); //Combine base and overlay image if neccessary - if(map_.get_terrain_info(map_.get_terrain(loc)).is_combined()) { + if(map().get_terrain_info(map().get_terrain(loc)).is_combined()) { const std::string filename_ovl = - map_.get_terrain_info(map_.get_terrain(loc)).minimap_image_overlay(); + map().get_terrain_info(map().get_terrain(loc)).minimap_image_overlay(); animated img_loc_ovl; img_loc_ovl.add_frame(100,image::locator("terrain/" + filename_ovl + ".png")); img_loc_ovl.start_animation(0, true); @@ -872,11 +878,11 @@ bool terrain_builder::rule_matches(const terrain_builder::building_rule &rule, return false; } - //std::cout << "testing..." << builder_letter(map_.get_terrain(tloc)) + //std::cout << "testing..." << builder_letter(map().get_terrain(tloc)) // check if terrain matches except if we already know that it does if(cons != type_checked && - !terrain_matches(map_.get_terrain(tloc), cons->second.terrain_types_match)) { + !terrain_matches(map().get_terrain(tloc), cons->second.terrain_types_match)) { return false; } @@ -937,10 +943,10 @@ void terrain_builder::build_terrains() log_scope("terrain_builder::build_terrains"); // Builds the terrain_by_type_ cache - for(int x = -2; x <= map_.w(); ++x) { - for(int y = -2; y <= map_.h(); ++y) { + for(int x = -2; x <= map().w(); ++x) { + for(int y = -2; y <= map().h(); ++y) { const map_location loc(x,y); - const t_translation::t_terrain t = map_.get_terrain(loc); + const t_translation::t_terrain t = map().get_terrain(loc); terrain_by_type_[t].push_back(loc); } diff --git a/src/builder.hpp b/src/builder.hpp index bf0500bc5ef..23980978e8c 100644 --- a/src/builder.hpp +++ b/src/builder.hpp @@ -75,13 +75,17 @@ public: * and '.png' suffix */ terrain_builder(const config& cfg, const config &level, - const gamemap& map, const std::string& offmap_image); + const gamemap* map, const std::string& offmap_image); + + const gamemap& map() const { return *map_; } /** * Updates internals that cache map size. This should be called when the map * size has changed. */ void reload_map(); + + void change_map(const gamemap* m); /** Returns a vector of strings representing the images to load & blit * together to get the built content for this tile. @@ -360,7 +364,7 @@ private: * Constructs a tilemap of dimensions x * y */ tilemap(int x, int y) : - map_((x + 4) * (y + 4)), + tiles_((x + 4) * (y + 4)), x_(x), y_(y) {} @@ -400,7 +404,7 @@ private: void reload(int x, int y); private: /** The map */ - std::vector map_; + std::vector tiles_; /** The x dimension of the map */ int x_; /** The y dimension of the map */ @@ -716,9 +720,10 @@ private: void build_terrains(); /** - * A reference to the gamemap class used in the current level. + * A pointer to the gamemap class used in the current level. */ - const gamemap& map_; + const gamemap* map_; + /** * The tile_map_ for the current level, which is filled by the * build_terrains_ method to contain "tiles" representing images diff --git a/src/display.cpp b/src/display.cpp index dc4d104bf15..9d371b4c374 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -20,6 +20,7 @@ #include "global.hpp" #include "actions.hpp" +#include "builder.hpp" #include "cursor.hpp" #include "display.hpp" #include "events.hpp" @@ -33,6 +34,7 @@ #include "log.hpp" #include "marked-up_text.hpp" #include "map.hpp" +#include "map_label.hpp" #include "minimap.hpp" #include "pathfind.hpp" #include "preferences.hpp" @@ -73,7 +75,7 @@ namespace { bool benchmark = false; } -display::display(CVideo& video, const gamemap& map, const config& theme_cfg, const config& cfg, const config& level) : +display::display(CVideo& video, const gamemap* map, const config& theme_cfg, const config& cfg, const config& level) : screen_(video), map_(map), viewpoint_(NULL), @@ -82,7 +84,7 @@ display::display(CVideo& video, const gamemap& map, const config& theme_cfg, con theme_(theme_cfg, screen_area()), zoom_(DefaultZoom), last_zoom_(SmallZoom), - builder_(cfg, level, map, theme_.border().tile_image), + builder_(new terrain_builder(cfg, level, map, theme_.border().tile_image)), minimap_(NULL), minimap_location_(empty_rect), redrawMinimap_(false), @@ -94,9 +96,9 @@ display::display(CVideo& video, const gamemap& map, const config& theme_cfg, con turbo_speed_(2), turbo_(false), invalidateGameStatus_(true), - map_labels_(*this,map, 0), - shroud_image_("terrain/" + map_.get_terrain_info(t_translation::VOID_TERRAIN).minimap_image() + ".png"), - fog_image_("terrain/" + map_.get_terrain_info(t_translation::FOGGED).minimap_image() + ".png"), + map_labels_(new map_labels(*this, 0)), + shroud_image_("terrain/" + get_map().get_terrain_info(t_translation::VOID_TERRAIN).minimap_image() + ".png"), + fog_image_("terrain/" + get_map().get_terrain_info(t_translation::FOGGED).minimap_image() + ".png"), tod_(time_of_day()), _scroll_event("scrolled"), nextDraw_(0), @@ -141,9 +143,20 @@ display::~display() { } +void display::rebuild_all() +{ + builder_->rebuild_all(); +} + void display::reload_map() { - builder_.reload_map(); + builder_->reload_map(); +} + +void display::change_map(const gamemap* m) +{ + map_ = m; + builder_->change_map(m); } const SDL_Rect& display::max_map_area() const @@ -157,8 +170,8 @@ const SDL_Rect& display::max_map_area() const // To display a hex fully on screen, // a little bit extra space is needed. // Also added the border two times. - max_area.w = static_cast((map_.w() + 2 * theme_.border().size + 1.0/3.0) * hex_width()); - max_area.h = static_cast((map_.h() + 2 * theme_.border().size + 0.5) * hex_size()); + max_area.w = static_cast((get_map().w() + 2 * theme_.border().size + 1.0/3.0) * hex_width()); + max_area.h = static_cast((get_map().h() + 2 * theme_.border().size + 0.5) * hex_size()); return max_area; } @@ -418,19 +431,19 @@ map_location display::minimap_location_on(int x, int y) // probably more adjustements to do (border, minimap shift...) // but the mouse and human capacity to evaluate the rectangle center // is not pixel precise. - int px = (x - minimap_location_.x) * map_.w()*hex_width() / minimap_location_.w; - int py = (y - minimap_location_.y) * map_.h()*hex_size() / minimap_location_.h; + int px = (x - minimap_location_.x) * get_map().w()*hex_width() / minimap_location_.w; + int py = (y - minimap_location_.y) * get_map().h()*hex_size() / minimap_location_.h; map_location loc = pixel_position_to_hex(px, py); if (loc.x < 0) loc.x = 0; - else if (loc.x >= map_.w()) - loc.x = map_.w() - 1; + else if (loc.x >= get_map().w()) + loc.x = get_map().w() - 1; if (loc.y < 0) loc.y = 0; - else if (loc.y >= map_.h()) - loc.y = map_.h() - 1; + else if (loc.y >= get_map().h()) + loc.y = get_map().h() - 1; return loc; } @@ -443,7 +456,7 @@ int display::screenshot(std::string filename, bool map_screenshot) SDL_SaveBMP(screenshot_surf, filename.c_str()); size = screenshot_surf->w * screenshot_surf->h; } else { - if (map_.empty()) { + if (get_map().empty()) { // Map Screenshot are big, abort and warn the user if he does strange things std::cerr << "No map, can't do a Map Screenshot. If it was not wanted, check your hotkey.\n"; return -1; @@ -590,7 +603,7 @@ std::vector display::get_fog_shroud_graphics(const map_location& lo // stream << "void"; //else // stream << "fog"; - stream << "terrain/" << map_.get_terrain_info(*terrain).minimap_image(); + stream << "terrain/" << get_map().get_terrain_info(*terrain).minimap_image(); for(int n = 0; *terrain == tiles[i] && n != 6; i = (i+1)%6, ++n) { stream << get_direction(i); @@ -647,7 +660,7 @@ std::vector display::get_terrain_images(const map_location &loc, terrain_builder::ADJACENT_TERRAIN_TYPE builder_terrain_type = (terrain_type == ADJACENT_FOREGROUND ? terrain_builder::ADJACENT_FOREGROUND : terrain_builder::ADJACENT_BACKGROUND); - const terrain_builder::imagelist* const terrains = builder_.get_terrain_at(loc, + const terrain_builder::imagelist* const terrains = builder_->get_terrain_at(loc, timeid, builder_terrain_type); if(terrains != NULL) { @@ -1081,7 +1094,7 @@ bool display::draw_init() { bool changed = false; - if (map_.empty()) { + if (get_map().empty()) { return changed; } @@ -1251,7 +1264,7 @@ void display::draw_border(const map_location& loc, const int xpos, const int ypo if(loc.x == -1 && loc.y == -1) { // top left corner drawing_buffer_add(LAYER_BORDER, drawing_order, tblit(xpos + zoom_/4, ypos, image::get_image(theme_.border().corner_image_top_left, image::SCALED_TO_ZOOM))); - } else if(loc.x == map_.w() && loc.y == -1) { // top right corner + } else if(loc.x == get_map().w() && loc.y == -1) { // top right corner // We use the map idea of odd and even, and map coords are internal coords + 1 if(loc.x%2 == 0) { drawing_buffer_add(LAYER_BORDER, drawing_order, tblit(xpos, ypos + zoom_/2, @@ -1260,11 +1273,11 @@ void display::draw_border(const map_location& loc, const int xpos, const int ypo drawing_buffer_add(LAYER_BORDER, drawing_order, tblit(xpos, ypos, image::get_image(theme_.border().corner_image_top_right_even, image::SCALED_TO_ZOOM))); } - } else if(loc.x == -1 && loc.y == map_.h()) { // bottom left corner + } else if(loc.x == -1 && loc.y == get_map().h()) { // bottom left corner drawing_buffer_add(LAYER_BORDER, drawing_order, tblit(xpos + zoom_/4, ypos, image::get_image(theme_.border().corner_image_bottom_left, image::SCALED_TO_ZOOM))); - } else if(loc.x == map_.w() && loc.y == map_.h()) { // bottom right corner + } else if(loc.x == get_map().w() && loc.y == get_map().h()) { // bottom right corner // We use the map idea of odd and even, and map coords are internal coords + 1 if(loc.x%2 == 1) { drawing_buffer_add(LAYER_BORDER, drawing_order, tblit(xpos, ypos, @@ -1278,7 +1291,7 @@ void display::draw_border(const map_location& loc, const int xpos, const int ypo } else if(loc.x == -1) { // left side drawing_buffer_add(LAYER_BORDER, drawing_order, tblit(xpos + zoom_/4, ypos, image::get_image(theme_.border().border_image_left, image::SCALED_TO_ZOOM))); - } else if(loc.x == map_.w()) { // right side + } else if(loc.x == get_map().w()) { // right side drawing_buffer_add(LAYER_BORDER, drawing_order, tblit(xpos + zoom_/4, ypos, image::get_image(theme_.border().border_image_right, image::SCALED_TO_ZOOM))); } else if(loc.y == -1) { // top side @@ -1290,7 +1303,7 @@ void display::draw_border(const map_location& loc, const int xpos, const int ypo drawing_buffer_add(LAYER_BORDER, drawing_order, tblit(xpos, ypos + zoom_/2, image::get_image(theme_.border().border_image_top_odd, image::SCALED_TO_ZOOM))); } - } else if(loc.y == map_.h()) { // bottom side + } else if(loc.y == get_map().h()) { // bottom side // We use the map idea of odd and even, and map coords are internal coords + 1 if(loc.x%2 == 1) { drawing_buffer_add(LAYER_BORDER, drawing_order, tblit(xpos, ypos, @@ -1306,7 +1319,7 @@ void display::draw_minimap() { const SDL_Rect& area = minimap_area(); if(minimap_ == NULL || minimap_->w > area.w || minimap_->h > area.h) { - minimap_ = image::getMinimap(area.w, area.h, map_, viewpoint_); + minimap_ = image::getMinimap(area.w, area.h, get_map(), viewpoint_); if(minimap_ == NULL) { return; } @@ -1328,8 +1341,8 @@ void display::draw_minimap() // calculate the visible portion of the map: // scaling between minimap and full map images - double xscaling = 1.0*minimap_->w / (map_.w()*hex_width()); - double yscaling = 1.0*minimap_->h / (map_.h()*hex_size()); + double xscaling = 1.0*minimap_->w / (get_map().w()*hex_width()); + double yscaling = 1.0*minimap_->h / (get_map().h()*hex_size()); // we need to shift with the border size // and the 0.25 from the minimap balanced drawing @@ -1366,7 +1379,7 @@ void display::scroll(int xmove, int ymove) if(dx == 0 && dy == 0) return; - map_labels_.scroll(dx, dy); + labels().scroll(dx, dy); font::scroll_floating_labels(dx, dy); surface screen(screen_.getSurface()); @@ -1421,7 +1434,7 @@ void display::set_zoom(int amount) bounds_check_position(); image::set_zoom(zoom_); - map_labels_.recalculate_labels(); + labels().recalculate_labels(); redraw_background_ = true; invalidate_all(); @@ -1534,7 +1547,7 @@ void display::scroll_to_xy(int screenxpos, int screenypos, SCROLL_TYPE scroll_ty void display::scroll_to_tile(const map_location& loc, SCROLL_TYPE scroll_type, bool check_fogged) { - if(map_.on_board(loc) == false) { + if(get_map().on_board(loc) == false) { ERR_DP << "Tile at " << loc << " isn't on the map, can't scroll to the tile.\n"; return; } @@ -1567,7 +1580,7 @@ void display::scroll_to_tiles(const std::vector& locs, bool first_tile_on_screen = false; for(std::vector::const_iterator itor = locs.begin(); itor != locs.end() ; itor++) { - if(map_.on_board(*itor) == false) continue; + if(get_map().on_board(*itor) == false) continue; if(check_fogged && fogged(*itor)) continue; int x = get_location_x(*itor); @@ -1711,8 +1724,8 @@ void display::bounds_check_position(int& xpos, int& ypos) const int tile_width = hex_width(); // Adjust for the border 2 times - const int xend = static_cast(tile_width * (map_.w() + 2 * theme_.border().size) + tile_width/3); - const int yend = static_cast(zoom_ * (map_.h() + 2 * theme_.border().size) + zoom_/2); + const int xend = static_cast(tile_width * (get_map().w() + 2 * theme_.border().size) + tile_width/3); + const int yend = static_cast(zoom_ * (get_map().h() + 2 * theme_.border().size) + zoom_/2); if(xpos > xend - map_area().w) { xpos = xend - map_area().w; @@ -1775,7 +1788,7 @@ void display::redraw_everything() panelsDrawn_ = false; - map_labels_.recalculate_labels(); + labels().recalculate_labels(); redraw_background_ = true; @@ -1811,7 +1824,7 @@ void display::draw(bool update,bool force) { invalidate_animations(); pre_draw(); update_time_of_day(); - if(!map_.empty()) { + if(!get_map().empty()) { //int simulate_delay = 0; if(!invalidated_.empty()) { changed = true; @@ -1829,6 +1842,16 @@ void display::draw(bool update,bool force) { draw_wrap(update, force, changed); } +map_labels& display::labels() +{ + return *map_labels_; +} + +const map_labels& display::labels() const +{ + return *map_labels_; +} + void display::clear_screen() { surface const disp(screen_.getSurface()); @@ -1849,8 +1872,8 @@ void display::draw_invalidated() { foreach (map_location loc, invalidated_) { int xpos = get_location_x(loc); int ypos = get_location_y(loc); - const bool on_map = map_.on_board(loc); - const bool off_map_tile = (map_.get_terrain(loc) == t_translation::OFF_MAP_USER); + const bool on_map = get_map().on_board(loc); + const bool off_map_tile = (get_map().get_terrain(loc) == t_translation::OFF_MAP_USER); SDL_Rect hex_rect = {xpos, ypos, zoom_, zoom_}; if(!rects_overlap(hex_rect,clip_rect)) { continue; @@ -1868,8 +1891,8 @@ void display::draw_hex(const map_location& loc) { int ypos = get_location_y(loc); int drawing_order = loc.get_drawing_order(); image::TYPE image_type = get_image_type(loc); - const bool on_map = map_.on_board(loc); - const bool off_map_tile = (map_.get_terrain(loc) == t_translation::OFF_MAP_USER); + const bool on_map = get_map().on_board(loc); + const bool off_map_tile = (get_map().get_terrain(loc) == t_translation::OFF_MAP_USER); if(!shrouded(loc)) { // unshrouded terrain (the normal case) drawing_buffer_add(LAYER_TERRAIN_BG, drawing_order, tblit(xpos, ypos, @@ -1901,7 +1924,7 @@ void display::draw_hex(const map_location& loc) { if(loc == selectedHex_ && on_map && selected_hex_overlay_ != NULL) { drawing_buffer_add(LAYER_TERRAIN_TMP_BG, drawing_order, tblit(xpos, ypos, selected_hex_overlay_)); } - if(loc == mouseoverHex_ && (on_map || (in_editor() && map_.on_board_with_border(loc))) && mouseover_hex_overlay_ != NULL) { + if(loc == mouseoverHex_ && (on_map || (in_editor() && get_map().on_board_with_border(loc))) && mouseover_hex_overlay_ != NULL) { drawing_buffer_add(LAYER_TERRAIN_TMP_BG, drawing_order, tblit(xpos, ypos, mouseover_hex_overlay_)); } @@ -1940,7 +1963,7 @@ void display::draw_hex(const map_location& loc) { if (draw_terrain_codes_ && (game_config::debug || !shrouded(loc))) { int off_x = xpos + hex_size()/2; int off_y = ypos + hex_size()/2; - surface text = font::get_rendered_text(lexical_cast(map_.get_terrain(loc)), font::SIZE_SMALL, font::NORMAL_COLOUR); + surface text = font::get_rendered_text(lexical_cast(get_map().get_terrain(loc)), font::SIZE_SMALL, font::NORMAL_COLOUR); surface bg = create_neutral_surface(text->w, text->h); SDL_Rect bg_rect = {0, 0, text->w, text->h}; SDL_FillRect(bg, &bg_rect, 0xaa000000); @@ -2226,7 +2249,7 @@ void display::invalidate_animations() { rect_of_hexes::iterator i = hexes.begin(), end = hexes.end(); for (;i != end; ++i) { if (!shrouded(*i)) { - if (builder_.update_animation(*i)) { + if (builder_->update_animation(*i)) { invalidate(*i); } else { invalidate_animations_location(*i); diff --git a/src/display.hpp b/src/display.hpp index 2ffe393fa36..336ea9ad8a1 100644 --- a/src/display.hpp +++ b/src/display.hpp @@ -38,13 +38,14 @@ class gamestatus; class team; class unit; class unit_map; +class terrain_builder; +class map_labels; -#include "builder.hpp" #include "generic_event.hpp" #include "image.hpp" +#include "font.hpp" #include "key.hpp" #include "map_location.hpp" -#include "map_label.hpp" #include "reports.hpp" #include "time_of_day.hpp" #include "theme.hpp" @@ -58,12 +59,13 @@ class unit_map; #include #include +#include class display { public: - display(CVideo& video, const gamemap& map, const config& theme_cfg, + display(CVideo& video, const gamemap* map, const config& theme_cfg, const config& cfg, const config& level); virtual ~display(); @@ -72,7 +74,9 @@ public: * size has changed. */ void reload_map(); - + + void change_map(const gamemap* m); + static Uint32 rgb(Uint8 red, Uint8 green, Uint8 blue) { return 0xFF000000 | (red << 16) | (green << 8) | blue; } @@ -282,7 +286,7 @@ public: */ virtual void invalidate_animations_location(const map_location& /*loc*/) {} - const gamemap& get_map()const { return map_;} + const gamemap& get_map()const { return *map_; } /** * The last action in drawing a tile is adding the overlays. @@ -329,7 +333,7 @@ public: void update_display(); /** Rebuild all dynamic terrain. */ - void rebuild_all() { builder_.rebuild_all(); } + void rebuild_all(); const theme::menu* menu_pressed(); @@ -434,8 +438,8 @@ public: */ void draw(bool update=true, bool force=false); - map_labels& labels() { return map_labels_; } - const map_labels& labels() const { return map_labels_; } + map_labels& labels(); + const map_labels& labels() const; /** Announce a message prominently. */ void announce(const std::string msg, @@ -545,13 +549,13 @@ protected: void scroll_to_xy(int screenxpos, int screenypos, SCROLL_TYPE scroll_type); CVideo& screen_; - const gamemap& map_; + const gamemap* map_; const viewpoint *viewpoint_; int xpos_, ypos_; theme theme_; int zoom_; int last_zoom_; - terrain_builder builder_; + boost::scoped_ptr builder_; surface minimap_; SDL_Rect minimap_location_; bool redrawMinimap_; @@ -563,7 +567,7 @@ protected: double turbo_speed_; bool turbo_; bool invalidateGameStatus_; - map_labels map_labels_; + boost::scoped_ptr map_labels_; std::string shroud_image_; std::string fog_image_; time_of_day tod_; diff --git a/src/editor2/editor_controller.cpp b/src/editor2/editor_controller.cpp index f550c819e58..54077c9eed5 100644 --- a/src/editor2/editor_controller.cpp +++ b/src/editor2/editor_controller.cpp @@ -37,16 +37,42 @@ namespace { const char* prefkey_default_dir = "editor2_default_dir"; const char* prefkey_auto_update_transitions = "editor2_auto_update_transitions"; + const char* prefkey_use_mdi = "editor2_use_mdi"; } namespace editor2 { +/** + * Utility class to properly refresh the display when the map context object is replaced + * without duplicating code. + */ +class map_context_refresher +{ +public: + map_context_refresher(editor_controller& ec, const map_context& other_mc) + : ec_(ec), size_changed_(!ec.get_map().same_size_as(other_mc.get_map())), refreshed_(false) + { + } + ~map_context_refresher() { + if (!refreshed_) refresh(); + } + void refresh() { + ec_.gui().change_map(&ec_.get_map()); + ec_.reload_map(); + } +private: + editor_controller& ec_; + bool size_changed_; + bool refreshed_; +}; + editor_controller::editor_controller(const config &game_config, CVideo& video, map_context* init_map_context /*=NULL*/) : controller_base(SDL_GetTicks(), game_config, video) - , mouse_handler_base(get_map()) + , mouse_handler_base() , rng_(NULL) , rng_setter_(NULL) - , map_context_(NULL) + , map_contexts_() + , current_context_index_(0) , gui_(NULL) , map_generators_() , tods_() @@ -68,12 +94,13 @@ editor_controller::editor_controller(const config &game_config, CVideo& video, m , background_terrain_(t_translation::GRASS_LAND) , clipboard_() , auto_update_transitions_(utils::string_bool(preferences::get(prefkey_auto_update_transitions), true)) + , use_mdi_(utils::string_bool(preferences::get(prefkey_use_mdi), true)) , default_dir_(preferences::get(prefkey_default_dir)) { if (init_map_context == NULL) { - map_context_ = new map_context(editor_map(game_config, 44, 33, t_translation::GRASS_LAND)); + create_default_context(); } else { - map_context_ = init_map_context; + add_map_context(init_map_context); } if (default_dir_.empty()) { default_dir_ = get_dir(get_dir(get_user_data_dir() + "/editor") + "/maps"); @@ -207,7 +234,9 @@ editor_controller::~editor_controller() delete prefs_disp_manager_; delete rng_setter_; delete rng_; - delete map_context_; + foreach (map_context* mc, map_contexts_) { + delete mc; + } } EXIT_STATUS editor_controller::main_loop() @@ -225,10 +254,26 @@ EXIT_STATUS editor_controller::main_loop() void editor_controller::quit_confirm(EXIT_STATUS mode) { - std::string message = _("Do you really want to quit?"); - if (get_map_context().modified()) { - message += " "; - message += _("There are unsaved changes in the map."); + std::vector modified; + foreach (map_context* mc, map_contexts_) { + if (mc->modified()) { + if (!mc->get_filename().empty()) { + modified.push_back(mc->get_filename()); + } else { + modified.push_back(_("(New Map)")); + } + } + } + std::string message; + if (modified.empty()) { + message = _("Do you really want to quit?"); + } else if (modified.size() == 1) { + message = _("Do you really want to quit? Changes in the map since the last save will be lost."); + } else { + message = _("Do you really want to quit? The following maps were modified and all changes since the last save will be lost:"); + foreach (std::string& str, modified) { + message += "\n" + str; + } } int res = gui::dialog(gui(),_("Quit"),message,gui::YES_NO).show(); if (res == 0) { @@ -237,10 +282,55 @@ void editor_controller::quit_confirm(EXIT_STATUS mode) } } +int editor_controller::add_map_context(map_context* mc) +{ + map_contexts_.push_back(mc); + return map_contexts_.size() - 1; +} + +void editor_controller::create_default_context() +{ + map_context* mc = new map_context(editor_map(game_config_, 44, 33, t_translation::GRASS_LAND)); + add_map_context(mc); +} + +void editor_controller::close_current_context() +{ + if (!confirm_discard()) return; + map_context* current = map_contexts_[current_context_index_]; + if (map_contexts_.size() == 1) { + create_default_context(); + map_contexts_.erase(map_contexts_.begin()); + } else if (current_context_index_ == map_contexts_.size() - 1) { + map_contexts_.pop_back(); + current_context_index_--; + } else { + map_contexts_.erase(map_contexts_.begin() + current_context_index_); + } + map_context_refresher(*this, *current); + delete current; +} + +void editor_controller::switch_context(int index) +{ + if (index < 0 || index >= map_contexts_.size()) { + WRN_ED << "Invalid index in switch map context: " << index << "\n"; + return; + } + map_context_refresher mcr(*this, *map_contexts_[index]); + current_context_index_ = index; +} + +void editor_controller::replace_map_context(const map_context& new_mc) +{ + map_context_refresher mcr(*this, new_mc); + get_map_context() = new_mc; +} + void editor_controller::editor_settings_dialog() { if (tods_.empty()) { - gui::message_dialog(gui(), _("Error"), _("No editor time-of-day found")).show(); + gui::message_dialog(gui(), _("Error"), _("No editor time-of-day found.")).show(); return; } @@ -249,6 +339,7 @@ void editor_controller::editor_settings_dialog() dialog.set_current_adjustment(preferences::editor_r(), preferences::editor_g(), preferences::editor_b()); dialog.set_redraw_callback(boost::bind(&editor_controller::editor_settings_dialog_redraw_callback, this, _1, _2, _3)); image::colour_adjustment_resetter adjust_resetter; + dialog.set_use_mdi(use_mdi_); dialog.show(gui().video()); int res = dialog.get_retval(); @@ -257,6 +348,8 @@ void editor_controller::editor_settings_dialog() preferences::set_editor_r(dialog.get_red()); preferences::set_editor_g(dialog.get_green()); preferences::set_editor_b(dialog.get_blue()); + use_mdi_ = dialog.get_use_mdi(); + preferences::set(prefkey_use_mdi, lexical_cast(use_mdi_)); } else { adjust_resetter.reset(); } @@ -282,20 +375,20 @@ bool editor_controller::confirm_discard() void editor_controller::load_map_dialog() { - if (!confirm_discard()) return; + if (!use_mdi_ && !confirm_discard()) return; std::string fn = get_map_context().get_filename(); if (fn.empty()) { fn = default_dir_; } int res = dialogs::show_file_chooser_dialog(gui(), fn, _("Choose a Map to Load")); if (res == 0) { - load_map(fn); + load_map(fn, use_mdi_); } } void editor_controller::new_map_dialog() { - if (!confirm_discard()) return; + if (!use_mdi_ && !confirm_discard()) return; gui2::teditor_new_map dialog; dialog.set_map_width(get_map().w()); dialog.set_map_height(get_map().h()); @@ -306,7 +399,7 @@ void editor_controller::new_map_dialog() int w = dialog.map_width(); int h = dialog.map_height(); t_translation::t_terrain fill = t_translation::GRASS_LAND; - new_map(w, h, fill); + new_map(w, h, fill, use_mdi_); } } @@ -342,7 +435,7 @@ void editor_controller::save_map_as_dialog() void editor_controller::generate_map_dialog() { if (map_generators_.empty()) { - gui::message_dialog(gui(), _("Error"), _("No random map generators found")).show(); + gui::message_dialog(gui(), _("Error"), _("No random map generators found.")).show(); return; } gui2::teditor_generate_map dialog; @@ -498,12 +591,16 @@ bool editor_controller::save_map(bool display_confirmation) return true; } -void editor_controller::load_map(const std::string& filename) +void editor_controller::load_map(const std::string& filename, bool new_context) { try { - get_map_context().load_map(game_config_, filename); - get_map_context().clear_undo_redo(); - refresh_after_action(); + if (new_context) { + std::auto_ptr mc(new map_context(game_config_, filename)); + int new_id = add_map_context(mc.release()); + switch_context(new_id); + } else { + replace_map_context(map_context(game_config_, filename)); + } } catch (editor_map_load_exception& e) { gui::message_dialog(gui(), _("Error loading map"), e.what()).show(); return; @@ -518,14 +615,18 @@ void editor_controller::revert_map() ERR_ED << "Empty filename in map revert\n"; return; } - load_map(filename); + load_map(filename, false); } -void editor_controller::new_map(int width, int height, t_translation::t_terrain fill) +void editor_controller::new_map(int width, int height, t_translation::t_terrain fill, bool new_context) { - get_map_context().set_map(editor_map(game_config_, width, height, fill)); - get_map_context().clear_undo_redo(); - refresh_after_action(); + editor_map m(game_config_, width, height, fill); + if (new_context) { + int new_id = add_map_context(new map_context(m)); + switch_context(new_id); + } else { + replace_map_context(map_context(m)); + } } void editor_controller::reload_map() @@ -577,10 +678,19 @@ void editor_controller::refresh_after_action(bool drag_part) gui().recalculate_minimap(); } -bool editor_controller::can_execute_command(hotkey::HOTKEY_COMMAND command, int /*index*/) const +bool editor_controller::can_execute_command(hotkey::HOTKEY_COMMAND command, int index) const { + using namespace hotkey; //reduce hotkey:: clutter switch (command) { + case HOTKEY_NULL: + if (index >= 0) { + unsigned i = static_cast(index); + if (i < map_contexts_.size()) { + return true; + } + } + return false; case HOTKEY_ZOOM_IN: case HOTKEY_ZOOM_OUT: case HOTKEY_ZOOM_DEFAULT: @@ -610,6 +720,8 @@ bool editor_controller::can_execute_command(hotkey::HOTKEY_COMMAND command, int case HOTKEY_EDITOR_TERRAIN_PALETTE_SWAP: return true; //editor hotkeys we can always do case HOTKEY_EDITOR_MAP_SAVE: + case HOTKEY_EDITOR_SWITCH_MAP: + case HOTKEY_EDITOR_CLOSE_MAP: return true; case HOTKEY_EDITOR_MAP_REVERT: return !get_map_context().get_filename().empty(); @@ -657,7 +769,7 @@ bool editor_controller::can_execute_command(hotkey::HOTKEY_COMMAND command, int } } -hotkey::ACTION_STATE editor_controller::get_action_state(hotkey::HOTKEY_COMMAND command) const { +hotkey::ACTION_STATE editor_controller::get_action_state(hotkey::HOTKEY_COMMAND command, int index) const { using namespace hotkey; switch (command) { case HOTKEY_EDITOR_TOOL_PAINT: @@ -671,8 +783,10 @@ hotkey::ACTION_STATE editor_controller::get_action_state(hotkey::HOTKEY_COMMAND return gui_->get_draw_coordinates() ? ACTION_ON : ACTION_OFF; case HOTKEY_EDITOR_DRAW_TERRAIN_CODES: return gui_->get_draw_terrain_codes() ? ACTION_ON : ACTION_OFF; + case HOTKEY_NULL: + return index == current_context_index_ ? ACTION_ON : ACTION_OFF; default: - return command_executor::get_action_state(command); + return command_executor::get_action_state(command, index); } } @@ -681,6 +795,15 @@ bool editor_controller::execute_command(hotkey::HOTKEY_COMMAND command, int inde SCOPE_ED; using namespace hotkey; switch (command) { + case HOTKEY_NULL: + if (index >= 0) { + unsigned i = static_cast(index); + if (i < map_contexts_.size()) { + switch_context(index); + return true; + } + } + return false; case HOTKEY_QUIT_GAME: quit_confirm(EXIT_NORMAL); return true; @@ -752,6 +875,9 @@ bool editor_controller::execute_command(hotkey::HOTKEY_COMMAND command, int inde case HOTKEY_EDITOR_SELECTION_RANDOMIZE: perform_refresh(editor_action_shuffle_area(get_map().selection())); return true; + case HOTKEY_EDITOR_CLOSE_MAP: + close_current_context(); + return true; case HOTKEY_EDITOR_MAP_LOAD: load_map_dialog(); return true; @@ -812,20 +938,22 @@ bool editor_controller::execute_command(hotkey::HOTKEY_COMMAND command, int inde return false; } -void editor_controller::expand_starting_position_menu(std::vector& items) +void editor_controller::expand_open_maps_menu(std::vector& items) { for (unsigned int i = 0; i < items.size(); ++i) { - if (items[i] == "editor-STARTING-POSITION") { + if (items[i] == "editor-switch-map") { items.erase(items.begin() + i); - std::vector newitems; - std::vector newsaves; - for (int player_i = 0; player_i < gamemap::MAX_PLAYERS; ++player_i) { - //TODO gettext format - std::string name = "Set starting position for player " + lexical_cast(player_i); - newitems.push_back(name); + std::vector contexts; + for (int mci = 0; mci < map_contexts_.size(); ++mci) { + std::string filename = map_contexts_[mci]->get_filename(); + if (filename.empty()) { + filename = _("(New Map)"); + } + std::string label = "[" + lexical_cast(mci) + "] " + + filename; + contexts.push_back(label); } - - items.insert(items.begin()+i, newitems.begin(), newitems.end()); + items.insert(items.begin() + i, contexts.begin(), contexts.end()); break; } } @@ -859,21 +987,18 @@ void editor_controller::show_menu(const std::vector& items_arg, int } ++i; } - expand_starting_position_menu(items); - controller_base::show_menu(items, xloc, yloc, context_menu); + expand_open_maps_menu(items); + command_executor::show_menu(items, xloc, yloc, context_menu, gui()); } void editor_controller::cycle_brush() { - DBG_ED << __func__ << "\n"; if (brush_ == &brushes_.back()) { brush_ = &brushes_.front(); } else { ++brush_; } - DBG_ED << &brushes_.front() << " " << brush_ << " " << &brushes_.back() << "\n"; update_mouse_action_highlights(); - DBG_ED << "END\n"; } void editor_controller::preferences() @@ -899,14 +1024,12 @@ void editor_controller::copy_selection() void editor_controller::cut_selection() { copy_selection(); - editor_action_paint_area a(get_map().selection(), background_terrain_); - perform_refresh(a); + perform_refresh(editor_action_paint_area(get_map().selection(), background_terrain_)); } void editor_controller::fill_selection() { - editor_action_paint_area a(get_map().selection(), foreground_terrain_); - perform_refresh(a); + perform_refresh(editor_action_paint_area(get_map().selection(), foreground_terrain_)); } @@ -1050,7 +1173,6 @@ void editor_controller::mouse_motion(int x, int y, const bool /*browse*/, bool u if (!get_map().on_board_with_border(hex_clicked)) return; a = get_mouse_action()->drag_right(*gui_, x, y, partial, last_undo); } - //Partial means that the mouse action has modified the //last undo action and the controller shouldn't add //anything to the undo stack (hence a different @@ -1084,7 +1206,6 @@ bool editor_controller::right_click_show_menu(int /*x*/, int /*y*/, const bool / bool editor_controller::left_click(int x, int y, const bool browse) { clear_mouseover_overlay(); - LOG_ED << "Left click\n"; if (mouse_handler_base::left_click(x, y, browse)) return true; LOG_ED << "Left click, after generic handling\n"; map_location hex_clicked = gui().hex_clicked_on(x, y); @@ -1112,7 +1233,6 @@ void editor_controller::left_mouse_up(int x, int y, const bool /*browse*/) bool editor_controller::right_click(int x, int y, const bool browse) { clear_mouseover_overlay(); - LOG_ED << "Right click\n"; if (mouse_handler_base::right_click(x, y, browse)) return true; LOG_ED << "Right click, after generic handling\n"; map_location hex_clicked = gui().hex_clicked_on(x, y); diff --git a/src/editor2/editor_controller.hpp b/src/editor2/editor_controller.hpp index e2667bb36be..0d7568a30cd 100644 --- a/src/editor2/editor_controller.hpp +++ b/src/editor2/editor_controller.hpp @@ -66,21 +66,26 @@ class editor_controller : public controller_base, private boost::noncopyable { public: + /** + * The constructor. A initial map context can be specified here, the controller + * will assume ownership and delete the pointer during destruction, but changes + * to the map can be retrieved between the main loop's end and the controller's + * destruction. + */ editor_controller(const config &game_config, CVideo& video, map_context* init_map_context = NULL); + ~editor_controller(); + /** Editor main loop */ EXIT_STATUS main_loop(); - /** - * Process a hotkey quit command - */ + /** Process a hotkey quit command */ void hotkey_quit(); - /** - * Show a quit confirmation dialog and if confirmed quit with the given exit status - */ + /** Show a quit confirmation dialog and if confirmed quit with the given exit status */ void quit_confirm(EXIT_STATUS status); + /** Display the settings dialog, used to control e.g. the lighting settings */ void editor_settings_dialog(); /** @@ -90,14 +95,32 @@ class editor_controller : public controller_base, bool confirm_discard(); /** Get the current map context object */ - map_context& get_map_context() { return *map_context_; } + map_context& get_map_context() { return *map_contexts_[current_context_index_]; } + /** Get the current map context object - const version */ - const map_context& get_map_context() const { return *map_context_; } + const map_context& get_map_context() const { return *map_contexts_[current_context_index_]; } + /** Get the map from the current map context object */ editor_map& get_map() { return get_map_context().get_map(); } + /** Get the map from the current map context object - const version*/ const editor_map& get_map() const { return get_map_context().get_map(); } + /** + * Add a map context. The controller assumes ownership. + * @return the index of the added map context in the map_contexts_ array + */ + int add_map_context(map_context* mc); + + /** Creates a default map context object, used to ensure there is always at least one. */ + void create_default_context(); + + /** Closes the active map context. Switches to a valid context afterward or creates a dummy one. */ + void close_current_context(); + + /** Switches the context to the one under the specified index. */ + void switch_context(int idx); + /** Display a load map dialog and process user input. */ void load_map_dialog(); @@ -134,12 +157,12 @@ class editor_controller : public controller_base, /** * Create a new map. */ - void new_map(int width, int height, t_translation::t_terrain fill); + void new_map(int width, int height, t_translation::t_terrain fill, bool new_context); /** * Load a map given the filename */ - void load_map(const std::string& filename); + void load_map(const std::string& filename, bool new_context); /** * Revert the map by reloading it from disk @@ -152,17 +175,22 @@ class editor_controller : public controller_base, */ void reload_map(); + /** + * Refresh everything, i.e. invalidate all hexes and redraw them. Does *not* reload the map. + */ + void refresh_all(); + /** command_executor override */ bool can_execute_command(hotkey::HOTKEY_COMMAND, int index = -1) const; /** command_executor override */ - hotkey::ACTION_STATE get_action_state(hotkey::HOTKEY_COMMAND command) const; + hotkey::ACTION_STATE get_action_state(hotkey::HOTKEY_COMMAND command, int index) const; /** command_executor override */ bool execute_command(hotkey::HOTKEY_COMMAND command, int index = -1); - /** WIP idea for context-menu starting position setting*/ - void expand_starting_position_menu(std::vector& items); + /** Menu expanding for open maps list */ + void expand_open_maps_menu(std::vector& items); /** controller_base override */ void show_menu(const std::vector& items_arg, int xloc, int yloc, bool context_menu); @@ -242,7 +270,12 @@ class editor_controller : public controller_base, */ void perform_refresh(const editor_action& action, bool drag_part = false); + /** + * Callback for the editor settings dialog to allow on-the-fly + * updating of the lighting display on the game map behing the dialog + */ void editor_settings_dialog_redraw_callback(int r, int g, int b); + private: /** init the display object and general set-up */ void init_gui(CVideo& video); @@ -277,10 +310,10 @@ class editor_controller : public controller_base, void refresh_after_action(bool drag_part = false); /** - * Refresh everything unconditionally + * Replace the current map context and refresh accordingly */ - void refresh_all(); - + void replace_map_context(const map_context& new_mc); + /** * Callback function passed to display to be called on each redraw_everything run. * Redraws toolbar, brush bar and related items. @@ -301,14 +334,19 @@ class editor_controller : public controller_base, rand_rng::set_random_generator* rng_setter_; - /** The current map object */ - map_context* map_context_; + /** The currently opened map context object */ + std::vector map_contexts_; + + /** Index into the map_contexts_ array */ + int current_context_index_; /** The display object used and owned by the editor. */ editor_display* gui_; + /** Available random map generators */ std::vector map_generators_; + /** Pre-defined time of day lighting settings for the settings dialog */ std::vector tods_; /** Legacy object required by the legacy terrain palette and brush bar */ @@ -361,6 +399,8 @@ class editor_controller : public controller_base, /** Flag to rebuild terrain on every terrain change */ bool auto_update_transitions_; + bool use_mdi_; + /** Default directory for map load/save as dialogs */ std::string default_dir_; }; diff --git a/src/editor2/editor_display.cpp b/src/editor2/editor_display.cpp index 33b127f7b89..dcd47d0d80b 100644 --- a/src/editor2/editor_display.cpp +++ b/src/editor2/editor_display.cpp @@ -12,6 +12,7 @@ */ #include "editor_display.hpp" +#include "builder.hpp" @@ -20,7 +21,7 @@ namespace editor2 { editor_display::editor_display(CVideo& video, const editor_map& map, const config& theme_cfg, const config& cfg, const config& level) - : display(video, map, theme_cfg, cfg, level) + : display(video, &map, theme_cfg, cfg, level) , brush_locations_() , toolbar_hint_() { @@ -53,7 +54,7 @@ void editor_display::remove_brush_loc(const map_location& hex) } void editor_display::rebuild_terrain(const map_location &loc) { - builder_.rebuild_terrain(loc); + builder_->rebuild_terrain(loc); } void editor_display::pre_draw() @@ -95,11 +96,11 @@ const SDL_Rect& editor_display::get_clip_rect() void editor_display::draw_sidebar() { // Fill in the terrain report - if(map_.on_board_with_border(mouseoverHex_)) { - refresh_report(reports::TERRAIN, reports::report(map_.get_terrain_string(mouseoverHex_))); + if(get_map().on_board_with_border(mouseoverHex_)) { + refresh_report(reports::TERRAIN, reports::report(get_map().get_terrain_string(mouseoverHex_))); refresh_report(reports::POSITION, reports::report(lexical_cast(mouseoverHex_))); } - refresh_report(reports::VILLAGES, reports::report(lexical_cast(map_.villages().size()))); + refresh_report(reports::VILLAGES, reports::report(lexical_cast(get_map().villages().size()))); refresh_report(reports::EDITOR2_TOOL_HINT, reports::report(toolbar_hint_)); } diff --git a/src/editor2/editor_display.hpp b/src/editor2/editor_display.hpp index 2528141d319..5150a227009 100644 --- a/src/editor2/editor_display.hpp +++ b/src/editor2/editor_display.hpp @@ -31,7 +31,7 @@ public: void set_brush_locs(const std::set& hexes); void clear_brush_locs(); void remove_brush_loc(const map_location& hex); - const editor_map& map() const { return static_cast(map_); } + const editor_map& map() const { return static_cast(get_map()); } void rebuild_terrain(const map_location &loc); void set_toolbar_hint(const std::string value) { toolbar_hint_ = value; } diff --git a/src/editor2/editor_map.cpp b/src/editor2/editor_map.cpp index 2343a5d9154..43b267e25a2 100644 --- a/src/editor2/editor_map.cpp +++ b/src/editor2/editor_map.cpp @@ -19,6 +19,7 @@ #include "../filesystem.hpp" #include "../foreach.hpp" #include "../map_exception.hpp" +#include "../map_label.hpp" #include "../pathutils.hpp" #include "../wml_exception.hpp" @@ -279,6 +280,12 @@ gamemap editor_map::mask_to(const gamemap& target) const return mask; } +bool editor_map::same_size_as(const gamemap& other) const +{ + return h() == other.h() + && w() == other.w(); +} + void editor_map::swap_starting_position(int x1, int y1, int x2, int y2) { int pos1 = is_starting_position(map_location(x1, y1)); diff --git a/src/editor2/editor_map.hpp b/src/editor2/editor_map.hpp index d5d47d4b3a6..1cd6a74d3d2 100644 --- a/src/editor2/editor_map.hpp +++ b/src/editor2/editor_map.hpp @@ -146,6 +146,8 @@ public: * will transform it into the target map. */ gamemap mask_to(const gamemap& target) const; + + bool same_size_as(const gamemap& other) const; protected: void swap_starting_position(int x1, int y1, int x2, int y2); diff --git a/src/editor2/map_context.cpp b/src/editor2/map_context.cpp index 2ee3bc0b918..ebf29ecd581 100644 --- a/src/editor2/map_context.cpp +++ b/src/editor2/map_context.cpp @@ -18,6 +18,7 @@ #include "../display.hpp" #include "../filesystem.hpp" #include "../foreach.hpp" +#include "../map_label.hpp" @@ -135,9 +136,7 @@ bool map_context::everything_changed() const void map_context::clear_starting_position_labels(display& disp) { - foreach (const map_location& loc, starting_position_label_locs_) { - disp.labels().set_label(loc, ""); - } + disp.labels().clear_all(); starting_position_label_locs_.clear(); } @@ -158,7 +157,7 @@ bool map_context::save() { std::string data = map_.write(); write_file(get_filename(), data); - actions_since_save_ = 0; + clear_modified(); return true; } @@ -180,7 +179,6 @@ void map_context::set_map(const editor_map& map) set_needs_terrain_rebuild(); } map_ = map; - } void map_context::perform_action(const editor_action& action) @@ -219,6 +217,11 @@ bool map_context::modified() const return actions_since_save_ != 0; } +void map_context::clear_modified() +{ + actions_since_save_ = 0; +} + bool map_context::can_undo() const { return !undo_stack_.empty(); diff --git a/src/editor2/map_context.hpp b/src/editor2/map_context.hpp index 2118de1a3cc..5729883b611 100644 --- a/src/editor2/map_context.hpp +++ b/src/editor2/map_context.hpp @@ -31,13 +31,13 @@ namespace editor2 { * as e.g. the undo stack is part of the map, not the editor as a whole. This might allow many * maps to be open at the same time. */ -class map_context : private boost::noncopyable +class map_context { public: /** * A map context can only by created from an existing map */ - map_context(const editor_map& map); + explicit map_context(const editor_map& map); /** * Create map_context from a map file. If the map cannot be @@ -152,6 +152,9 @@ public: /** @return whether the map was modified since the last save */ bool modified() const; + + /** Clear the modified state */ + void clear_modified(); /** @return true when undo can be performed, false otherwise */ bool can_undo() const; diff --git a/src/game_display.cpp b/src/game_display.cpp index 25528b2d57e..b5e85a22b2b 100644 --- a/src/game_display.cpp +++ b/src/game_display.cpp @@ -24,6 +24,7 @@ #include "halo.hpp" #include "log.hpp" #include "map.hpp" +#include "map_label.hpp" #include "marked-up_text.hpp" #include "game_preferences.hpp" #include "gamestatus.hpp" @@ -39,7 +40,7 @@ std::map game_display::debugHighlights_; game_display::game_display(unit_map& units, CVideo& video, const gamemap& map, const gamestatus& status, const std::vector& t, const config& theme_cfg, const config& cfg, const config& level) : - display(video, map, theme_cfg, cfg, level), + display(video, &map, theme_cfg, cfg, level), units_(units), temp_unit_(NULL), temp_unit_loc_(), @@ -208,12 +209,12 @@ void game_display::select_hex(map_location hex) void game_display::highlight_hex(map_location hex) { - unit_map::const_iterator u = find_visible_unit(units_,hex, map_, teams_,teams_[viewing_team()]); + unit_map::const_iterator u = find_visible_unit(units_,hex, get_map(), teams_,teams_[viewing_team()]); if (u != units_.end()) { displayedUnitHex_ = hex; invalidate_unit(); } else { - u = find_visible_unit(units_,mouseoverHex_, map_, teams_,teams_[viewing_team()]); + u = find_visible_unit(units_,mouseoverHex_, get_map(), teams_,teams_[viewing_team()]); if (u != units_.end()) { // mouse moved from unit hex to non-unit hex if (units_.count(selectedHex_)) { @@ -230,7 +231,7 @@ void game_display::highlight_hex(map_location hex) void game_display::display_unit_hex(map_location hex) { - unit_map::const_iterator u = find_visible_unit(units_,hex, map_, teams_,teams_[viewing_team()]); + unit_map::const_iterator u = find_visible_unit(units_,hex, get_map(), teams_,teams_[viewing_team()]); if (u != units_.end()) { displayedUnitHex_ = hex; invalidate_unit(); @@ -292,11 +293,11 @@ std::vector game_display::get_invalidated_unit_locations() { image::TYPE game_display::get_image_type(const map_location& loc) { // We highlight hex under the mouse, or under a selected unit. - if (map_.on_board(loc)) { + if (get_map().on_board(loc)) { if (loc == mouseoverHex_ || loc == attack_indicator_src_) { return image::BRIGHTENED; } else if (loc == selectedHex_) { - unit_map::iterator un = find_visible_unit(units_, loc, map_, + unit_map::iterator un = find_visible_unit(units_, loc, get_map(), teams_,teams_[currentTeam_]); if (un != units_.end()) { return image::BRIGHTENED; @@ -322,7 +323,7 @@ void game_display::post_commit() void game_display::draw_hex(const map_location& loc) { - const bool on_map = map_.on_board(loc); + const bool on_map = get_map().on_board(loc); const bool is_shrouded = shrouded(loc); const bool is_fogged = fogged(loc); int xpos = get_location_x(loc); @@ -352,7 +353,7 @@ void game_display::draw_hex(const map_location& loc) // Draw the time-of-day mask on top of the terrain in the hex. // tod may differ from tod if hex is illuminated. - std::string tod_hex_mask = timeofday_at(status_,units_,loc,map_).image_mask; + std::string tod_hex_mask = timeofday_at(status_,units_,loc,get_map()).image_mask; if(tod_hex_mask1 != NULL || tod_hex_mask2 != NULL) { drawing_buffer_add(LAYER_TERRAIN_FG, drawing_order, tblit(xpos, ypos, tod_hex_mask1)); drawing_buffer_add(LAYER_TERRAIN_FG, drawing_order, tblit(xpos, ypos, tod_hex_mask2)); @@ -431,7 +432,7 @@ void game_display::draw_report(reports::TYPE report_num) return; } - reports::report report = reports::generate_report(report_num,report_,map_, + reports::report report = reports::generate_report(report_num,report_,get_map(), units_, teams_, teams_[viewing_team()], size_t(currentTeam_+1),size_t(activeTeam_+1), @@ -439,7 +440,7 @@ void game_display::draw_report(reports::TYPE report_num) brighten = false; if(report_num == reports::TIME_OF_DAY) { - time_of_day tod = timeofday_at(status_,units_,mouseoverHex_,map_); + time_of_day tod = timeofday_at(status_,units_,mouseoverHex_,get_map()); // Don't show illuminated time on fogged/shrouded tiles if (teams_[viewing_team()].fogged(mouseoverHex_) || teams_[viewing_team()].shrouded(mouseoverHex_)) { @@ -477,7 +478,7 @@ void game_display::draw_sidebar() // otherwise we display the unit that is selected. unit_map::const_iterator i = find_visible_unit(units_,displayedUnitHex_, - map_, + get_map(), teams_,teams_[viewing_team()]); if(i != units_.end()) { @@ -497,8 +498,8 @@ void game_display::draw_sidebar() void game_display::draw_minimap_units() { - double xscaling = 1.0 * minimap_location_.w / map_.w(); - double yscaling = 1.0 * minimap_location_.h / map_.h(); + double xscaling = 1.0 * minimap_location_.w / get_map().w(); + double yscaling = 1.0 * minimap_location_.h / get_map().h(); for(unit_map::const_iterator u = units_.begin(); u != units_.end(); ++u) { if(fogged(u->first) || @@ -614,7 +615,7 @@ void game_display::draw_movement_info(const map_location& loc) const unit_map::const_iterator un = units_.find(route_.steps.front()); if(un != units_.end()) { // Display the def% of this terrain - const int def = 100 - un->second.defense_modifier(map_.get_terrain(loc)); + const int def = 100 - un->second.defense_modifier(get_map().get_terrain(loc)); std::stringstream def_text; def_text << def << "%"; @@ -680,7 +681,7 @@ std::vector game_display::footsteps_images(const map_location& loc) int move_cost = 1; const unit_map::const_iterator u = units_.find(route_.steps.front()); if(u != units_.end()) { - move_cost = u->second.movement_cost(map_.get_terrain(loc)); + move_cost = u->second.movement_cost(get_map().get_terrain(loc)); } int image_number = std::min(move_cost, game_config::foot_speed_prefix.size()); if (image_number < 1) { @@ -731,9 +732,9 @@ std::vector game_display::footsteps_images(const map_location& loc) surface game_display::get_flag(const map_location& loc) { - t_translation::t_terrain terrain = map_.get_terrain(loc); + t_translation::t_terrain terrain = get_map().get_terrain(loc); - if(!map_.is_village(terrain)) { + if(!get_map().is_village(terrain)) { return surface(NULL); } @@ -894,7 +895,7 @@ const SDL_Rect& game_display::calculate_energy_bar(surface surf) } void game_display::invalidate_animations_location(const map_location& loc) { - if (map_.is_village(loc)) { + if (get_map().is_village(loc)) { const int owner = player_teams::village_owner(loc); if (owner >= 0 && flags_[owner].need_update() && (!fogged(loc) || !teams_[currentTeam_].is_enemy(owner+1))) { diff --git a/src/game_display.hpp b/src/game_display.hpp index e803ccb8125..a969e5eb7c0 100644 --- a/src/game_display.hpp +++ b/src/game_display.hpp @@ -23,6 +23,7 @@ class team; class unit; class unit_map; +#include "animated.hpp" #include "display.hpp" #include "image.hpp" #include "pathfind.hpp" diff --git a/src/game_events.cpp b/src/game_events.cpp index c0d0d05a248..e71eeacef94 100644 --- a/src/game_events.cpp +++ b/src/game_events.cpp @@ -26,6 +26,7 @@ #include "gui/widgets/spacer.hpp" #include "gui/widgets/window.hpp" #include "map.hpp" +#include "map_label.hpp" #include "map_exception.hpp" #include "menu_events.hpp" #include "replay.hpp" diff --git a/src/gui/dialogs/editor_settings.cpp b/src/gui/dialogs/editor_settings.cpp index ce68b4c7839..029e23fd809 100644 --- a/src/gui/dialogs/editor_settings.cpp +++ b/src/gui/dialogs/editor_settings.cpp @@ -36,7 +36,8 @@ custom_tod_toggle_field_(register_bool("custom_tod_toggle", false)), custom_tod_red_(NULL), custom_tod_green_(NULL), custom_tod_blue_(NULL), custom_tod_red_field_(register_integer("custom_tod_red", false)), custom_tod_green_field_(register_integer("custom_tod_green", false)), -custom_tod_blue_field_(register_integer("custom_tod_blue", false)) +custom_tod_blue_field_(register_integer("custom_tod_blue", false)), +use_mdi_field_(register_bool("use_mdi")) { } @@ -66,6 +67,10 @@ int teditor_settings::get_blue() const { return custom_tod_blue_field_->get_cache_value(); } +bool teditor_settings::get_use_mdi() const +{ + return use_mdi_field_->get_cache_value(); +} void teditor_settings::update_tod_display(twindow& /*window*/) { diff --git a/src/gui/dialogs/editor_settings.hpp b/src/gui/dialogs/editor_settings.hpp index 08624d2e7b2..807f89cb3cb 100644 --- a/src/gui/dialogs/editor_settings.hpp +++ b/src/gui/dialogs/editor_settings.hpp @@ -55,6 +55,9 @@ public: void update_selected_tod_info(twindow& window); + bool get_use_mdi() const; + void set_use_mdi(bool value) { use_mdi_ = value; } + private: /** Inherited from tdialog. */ twindow build_window(CVideo& video); @@ -91,6 +94,10 @@ private: tfield_integer* custom_tod_red_field_; tfield_integer* custom_tod_green_field_; tfield_integer* custom_tod_blue_field_; + + tfield_bool* use_mdi_field_; + + bool use_mdi_; }; } // namespace gui2 diff --git a/src/halo.cpp b/src/halo.cpp index ac18b5da73e..12a1461dd44 100644 --- a/src/halo.cpp +++ b/src/halo.cpp @@ -19,7 +19,7 @@ */ #include "global.hpp" - +#include "animated.hpp" #include "display.hpp" #include "game_preferences.hpp" #include "halo.hpp" diff --git a/src/hotkeys.cpp b/src/hotkeys.cpp index 1e8949eeb00..86e14c61662 100644 --- a/src/hotkeys.cpp +++ b/src/hotkeys.cpp @@ -97,6 +97,8 @@ const struct { #ifndef DISABLE_EDITOR2 { hotkey::HOTKEY_EDITOR_QUIT_TO_DESKTOP, "editor-quit-to-desktop", N_("Quit to Desktop"), false, hotkey::SCOPE_EDITOR }, + { hotkey::HOTKEY_EDITOR_CLOSE_MAP, "editor-close-map", N_("Close Map"), false, hotkey::SCOPE_EDITOR }, + { hotkey::HOTKEY_EDITOR_SWITCH_MAP, "editor-switch-map", N_("Switch Map"), false, hotkey::SCOPE_EDITOR }, { hotkey::HOTKEY_EDITOR_SETTINGS, "editor-settings", N_("Editor Settings"), false, hotkey::SCOPE_EDITOR }, { hotkey::HOTKEY_EDITOR_PARTIAL_UNDO, "editor-partial-undo", N_("Partial Undo"), false, hotkey::SCOPE_EDITOR }, { hotkey::HOTKEY_EDITOR_MAP_NEW, "editor-map-new", N_("New Map"), false, hotkey::SCOPE_EDITOR }, @@ -966,7 +968,7 @@ void command_executor::show_menu(const std::vector& items_arg, int } std::string command_executor::get_menu_image(hotkey::HOTKEY_COMMAND command, int index) const { - switch(get_action_state(command)) { + switch(get_action_state(command, index)) { case ACTION_ON: return game_config::checked_menu_image; case ACTION_OFF: return game_config::unchecked_menu_image; default: return get_action_image(command, index); diff --git a/src/hotkeys.hpp b/src/hotkeys.hpp index df0e638f62b..233b482e45b 100644 --- a/src/hotkeys.hpp +++ b/src/hotkeys.hpp @@ -62,6 +62,7 @@ enum HOTKEY_COMMAND { #ifndef DISABLE_EDITOR2 HOTKEY_EDITOR_QUIT_TO_DESKTOP, + HOTKEY_EDITOR_CLOSE_MAP, HOTKEY_EDITOR_SWITCH_MAP, HOTKEY_EDITOR_SETTINGS, HOTKEY_EDITOR_PARTIAL_UNDO, HOTKEY_EDITOR_MAP_NEW, HOTKEY_EDITOR_MAP_LOAD, HOTKEY_EDITOR_MAP_SAVE, @@ -295,7 +296,7 @@ public: //Gets the action's image (if any). Displayed left of the action text in menus. virtual std::string get_action_image(hotkey::HOTKEY_COMMAND /*command*/, int /*index*/) const { return ""; } //Does the action control a toggle switch? If so, return the state of the action (on or off) - virtual ACTION_STATE get_action_state(hotkey::HOTKEY_COMMAND /*command*/) const { return ACTION_STATELESS; } + virtual ACTION_STATE get_action_state(hotkey::HOTKEY_COMMAND /*command*/, int /*index*/) const { return ACTION_STATELESS; } //Returns the appropriate menu image. Checkable items will get a checked/unchecked image. std::string get_menu_image(hotkey::HOTKEY_COMMAND command, int index=-1) const; //Returns a vector of images for a given menu diff --git a/src/map_label.cpp b/src/map_label.cpp index 1628e416d1a..9fc6d4dea7f 100644 --- a/src/map_label.cpp +++ b/src/map_label.cpp @@ -15,6 +15,7 @@ #include "global.hpp" #include "display.hpp" +#include "map_label.hpp" namespace { @@ -31,11 +32,9 @@ static bool is_shrouded(const display& disp, const map_location& loc) } map_labels::map_labels(const display& disp, - const gamemap& map, const team* team) : disp_(disp), team_(team), - map_(map), labels_(), label_cache_() { @@ -43,12 +42,10 @@ map_labels::map_labels(const display& disp, map_labels::map_labels(const display& disp, const config& cfg, - const gamemap& map, const team* team, const variable_set *variables) : disp_(disp), team_(team), - map_(map), labels_(), label_cache_() { diff --git a/src/map_label.hpp b/src/map_label.hpp index 25b84a38ca7..5176391757e 100644 --- a/src/map_label.hpp +++ b/src/map_label.hpp @@ -36,8 +36,8 @@ public: typedef std::map label_map; typedef std::map team_label_map; - map_labels(const display& disp, const gamemap& map, const team*); - map_labels(const display& disp, const config& cfg, const gamemap& map, const team*, const variable_set *variables); + map_labels(const display& disp, const team*); + map_labels(const display& disp, const config& cfg, const team*, const variable_set *variables); ~map_labels(); void write(config& res) const; @@ -71,15 +71,14 @@ public: void set_team(const team*); + void clear_all(); private: void clear_map(const label_map&); - void clear_all(); map_labels(const map_labels&); void operator=(const map_labels&); const display& disp_; const team* team_; - const gamemap& map_; team_label_map labels_; label_map label_cache_; diff --git a/src/menu_events.cpp b/src/menu_events.cpp index 8f7aa55ece3..e81950ae869 100644 --- a/src/menu_events.cpp +++ b/src/menu_events.cpp @@ -27,6 +27,7 @@ #include "gettext.hpp" #include "help.hpp" #include "log.hpp" +#include "map_label.hpp" #include "marked-up_text.hpp" #include "menu_events.hpp" #include "mouse_events.hpp" @@ -723,6 +724,7 @@ private: } catch(game::save_game_failed&) { gui::message_dialog(*gui_,"",_("Could not auto save the game. Please save the game manually.")).show(); //do not bother retrying, since the user can just save the game + //maybe show a yes-no dialog for "disable autosaves now"? } end = SDL_GetTicks(); LOG_NG << "Milliseconds to save " << savename << ": " << end - start << "\n"; diff --git a/src/mouse_events.cpp b/src/mouse_events.cpp index 5a0e61419d7..41e00dc88ca 100644 --- a/src/mouse_events.cpp +++ b/src/mouse_events.cpp @@ -39,7 +39,8 @@ namespace{ mouse_handler::mouse_handler(game_display* gui, std::vector& teams, unit_map& units, gamemap& map, gamestatus& status, undo_list& undo_stack, undo_list& redo_stack) : - mouse_handler_base(map), + mouse_handler_base(), + map_(map), gui_(gui), teams_(teams), units_(units), diff --git a/src/mouse_events.hpp b/src/mouse_events.hpp index f87ec3962a9..13a1a644252 100644 --- a/src/mouse_events.hpp +++ b/src/mouse_events.hpp @@ -88,6 +88,7 @@ protected: unit_map::iterator find_unit(const map_location& hex); bool unit_in_cycle(unit_map::const_iterator it); private: + gamemap& map_; game_display* gui_; std::vector& teams_; unit_map& units_; diff --git a/src/mouse_handler_base.cpp b/src/mouse_handler_base.cpp index 096cafaf6c8..9f87fd8a15a 100644 --- a/src/mouse_handler_base.cpp +++ b/src/mouse_handler_base.cpp @@ -45,7 +45,7 @@ static bool command_active() #endif } -mouse_handler_base::mouse_handler_base(gamemap& map) : +mouse_handler_base::mouse_handler_base() : minimap_scrolling_(false), dragging_left_(false), dragging_started_(false), @@ -54,8 +54,7 @@ mouse_handler_base::mouse_handler_base(gamemap& map) : drag_from_y_(0), drag_from_hex_(), last_hex_(), - show_menu_(false), - map_(map) + show_menu_(false) { } diff --git a/src/mouse_handler_base.hpp b/src/mouse_handler_base.hpp index d8ad83b2f76..34c023d926f 100644 --- a/src/mouse_handler_base.hpp +++ b/src/mouse_handler_base.hpp @@ -32,7 +32,7 @@ extern int commands_disabled; class mouse_handler_base { public: - mouse_handler_base(gamemap& map); + mouse_handler_base(); virtual ~mouse_handler_base() {} /** @@ -153,8 +153,6 @@ protected: /** Show context menu flag */ bool show_menu_; - gamemap& map_; - static const int drag_threshold_; }; diff --git a/src/play_controller.cpp b/src/play_controller.cpp index 143857bd861..398838f3ac5 100644 --- a/src/play_controller.cpp +++ b/src/play_controller.cpp @@ -925,7 +925,7 @@ std::string play_controller::get_action_image(hotkey::HOTKEY_COMMAND command, in return command_executor::get_action_image(command, index); } -hotkey::ACTION_STATE play_controller::get_action_state(hotkey::HOTKEY_COMMAND command) const +hotkey::ACTION_STATE play_controller::get_action_state(hotkey::HOTKEY_COMMAND command, int /*index*/) const { switch(command) { case hotkey::HOTKEY_DELAY_SHROUD: diff --git a/src/play_controller.hpp b/src/play_controller.hpp index c9a511cd9ba..fe9506b2881 100644 --- a/src/play_controller.hpp +++ b/src/play_controller.hpp @@ -87,7 +87,7 @@ protected: void post_mouse_press(const SDL_Event& event); virtual std::string get_action_image(hotkey::HOTKEY_COMMAND, int index) const; - virtual hotkey::ACTION_STATE get_action_state(hotkey::HOTKEY_COMMAND command) const; + virtual hotkey::ACTION_STATE get_action_state(hotkey::HOTKEY_COMMAND command, int index) const; /** Check if a command can be executed. */ virtual bool can_execute_command(hotkey::HOTKEY_COMMAND command, int index=-1) const; virtual bool execute_command(hotkey::HOTKEY_COMMAND command, int index=-1); diff --git a/src/playsingle_controller.cpp b/src/playsingle_controller.cpp index 2bbb47411a3..a3cf623b656 100644 --- a/src/playsingle_controller.cpp +++ b/src/playsingle_controller.cpp @@ -25,6 +25,7 @@ #include "gettext.hpp" #include "intro.hpp" #include "log.hpp" +#include "map_label.hpp" #include "marked-up_text.hpp" #include "sound.hpp" #include "upload_log.hpp" diff --git a/src/replay.cpp b/src/replay.cpp index 76a66c470c7..2b985d69522 100644 --- a/src/replay.cpp +++ b/src/replay.cpp @@ -26,6 +26,7 @@ #include "game_events.hpp" #include "log.hpp" #include "map.hpp" +#include "map_label.hpp" #include "menu_events.hpp" #include "replay.hpp" #include "statistics.hpp"