editor/map/map_context.cpp

Go to the documentation of this file.
00001 /* $Id: map_context.cpp 53522 2012-03-13 18:19:16Z fendrin $ */
00002 /*
00003    Copyright (C) 2008 - 2012 by Tomasz Sniatowski <kailoran@gmail.com>
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 as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 #define GETTEXT_DOMAIN "wesnoth-editor"
00016 
00017 #include "editor/action/action.hpp"
00018 #include "map_context.hpp"
00019 
00020 #include "display.hpp"
00021 #include "filesystem.hpp"
00022 #include "foreach.hpp"
00023 #include "gettext.hpp"
00024 #include "map_exception.hpp"
00025 #include "map_label.hpp"
00026 #include "serialization/binary_or_text.hpp"
00027 #include "serialization/parser.hpp"
00028 #include "wml_exception.hpp"
00029 
00030 
00031 #include "formula_string_utils.hpp"
00032 
00033 #include <boost/regex.hpp>
00034 
00035 
00036 namespace editor {
00037 
00038 const size_t map_context::max_action_stack_size_ = 100;
00039 
00040 map_context::map_context(const editor_map& map)
00041     : filename_()
00042     , map_data_key_()
00043     , embedded_(false)
00044     , map_(map)
00045     , undo_stack_()
00046     , redo_stack_()
00047     , actions_since_save_(0)
00048     , starting_position_label_locs_()
00049     , needs_reload_(false)
00050     , needs_terrain_rebuild_(false)
00051     , needs_labels_reset_(false)
00052     , changed_locations_()
00053     , everything_changed_(false)
00054 {
00055 }
00056 
00057 map_context::map_context(const config& game_config, const std::string& filename, const display& disp)
00058     : filename_(filename)
00059     , map_data_key_()
00060     , embedded_(false)
00061     , map_(game_config, disp)
00062     , undo_stack_()
00063     , redo_stack_()
00064     , actions_since_save_(0)
00065     , starting_position_label_locs_()
00066     , needs_reload_(false)
00067     , needs_terrain_rebuild_(false)
00068     , needs_labels_reset_(false)
00069     , changed_locations_()
00070     , everything_changed_(false)
00071 {
00072     log_scope2(log_editor, "Loading map " + filename);
00073     if (!file_exists(filename) || is_directory(filename)) {
00074         throw editor_map_load_exception(filename, _("File not found"));
00075     }
00076 
00077     std::string map_string = read_file(filename);
00078 
00079     if (map_string.empty()) {
00080         std::string message = _("Empty file");
00081         throw editor_map_load_exception(filename, message);
00082     }
00083 
00084     try {
00085         config file;
00086 		::read(file, map_string);
00087 
00088         map_ = editor_map(game_config, file, disp);
00089         return;
00090     } catch (config::error&) {
00091     }
00092 
00093     boost::regex re("data\\s*=\\s*\"(.+?)\"");
00094     boost::smatch m;
00095     if (boost::regex_search(map_string, m, re, boost::regex_constants::match_not_dot_null)) {
00096         boost::regex re2("\\{(.+?)\\}");
00097         boost::smatch m2;
00098         std::string m1 = m[1];
00099         if (boost::regex_search(m1, m2, re2)) {
00100             map_data_key_ = m1;
00101             LOG_ED << "Map looks like a scenario, trying {" << m2[1] << "}\n";
00102             std::string new_filename = get_wml_location(m2[1], directory_name(m2[1]));
00103             if (new_filename.empty()) {
00104                 std::string message = _("The map file looks like a scenario, "
00105                     "but the map_data value does not point to an existing file")
00106                     + std::string("\n") + m2[1];
00107                 throw editor_map_load_exception(filename, message);
00108             }
00109             LOG_ED << "New filename is: " << new_filename << "\n";
00110             filename_ = new_filename;
00111             map_string = read_file(filename_);
00112         } else {
00113             LOG_ED << "Loading embedded map file\n";
00114             embedded_ = true;
00115             map_string = m[1];
00116         }
00117     }
00118     if (map_string.empty()) {
00119         std::string message = _("Empty map file");
00120         throw editor_map_load_exception(filename, message);
00121     }
00122 
00123     map_ = editor_map::from_string(game_config, map_string, disp); //throws on error
00124 }
00125 
00126 map_context::~map_context()
00127 {
00128     clear_stack(undo_stack_);
00129     clear_stack(redo_stack_);
00130 }
00131 
00132 void map_context::draw_terrain(t_translation::t_terrain terrain,
00133     const map_location& loc, bool one_layer_only)
00134 {
00135     if (!one_layer_only) {
00136         terrain = map_.get_terrain_info(terrain).terrain_with_default_base();
00137     }
00138     draw_terrain_actual(terrain, loc, one_layer_only);
00139 }
00140 
00141 void map_context::draw_terrain_actual(t_translation::t_terrain terrain,
00142     const map_location& loc, bool one_layer_only)
00143 {
00144     if (!map_.on_board_with_border(loc)) {
00145         //requests for painting off the map are ignored in set_terrain anyway,
00146         //but ideally we should not have any
00147         LOG_ED << "Attempted to draw terrain off the map (" << loc << ")\n";
00148         return;
00149     }
00150     t_translation::t_terrain old_terrain = map_.get_terrain(loc);
00151     if (terrain != old_terrain) {
00152         if (terrain.base == t_translation::NO_LAYER) {
00153             map_.set_terrain(loc, terrain, gamemap::OVERLAY);
00154         } else if (one_layer_only) {
00155             map_.set_terrain(loc, terrain, gamemap::BASE);
00156         } else {
00157             map_.set_terrain(loc, terrain);
00158         }
00159         add_changed_location(loc);
00160     }
00161 }
00162 
00163 void map_context::draw_terrain(t_translation::t_terrain terrain,
00164     const std::set<map_location>& locs, bool one_layer_only)
00165 {
00166     if (!one_layer_only) {
00167         terrain = map_.get_terrain_info(terrain).terrain_with_default_base();
00168     }
00169     foreach (const map_location& loc, locs) {
00170         draw_terrain_actual(terrain, loc, one_layer_only);
00171     }
00172 }
00173 
00174 void map_context::clear_changed_locations()
00175 {
00176     everything_changed_ = false;
00177     changed_locations_.clear();
00178 }
00179 
00180 void map_context::add_changed_location(const map_location& loc)
00181 {
00182     if (!everything_changed()) {
00183         changed_locations_.insert(loc);
00184     }
00185 }
00186 
00187 void map_context::add_changed_location(const std::set<map_location>& locs)
00188 {
00189     if (!everything_changed()) {
00190         changed_locations_.insert(locs.begin(), locs.end());
00191     }
00192 }
00193 
00194 void map_context::set_everything_changed()
00195 {
00196     everything_changed_ = true;
00197 }
00198 
00199 bool map_context::everything_changed() const
00200 {
00201     return everything_changed_;
00202 }
00203 
00204 void map_context::clear_starting_position_labels(display& disp)
00205 {
00206     disp.labels().clear_all();
00207     starting_position_label_locs_.clear();
00208 }
00209 
00210 void map_context::set_starting_position_labels(display& disp)
00211 {
00212     std::set<map_location> new_label_locs = map_.set_starting_position_labels(disp);
00213     starting_position_label_locs_.insert(new_label_locs.begin(), new_label_locs.end());
00214 }
00215 
00216 void map_context::reset_starting_position_labels(display& disp)
00217 {
00218     clear_starting_position_labels(disp);
00219     set_starting_position_labels(disp);
00220     set_needs_labels_reset(false);
00221 }
00222 
00223 bool map_context::save()
00224 {
00225     //TODO the return value of this method does not need to be bool.
00226     //We either return true or there is an exception thrown.
00227 
00228     config data;
00229     map_.write(data);
00230 
00231     std::stringstream ss;
00232     {
00233         config_writer out(ss, false);
00234         out.write(data);
00235     }
00236 
00237     try {
00238         if (!is_embedded()) {
00239             std::stringstream ss;
00240             config_writer writer(ss, false);
00241             writer.write(data);
00242             write_file(get_filename(), ss.str());
00243         } else {
00244             std::string map_string = read_file(get_filename());
00245             boost::regex re("(.*)(map_data\\s*=\\s*\".+?\")(.*)");
00246             boost::smatch m;
00247             if (boost::regex_search(map_string, m, re, boost::regex_constants::match_not_dot_null)) {
00248                 std::stringstream ss2;
00249                 ss2 << m[1];
00250                 ss2 << ss.str();
00251                 ss2 << m[3];
00252                 write_file(get_filename(), ss2.str());
00253             } else {
00254                 //TODO that reg expression is still not working.
00255                 boost::regex re_maptag("(.*)([map].+?)([/map].*)");
00256                 boost::smatch match_maptag;
00257                 if (boost::regex_search(map_string, match_maptag, re_maptag, boost::regex_constants::match_not_dot_null)) {
00258                     std::stringstream ss3;
00259                     ss3 << match_maptag[1];
00260                     ss3 << ss.str();
00261                     ss3 << match_maptag[3];
00262                     write_file(get_filename(), ss3.str());
00263                 } else {
00264                     throw editor_map_save_exception(_("Could not save into scenario"));
00265                 }
00266             }
00267         }
00268         clear_modified();
00269     } catch (io_exception& e) {
00270         utils::string_map symbols;
00271         symbols["msg"] = e.what();
00272         const std::string msg = vgettext("Could not save the map: $msg", symbols);
00273         throw editor_map_save_exception(msg);
00274     }
00275     return true;
00276 }
00277 
00278 void map_context::set_map(const editor_map& map)
00279 {
00280     if (map_.h() != map.h() || map_.w() != map.w()) {
00281         set_needs_reload();
00282     } else {
00283         set_needs_terrain_rebuild();
00284     }
00285     map_ = map;
00286 }
00287 
00288 void map_context::perform_action(const editor_action& action)
00289 {
00290     LOG_ED << "Performing action " << action.get_id() << ": " << action.get_name()
00291         << ", actions count is " << action.get_instance_count() << "\n";
00292     editor_action* undo = action.perform(*this);
00293     if (actions_since_save_ < 0) {
00294         //set to a value that will make it impossible to get to zero, as at this point
00295         //it is no longer possible to get back the original map state using undo/redo
00296         actions_since_save_ = 1 + undo_stack_.size();
00297     }
00298     actions_since_save_++;
00299     undo_stack_.push_back(undo);
00300     trim_stack(undo_stack_);
00301     clear_stack(redo_stack_);
00302 }
00303 
00304 void map_context::perform_partial_action(const editor_action& action)
00305 {
00306     LOG_ED << "Performing (partial) action " << action.get_id() << ": " << action.get_name()
00307         << ", actions count is " << action.get_instance_count() << "\n";
00308     if (!can_undo()) {
00309         throw editor_logic_exception("Empty undo stack in perform_partial_action()");
00310     }
00311     editor_action_chain* undo_chain = dynamic_cast<editor_action_chain*>(last_undo_action());
00312     if (undo_chain == NULL) {
00313         throw editor_logic_exception("Last undo action not a chain in perform_partial_action()");
00314     }
00315     editor_action* undo = action.perform(*this);
00316     //actions_since_save_ += action.action_count();
00317     undo_chain->prepend_action(undo);
00318     clear_stack(redo_stack_);
00319 }
00320 bool map_context::modified() const
00321 {
00322     return actions_since_save_ != 0;
00323 }
00324 
00325 void map_context::clear_modified()
00326 {
00327     actions_since_save_ = 0;
00328 }
00329 
00330 bool map_context::can_undo() const
00331 {
00332     return !undo_stack_.empty();
00333 }
00334 
00335 bool map_context::can_redo() const
00336 {
00337     return !redo_stack_.empty();
00338 }
00339 
00340 editor_action* map_context::last_undo_action()
00341 {
00342     return undo_stack_.empty() ? NULL : undo_stack_.back();
00343 }
00344 
00345 editor_action* map_context::last_redo_action()
00346 {
00347     return redo_stack_.empty() ? NULL : redo_stack_.back();
00348 }
00349 
00350 const editor_action* map_context::last_undo_action() const
00351 {
00352     return undo_stack_.empty() ? NULL : undo_stack_.back();
00353 }
00354 
00355 const editor_action* map_context::last_redo_action() const
00356 {
00357     return redo_stack_.empty() ? NULL : redo_stack_.back();
00358 }
00359 
00360 void map_context::undo()
00361 {
00362     LOG_ED << "undo() beg, undo stack is " << undo_stack_.size() << ", redo stack " << redo_stack_.size() << "\n";
00363     if (can_undo()) {
00364         perform_action_between_stacks(undo_stack_, redo_stack_);
00365         actions_since_save_--;
00366     } else {
00367         WRN_ED << "undo() called with an empty undo stack\n";
00368     }
00369     LOG_ED << "undo() end, undo stack is " << undo_stack_.size() << ", redo stack " << redo_stack_.size() << "\n";
00370 }
00371 
00372 void map_context::redo()
00373 {
00374     LOG_ED << "redo() beg, undo stack is " << undo_stack_.size() << ", redo stack " << redo_stack_.size() << "\n";
00375     if (can_redo()) {
00376         perform_action_between_stacks(redo_stack_, undo_stack_);
00377         actions_since_save_++;
00378     } else {
00379         WRN_ED << "redo() called with an empty redo stack\n";
00380     }
00381     LOG_ED << "redo() end, undo stack is " << undo_stack_.size() << ", redo stack " << redo_stack_.size() << "\n";
00382 }
00383 
00384 void map_context::partial_undo()
00385 {
00386     //callers should check for these conditions
00387     if (!can_undo()) {
00388         throw editor_logic_exception("Empty undo stack in partial_undo()");
00389     }
00390     editor_action_chain* undo_chain = dynamic_cast<editor_action_chain*>(last_undo_action());
00391     if (undo_chain == NULL) {
00392         throw editor_logic_exception("Last undo action not a chain in partial undo");
00393     }
00394     //a partial undo performs the first action form the current action's action_chain that would be normally performed
00395     //i.e. the *first* one.
00396     boost::scoped_ptr<editor_action> first_action_in_chain(undo_chain->pop_first_action());
00397     if (undo_chain->empty()) {
00398         actions_since_save_--;
00399         delete undo_chain;
00400         undo_stack_.pop_back();
00401     }
00402     redo_stack_.push_back(first_action_in_chain.get()->perform(*this));
00403     //actions_since_save_ -= last_redo_action()->action_count();
00404 }
00405 
00406 void map_context::clear_undo_redo()
00407 {
00408     clear_stack(undo_stack_);
00409     clear_stack(redo_stack_);
00410 }
00411 
00412 void map_context::trim_stack(action_stack& stack)
00413 {
00414     if (stack.size() > max_action_stack_size_) {
00415         delete stack.front();
00416         stack.pop_front();
00417     }
00418 }
00419 
00420 void map_context::clear_stack(action_stack& stack)
00421 {
00422     foreach (editor_action* a, stack) {
00423         delete a;
00424     }
00425     stack.clear();
00426 }
00427 
00428 void map_context::perform_action_between_stacks(action_stack& from, action_stack& to)
00429 {
00430     assert(!from.empty());
00431     boost::scoped_ptr<editor_action> action(from.back());
00432     from.pop_back();
00433     editor_action* reverse_action = action->perform(*this);
00434     to.push_back(reverse_action);
00435     trim_stack(to);
00436 }
00437 
00438 } //end namespace editor
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:02:50 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs